惠东网站设计,郑州营销型网站制作策划,上海注销公司需要什么资料和流程,百度搜索链接参考资料#xff1a;《Linux环境编程#xff1a;从应用到内核》
僵尸进程
进程退出时会进行内核清理#xff0c;基本就是释放进程所有的资源#xff0c;这些资源包括内存资源、文件资源、信号量资源、共享内存资源#xff0c;或者引用计数减一#xff0c;或者彻底释放。…参考资料《Linux环境编程从应用到内核》
僵尸进程
进程退出时会进行内核清理基本就是释放进程所有的资源这些资源包括内存资源、文件资源、信号量资源、共享内存资源或者引用计数减一或者彻底释放。不过进程的退出其实并没有将所有的资源完全释放仍保留了少量的资源比如进程的PID依然被占用着不可被系统分配。此时的进程不可运行事实上也没有地址空间让其运行进程进入僵尸状态。
僵尸进程依然保留的资源有进程控制块task_struct、内核栈等。这些资源不释放是为了提供一些重要的信息比如进程为何退出是收到信号退出还是正常退出进程退出码是多少进程一共消耗了多少系统CPU时间多少用户CPU时间收到了多少信号发生了多少次上下文切换最大内存驻留集是多少产生多少缺页中断等等。
父进程通过fork()函数创建子进程后子进程退出但父进程没有调用wait()或waitpid()回收子进程的资源的话这个时候子进程变成僵尸进程。
清除僵尸进程有以下两种方法
父进程调用wait函数或waitpid函数为子进程“收尸”。父进程退出init进程会为子进程“收尸”。
一般而言系统不希望大量进程长期处于僵尸状态因为会浪费系统资源。除了少量的内存资源外比较重要的是进程ID。僵尸进程并没有将自己的进程ID归还给系统而是依然占有这个进程ID因此系统不能将该ID分配给其他进程。
如果我们不关心子进程的退出状态就应该将父进程对SIGCHLD的处理函数设置为SIG_IGN或者在调用sigaction函数时设置SA_NOCLDWAIT标志位。这两者都会明确告诉子进程父进程很“绝情”不会为子进程“收尸”。子进程退出的时候内核会检查父进程的SIGCHLD信号处理结构体是否设置了SA_NOCLDWAIT标志位或者是否将信号处理函数显式地设为SIG_IGN。如果是则autoreap为true子进程发现autoreap为true也就“死心”了不会进入僵尸状态而是调用release_task函数“自行了断”了。
等待子进程之wait()
Linux提供了wait()函数来获取子进程的退出状态
#include sys/wait.hpid_t wait(int* status);成功时返回已退出子进程的进程ID失败时则返回-1并设置errno常见的errno。 注意父子进程是两个进程子进程退出和父进程调用wait()函数获取子进程状态在时间上是独立的所以会出现以下两种情况
子进程先退出父进程后调用wait()函数父进程先调用wait()函数子进程后退出
对于第一种情况子进程执行完毕已经退出只留下了少量的信息等待父进程回收。当父进程调用wait()函数时候父进程获取到子进程的状态信息wait函数立刻返回。
对于第二种情况如果父进程先调用wait()函数此时子进程还没退出wait()函数就会阻塞在这里直到某个子进程退出。
// 示例#include iostream
#include sys/wait.h
#include sys/types.h
#include fcntl.h
#include unistd.hint main()
{pid_t pid fork();if (pid 0){std::cout 子进程创建失败 std::endl;return -1;}else if (pid 0){// 这是子进程std::cout 打印子进程的进程ID: getpid() std::endl;sleep(10);}else{// 子进程还没退出会阻塞在wait函数这里std::cout 子进程还没退出 std::endl;// 这是父进程pid_t pc wait(nullptr);std::cout 子进程退出, 进程ID: pc std::endl;}return 0;
}[rootZhn test4]# g test1.cpp -o test1
[rootZhn test4]# ./test1
子进程还没退出
打印子进程的进程ID: 3400
子进程退出, 进程ID: 3400
[rootZhn test4]# 可以看到父进程阻塞在wait()函数等待子进程退出后执行。
wait()函数等待的是任意一个子进程任何一个子进程退出都会让其立刻返回。当多个子进程都处于僵尸状态时wait()函数获取到其中一个子进程的信息后立刻返回。由于wait()函数不会接收pid_t类型的参数所以也不能明确等待某一个子进程的退出。
那么一个进程如何等待所有子进程都返回呢
wait()函数返回有三种可能性
等到了子进程退出获取其退出信息返回值是该子进程的进程ID等待过程中收到了信号信号打断了系统调用并且注册信号处理函数时并没有设置SA_RESTART标志位这样的话系统调用就不会重启wait()函数wait()函数会返回-1并且errno设置为EINTR已经成功等待了所有子进程退出没有子进程的退出信息需要接收这种情况下wait()函数返回-1并且errno设置为ECHILD。
《Linux/Unix系统编程手册》给出下面的代码来等待所有子进程的退出
while((childPid wait(NULL)) ! -1)continue;if(errno !ECHILD)errExit(wait);但是这种方法忽略了wait()函数被信号中断这种情况如果wait()函数被信号中断上述代码就不能成功等待所有子进程退出。
所以我们需要把上面代码封装以下
pid_t r_wait(int *stat_loc)
{int retval;// 如果被信号中断表达式为真重启wait()函数while(((retval wait(stat_loc)) -1 (errno EINTR));return retval;
}while((childPid r_wait(NULL)) ! -1)continue;If(errno ! ECHILD)
{/*some error happened*/
}由上面可以看出wait()函数具有一些局限性
不能等待特定的子进程如果进程存在多个子进程而它只想获取某一个子进程的退出状态就需要一一等待通过返回的进程ID判断是不是自己关心的子进程如果不存在子进程退出wait函数就会阻塞有时候只是想尝试获取子进程退出的状态如果没有子进程退出就立刻返回不需要阻塞等待wait()函数只能发现子进程的终止事件如果某些子进程因某信号而停止或者停止的子进程收到SIGCONT信号又恢复执行这些事wait函数无法获知。
为了解决这三个缺点引入了waitpid()函数。
等待子进程之waitpid()
waitpid()函数接口如下 #include sys/wait.hpid_t waitpid(pid_t pid, int *status, int options);waitpid()与wait()相同的地方
返回值的含义相同都是终止子进程或因信号停止或因信号恢复而执行的子进程的进程ID。status的含义相同都是用来记录子进程的相关事件后面将会详细介绍。
接下来介绍waitpid函数特有的功能
第一个参数是pid_t类型所以waitpid()可以明确指定要等待哪一个子进程的退出
pid 0表示等待进程ID为pid的子进程pid 0表示等待与调用进程同一个进程组的任意子进程因为子进程可以设置进程组那么如果某些子进程和父进程不在同一个进程组这样的进程就不关心它的退出状态pid -1表示等待任意子进程同wait类似waitpid(-1, status, 0)与wait(status)完全等价pid -1等待所有子进程中进程组ID与pid绝对值相等的子进程。
第二个参数是int*类型
无论是wait()还是waitpid()都有一个status变量这个变量是一个int类型指针。可以传递mullptr表示不关心子进程退出状态不为空就可以获得更多子进程的状态。 子进程是正常退出的 进程收到信号导致退出 进程收到信号被停止 子进程恢复执行
第三个参数options是一个位掩码可以同时存在多个标志位。如果options没有设置任何标志位其行为与wait类似即阻塞等待与pid匹配的进程退出。
options的标志位可以是如下标志位的组合
WUNTRACED除了关心终止子进程的信息也关心那些因信号而停止的子进程信息。WCONTINUED除了关心终止子进程的信息也关心那些因收到信号而恢复执行的子进程的状态信息。WNOHANG指定的子进程并未发生状态变化立刻返回不会阻塞。这种情况下返回值是0。如果调用进程并没有与pid匹配的子进程则返回-1并设置errno为ECHILD根据返回值和errno可以区分这两种情况。
Linux提供了SIGSTOP信号值19和SIGCONT信号值18两个信号来完成暂停和恢复的动作可以通过执行kill-SIGSTOP或kill-19来暂停一个进程的执行通过执行kill-SIGCONT或kill-18来让一个暂停的进程恢复执行。
// 示例// 等待子进程之waitpid#include iostream
#include unistd.h
#include sys/wait.h
#include sys/types.hint main()
{int status;pid_t pid fork();if (pid 0){std::cout 子进程创建失败 std::endl;return -1;}else if (pid 0){std::cout 这是子进程: getpid() std::endl;sleep(3);exit(3);}else{std::cout 这是父进程: getpid() std::endl;pid_t pc waitpid(0, status, WNOHANG);if (pc 0)std::cout 此时没有子进程退出 std::endl;else if (WIFEXITED(status))std::cout 子进程: pc 正常退出, 退出状态为 WEXITSTATUS(status) std::endl;elsestd::cout 子进程: pc 非正常退出 std::endl;}return 0;
}[rootZhn test4]# g test2.cpp -o test2
[rootZhn test4]# ./test2
这是父进程: 4551
此时没有子进程退出
这是子进程: 4552
[rootZhn test4]# 示例中父进程waitpid函数的参数设置的意思是等待父进程同一进程组的任意子进程退出事件如果没有子进程退出则返回值为0子进程中睡眠了3秒这时父进程调用waitpid发现没有子进程退出所以返回值为0。
这是一个简单的小例子其他例子都是举一反三。