Linux进程等待

进程终止

一个进程的终止无非就是以下三种:
1.正常运行结束
2.运行结束但结果不正确
3.因为异常而中途退出
进程退出常见方法:
正常终止:
1.从main函数返回
2.调用exit
3._exit
异常终止:
Ctrl+c,通过信号终止

exit()函数

在main函数中

#include <stdio.h>
#include <stdlib.h>

int main()
{
    printf("hello Linux\n");
    exit(0);
    printf("hello!\n");     
    return 0;
}

在这里插入图片描述
echo $?用于打印输出上一进程的退出码
在普通函数中且打印时不加‘\n’的情况

#include <stdio.h>
#include <stdlib.h>

void func()
{
    printf("func");
    exit(0);
}
int main()
{
    printf("hello Linux\n");
    func();                 
    printf("hello!\n");
    return 0;
}

在这里插入图片描述
在普通函数中且打印时加‘\n’的情况

#include <stdio.h>
#include <stdlib.h>

void func()
{
    printf("func\n");        
    exit(0);
}
int main()
{
    printf("hello Linux\n");
    func();
    printf("hello!\n");
    return 0;
}

在这里插入图片描述
在除main函数中return和exit区别

#include <stdio.h>
#include <stdlib.h>

void func()
{
    printf("func\n");
    return;                 
}                           
int main()                  
{                           
    printf("hello Linux\n");
    func();                 
    printf("hello!\n");     
    return 0;               
}                           

在这里插入图片描述
通过上述例子我们可以看到无论是在main函数中还是普通函数中exit的作用都是直接退出进程,而return在main函数中是退出进程,在普通函数中只是退出该函数,不是把整个进程给退出了

_exit()函数

在函数打印时加‘\n’的情况

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>         

void func()
{
    printf("func\n");
    _exit(0);
}
int main()
{
    printf("hello Linux\n");
    func();
    printf("hello!\n");
    return 0;
}

在这里插入图片描述
在函数打印时不加‘\n’的情况

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void func()
{
    printf("func");         
    _exit(0);
}
int main()
{
    printf("hello Linux\n");
    func();
    printf("hello!\n");
    return 0;
}

在这里插入图片描述
_exit()和exit()一样无论是在main函数还是普通函数中都是退出进程而不是退出函数,不同的是_exit前打印函数在不加’\n’时,不会把打印的东西打印出来,而exit函数在不加‘\n’时会打印出来
注意:
exit也会调用_exit函数,但在调用_exit函数之前做了以下工作:
1.执行用户通过atexit或on_exit去定义的清理函数。
2.关闭所有打开的流,所有的缓存数据均被写入
3.调用_exit
exit是库函数接口,_exit是系统调用接口
在这里插入图片描述

return退出

return退出进程是一种更为常见的退出进程的方式,在main函数中执行return n;等同于执行exit(n),因为调用main函数运行时,函数会将main函数的返回值当做exit的参数

退出码

什么是退出码?为什么要有退出码?
退出码其实就是一些运行结果的代号,比如0表示正常退出,2表示文件不存在等,通过返回退出码从而让关心他的父进程直到他做的事情怎么样了
我们来打印一下退出码,看一下对应的退出码都代表什么意思

#include <stdio.h>  
#include <string.h>  
  
void print()  
{  
    for(int i = 0; i < 200; i++)  
    {  
        printf("%d: %s\n", i, strerror(i));
    }  
    return;  
}  
  
int main()  
{
    print();
    return 0;
}

在这里插入图片描述
上述只列出部分退出码
在这里插入图片描述
错误信息是通过的strerror()函数来输出的

Linux进程等待

进程在退出时,会返回退出码,而这个退出码一般是交给父进程,让关心它的父进程知道他办的事情怎么样了,在上述过程中我们一直都是使用echo $?去获取最近的退出进程的退出码,显然这种方式不是有效地,因为并不是直接交给了父进程,接下来我们将通过两个函数让父进程拿到子进程的退出码
进程等待的必要性:
1.让父进程知道父进程交给他做的事情他做的怎么样了
2.回收该子进程的资源,避免内存泄漏,因为处于该僵尸状态的子进程,即使是kill -9也无能为力

在这里插入图片描述
先来介绍一下wait函数:
1.参数:这里有一个形参,类型是指针,这其实是一个输出型参数本地状态(local status),就是参数变化外面调用部分对应的变量也会发生改变,而这个指针里面放的就是该进程的退出状态,如果不关心可以设为NULL。
2.返回值:返回值pid_t,若回收成功返回被等待进程的pid,失败则返回-1.

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
                   
