[TOC]
## 环境准备
### 虚拟机
这里我使用的虚拟机是:[virtualbox](https://www.virtualbox.org/wiki/Downloads)
![](https://img.kancloud.cn/57/f7/57f79fbd0287f3540d67d23fba1e2848_1281x629.png)
### 镜像
镜像使用的[Ubuntu 20.04.2.0 LTS](http://releases.ubuntu.com/20.04/)
![](https://img.kancloud.cn/33/2a/332a6f0c3d68e438bf965347ae8322f4_1284x687.png)
关于virtualbox环境设置没什么说的,网络记得启用并且使用桥接模式即可
![](https://img.kancloud.cn/c6/11/c611bdec9462dbcc4d29bf3a8cfa50c2_863x569.png)
### 安装gcc,vim,git
这个就很简单了三条命令
`sudo apt-get update`
`sudo apt-get install gcc`
`sudo apt-get install vim`
`sudo apt-get install git`
## 开始编码
### HelloWord
还是从C语言最开始的HelloWorld开始
```c
#include "stdio.h"
int main(int argc, char const *argv[])
{
printf("Hello World!\n");
return 0;
}
```
![](https://img.kancloud.cn/30/dc/30dcbfb84151099de6faf38527000ddb_414x162.png)
### 程序编码过程
使用gcc编译这段代码
`gcc HelloWorld.c -o HelloWorld`
然后执行发现执行成功
![](https://img.kancloud.cn/03/f9/03f916cdf95c31609129f350c1e7b9ea_690x156.png)
gcc -o 只是完成编译工作的驱动程序,它会根据编译流程分别调用**预处理程序**、**编译程序**、**汇编程序**、**链接程序**来完成具体工作。
接下来对比着图片按照每一个步骤编译一下这个程序来提升一下对这个过程的理解:
![](https://img.kancloud.cn/ac/85/ac8576000382550178084aeab2022886_3015x2410.png)
#### 源文件生成预处理文件,加入头文件,替换宏。
`gcc -E HelloWorld.c -o HelloWorld.i `
#### 预处理文件生成编译文件
`gcc -S HelloWorld.i -o HelloWorld.s `
#### 编译文件生成汇编文件:
`gcc -c HelloWorld.s -o HelloWorld.o `
#### 汇编文件生成可执行文件:
`gcc HelloWorld.o -o HelloWorld `
#### 源文件生成可执行文件:
`gcc HelloWorld.c -o HelloWorld `
#### Linux系统运行可执行文件:
`./HelloWorld`
### 程序装载执行
在讲到这里的时候彭东老师提到了图灵机和冯诺依曼,这里我把中专栏的图片贴出来,我感觉这两个概念只要理解即可,所以不做深入学习,理解即可,他们只是计算机形成过程中的几个基础理论或者说必要条件。![](https://img.kancloud.cn/88/07/8807afe9e6991dff6e0caee41a26488a_1386x1026.png)
### 更形象地将 HelloWorld 程序装入原型计算机
#### 反汇编特定指令机器码
使用**objdump**从objfile中反汇编那些特定指令机器码的section
`objdump -d HelloWorld`
![](https://img.kancloud.cn/41/15/41155454015deb996b0791c9e7a8dcad_701x287.png)
第一列为地址;
第二列为十六进制,表示真正装入机器中的代码数据;
第三列是对应的汇编代码;
第四列是相关代码的注释。这是 x86_64 体系的代码,由此可以看出 x86 CPU 是变长指令集。
#### 将代码装入上面图灵机+冯诺依曼体系结构
![](https://img.kancloud.cn/27/f1/27f11e2facb7088a65434606e2f07656_3810x1815.png)
#### 以上知识涉及到知识盲区查阅的资料
##### 汇编中的栈帧理解
###### 基本概念
引用百度百科的介绍:C语言中,每个栈帧对应着一个未运行完的函数。栈帧中保存了该函数的返回地址和局部变量。栈帧也叫过程[活动记录](https://baike.baidu.com/item/%E6%B4%BB%E5%8A%A8%E8%AE%B0%E5%BD%95),是[编译器](https://baike.baidu.com/item/%E7%BC%96%E8%AF%91%E5%99%A8)用来实现过程/[函数调用](https://baike.baidu.com/item/%E5%87%BD%E6%95%B0%E8%B0%83%E7%94%A8)的一种数据结构。可以理解为:栈帧就是存储在用户栈上的(当然内核栈同样适用)每一次函数调用涉及的相关信息的记录单元。
###### 分析栈帧的记录活动
简单用c写个函数调用的demo
![](https://img.kancloud.cn/4a/56/4a565f2c1b4fbad02e9e5e77330a2334_382x251.png)
使用objdump命令反汇编看一下
![](https://img.kancloud.cn/47/4a/474a822dded52293e79cf10152094132_859x490.png)
> x86\_64通用寄存器
> %rax 通常用于存储函数调用的返回结果,同时也用于乘法和除法指令中。
> %rsp 是堆栈指针寄存器,通常会指向栈顶位置
> %rbp 是栈帧指针寄存器,用于标识当前栈帧的起始位置
> %rdi,%rsi,%rdx,%rcx,%r8,%r9 用来传递函数参数,依次对应第1参数,第2参数至第6参数
> %rbx,%r12,%r13,%14,%15 ,%r10,%r11 用作数据存储,属于通用性更为广泛的寄存器,编译器或汇编程序可以根据需要存储任何数据。
![](https://img.kancloud.cn/24/ab/24ab569ffffbf3a5861a51ac29e4304a_1695x648.png)
## 课后思考题
为了实现 C 语言中函数的调用和返回功能,CPU 实现了函数调用和返回指令,即上图汇编代码中的“call”,“ret”指令,请你思考一下:call 和 ret 指令在逻辑上执行的操作是怎样的呢?
经过上面的代码分析,已经可以解决这个问题了,下面附一些精彩留言以供参考。
> 针对第一个问题,在gcc编译完成之后,函数对应的指令序列所在的位置就已经确定了,因此这是编译阶段需要考虑的问题 。
> 至于第二个问题,在执行完call指令的同时,需要将call指令下面一条指令的地址保存到栈内存中,同时更新%rsp寄存器指向的位置,然后就可以开始执行被调函数的指令序列,执行完毕后,由ret指令从rsp中获取栈顶的returnadress地址,然后跳转到call的下一条指令继续执行
>call和ret其实是一对相反指令,调用call时会将当前IP入栈,即push IP,然后执行跳转即jmp,而ret也是将栈中的IP推出写入IP寄存器,即pop IP。
- 微服务
- 服务器相关
- 操作系统
- 极客时间操作系统实战笔记
- 01 程序的运行过程:从代码到机器运行
- 02 几行汇编几行C:实现一个最简单的内核
- 03 黑盒之中有什么:内核结构与设计
- Rust
- 入门:Rust开发一个简单的web服务器
- Rust的引用和租借
- 函数与函数指针
- Rust中如何面向对象编程
- 构建单线程web服务器
- 在服务器中增加线程池提高吞吐
- Java
- 并发编程
- 并发基础
- 1.创建并启动线程
- 2.java线程生命周期以及start源码剖析
- 3.采用多线程模拟银行排队叫号
- 4.Runnable接口存在的必要性
- 5.策略模式在Thread和Runnable中的应用分析
- 6.Daemon线程的创建以及使用场景分析
- 7.线程ID,优先级
- 8.Thread的join方法
- 9.Thread中断Interrupt方法学习&采用优雅的方式结束线程生命周期
- 10.编写ThreadService实现暴力结束线程
- 11.线程同步问题以及synchronized的引入
- 12.同步代码块以及同步方法之间的区别和关系
- 13.通过实验分析This锁和Class锁的存在
- 14.多线程死锁分析以及案例介绍
- 15.线程间通信快速入门,使用wait和notify进行线程间的数据通信
- 16.多Product多Consumer之间的通讯导致出现程序假死的原因分析
- 17.使用notifyAll完善多线程下的生产者消费者模型
- 18.wait和sleep的本质区别
- 19.完善数据采集程序
- 20.如何实现一个自己的显式锁Lock
- 21.addShutdownHook给你的程序注入钩子
- 22.如何捕获线程运行期间的异常
- 23.ThreadGroup API介绍
- 24.线程池原理与自定义线程池一
- 25.给线程池增加拒绝策略以及停止方法
- 26.给线程池增加自动扩充,闲时自动回收线程的功能
- JVM
- C&C++
- GDB调试工具笔记
- C&C++基础
- 一个例子理解C语言数据类型的本质
- 字节顺序-大小端模式
- Php
- Php源码阅读笔记
- Swoole相关
- Swoole基础
- php的五种运行模式
- FPM模式的生命周期
- OSI网络七层图片速查
- IP/TCP/UPD/HTTP
- swoole源代码编译安装
- 安全相关
- MySql
- Mysql基础
- 1.事务与锁
- 2.事务隔离级别与IO的关系
- 3.mysql锁机制与结构
- 4.mysql结构与sql执行
- 5.mysql物理文件
- 6.mysql性能问题
- Docker&K8s
- Docker安装java8
- Redis
- 分布式部署相关
- Redis的主从复制
- Redis的哨兵
- redis-Cluster分区方案&应用场景
- redis-Cluster哈希虚拟槽&简单搭建
- redis-Cluster redis-trib.rb 搭建&原理
- redis-Cluster集群的伸缩调优
- 源码阅读笔记
- Mq
- ELK
- ElasticSearch
- Logstash
- Kibana
- 一些好玩的东西
- 一次折腾了几天的大华摄像头调试经历
- 搬砖实用代码
- python读取excel拼接sql
- mysql大批量插入数据四种方法
- composer好用的镜像源
- ab
- 环境搭建与配置
- face_recognition本地调试笔记
- 虚拟机配置静态ip
- Centos7 Init Shell
- 发布自己的Composer包
- git推送一直失败怎么办
- Beyond Compare过期解决办法
- 我的Navicat for Mysql
- 小错误解决办法
- CLoin报错CreateProcess error=216
- mysql error You must reset your password using ALTER USER statement before executing this statement.
- VM无法连接到虚拟机
- Jetbrains相关
- IntelliJ IDEA 笔记
- CLoin的配置与使用
- PhpStormDocker环境下配置Xdebug
- PhpStorm advanced metadata
- PhpStorm PHP_CodeSniffer