进程的退出状态 和 进程等待 exit()和_exit(),wait()和waipid()

一、进程退出

当一个进程终止了,我们需要知道进程退出时的状态,我们需要通过进程结束时的退出码来识别。

进程正常结束时:

1.从main()函数返回

2.调用exit()函数

3.调用_exit()//系统调用

我们可以用(echo $?)查看上一次进程执行结束的退出码。

来看下面一个例子:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
    printf("hello world\n");
    exit(2);
    printf("continue\n");
    return 3;                                      
}

 

按照我们上面讲的,当这个程序执行结束后,我们查看退出码:发现从exit(2)处将进程结束了。退出码为exit()的参数。

 

再来看看exit(),和_exit()的区别吧: 

 
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
 printf("hello world");
 sleep(2);
 exit(2);
}

 

执行结果为:等待2秒之后打印"hello world",进程结束

include <stdio.h>                                                                                                                   #include <stdlib.h>
#include <unistd.h>
int main()
{
    printf("hello world");
    sleep(2);
    _exit(2);
}

 

 

等待两秒之后,没有进行输出字符串,进程结束。

这里为什会先等2秒再打印,因为这里的printf()是按行刷新的,但是这里并没有'\n'就不会按行刷新缓冲区,等到进程结束再刷新缓冲区,进行输出,切记,这里不可以理解为sleep(),在printf()之前执行。

exit()和_exit()都是进程正常退出的的方法,从上面的例子可以看到,_exit(),就是将进程强行退出,而exit()在这里做了一些事情,执行用户定义的清理函数,还有这里可以看到的刷新缓冲区,还有关闭文件流等

二、进程等待:

在之前讲进程状态是讲过,如果子进程退出,而父进程对其不管不顾,就有可能造成僵尸进程,而造成内存泄露,而且父进程创建的子进程是为了让其执行任务,当然父进程需要知道子进程任务完成的如何,那么父进程就需要通过进程等待的方式来回收子进程资源,获取子进程推出状态。

pid_t wait(int *status);

我们称其为阻塞式等待,等待成功之前父进程处于阻塞状态。

返回值: 等待成功为被等待进程的pid,失败为-1.

参数:输出型参数,获取子进程的退出状态,不关心则可以设置为NULL 。

 

pid_t waitpid(pid_t pid,int *status,int options);

第一个参数:pid>0代表等待指定的进程。pid=-1,代表等待任意一个进程

第二个参数:输出型参数,获取子进程的推出状态,不关心则可以设置为NULL。

第三个参数://TODO下面有讲到

返回值:若等待成功该值为被等待的进程的pid,否则为-1。

 

这里的进程退出状态不能简单的当作int类型来看,可以当作位图来看,具体细节如下图示:

 

我们可以根据退出状态的低7位是否位0来判断进程是否是正常终止的

(一)wait()

如果父进程创建了多个子进程,要保证wait()调用次数和子进程数量个数相同

#include <stdio.h>                                                                                                                                                                                                                                                           
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <sys/types.h>

int main()
{

    pid_t id=fork();
    if(id<0)
    {
        perror("fork");
        exit(1);
    }
    if(id==0)
    {//child1
        printf("child 1 pid:%d\n",getpid());
        exit(0);
    }
    if(id>0)
    {
        pid_t pid=fork();
        if(pid<0)
        {
            perror("fork");
            exit(1);
        }
        if(pid==0)
        {//child 2
            printf("child 2 pid: %d\n",getpid());
            sleep(1);
            exit(0);
        }
        if(pid>0)
        {//father
        printf("parent pid:%d\n",getpid());
        wait(NULL);//这里父进程只等待了一次
        while(1)
        {
        sleep(1);

        }

        }

    }
}

 

上面的父进程创建了两个子进程,但是只等待了一次,并且并不确定等待是哪个子进程,这样就会出现僵尸进程

 

(二)waitpid()

可以根据第3个字节求出退出状态。