void Func()        
{                    

    for(int i = 0; i < 10; i++)
    {
        pid_t id = fork();
        if(id > 0)             
        {                                                                 
            printf("I am father ppid: %d pid: %d\n", getppid(), getpid());
            sleep(5);
            wait(NULL);//wait函数回收子进程
                                                                          
        }            
        else if(id == 0)
        {
            printf("I am child  ppid: %d pid: %d\n", getppid(), getpid());
            sleep(2);   
            exit(0);
        }                                                                 
        else         
        {           
            perror("进程错误!\n");
        }   
    }    
    sleep(5);                     
}        
     
int main()   
{
    Func();
    return 0;
}

waitpid函数

在这里插入图片描述
我们可以看到,相比于wait函数,wait_pid多了两个参数,这两个参数又分别有什么作用?
1.第一个参数:用于等待指定pid的进程,如果给-1,就是可以等待任意退出的进程。
2.第二个参数:与wait函数相同都是输出型参数,返回状态信息。
3.第三个参数:用于选择等待方式,要等待的指定子进程有三种情况,一种是已经退出正等待回收,一种是还在运行,另一种是该进程不存在。在进程回收等待的过程中,有两种方式,一种是阻塞式等待一种是非阻塞式等待,阻塞式等待,如果该进程未执行完退出,就等到该进程退出,非阻塞式等待,如果该进程还在运行,就不再等待该进程完成。
非阻塞式等待更有利于利用资源,减少进程阻塞的发生。
options值为0则表示阻塞式等待,值为WNOHANG表示非阻塞式等待
返回值
当正常返回的时候waitpid返回收集到的子进程的进程ID;
如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
在这里插入图片描述

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

void Func()
{

    for(int i = 0; i < 10; i++)
    {
        pid_t pid = fork();
        if(pid > 0)
        {
            printf("I am father ppid: %d pid: %d\n", getppid(), getpid());
            sleep(3);
        }
        else if(pid == 0)
        {
            printf("I am child  ppid: %d pid: %d\n", getppid(), getpid());
            sleep(2);
            exit(0);
        }
        else
        {
            perror("进程错误!\n");
        }

            int status = 0;
            pid_t ret = waitpid(-1, &status, 0);
            if(ret == pid && WIFEXITED(status))
            {
                printf("等待子进程成功\n");
            }                                                             
            else{
                printf("等待子进程失败\n");
             }
    }
    sleep(5);
}

int main()
{
	Func();
	return 0;
}

参数status

参数status并不能只看做一个简单整型变量,他里面存的是状态信息,那又是怎么存储的呢?
在这里插入图片描述
一个整型占32比特位,这里的int是被当做好几部分使用的
1.0-7位表示当被信号所杀的终止信号是什么
当终止信号为0时,则表示正常终止,此时查看退出状态信息,这里的退出状态信息时可信的,当终止信号不为0时,说明是异常退出,此时就8-15位的退出状态就是不可信的。因为8-15位是正常终止下的退出状态,而是不是正常终止就要看0-7位终止信号是多少
2.其中8-15位表示正常终止情况下的退出状态信息,若8-15为都为0,则表示正常终止下的正常退出,8-15不为0时表示正常运行结束但结果不正确。剩下的部分并没有被使用。

查看进程是否是正常退出:WIFEXITED(status)—(status) & 0X7F): 若为正常终止子进程返回的状态,则为真。
查看进程的退出码:WEXITSTATUS(status)----(status >> 8) & 0XFF): 若WIFEXITED非零,提取子进程退出码。

接下来我们通过程序来取出这部分的退出码:

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

void Func()
{

    for(int i = 0; i < 10; i++)
    {
        pid_t pid = fork();
        if(pid > 0)
        {
            printf("I am father ppid: %d pid: %d\n", getppid(), getpid());
            sleep(3);
        }
        else if(pid == 0)
        {
            printf("I am child  ppid: %d pid: %d\n", getppid(), getpid());
            sleep(2);
            exit(0);
        }
        else
        {
            perror("进程错误!\n");
        }

            int status = 0;
            pid_t ret = waitpid(-1, &status, 0);
            if(ret == pid && WIFEXITED(status))                            
            {
                printf("等待子进程成功\n");
                printf("对应子进程的退出码:%d\n", (status >> 8) & 0XFF);
            }
            else{
                printf("等待子进程出异常了\n");
                printf("对应子进程的退出码:%d\n", (status) & 0X7F);
             }
    }
    sleep(5);
}
int main()
{
    Func();
    return 0;
}

对于上面的(status) & 0X7F)和(status >> 8) & 0XFF)可以通过库里面的宏(宏函数)来实现,WIFEXITED(status)和WEXITSTATUS(status)这两个宏就是用来计算上述两个操作的。
WIFEXITED(status)和(status) & 0X7F)等价,WEXITSTATUS(status)和(status >> 8) & 0XFF)等价
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

梦想很美

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值