摘要:进程是操作系统的核心概念,这块主要讨论Linux的进程模型,包括有进程的创建、销毁以及进程的状态迁移。
1:Linux中的进程
进程是一个程序正在执行的实例。每个进程都有自己的地址空间与执行状态。进程必须有一个PID(Process ID进程标识),以便操作系统能够区分各个不同的进程。(就像每个人都有身份证号一样)
一个进程需要具有以下核心要素:
(1)程序映像:二进制指令序列
(2)地址空间:用于存放程序和执行程序
(3)PCB(Process Control Block):内核中描述进程的主要数据结构。
2:创建进程
Linux系统中的进程间具有父子关系。其中init进程是系统启动后创建的第一个用户态进程,是其他进程的祖先。
一个进程有且只能有一个父进程,但是可以有多个子进程。
Linux系统中创建一个子进程的方法有fork,vfork,clone(Linux所特有)。考虑到移植行,不建议使用clone
1)fork系统调用的函数原型
#include<unistd.h>
pid_t fork(void);
fork函数的返回值是需要关注的重点,从返回值的类型判断是父进程还是子进程:
(1)返回值为-1:表示创建进程失败
(2)父进程中返回非零值,该值是其子进程的进程ID
(3)子进程中永远返回0.
fork系统调用生成的子进程几乎完全克隆了父进程的一切特性,包括虚拟地址空间和执行进度(当然两个进程的进程ID肯定是不同的)。
2)vfork系统调用的函数原型
#include<sys/types.h>
#include<unistd.h>
pid_t vfork(void);
vfork系统调用与fork基本是一样的,但是用它创建了子进程后并不完全复制父进程的虚拟地址空间。
使用vfork时,父进程会一直阻塞,直到子进程退出或调用exec执行新程序,在子进程中最好不要修改任何全局变量,因为实际上操作系统并没有为这些变量分配物理内存。
现在的操作系统中,fork调用都使用了所谓的“写时复制”技术,因此fork和vfork的工作效率几乎是一样的了。
3:进程的状态迁移
进程被创建后就处于可运行的状态,称为就绪态,当内核的调度器选择了这个进程并让它在某个CPU上执行时,它才真正被执行,这时称为执行态。内核也可以让执行态的进程重新回到就绪态。此外,进程还有一种状态为睡眠态,在这种情况下,进程不仅停止执行,而且在其未被唤醒到就绪态时,是不会直接转到执行态的。
4:进程的终止
从应用程序的角度看,进程的终止可分为自愿终止和*终止。
1)自愿终止是指应用程序中主动调用了执行退出过程的系统调用而结束,可以通过exit函数来实现
#include<stdlib.h>
void exit(int status);
2)*终止是指应用程序中没有主动退出进程的系统调用而被内核强制终止的情形。
3)当一个进程终止时,内核会通知其父进程,在父进程进行处理前,这个进程成为所谓的僵尸进程,它所占的资源已被回收,但进程描述符仍然存在,以便父进程获取它的退出状态。父进程可以使用wait函数或waitpid函数获取子进程的退出状态。
4)如果父进程在子进程退出前就已经退出,这时其子进程称为孤儿进程。