Linux进程创建与管理
在Linux系统中,除了系统启动之后的第一个进程由系统来创建,其余的进程都必须由已存在的进程来创建,新创建的进程叫做子进程,而创建子进程的进程叫做父进程。那个在系统启动及完成初始化之后,Linux自动创建的进程叫做根进程。根进程是Linux中所有进程的祖宗,其余进程都是根进程的子孙。具有同一个父进程的进程叫做兄弟进程
1. 创建子进程fork()
使用fork()系统调用可以创建一个子进程
首先要明白一个概念:进程控制块 PCB (Process Control Block),它是描述进程状态、资源、和相关进程关系的一种数据结构(比如进程状态、调度信息、标识符等等),是进程的标志,与进程共存亡。
进程=程序+PCB
首先来看个小栗子:
1 |
|
程序的运行结果为:
然后分析
- 使用fork()函数在一个进程中分裂出一个子进程,主要工作就是创建了一个新的控制块PCB,并未在内存中给子进程配置独立的程序运行空间,而只是简单地将程序指针指向父进程的代码,所以①处是父进程执行的结果,而子进程不会再执行了,所以只会输出一遍count1 = 1, count2 = 1。
- 两个进程具有各自的数据区和用户堆栈,在函数fork()生成子进程时,将父进程数据区和用户堆栈的内容分别复制给了子进程。同时,接下来的内容,父进程和子进程都是对自己的数据区和堆栈中的内容进行修改运算了,所以父子进程输出的都是count1 = 2, count2 = 2,即②③所示。
- 在子进程中
pid = 0
(没啥理由~),父进程中pid保存的是创建好的子进程的进程号,即④⑤所示。
2. 查看进程号getpid(), getppid()
linux下,getpid()可以返回当前进程进程号, getppid()可以返回当前进程父进程号
看下面这个栗子:
1 |
|
程序执行结果:
可以看到创建的子进程的父进程pid为157,即创建它的父进程号,如果父进程已经结束,那么子进程的gitppid()会是多少?
下面这个栗子,子进程sleep 2秒钟,此时父进程以及结束,可以看到此时父进程id为1,为init()进程。
1 |
|
3. 终结进程exit()
如果一个进程调用exit(),那么这个进程会立即退出运行,并负责释放被中止进程除了进程控制块之外的各种内核数据结构。这种只剩下“身份证”的进程叫做“僵尸进程”。
exit(int status)就是退出,传入的参数是程序退出时的状态码,0表示正常退出,其他表示非正常退出,一般都用-1或者1。父进程可以使用wait()函数获取,wait函数可以参考后面。
看下面这个栗子
1 |
|
输出结果如下
4. 进程的阻塞wait()
wait()函数功能是:父进程一旦调用了wait就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止
这里给出僵尸进程和孤儿进程的概念
- 孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作;
- 僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程(也就是进程为中止状态,僵死状态)。
wait函数原型:pid_t wait(int * status);
返回值为子进程的pid(失败返回-1),status用于获取子进程的状态码。下图是status后16位的划分,正常退出低8位为0,8~16为退出状态(0~255)
下面这个栗子,父进程就会等待子进程结束再结束~
1 |
|