3. 进程管理
创建进程
在Linux中创建进程通常有两个目的:
- 将同一个程序分成多个进程进行处理(例如,使用Web服务器接收多个请求)
- 创建另一个程序(例如,从bash启动一个新的程序)
为了达成这两个目的,Linux提供了fork()函数与execve()函数(其底层分别请求名为clone()与execve()的系统调用)
fork函数
fork函数可以让同一个程序分成多个进程进行处理。在调用fork()函数后,就会基于发起调用的进程,创建一个新的进程。发出请求的进程称为父进程,新创建的进程称为子进程。

创建新进程的流程如下:
-
为子进程申请内存空间,并复制父进程的内存到子进程的内存空间
-
父进程与子进程分裂成两个进程,以执行不同的代码。实现依赖于fork()函数分别返回不同的值给父进程和子进程。
- fork()会返回0给子进程
- fork()会返回新创建的子进程id给父进程
execve函数
启动另一个程序的流程:
- 读取可执行文件,并读取创建进程的内存映像所需的信息
- 包括代码的代码段在文件中的偏移量,大小,以及内存映像的起始地址
- 包含代码以外的变量等数据的数据段在文件中的偏移量,大小,以及内存映像的起始地址
- 程序执行的第一条指令的内存地址(入口点)
![]()
-
用新进程的数据覆盖当前进程的内存
基于读取的信息,将程序映射到内存上。
![]()
-
从最初的命令开始运行新的进程
在启动另一个程序时,并非新增一个进程,而是替换了当前进程

Linux的可执行文件的结构遵循的是ELF(Executable and Linkable Format)的格式,其相关信息可以通过readelf命令来获取。
-
-h选项:可以获取起始地址
![]()
-
-S选项:可以获取代码段与数据段在文件中的偏移量,大小和起始位置
- 输出的数据每两行为一组
- 全部数值皆为十六进制数
- 在每组的第一行的第2个字段中,.text对应的是代码段的信息,.data对应的是数据段的信息
- 在这些信息中,对于每组输出,只需要关注以下内容即可:
- 每组的第1行的第4个字段 address:内存映像的起始地址
- 每组的第1行的第5个字段 offset:在文件中的偏移量
- 每种的第2行的第一个字段 size: 大小

# 获取/bin/sleep的elf信息
readelf -h /bin/sleep
在程序运行时创建的进程的内存映像信息,可以从/proc/{pid}/maps这个文件中找到。
在打算新建一个别的进程时,通常采用被称为fork and exec的方式,即由父进程调用fork()创建子进程,再由子进程调用exec()。

结束进程
可以使用_exit()函数(底层发起exit_group()系统调用)来结束进程。在进程运行结束后,所有分配给进程的内存将被回收。

不过,通常我们很少会直接调用_exit()函数,而是通过调用C标准库的exit()函数结束进程的运行。




浙公网安备 33010602011771号