linux子进程的创建和回收
子进程的创建
我们可以采用以下方式循环的创建多个子进程
1 |
|
上述创建方式的问题
子进程没有被父进程回收
父进程先于子进程结束,子进程变成了孤儿进程。
在linux系统中,孤儿进程会被init进程所收养。并由init进程负责回收。
在上述创建的过程中,可能会存在着下面这种情况。
我们发现,该程序的输出结果夹在了两条bash之间,这是因为thread.out这个进程是bash的子进程。bash在发现该进程结束后,就继续输出了。但是此时thread.out这个进程的子进程还没有结束。因此我们需要让thread.out这个父进程在其子进程结束之后结束。因此我们需要引入wait函数。
子进程的回收
在上述例子中,子进程变成了孤儿进程。被init进程所收养和回收。但更好的方式是我们通过父进程的调用来手动回收子进程。此时便引入了wait和waitpid函数。
wait函数
1 | pid_t wait(int * status) |
参数说明:
pid_t——回收进程的pid (失败为-1)
status——回收进程的状态
status这一变量可通过调用不同的宏来读取,可以用来判断子进程是否为正常终止,或者异常终止的信号是多少。
注意:这一函数仅能回收一个子进程。如果要回收多个子进程的话,需要用while来循环调用。
status的使用
1.判断是否为正常结束
1 | WIFEXITED(status) |
若程序为正常退出(通过exit() ,return from main()等方式),则WIFEXITED返回true
如果程序异常终止,被信号终止(例如kill -9),那么WIFSIGNALED会返回true
2.获取结束值
1 | WEXITSTATUS(status) |
WEXITSTATUS在子进程正常退出的情况下调用,会得到子进程的返回值
WTERMSIG在子进程异常退出的情况下调用,会得到子进程异常终止的信息编号。(例如被kill -9终止,则会返回9)
3.过程总结
获取子进程正常终止值:
WIFEXITED(status) ——> 为真 ——>调用 WEXITSTATUS(status) ——> 得到 子进程 退出值。
获取导致子进程异常终止信号:
WIFSIGNALED(status) ——> 为真——>调用 WTERMSIG(status——> 得到 导致子进程异常 终止的信号编号
waitpid函数
1 | pid_t waitpid(pid_t pid, int *status, int options) |
作用和wait函数相同,但可以指定某一个进程回收,并且可以选择不阻塞。
参数说明
参数pid:
> 0:待回收的子进程pid
-1:任意子进程
0:同组的子进程(?)
status:同wait
options:可指定为WNOHANG,表示回收方式为非阻塞。若指定为0,则同wait(NULL),表示阻塞等待
返回值pid_t:
- > 0 : 成功回收的子进程 pid
- 0 : 函数调用时, option指定了WNOHANG, 并且在函数调用时所指定的子进程没有结束。
- -1: 指定的进程已经被回收,回收失败。(当指定的进程为任意子进程的时候,就说明该父进程已经没有任何的子进程了)
循环回收子进程
1 |
|
补充
僵尸进程
子进程结束后,父进程尚未做出回收操作,子进程其他资源虽然已经被系统回收(会关闭所有文件描述符,释放内存等),但是PCB依旧保留于内核中,使子进程变成僵尸进程。
每一个子进程都会经历僵尸进程的阶段,即已经结束但还未被回收的时间。僵尸进程在系统的进程列表中会被明确的用方括号和
虽然每个进程都会经历僵尸进程的阶段,但是若一直不被父进程回收也是会存在着问题的。此时可以kill父进程。从而使子进程被init进程收养,由init进程负责回收
Q:为什么在进程结束后PCB仍然留在内核中
A:方便父进程判断子进程结束的方式和状态等,同时也可以接收子进程的返回值。如果是正常终止则保留着推出状态,如果是异常终止,则保留着使该进程终止的信号,方便父进程依此做出决策。