
本文共 5093 字,大约阅读时间需要 16 分钟。
1、进程退出
退出方式有两种:正常退出,异常退出
正常退出有5种:
1、Main函数调用return
2、进程调用exit(),标准C库 3、进程调用_exit()或者_Exit(),属于系统调用 和线程有关的 4、进程最后一个线程返回 5、最后一个线程调用pthread_exit异常退出有三种:
1、调用abort 2、当进程收到某些信号时,如Ctrl + C 3、最后一个线程对取消(cancellation),请求作出响应不管进程如何终止,最后都会执行内核中的同一段代码。这段代码为相应进程关闭所有打开描述符,释放它所使用的存储器等。对上述任意一种终止情形、我们都希望终止进程能够通知其父进程它是如何终止的。对于三个终止函数(exit._exit和_Exit),实现这一点的方法是,将其退出状态((exit status)作为参数传送给函数。在异常终止情况下,内核(不是进程本身)产生一个指示其异常终止原因的终止状态(termination status)。在任意一种情况下,该终止进程的父进程都能用wait或waitpid函数,取得其终止状态。
二、子进程退出的信息收集
为什么要等待子进程退出
1、防止僵尸进程,造成内存泄漏 2、父进程要管理子进程,所以父进程交代给子进程的任务完成的如何,都需要知道,如,子进程运行完成,运行结果对还是不对,或者是否正常退出 3、父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息如果不收集子进程的退出信息会怎么样?
如果子进程的退出状态不被收集,子进程会变成僵死进程(僵尸进程)。
例如
#include#include #include #include int main(){ int data = 3; pid_t pid; pid = vfork(); if(pid > 0){ while(1){ printf("Father pid = %d\n",getpid()); sleep(1); } }else if(pid == 0){ while(1){ data--; printf("Child pid = %d\n",getpid()); sleep(3); if(data == 0){ exit(0); } } } return 0;}

收集子进程退出信息的函数wait和waitpid
头文件
#include#include
函数原型
pid_t wait(int *status);
status:是一个整型数指针
指针为空:不关心退出状态 指针非空:子进程退出状态放在它所指向的地址中函数作用:父进程一旦调用了wait就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。
wait()要与fork()配套出现,如果在使用fork()之前调用wait(),程序出错时wait()的返回值则为-1,正常情况下wait()的返回值为子进程的PID.
wait函数的两种参数情况
1、status为NULL时int main(){ int data = 3; pid_t pid; pid = vfork(); if(pid > 0){ wait(NULL); while(1){ printf("Father pid = %d\n",getpid()); sleep(1); } }else if(pid == 0){ while(1){ data--; printf("Child pid = %d\n",getpid()); sleep(3); if(data == 0){ exit(0); } } } return 0;}
2、status非空时
#include#include #include #include int main(){ int data = 3; pid_t pid; pid = vfork(); int status = 10; if(pid > 0){ wait(&status); printf("child quit status = %d\n",WEXITSTATUS(status)); while(1){ printf("Father pid = %d\n",getpid()); sleep(1); } }else if(pid == 0){ while(1){ data--; printf("Child pid = %d\n",getpid()); sleep(3); if(data == 0){ exit(3); } } } return 0;}

一些宏

头文件
#include#include
函数原型
pid_t waitpid(pid_t pid, int *status, int options);int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
从本质上讲,系统调用waitpid和wait的作用是完全相同的,但waitpid多出了两个可由用户控制的参数pid和options,wait使调用者阻塞,waitpid 可以使调用者不阻塞
PID:
pid > 0时,只等待进程ID等于pid的子进程,不管其它已经有多少子进程运行结束退出了,只要指定的子进程还没有结束,waitpid就会一直等下去。 pid = -1时,等待任何一个子进程退出,没有任何限制,此时waitpid和wait的作用一模一样。 pid == 0时,等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid不会对它做任何理睬。 pid < -1时,等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值。status:是一个整型数指针,和wait一样
options:options提供了一些额外的选项来控制waitpid,目前 在Linux中只支持WNOHANG和WUNTRACED两个选项,这是两个常数,可以用"|"运算符把它们连接起来使用,比如:ret = waitpid(-1, NULL, WNOHANG | WUNTRACED);
如果我们不想使用它们,也可以把options设为0,如:
ret = waitpid(-1, NULL, 0);
如果使用了WNOHANG参数调用waitpid,即使没有子进程退出,它也会立即返回,不会像wait那样永远等下去。
返回值:
1、当正常返回的时候,waitpid返回收集到的子进程的进程ID 2、如果设置了选项WNOHANG,在调用中waitpid发现没有已经退出的子进程可收集时,返回0 3、如果调用中出错,则返回-1例:
#include#include #include #include int main(){ int data = 3; pid_t pid; pid = fork(); int status = 10; if(pid > 0){ waitpid(pid,&status,WNOHANG); printf("child quit status = %d\n",WEXITSTATUS(status)); while(1){ printf("Father pid = %d\n",getpid()); sleep(1); } }else if(pid == 0){ while(1){ data--; printf("Child pid = %d\n",getpid()); sleep(3); if(data == 0){ exit(3); } } } return 0;}

当把fork改为vfork时,就可以解决
pid = vfork();

三、孤儿进程
父进程如果不等待子进程退出,在子进程之前就结束了自己的“生命”,此时子进程叫做孤儿进程 Linux为了避免孤儿进程过多,init进程收留孤儿进程,变成孤儿进程的父进程(init进程为系统初始化进程,进程ID为1)
例如
#include#include #include #include int main(){ int data = 3; pid_t pid; pid = fork(); if(pid > 0){ printf("Father pid = %d\n",getpid()); } else if(pid == 0){ while(1){ data--; printf("my Father pid = %d Child pid = %d\n",getppid(),getpid()); sleep(3); if(data == 0){ exit(3); } } } return 0;}
发表评论
最新留言
关于作者
