什么是进程等待?
进程等待是一种操作系统中的机制,它存在于父进程与子进程之间的关系。在一个进程创建了新的子进程后,这个父进程可能会需要等待子进程的完成,以收集子进程的资源和退出状态。
在Linux系统中,wait函数和waitpid函数是用于实现进程等待的系统调用方法。wait函数会暂停当前进程的执行,直到一个子进程终止。当子进程终止后,wait函数会返回子进程的进程ID(PID),并将子进程的终止状态存储在指针status指向的变量中。
进程等待必要性
之前的文章讲过,子进程退出,父进程如果不管不顾,就可能造成‘僵尸进程’的问题,进而造成内存泄漏。另外,进程一旦变成僵尸状态,那就刀枪不入,“杀人不眨眼”的kill -9 也无能为力,因为谁也没有办法杀死一个已经死去的进程。最后,父进程派给子进程的任务完成的如何,我们需要知道。如,子进程运行完成,结果对还是不对,或者是否正常退出。父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息。
进程如何等待?
wait方法
#include<sys/types.h> #include<sys/wait.h> pid_t wait(int*status); 返回值: 成功返回被等待进程pid,失败返回-1。 参数: 输出型参数,获取子进程退出状态,不关心则可以设置成为NULL
进程等待回收子进程僵尸状态,这个状态的形成还是主要因为执行父进程代码前加了睡眠的状态,否则在子进程退出的一瞬间,父进程就已经回收完资源了,我们是看不到僵死进程的:
1 #include <stdio.h> 2 #include <unistd.h> 3 #include <stdlib.h> 4 #include <sys/types.h> 5 #include <sys/wait.h> 6 7 //int status = 0; 8 void Worker(int number) 9 { 10 int cnt = 5; 11 while(cnt) 12 { 13 printf("I am child process, pid: %d, ppid: %d, cnt: %d, number: %d\n", getpid(), getppid() , cnt--, number); 14 sleep(1); 15 16 //*p = 100; 17 //int a = 10; 18 //a /= 0; 19 } 20 exit(0); 21 } 22 23 int main() 24 { 25 pid_t id=fork(); 26 if(id==0) 27 { 28 Worker(1); 29 }else{ 30 sleep(7); 31 pid_t rid=wait(NULL); 32 if(rid==id) 33 { 34 printf("i am father process,pid:%d,ppid:%d rid: %d\n",getpid(),getppid(),rid); 35 } 36 } 37 return 0; 38 } .
进程等待,父进程必须在wait上进行阻塞等待,直到子进程僵尸状态,wait自动回收,因此父进程一般是最后退出的!
1 #include <stdio.h> 2 #include <unistd.h> 3 #include <stdlib.h> 4 #include <sys/types.h> 5 #include <sys/wait.h> 6 7 //int status = 0; 8 void Worker(int number) 9 { 10 int cnt = 5; 11 while(cnt) 12 { 13 printf("I am child process, pid: %d, ppid: %d, cnt: %d, number: %d\n", getpid(), getppid() , cnt--, number); 14 sleep(1); 15 16 //*p = 100; 17 //int a = 10; 18 //a /= 0; 19 } 20 exit(0); 21 } 22 23 int main() 24 { 25 pid_t id=fork(); 26 if(id==0) 27 { 28 Worker(1); 29 }else{ 30 pid_t rid=wait(NULL); 31 if(rid==id) 32 { 33 printf("i am father process,pid:%d,ppid:%d rid: %d\n",getpid(),getppid(),rid); 34 } 35 } 36 return 0; 37 }
waitpid方法
pid_ t waitpid(pid_t pid, int *status, int options); 参数: pid: Pid=-1,等待任一个子进程。与wait等效。 Pid>0.等待其进程ID与pid相等的子进程。 status: WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出) WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码) options: WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进 程的ID。
返回值
当正常返回的时候 waitpid 返回收集到的子进程的进程ID;
如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
参数status
参数status是一个指向整数的指针,用于保存被收集子进程的退出状态。他32bit,其中低16位为中储存着数据。需要注意的是status不能简单的当作整形来看待,可以当作位图来看待。他的返回值实际上如下图所示:
对此,如果我们要读取正确的信息,需要将status右移动8位。需要注意的是:如果进程被杀死了,如上图所示,status是无意义的!
1 #include <sys/wait.h> 2 #include <stdio.h> 3 #include<unistd.h> 4 #include <stdlib.h> 5 #include <string.h> 6 #include <errno.h> 7 int main( void ) 8 { 9 pid_t pid; 10 if ( (pid=fork()) == -1 ) 11 perror("fork"),exit(1); 12 if ( pid == 0 ){ 13 int ct=5; 14 while(ct--) 15 { 16 printf("i am a child process times:%d,pid:%d,ppid:%d\n",ct,getpid(),getppid()); 17 sleep(1); 18 } 19 sleep(5); 20 exit(11); 21 } else { 22 int st; 23 int ret = waitpid(pid,&st,0); 24 if ( ret > 0 && ( st & 0X7F ) == 0 ){ // 正常退出 25 printf("child exit code:%d\n", (st>>8)&0XFF); 26 } else if( ret > 0 ) { // 异常退出 27 printf("sig code : %d\n", st&0X7F ); 28 } 29 } 30 }
正常退出情况:
非正常退出,被进程杀死情况,此时为进程的信号量:
WEXITSTATUS
WEXITSTATUS是一个宏,用于获取父进程中由子进程返回的退出状态。在C语言中,当一个进程通过调用exit()函数或_exit()函数来终止时,它会向其父进程返回一个整数值作为退出状态。
WEXITSTATUS宏定义如下:
#define WEXITSTATUS(status) ((unsigned int)(status) >> 8)
其中,status是子进程返回的退出状态。该宏将退出状态右移8位,以获取实际的退出状态值。
使用WEXITSTATUS宏可以方便地获取子进程的退出状态,以便在父进程中进行进一步的处理或判断。
附加option选项
- WNOHANG:如果设置了这个选项,那么即使有子进程没有结束,waitpid也会立即返回,不会阻塞父进程。此时,waitpid函数会返回0,表示没有子进程已经结束。如果没有设置WNOHANG选项,那么waitpid会一直阻塞父进程,直到有一个子进程结束为止。
- WUNTRACED:如果设置了这个选项,那么即使子进程处于暂停状态(被暂停、停止或跟踪),waitpid也会返回。如果没有设置WUNTRACED选项,那么只有当子进程正常结束时,waitpid才会返回。
- WSTOPPED:如果设置了这个选项,那么只有当子进程处于暂停状态时,waitpid才会返回。如果没有设置WSTOPPED选项,那么只有当子进程正常结束时,waitpid才会返回。
- WCONTINUED:如果设置了这个选项,那么只有当子进程从暂停状态中恢复运行时,waitpid才会返回。如果没有设置WCONTINUED选项,那么只有当子进程正常结束时,waitpid才会返回。
进程阻塞等待方式:
#include <sys/wait.h> #include <stdio.h> #include<unistd.h> #include <stdlib.h> #include <string.h> #include <errno.h> int main() { pid_t pid; pid = fork(); if(pid < 0){ printf("%s fork error\n",__FUNCTION__); return 1; } else if( pid == 0 ){ //child printf("child is run, pid is : %d\n",getpid()); sleep(5); exit(257); } else{ int status = 0; pid_t ret = waitpid(-1, &status, 0);//阻塞式等待,等待5S printf("this is test for wait\n"); if( WIFEXITED(status) && ret == pid ){ printf("wait child 5s success, child return code is :%d.\n",WEXITSTATUS(status)); }else{ printf("wait child failed, return.\n"); return 1; } return 0; } }
进程的非阻塞等待方式:
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/wait.h> int main() { pid_t pid; pid = fork(); if(pid < 0){ printf("%s fork error\n",__FUNCTION__); return 1; }else if( pid == 0 ){ //child printf("child is run, pid is : %d\n",getpid()); sleep(5); exit(1); } else{ int status = 0; pid_t ret = 0; do { ret = waitpid(-1, &status, WNOHANG);//非阻塞式等待 if( ret == 0 ){ printf("child is running\n"); } sleep(1); }while(ret == 0); if( WIFEXITED(status) && ret == pid ){ printf("wait child 5s success, child return code is :%d.\n",WEXITSTATUS(status)); }else{ printf("wait child failed, return.\n"); return 1; } } return 0; }
感谢你耐心的看到这里ღ( ´・ᴗ・` )比心,如有哪里有错误请踢一脚作者o(╥﹏╥)o!