一、进程的概念与 Linux 中的描述
进程是正在运行的程序的实例,包含了程序的代码、数据、堆栈等资源。在 Linux 中,通过 pcb
(进程控制块)即 struct task_struct
来描述进程的状态和信息。
二、进程的一生
(一)进程创建 - fork
通过 fork
函数创建子进程,创建后父子进程各自拥有 4G 独立的内存空间,包括数据段、堆、栈、正文段等。子进程会继承父进程已打开的文件描述符,但父子进程对数据的修改相互独立。
(二)进程执行
- 子承父业:子进程与父进程执行相似的任务。
- 独立创业:通过
fork
结合exec
函数族,子进程可以执行全新的程序。
(三)进程退出
进程退出有多种情况:
main
函数中使用return
。- 调用
exit
库函数,会执行IO
库的清理工作,关闭所有流和打开的文件,并注册清理函数atexit
。 - 调用
_exit
或_Exit
系统调用,会关闭所有已打开的文件,但不执行清理函数。
异常终止的情况包括:
abort
函数调用。- 收到
signal
信号或kill
命令。
三、进程的特殊状态
(一)僵尸进程
当子进程先结束,而父进程未做收尸操作时,子进程进入僵尸态。此时,子进程的空间未被回收,可通过 wait
或 waitpid
函数查看子进程的退出状态并进行资源回收。
wait
函数本身是阻塞的,用于等待子进程的终止并回收其状态。
waitpid
可以以非阻塞的方式实现进程资源回收。
wait
函数的行为如下:
- 若所有子进程都在运行,会阻塞等待。
- 若有子进程终止,父进程获取终止状态后立刻返回。
- 若没有子进程,立即出错退出。
waitpid
函数中,pid
的取值有不同含义:
< -1
:回收指定进程组内的任意子进程。-1
:回收任意子进程,不分组内外。0
:回收和当前调用waitpid
同组的所有子进程。> 0
:回收指定 ID 的子进程。
且 waitpid(-1, status, 0)
等价于 wait(status)
。
(二)孤儿进程
若子进程还在但父进程已经结束,为避免子进程无人收尸,将由 init
进程收养子进程。
四、进程间通信(IPC)
父子进程空间相互独立,这在保障安全的同时也带来了通信的不便,需要特定的 IPC 机制来实现进程间的有效通信。
五、exec
函数族中的不同函数
execl
和execv
:参数传递方式不同,execl
是逐个列举参数,execv
是通过指针数组传递参数。execlp
和execvp
:可执行文件的寻找方式基于系统环境变量PATH
。execle
和execvpe
:不仅能从系统环境变量PATH
寻找可执行文件,还能为新程序传递所需的环境变量。
六、进程创建效率与切换效率
vfork
函数用于提高进程创建的效率,它会阻塞父进程,让子进程先执行,并采用写时拷贝的策略减少不必要的数据拷贝。- 进程的调度涉及进程上下文的切换,包括进程状态的保存和恢复,数据量较大,对切换效率有一定影响。