二十四、Linux 进程与信号---wait 函数
24.1 wait 函数说明
24.1.1 waitpid---等待子进程中断或结束
waitpid(等待子进程中断或结束)
相关函数 wait,fork
1 #include <sys/types.h> 2 #include <sys/wait.h> 3 pid_t waitpid(pid_t pid,int * status,int options);
- 函数说明
- waitpid() 会暂时停止目前进程的执行,直到有信号来到或子进程结束。
- 如果在调用 wait() 时子进程已经结束,则 wait()会立即返回子进程结束状态值。子进程的结束状态值会由参数 status 返回,而子进程的进程识别码也会一快返回。
- 如果不在意结束状态值,则参数status可以设成NULL。
- 函数功能:wait 函数的非阻塞版本
- 函数参数
- @pid 为欲等待的子进程识别码,其他数值意义如下:
- pid< -1 等待其组ID等于 pid 的绝对值的任一子进程
- pid= -1 等待任一子进程,相当于wait()。
- pid= 0 等待进程组 ID 等于调用进程的组 ID 的任一子进程
- pid> 0 等待其进程ID 与 pid 相等的子进程。
- @option可以为0 或下面的OR 组合
- WNOHANG 如果没有任何已经结束的子进程则马上返回,不予以等待。(若由 pid 指定的子进程没有退出,则立即返回,则 waitpid 不阻塞,此时其返回值为0,若不设置就为阻塞,相当于wait)
- WUNTRACED 如果子进程进入暂停执行情况则马上返回,但结束状态不予以理会。(若某实现支持作业控制,则由 pid 指定的任一子进程状态已暂停,且其状态自暂停以来还未报告过,则返回其状态)
- @status 子进程的结束状态返回后存于 status,底下有几个宏可判别结束情况
- WIFEXITED(status) 如果子进程正常结束则为非0值。
- WEXITSTATUS(status) 取得子进程 exit() 返回的结束代码,一般会先用 WIFEXITED 来判断是否正常结束才能使用此宏。
- WIFSIGNALED(status) 如果子进程是因为信号而结束则此宏值为真(接到一个不能捕捉的信号)
- WTERMSIG(status) 取得子进程因信号而中止的信号代码,一般会先用 WIFSIGNALED 来判断后才使用此宏。
- WIFSTOPPED(status) 如果子进程处于暂停执行情况则此宏值为真。一般只有使用 WUNTRACED 时才会有此情况。
- WSTOPSIG(status) 取得引发子进程暂停的信号代码,一般会先用 WIFSTOPPED 来判断后才使用此宏。
- @pid 为欲等待的子进程识别码,其他数值意义如下:
- 返回值
- 如果执行成功则返回子进程识别码(PID),如果有错误发生则返回 -1 。失败原因存于 errno 中。
24.1.2 wait --- 等待子进程中断或结束
wait(等待子进程中断或结束)
相关函数 waitpid,fork
1 #include <sys/types.h> 2 #include <sys/wait.h> 3 pid_t wait (int * status);
- 函数说明
- wait()会暂时停止目前进程的执行,直到有信号来到或子进程结束。
- 如果在调用wait()时子进程已经结束,则wait()会立即返回子进程结束状态值。子进程的结束状态值会由参数 status 返回,而子进程的进程识别码也会一快返回。
- 如果不在意结束状态值,则参数 status 可以设成 NULL。子进程的结束状态值请参考waitpid()。
- 函数功能:等待子进程退出并回收,防止僵尸进程产生
- 函数参数
- @status:为空时,代表任意状态结束的子进程,若不为空,则代表指定状态结束的子进程
- 返回值
- 如果执行成功则返回子进程识别码 (PID),如果有错误发生则返回 -1 。失败原因存于 errno 中。
24.1.3 wait 和 waitpid 函数的区别
- 在一个子进程终止前,wait 使其调用者阻塞
- waitpid 由一个选择项,可使调用者不阻塞
- waitpid 等待一个指定的子进程,而 wait 等待所有的子进程,返回任一终止子进程的状态
24.2 案例
24.2.1 wait 函数
process_wait.c
1 #include <sys/types.h> 2 #include <sys/wait.h> 3 #include <unistd.h> 4 #include <stdio.h> 5 #include <stdlib.h> 6 7 8 void out_status(int status) 9 { 10 if(WIFEXITED(status)) { 11 printf("normal exit: %d\n", WEXITSTATUS(status)); 12 } else if(WIFSIGNALED(status)) { 13 printf("abnormal term: %d\n", WTERMSIG(status)); 14 } else if(WIFSTOPPED(status)) { 15 printf("stopped sig: %d\n", WSTOPSIG(status)); 16 } else { 17 printf("unkown sig\n"); 18 } 19 } 20 21 int main(void) 22 { 23 int status;//存储子进程终止返回的状态 24 pid_t pid; 25 26 /* 正常终止 */ 27 if((pid = fork()) < 0) { 28 perror("fork error"); 29 exit(1); 30 } else if(pid == 0) { 31 printf("pid: %d, ppid: %d\n", getpid(), getppid()); 32 exit(3);//子进程终止运行 33 } 34 35 //父进程调用wait函数阻塞,等待子进程结束并回收 36 wait(&status); 37 out_status(status); 38 printf("==========================\n"); 39 40 /* 非正常终止 */ 41 if((pid = fork()) < 0) { 42 perror("fork error"); 43 exit(1); 44 } else if(pid == 0) { 45 printf("pid: %d, ppid: %d\n", getpid(), getppid()); 46 int i = 3; 47 int j = 0; 48 int k = i / j; 49 printf("k: %d\n", k); 50 } 51 52 wait(&status); 53 out_status(status); 54 printf("==========================\n"); 55 56 /* 暂停 */ 57 if((pid = fork()) < 0) { 58 perror("fork error"); 59 exit(1); 60 } else if(pid == 0) { 61 printf("pid: %d, ppid: %d\n", getpid(), getppid()); 62 pause();//暂停,即为阻塞,等待一个信号将它继续运行 63 /* 64 int i = 0; 65 while(++i > 0) sleep(3);*/ 66 } 67 68 wait(&status); 69 out_status(status); 70 71 return 0; 72 }
编译运行:
另开一终端,对进程发出停止信号发现无法停止:
这是因为 WIFSTOPPED(status) 和 WSTOPSIG(status) 必须在使用 waitpid 的 option 参数 WUNTRACED 时候才生效,wait 函数无此功能
22.2.2 waitpid
process_waitpid.c
1 #include <sys/types.h> 2 #include <sys/wait.h> 3 #include <unistd.h> 4 #include <stdio.h> 5 #include <stdlib.h> 6 7 8 void out_status(int status) 9 { 10 if(WIFEXITED(status)) { 11 printf("normal exit: %d\n", WEXITSTATUS(status)); 12 } else if(WIFSIGNALED(status)) { 13 printf("abnormal term: %d\n", WTERMSIG(status)); 14 } else if(WIFSTOPPED(status)) { 15 printf("stopped sig: %d\n", WSTOPSIG(status)); 16 } else { 17 printf("unkown sig\n"); 18 } 19 } 20 21 int main(void) 22 { 23 int status;//存储子进程终止返回的状态 24 pid_t pid; 25 26 /* 正常终止 */ 27 if((pid = fork()) < 0) { 28 perror("fork error"); 29 exit(1); 30 } else if(pid == 0) { 31 printf("pid: %d, ppid: %d\n", getpid(), getppid()); 32 exit(3);//子进程终止运行 33 } 34 35 //父进程调用wait函数阻塞,等待子进程结束并回收 36 wait(&status); 37 out_status(status); 38 printf("==========================\n"); 39 40 /* 非正常终止 */ 41 if((pid = fork()) < 0) { 42 perror("fork error"); 43 exit(1); 44 } else if(pid == 0) { 45 printf("pid: %d, ppid: %d\n", getpid(), getppid()); 46 int i = 3; 47 int j = 0; 48 int k = i / j; 49 printf("k: %d\n", k); 50 } 51 52 wait(&status); 53 out_status(status); 54 printf("==========================\n"); 55 56 /* 暂停 */ 57 if((pid = fork()) < 0) { 58 perror("fork error"); 59 exit(1); 60 } else if(pid == 0) { 61 printf("pid: %d, ppid: %d\n", getpid(), getppid()); 62 pause();//暂停,即为阻塞,等待一个信号将它继续运行 63 /* 64 int i = 0; 65 while(++i > 0) sleep(3);*/ 66 } 67 68 do{ 69 //子进程若结束,则 pid 为子进程编号 70 //WNOHANG 非阻塞,waitpid 即子进程还没结束,waitpid 直接返回 71 pid = waitpid(pid, &status, WNOHANG | WUNTRACED); 72 if(pid == 0) sleep(1);//pid = 0, 则子进程没有结束,则父进程睡眠 1 s 73 }while(pid == 0); 74 out_status(status); 75 76 return 0; 77 }
编译测试与 process_wait.c 一样
再次发出 kill -19 pid: