wait()和waitpid()解析

wait()函数说明

wait(等待子进程中断或结束,相关函数 waitpid,fork)
表头文件

#include
#include

函数原型

pid_t wait (int * status);

函数说明
参数 status 是一个整形指针。如果status不是一个空指针,则终止进程的终止状态将存储在该指针所指向的内存单元中。如果不关心终止状态,可以将 status参数设置为NULL。
status 不是NULL时子进程的结束状态值会由参数 status 返回,而子进程的进程识别码作为函数返回值返回。
调用 wait 函数时,调用进程将会出现下面的情况:
如果其所有子进程都还在运行,则阻塞。
如果一个子进程已经终止,正等待父进程获取其终止状态,则获取该子进程的终止状态然后立即返回。
如果没有任何子进程,则立即出错返回。
在这里插入图片描述
(图源网络,侵删)

返回值
如果执行成功则返回子进程识别码(PID),如果有错误发生则返回-1。失败原因存于errno 中。

//fork()的使用方法,如果不进行wait()则当子进程退出时会出现子进程的内存无法释放的状态,因此子进程便成为了僵尸进程。
#include 
#include 
#include 
int main(){

    pid_t pid;
    printf("Start of fork testing\n");
    pid = fork();

    printf("Return of fork success:pid = %d\n",pid);
    printf("father :%d       %d",getpid(),getppid());
    
    return 0;
}
//当没有子进程的时候,调用wait()的返回值是-1
#include 
#include 
#include 
#include 
int main(){

    printf("wait return :%d",wait(NULL));                                                                                                                      
        
    return 0;
}

运行结果:
wait return :-1
//一个wait(NULL)只能等待一个子进程结束,两个等待两个子进程
#include 
#include 
#include 
#include 
int main(){
    int pid;
    printf("father progress,and father progress pid is %d\n",getpid());

    if(0 == fork()){
        printf("father progress forked,and child progress pid is %d\n",getpid());
    }
    else{
        if(0 == fork()){
            printf("father progress forked,and child progress pid is %d\n",getpid());
        }
        else{
                if(-1 == (pid = wait(NULL))){
                    printf("no child progress,and wait() return -1\n");
                }
                else{
                    printf("wait() return child progress pid is %d\n",pid);
                }
                
                if(-1 == (pid = wait(NULL))){
                    printf("no child progress,and wait() return -1\n");
                }
                else{
                    printf("wait() return child progress pid is %d\n",pid);
                }
        }
    }
    return 0;
}

运行结果
father progress,and father progress pid is 24085
father progress forked,and child progress pid is 24086
father progress forked,and child progress pid is 24087
wait() return child progress pid is 24086
wait() return child progress pid is 24087

//当子进程有一个死循环无法终止,则父进程对应的wait(NULL)会一直阻塞,等待进程结束,所以就一直卡着程序无法终止。

#include 
#include 
#include 
#include 
int main(){
    int pid;
    printf("father progress,and father progress pid is %d\n",getpid());

    if(0 == fork()){
        printf("father progress forked,and child progress pid is %d\n",getpid());
    }
    else{
        if(0 == fork()){
            printf("father progress forked,and child progress pid is %d\n",getpid());
        }
        else{
            if(0 == fork()){
                printf("father progress forked,and child progress pid is %d\n",getpid());
                while(1) ;//对应第三个wait()
            }
            else{
                if(-1 == (pid = wait(NULL))){
                    printf("no child progress,and wait() return -1\n");
                }
                else{
                    printf("wait() return child progress pid is %d\n",pid);
                }
                
                if(-1 == (pid = wait(NULL))){
                    printf("no child progress,and wait() return -1\n");
                }
                else{
                    printf("wait() return child progress pid is %d\n",pid);
                }

                if(-1 == (pid = wait(NULL))){
                    printf("no child progress,and wait() return -1\n");
                }
                else{
                    printf("wait() return child progress pid is %d\n",pid);
                }
            }
        }
    }
    return 0;
}

wait()和waitpid()解析_第1张图片
作用及其用法
【status为NULL】:

因为一个进程,如果fork()之后生成子进程,如果不进行wait(NULL)则当子进程退出时会出现子进程的内存无法释放的状态,因此子进程便成为了僵尸进程,而一个wait(NULL)只能等待一个子进程,只要有子进程退出,wait(NULL)退出阻塞(且返回值为退出的子进程的进程号),否则一直阻塞直到有子进程退出。当调用wait()函数的进程没有子进程时,返回-1。

【status不为NULL】:

如果参数status的值不是NULL,wait就会把子进程退出时的状态取出并存入其中,这是一个整数值(int),指出了子进程是正常退出还是非正常结束的(一个进程也可以被其他进程用信号结束),以及正常结束时返回值,或被哪一个信号结束的等信息。由于这些信息被存放在一个整数的不同二进制位中,所以用常规的方法读取会非常麻烦,人们就设计了一套专门的宏(macro)来完成这项工作,下面我们来学习一下其中最常见的两个:

1、WIFEXITED(status):这个宏用来指出子进程是否为正常退出的,如果是,它会返回一个非零值。(请注意,虽然名字一样,这里的参数status并不同意wait唯一的参数——指向整数的指针status,而是那个指针指向的整数,切记不要搞混了。)