include <stdio.h>                                                                                                                                                                                                                                                           
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{
    pid_t pi=fork();
    if(pi<0)
    {
        perror("fork");
    }
    else
    {
        if(pi==0)
        {//child
            int count=3;
            while(count--)
            {
                printf("child:%d  %d\n",getpid(),getppid());
                sleep(1);
            }
            printf("child quit\n");
            exit(13);//注意这里退出码为13
        }
        else
        {
            int status;
            printf("father:%d\n",getpid());
            pid_t ret=waitpid(pi,&status,0);
            if(ret)
            {
                printf("sig:%d,exit code:%d\n",status&0x7f,(status>>8)&0xff);
            }
            printf("father quit\n");
        }
    }
    _exit(2);
}

 

 

这里可以了解到进程时正常退出的,并且退出码为13

再来看一种情况,我们让进程异常退出,在进程运行中,将进程kill掉(在另外一个终端下进行)

#include <stdio.h>                                                                                                                  #include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{
    pid_t pi=fork();
    if(pi<0)
    {
        perror("fork");
    }
    else
    {
        if(pi==0)
        {//child
            int count=100;//这里设置大一点的数
            while(count--)
            {
                printf("child:%d  %d\n",getpid(),getppid());
                sleep(1);
            }
            printf("child quit\n");
            exit(13);//注意这里退出码为13
        }
        else
        {
            int status;
            printf("father:%d\n",getpid());
            pid_t ret=waitpid(pi,&status,0);
            if(ret)
            {
                printf("sig:%d,exit code:%d\n",status&0x7f,(status>>8)&0xff);
                    //   status&0x7f拿到后7位,看是否正常退出
                    //    (status>>8)&0xff拿到第三个字节,即退出状态
            }
            printf("father quit\n");
        }
    }
    _exit(2);
}

运行之后,在另一终端下将子进程kill掉,

 

 

看到上面用的时9号信号将进程异常终止的,所以状态中的信号位置就为9,

上面是我们自己计算的终止信号和退出状态,其实系统中有两者宏供我们使用(但是我老记不住)

WIFEXITED(status)//(相当于(status&0x7f==0) ,查看进程是否正常退出) 为真 则表示正常退出则为进程退出状态,

WEXITSTATUS(status)//(相当于 (status>>8)&0xff)上面WIFEXITED为真,则提取退出码

例如下面代码

 
 
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{
    pid_t pi=fork();
    if(pi<0) 
    {
        perror("fork");
    } 
    else 
    { 
        if(pi==0) 
        {//child 
            int count=3; 
            while(count--) 
            {
                printf("child:%d %d\n",getpid(),getppid()); sleep(1); 
            } 
            printf("child quit\n");
            exit(13);//注意这里退出码为13 
        } 
        else 
        {
            int status;
            printf("father:%d\n",getpid());
            pid_t ret=waitpid(pi,&status,0);
            if(ret) 
            { 
                if( WIFEXITED(status)) 
                printf("exit code:%d\n",WEXITSTATUS(status));
                else 
                {
                     printf("异常终止\n");
                }
            }
            printf("father quit\n");
        } 
    }
 
    exit(0);
}




 

(三)非阻塞式等待

在上面两种等待的方式,父进程都是在等待的时候处于阻塞状态,当在waitpid()中的第三个参数为WNOHANG是,这时就是非阻塞式等待

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{ 
    pid_t pi=fork();
    if(pi<0) 
    { 
        perror("fork"); 
    } 
    else 
    { 
        if(pi==0)
        {//child 
            int count=3;
            while(count--) 
            { 
                printf("child:%d %d\n",getpid(),getppid()); 
                sleep(3); 
            }
            printf("child quit\n");
            exit(13);
            //注意这里退出码为13
        } 
        else
        {//father 
            int status; 
            printf("father:%d\n",getpid()); 
            while(1)
            { 
                pid_t ret=waitpid(pi,&status,WNOHANG);
                if(ret>0) 
                {//等待到子进程
                    if( WIFEXITED(status)) 
                    printf("exit code:%d\n",WEXITSTATUS(status));
                    break;
                }
                else 
                { 
                    if(ret==0)
                    {//等待的子进程没有结束,不予等待,去做别的事情
                        printf("child if running ,to do other thing\n"); 
                    } 
                    else
                    {
                        //错误状态 break;
                    }
                }
                sleep(1); 
            } 
            printf("father quit\n"); 
        } 
    } 
    exit(0);
} 

 

         

 

 

 

 

 

