目录:
1、wait函数和waitpid函数
1.1、wait和waitpid出现的原因
1.2、wait()和waitpid()函数使用
1.3、wait获取staus后检测处理
2、使用示例
2.1、wait()使用
2.2、waitpid使用
1、wait函数和waitpid函数:
1.1、wait和waitpid出现的原因:
1)、SIGCHLD:
- 当子进程退出的时候,内核会向父进程发送SIGCHLD信号,子进程的退出是个异步事件(子进程可以在父进程运行的任何时刻终止);
- 子进程退出时,内核将子进程置为僵尸状态,这个进程成为僵尸进程,它只保留最小的一些内核数据结构,以便父进程查询子进程的退出状态;
- 父进程查询子进程的退出状态可以用wait/waitpid函数;
1.2、wait()和waitpid()函数使用:
wait函数在默认的如下系统库:
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status)
进程一旦调用了wait,就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就 会一直阻塞在这里,直到有一个出现为止。waitpid()的作用和wait()一样,但它并不一定要等待一个终止的子进程,它还有若干选项,如可提供一个非阻塞版本的wait()功能,也能支持作用控制。
参数status用来保存被收集进程退出时的一些状态,它是一个指向int类型的指针。但如果我们对这个子进程是如何死掉的毫不在意,只想把这个僵尸进程消灭掉,(事实上绝大多数情况下,我们都会这样想),我们就可以设定这个参数为NULL,就象下面这样:
pid = wait(NULL);
1)、wait()函数:
函数签名: pid_t wait(int *status);
函数传入值:这里的status是一个整型指针,是该子进程退出的状态。若status不为空,则通过它可以获取子进程的结束状态。另外,子进程的结束状态有Linux系统中特定的宏来检测实际的进程返回的值;
函数返回值:成功返回已结束运行的子进程id,失败直接返回-1;
2)、waitpid()函数:
2.1)、函数签名:pid_t waitpid(pid_t pid, int *status, int options);
2.2)、函数入参:
pid:
- pid>0时,只等待进程ID等于pid的子进程,不管是否已经有其它子进程运行结束退出了,只要指定的子进程还没有结束,waitpid就会一直等下去。
- pid=-1时,等待任何一个子进程退出,没有任何限制,此时waitpid和wait的作用一模一样。
- pid=0时,等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid不会对它做任何理睬。
- pid<-1时,等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值。
options:
提供了一些额外的选项来控制waitpid,目前在Linux中只支持WNOHANG和WUNTRACED两个选项,这是两个常数,可以用"|"运算符把它们连接起来使用,比如:
ret=waitpid(-1,NULL,WNOHANG | WUNTRACED);
如果我们不想使用它们,也可以把options设为0,如:
ret=waitpid(-1,NULL,0);
- WNOHANG:若由指定的子进程没有结束,则waitpid()不阻塞而立即返回,此时返回值为0;
- WUNTRACED:指定的任一子进程已被暂停,且其状态子暂停以来还未报告过,则返回其状态;
2.3)、函数返回值:
1.正常结束返回子进程的id;
2.使用WNOHANG且没有子进程退出返回0;
3.失败返回-1;
1.3、wait获取staus后检测处理:
由于在使用waitpid()函数的时候获取到的进程状态值对其直接输出会与实际返回值不一致,所以需要引用(WIFEXITED|WEXITSTATUS)等宏定义来输出进程实际的状态信息值,宏定义和对应的描述,如下:
1)、WIFEXITED(status): 如果进程子进程正常结束,返回一个非零值;
- WEXITSTATUS(status):如果WIFEXITED非零,返回子进程退出码。如果子进程调用exit(5)退出,WEXITSTATUS(status)就会返回5;
2)、WIFSIGNALED(status): 子进程因为捕获信号而终止,返回非零值;
- WTERMSIG(status):如果WIFSIGNALED非零,返回信号代码;
3)、WIFSTOPPED(status): 如果进程被暂停,返回一个非零值;
- WSTOPSIG(status):如果WIFSTOPPED非零,返回信号代码;
2、使用示例:
2.1、wait()使用:
fork是一个创建新进程的系统调用。当调用 fork 时,它会创建一个与调用进程几乎完全相同的新进程(子进程)。fork 返回两次,一次在父进程中,另一次在子进程中。具体来说,fork 在父进程和子进程中的行为如下:
父进程:调用 fork 后,fork 返回子进程的 PID(正整数)。
子进程:调用 fork 后,fork 返回 0。
因此,fork 可以通过返回值来区分它是在父进程中还是在子进程中运行。
下面的代码流程如下:
1.父进程执行fork()创建一个子进程,fork()返回创建子进程的PID值非0值,进入else判断即父进程执行代码区域调用wait()方法等待子进程结束;
2.同样的代码拷贝到创建的子进程中,在子进程第一次调用fork()时候返回0值然后进去执行子进程执行代码相关逻辑(要是在子进程执行代码区域再次执行fork()那么子进程就会在创建出孙子进程);
void forkUsed(void){
int pid;
printf("father progress\n");
if (0 ==fork()){
// 子进程执行代码
printf("father progress forked\n");
}else{
// 父进程执行代码
if (-1 ==(pid=wait(NULL))){
printf("no child progress,and wait() return -1\n");
} else{
printf("wait() return child progress pid:%d\n",pid);
}
}
/*
* 输出结果为:
* father progress
* father progress forked
* wait() return child progress pid:xxx
*/
}
2.2、waitpid()使用:
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
int main(){
pid_t pc,pr;
pc= fork();
if (pc == 0){
sleep(5);
exit(0);
}else{
// 循环测试子进程是否退出
do {
pr= waitpid(pc,NULL,WNOHANG);
//若子进程还未退出,则父进程暂停1s
if (pr ==0){
printf("The child process has not exited\n");
sleep(1);
}
} while (pr ==0);
if (pr == pc){
printf("get child exit code:%d\n",pr);
} else{
printf("waitpid error\n");
}
}
/*
* 输出结果:
* The child process has not exited
* The child process has not exited
* The child process has not exited
* The child process has not exited
* The child process has not exited
* get child exit code:xxx
*
* 子进程暂停5s,而在父进程每隔1s来判断该子进程是否退出,所以这里总共打印5条。
* 说明waitpid没有阻塞,且指定子进程没有退出返回0,指定进程退出返回其进程ID
*/
}