2、WEXITSTATUS(status):当WIFEXITED返回非零值时,我们可以用这个宏来提取子进程的返回值,如果子进程调用exit(5)退出,WEXITSTATUS(status)就会返回5;如果子进程调用exit(7)退出,WEXITSTATUS(status)就会返回7。请注意,如果进程不是正常退出,也就是说,WIFEXITED返回0,这个值就毫无意义了。

问题来了,如果不用wait(),如何使一个子进程不会变成僵尸进程
答案是有的,那就是通过两个fork(),具体实现如下,通过先fork()一个子进程,然后在子进程里在fork()一个子进程,然后在退出第二个子进程,则第三个子进程会成为孤儿进程,被init收养,然后就可以实现不用wait()就可以使子进程不进入僵尸进程

#include
#include
#include 
#include
#include
int main(){
    
    pid_t pid;
    if((pid = fork()) < 0){
        printf("error\n");
        exit(1);
    }
    else if(pid == 0){
        if((pid = fork()) < 0){
            printf("error\n");
            exit(1);
        }
        else if(pid > 0){
            exit(0); //parent from second fork == first child
        }
        sleep(2);
        printf("second child ,parent pid = %ld\n",(long)getppid());
        exit(0);
    }

    if(waitpid(pid,NULL,0) != pid) //wait for first pid
    {
        printf("error\n");
        exit(1);
    }


    
    return 0;
}

运行如下
在这里插入图片描述
使用wait()函数有一个不方便的地方就是,一个wait()只能对应一个进程,而且无法定哪一个wait()等待的是哪一个进程,因此应该用waitpid()

在这里插入图片描述(图源网络,侵删)

函数说明

如果在调用 waitpid()时子进程已经结束,则 waitpid()会立即
返回子进程结束状态值。 子进程的结束状态值会由参数 status 返回,
而子进程的进程识别码也会一起返回。如果不在意结束状态值,则
参数 status 可以设成 NULL。参数 pid 为欲等待的子进程识别码,
其他数值意义如下:
pid<-1 等待进程组识别码为 pid 绝对值的任何子进程。
pid=-1 等待任何子进程,相当于 wait()。
pid=0 等待进程组识别码与目前进程相同的任何子进程。
pid>0 等待任何子进程识别码为 pid 的子进程。

参数options提供了一些额外的选项来控制waitpid,参数 option 可以为 0 或可以用"|"运算符把它们连接起来使用,比如:
ret=waitpid(-1,NULL,WNOHANG | WUNTRACED);
如果我们不想使用它们,也可以把options设为0,如:
ret=waitpid(-1,NULL,0);

WNOHANG 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若结束,则返回该子进程的ID。
WUNTRACED 若子进程进入暂停状态,则马上返回,但子进程的结束状态不予以理会。WIFSTOPPED(status)宏确定返回值是否对应与一个暂停子进程。
子进程的结束状态返回后存于 status,底下有几个宏可判别结束情况:
WIFEXITED(status)如果若为正常结束子进程返回的状态,则为真;对于这种情况可执行WEXITSTATUS(status),取子进程传给exit或_eixt的低8位。
WEXITSTATUS(status)取得子进程 exit()返回的结束代码,一般会先用 WIFEXITED 来判断是否正常结束才能使用此宏。
WIFSIGNALED(status)若为异常结束子进程返回的状态,则为真;对于这种情况可执行WTERMSIG(status),取使子进程结束的信号编号。
WTERMSIG(status) 取得子进程因信号而中止的信号代码,一般会先用 WIFSIGNALED 来判断后才使用此宏。
WIFSTOPPED(status) 若为当前暂停子进程返回的状态,则为真;对于这种情况可执行WSTOPSIG(status),取使子进程暂停的信号编号。
WSTOPSIG(status) 取得引发子进程暂停的信号代码,一般会先用 WIFSTOPPED 来判断后才使用此宏。

如果执行成功则返回子进程识别码(PID) ,如果有错误发生则返回
返回值-1。失败原因存于 errno 中。

由于wait()和waitpid()本质没有什么区别所以就只写了一个相关代码

//代码
#include 
#include 
#include 
#include 
#include 
int main()
{
    pid_t childpid;
    int status;
    childpid = fork();
if ( childpid < 0 )
{
    perror( "fork()" );
    exit(1);
}
else if ( childpid == 0 )
{
    puts( "In child process" );
    sleep( 3 );//让子进程睡眠3秒,看看父进程的行为
    printf("\tchild pid = %d\n", getpid());
    printf("\tchild ppid = %d\n", getppid());
    exit(1);
}
else
{
    waitpid( childpid, &status, 0 );
    puts( "in parent" );
    printf( "\tparent pid = %d\n", getpid() );
    printf( "\tparent ppid = %d\n", getppid() );
    printf( "\tchild process exited with status %d \n", status );
}
exit(0);
}

运行
In child process
child pid = 4469
child ppid = 4468
in parent
parent pid = 4468
parent ppid = 4379
child process exited with status 0

总结

wait()函数用于使父进程(也就是调用wait()的进程)阻塞,直到一个子进程结束或该进程接收到一个指定的信号为止。如果该父进程没有子进程或它的子进程已经结束,则wait()就会立即返回-1。而waitpid()的作用和wait()一样,但它并不一定要等待一个终止的子进程,它还有若干选项,如可提供一个非阻塞版本的wait()功能,也能支持各种作用控制。wait()函数只是waitpid()函数的一个特例,在Linux内部实现wait()函数时直接调用的就是waitpid()函数。

你可能感兴趣的:(Linux操作系统相关,C语言相关)