进程等待
一、进程等待必要性:
• 子进程退出,父进程如果不管不顾,就可能造成僵尸进程的问题,进而造成内存泄漏。
• 另外,进程一旦变成僵尸状态,那就刀枪不不入,kill-9也无能为力,因为谁也没有办法杀死⼀个已经死去的进程。
• 最后,父进程派给⼦进程的任务完成的如何,我们需要知道。如,子进程运行完成,结果对还是 不对,或者是否正常退出。
• 父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>int main(int argc, char *argv[])
{pid_t id = fork();if (id == 0){int cnt = 5;while (cnt--){printf("我是一个子进程:%p,pid:%d,ppid%d\n", getpid(), getppid());sleep(1);}}// 父进程sleep(100);return 0;
}以上代码最后会出现僵尸问题,接下来用进程等待的方式解决僵尸问题。
二、进程等待:
1.wait:status参数是个输出型参数,需要传整型变量的地址,把子进程这个退出信息给父进程拿到,wait接口等待任意一个退出的子进程,返回目标僵尸进程的退出码。
如果等待子进程没有退出,父进程会阻塞在wait处。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>int main(int argc, char *argv[])
{pid_t id = fork();if (id == 0){int cnt = 5;while (cnt--){printf("我是一个子进程:%p,pid:%d,ppid%d\n", getpid(), getppid());sleep(1);}}// 父进程sleep(100);pid_t rid = wait(NULL);if(rid>0){printf("wait success rid:%d", rid);}sleep(10);return 0;
}2.waitpid:pid_ t waitpid(pid_t pid, int *status, int options);
简单等待就用wait,想要有更多状态信息就用waitpid。
返回值:当正常返回的时候 waitpid 返回收集到的子进程的进程 ID,如果设置了选项 WNOHANG, 而调用中 waitpid 发现没有已退出的子进程可收集 , 则返回 0,如果调用中出错 , 则返回-1, 这时 errno 会被设置成相应的值以指示错误所在。
pid : Pid=-1, 等待任⼀个子进程。与 wait 等效。 Pid>0. 等待其进程 ID 与 pid 相等的子进程。
options: 默认为0,表示阻塞等待。
WNOHANG: 若 pid 指定的子进程没有结束,则 waitpid() 函数返回 0 ,不予以等待。若正常结束,则返回该子进程的ID。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <string.h>
#include <errno.h>int main(int argc, char *argv[])
{pid_t id = fork();if (id == 0){int cnt = 3;while (cnt--){printf("我是一个子进程:%p,pid:%d,ppid%d\n", getpid(), getppid());sleep(1);}exit(1);}// 父进程int status = 0;pid_t rid = waitpid(id, &status, 0);if (rid > 0){printf("wait success rid:%d , status:%d\n", rid, status);}else{printf("wait failed :%d:%s\n", errno, strerror(errno));}return 0;
}
status当作位图来看,被划分为若干区域,32比特位,从右向左,高16位不考虑,低16位里面的次低8位表示退出状态即退出码。后面有两部分,一部分叫core dump,占一个比特位默认为0,其他七个比特位表示退出时状态,没有异常,这8个数字都是0。所以整个数字就是1后面跟8个0就是2的8次方也就是256。
3.非阻塞等待:
父进程在等待子进程时周期性地去询问子进程的退出状态获取信息,直到等到子进程退出,利用非阻塞等待在子进程退出前可以让父进程去完成其他任务,解放生产力。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <string.h>
#include <errno.h>// 函数指针类型
typedef void (*func_t)(); // 函数指针
#define NUM 5
func_t handlers[NUM + 1]; // 数组里面要有方法// 如下是任务
void DownLoad()
{printf("我是一个下载任务...\n");
}
void Flush()
{printf("我是一个刷新任务...\n");
}
void Log()
{printf("我是一个记录日志任务...\n");
}void registerHadnlers(func_t h[], func_t f)
{int i = 0;for (; i < NUM; i++){if (h[i] == NULL)break;}if (i == NULL)return;h[i] = f;h[i + 1] = NULL;
}int main(int argc, char *argv[])
{registerHadnlers(handlers, DownLoad);registerHadnlers(handlers, Flush);registerHadnlers(handlers, Log);pid_t id = fork();if (id == 0){int cnt = 3;while (cnt--){printf("我是一个子进程:%p,pid:%d,ppid%d\n", getpid(), getppid());sleep(1);}exit(10);}// 父进程int status = 0;pid_t rid = waitpid(id, &status, WNOHANG);if (rid > 0){printf("wait success rid:%d , exit code: %d , exit signal: %d\n", rid, (status >> 8) & 0xFF, status & 0x7F);}else if (rid == 0){for (int i = 0; handlers[i]; i++){handlers[i](); // 函数回调处理}printf("本轮调用结束 子进程没有退出\n");sleep(1);}else{printf("等待失败\n");}return 0;
}