运行helloworld程序,计算机做了什么?
#include <stdio.h>
int main(){
printf("helloworld!");
}
当写下这个程序,点击运行⏯️
这个过程计算机做了什么
一、编译系统
1.1 编译过程
在Unix系统上,由编译器把源文件转化为目标文件
gcc -c -o hello.c hello.o
gcc -o hello.o hello
-o:output
它的具体过程如下:
- 预处理阶段:处理以 # 开头的预处理命令;
- 编译阶段:翻译成汇编文件;
- 汇编阶段:将汇编文件翻译成可重定位目标文件;
- 链接阶段:将可重定位目标文件和 printf.o 等单独预编译好的目标文件进行合并,得到最终的可执行目标文件。
1.2 目标文件
- 可执行目标文件:可以直接在内存中执行;
- 可重定位目标文件:可与其它可重定位目标文件在链接阶段合并,创建一个可执行目标文件,如静态链接;
- 共享目标文件:这是一种特殊的可重定位目标文件,可以在运行时被动态加载进内存并链接,如动态链接;
1.3 链接
编译的过程面向的是计算机,链接的过程面向的是人。如果没有链接,人们就不得不每次把需要用到的代码编译一遍,还需要自己在代码中填写相关的物理地址。
链接分为静态链接和动态链接。
1.3.1 静态链接
静态链接器以一组可重定位目标文件为输入,生成一个完全链接的可执行目标文件作为输出。链接器主要完成以下两个任务:
- 符号解析:每个符号对应于一个函数、一个全局变量或一个静态变量,符号解析的目的是将每个符号引用与一个符号定义关联起来。
- 重定位:链接器通过把每个符号定义与一个内存位置关联起来,然后修改所有对这些符号的引用,使得它们指向这个内存位置。
1.3.2 动态链接
静态库有以下两个问题:
- 当静态库更新时那么整个程序都要重新进行链接;
- 对于 printf 这种标准函数库,如果每个程序都要有printf的代码,这会极大浪费资源。
共享库是为了解决静态库的这两个问题而设计的,在 Linux 系统中通常用 .so 后缀来表示,Windows 系统上它们被称为 DLL。它具有以下特点:
- 在给定的文件系统中一个库只有一个文件,所有引用该库的可执行目标文件都共享这个文件,它不会被复制到引用它的可执行文件中;
- 在内存中,一个共享库的 .text 节(已编译程序的机器代码)的一个副本可以被不同的正在运行的进程共享。
二、操作系统
以中断控制方式为例
2.1 进程的创建
编译系统执行之后,会形成一个hello的可执行文件,windows下后缀是.exe
-
用户告诉操作系统执行hello程序
-
操作系统到外存找到该程序
-
操作系统分配内存,将程序装入内存:当然这个过程当中还涉及到分页的一些内容
-
为执行hello程序创建执行环境(创建新进程 )
创建进程的准备工作:操作系统设置CPU上下文环境, 并跳到程序开始处
这个过程需要的主要寄存器是:
寄存器 | 含义 | 作用 |
---|---|---|
PC | 程序计数器 | 存放将要执行的指令地址 |
IR | 指令寄存器 | 存放正在执行的指令 |
AC | 累加器 | 临时存储体和累加操作 |
这里有两个主要问题:
-
指令:程序的执行并不是一条条语句,而是一条条指令,每条指令执行完CPU会检查是否发生中断
-
CPU上下文:即程序计数器和累加器的值,在发生中断的时候,就要求硬件存储这两个值
2.2 进程的执行
- 程序的第一条指令执行
- 程序执行与printf对应的库函数、系统调用
产生系统调用,就会陷入内核态,执行内核的read函数
这个时候会提出I/O请求,同时这个hello程序就阻塞了
2.3 进程的阻塞
为什么会阻塞?
因为申请I/O的过程中需要进程等待,驱动程序需要写调用设备的状态寄存器,之后设备控制器需要进行输出的准备。大概过程是:
- 操作系统分配设备:需要文件系统的按名访问来获得有关的信息,找到驱动程序
- 执行显示驱动程序
在设备控制器进行准备的过程中,CPU调度新的进程来投入运行,这样能提高CPU的利用率,CPU与I/O系统并行工作
2.4 进程的唤醒
I/O完毕之后,CPU接收中断,处理中断,从而唤醒发出I/O请求的进程,这个时候hello进程就转为就绪态。
这个要注意,是就绪态,而不是直接就运行态,CPU处理完中断之后不一定执行hello进程
这个过程需要采用CPU的调度算法
三、计算机硬件
- 将像素写入存储映像区(显存)
- 视频硬件将像素表示转换成一组模拟信号控制显示器(重画屏幕)
- 显示器发射电子束
- 显示屏上出现 hello world
在程序的执行过程中还有磁盘读取的一些操作
3.1 磁盘读文件的过程
数据在扇区
-
逻辑编号,磁盘的逻辑地址转化为物理地址
每个磁道分为很多扇区,用户文件的数据就存放在扇区上。获得扇区的编号:逻辑编号(也叫做磁盘的逻辑地址)操作系统需要把逻辑编号转换为物理地址。
物理地址的构成:柱面号:半径相同的构成一个柱面;磁头号;扇区号
-
送寄存器的相关值
函数需要得到柱面号、磁头号和起始扇区号。然后将三者放到固定的寄存器中去。一旦启动中断,就开始了硬件上文件的读操作。
操作系统实质上在做什么?写寄存器、调用中断
3.2 设备控制器
主机I/O系统的结构是:
计算机 ------- I/O通道 -------- 控制器 ---------设备
设备控制器的共性部分:
I/O逻辑需要将命令和地址译码,驱动外设产生动作
参考
[1]:https://github.com/CyC2018/CS-Notes
[2]:https://my.oschina.net/hosee/blog/673628?p=%7b%7bcurrentPage+1%7d%7d