Linux进程_03
6.进程结束时的资源问题
- 进程的结束分为正常终止(运行完后正常退出)和非正常终止(发生某种异常如访问非法内存等异常退出,被kill而退出)。
- 进程在运行时需要消耗系统资源(内存、IO),进程终止时理应完全释放这些资源。
- 在Linux系统中,每一个进程退出时,操作系统会自动回收这个进程涉及到的所有的资源(比如malloc申请的内存没有free,当前进程结束时这个内存会被释放。比如open打开的文件没有close,在程序终止时这个文件也会被关闭)。但是操作系统只会回收这个进程工作时消耗的内存和IO,并不会回收这个进程本身占用的内存(8KB左右,主要是描述此进程的task_struct和栈内存)。
- 因为进程本身的8KB内存操作系统不能回收,需要别人来辅助回收,这个人就是这个进程的父进程。
6.1僵尸进程
- 子进程先于父进程结束但是父进程还没有回收它的资源,这时候子进程就是僵尸进程状态。 子进程处于僵尸进程状态时除task_struct和栈外其余内存空间都已经被操作系统清理。
- 父进程可以使用wait或waitpid显式回收子进程的剩余待回收内存资源并且获取子进程退出状态。 父进程也可以不使用wait或者waitpid回收子进程,父进程即将结束时一样会回收已经终止的子进程的剩余待回收内存资源(这样是为了防止父进程忘记显式调用wait/waitpid来回收子进程从而造成内存泄漏)。
6.2孤儿进程
- 父进程先于子进程结束,子进程成为一个孤儿进程。
- 在Linux系统中所有的孤儿进程都自动成为一个特殊进程(进程1,也就是init进程)的子进程。
7.父进程wait()和waitpid()回收子进程实践
7.1wait()原理
- 父子进程之间是异步的。子进程结束时,操作系统会自动向其父进程发送一个SIGCHILD信号。
- 父进程调用wait函数后阻塞等待SIGCHILD信号,被信号唤醒然后去回收僵尸子进程。SIGCHILD信号机制就是为了解决父子进程之间的异步通信问题,让父进程可以及时的去回收僵尸子进程。
- 若父进程没有任何子进程则wait返回错误。
- wait()主要是用来回收子进程资源,回收同时还可以得知被回收子进程的pid和退出状态。
wait()函数:
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
参数status用来返回子进程结束时的状态,父进程通过wait得到status后就可以知道子进程的一些结束状态信息。返回值pid_t就是本次wait()回收的子进程的PID。当前进程有可能有多个子进程,wait函数阻塞直到其中一个子进程结束wait()就会返回,wait()的返回值就可以用来判断到底是哪一个子进程本次被回收了。
下面几个宏可以用来获取子进程的退出状态:
宏 | 状态 |
---|---|
WIFEXITED宏 | 判断子进程是否正常终止(return,exit,_exit) |
WIFSIGNALED宏 | 判断子进程是否非正常终止(被信号所终止) |
WEXITSTATUS宏 | 得到正常终止情况下的进程返回值 |
7.2wait()函数实践
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char**argv)
{
pid_t pid = -1;
pid_t ret = -1;
int status = -1;
pid = fork();
if (pid > 0)
{
// 此进程为父进程
printf("parent.\n");
ret = wait(&status);
printf("子进程已经被回收,子进程pid = %d.\n", ret);
printf("子进程是否正常终止退出:%d\n", WIFEXITED(status));
printf("子进程是否非正常被信号终止退出:%d\n", WIFSIGNALED(status));
printf("正常终止的终止值是:%d.\n", WEXITSTATUS(status));
}
else if (pid == 0)
{
// 此进程为子进程
printf("child pid = %d.\n", getpid());
return 33;
}
else
{
perror("fork");
return -1;
}
return 0;
}
运行结果:
peco@ubuntu:~/linux_pro$ gcc wait.c
peco@ubuntu:~/linux_pro$ ./a.out
parent.
child pid = 26723.
子进程已经被回收,子进程pid = 26723.
子进程是否正常终止退出:1
子进程是否非正常被信号终止退出:0
正常终止的终止值是:33.
7.3waitpid()函数
- wait()和waitpid()基本功能一样,都是用来回收子进程的。
- wait()回收所有已经终止的子进程,而waitpid()可以回收指定PID的子进程,waitpid可以阻塞式或非阻塞式两种工作模式。
#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *status, int options);
7.4waitpid()函数实践:
使用waitpid实现wait回收子进程的效果:
ret = waitpid(-1, &status, 0);
-1表示不等待某个特定PID的子进程而是回收任意一个终止的子进程,0表示用默认的方式(阻塞式)来进行等待,返回值ret是本次回收的子进程的PID。
阻塞式回收指定子进程:
ret = waitpid(pid, &status, 0);
等待回收PID为pid的这个子进程,如果当前进程并没有一个ID号为pid的子进程,则返回值为-1;如果成功回收了pid这个子进程则返回值为回收的进程的PID。
非阻塞式回收指定子进程:
ret = waitpid(pid, &status, WNOHANG);
在这种情况下如果父进程执行waitpid时子进程已经先结束等待回收则waitpid直接回收成功,返回值是回收的子进程的PID;如果父进程waitpid时子进程尚未结束则父进程立刻返回(非阻塞),但是返回值为0(表示回收不成功)。