看到上面这种非阻塞式等待,父进程以循环的方式来查看子进程是否结束(等待子进程结束),若此时子进程没有结束,父进程可以转去做别的事情,而不是阻塞在这里啥也不做。

 

当父进程有多个子进程时:

此处应用的时轮询式非阻塞式等待

 

 

include <stdio.h>                                                                                                                                           
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <sys/types.h>

int main()
{
    pid_t id=fork();
    if(id<0)
    {
        perror("fork");
        exit(1);
    }
    if(id==0)
    {//child1
        printf("child 1 pid:%d\n",getpid());
        sleep(1);
        exit(0);
    }
    if(id>0)
    {
        pid_t pid=fork();
        if(pid<0)
        {
            perror("fork");
      exit(1);
        }
        if(pid==0)
        {//child 2
            printf("child 2 pid: %d\n",getpid());
            sleep(1);
            exit(0);
        }
        if(pid>0)
        {//father
        printf("parent pid:%d\n",getpid());
        int ret=0;
            while(1)
            {
                if((ret=waitpid(-1,NULL,WNOHANG))==0)
                {//说明没有等待子进程
                    printf("Do other thing\n");
                }
                if(ret<0)
                {//说明父进程已经没有进程可以等待了
                    break;
                }
                if(ret>0)
                {//说明父进程等到了一个子进程                                                                                                                
                    printf("Wait child pid:%d\n",ret);
                }
                sleep(1);
            }
        }
    }
}            

 

 

自己之前的一个问题没有解决:

Makefile 编写时用.PHONY定义一个伪目标,当我们普通的目标make时会根据依赖文件修改时间和目标生成时间来判断是否还需要执行编译语句,但是定义为伪目标的话会每次都认为是依赖文件每次都是最新的,所以每次都会重新生成伪目标,

静态链接:将静态库文件(.a)在程序的链接阶段被复制到程序中

动态链接:将动态库文件(.so)在程序运行时由系统自行的加载到内存中

 

 

 

 

  • 4
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以编写一个简单的程序来测试 exit 和 _exit 系统调用的区别。在程序中,我将会使用 exit 和 _exit 分别退出进程,并观察它们的行为差异。 下面是测试程序的代码: ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> int main() { printf("Before exit()...\n"); exit(0); printf("After exit()...\n"); // 这行代码将不会执行 printf("Before _exit()...\n"); _exit(0); printf("After _exit()...\n"); // 这行代码将会执行 return 0; } ``` 在程序中,我使用了 printf 函数打印一些信息,并在调用 exit 和 _exit 前后添加了一些语句,以观察它们的行为。 当我们运行程序时,我们会看到以下输出: ``` Before exit()... ``` 这表明程序成功调用了 exit 函数,并在调用 exit 后立即退出进程。我们还可以看到程序没有输出 "After exit()..." 的信息,这是因为在调用 exit 后,程序不再执行任何语句。 接下来,我们会看到以下输出: ``` Before _exit()... After _exit()... ``` 这表明程序成功调用了 _exit 函数,并在调用 _exit退出进程。与 exit 不同,_exit 函数会立即终止进程,不会执行后续的语句。因此,我们可以看到程序输出了 "After _exit()..." 的信息。 综上所述,exit 和 _exit 系统调用的主要区别在于它们退出进程的方式。exit 会先执行一些清理工作,然后终止进程,而 _exit 会立即终止进程,不会执行任何清理工作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值