Linux进程退出与等待

Linux进程退出与等待

int main()

{

return 10;

}

但是由于退出码都有特定的含义,所以不建议随便修改。

exit()与_exit() exit()和exit()也可以控制进程退出,其中括号中的内容为退出码。两者的区别在于:exit()退出后缓冲区会被刷新,而_exit退出后缓冲区不会进行刷新。

printf("hello exit");

sleep(5);

exit(1);

printf("wrong\n");

printf("hello exit");

sleep(5);

_exit(1);

printf("wrong\n");

其中使用exit退出可以打印出hello exit,因为刷新了缓冲区;而使用_exit退出没有打印出该字符串,因为没有刷新缓冲区。 这里将进程的退出码设为了1,可以在终端自行验证。 注意,虽然没有刷新缓冲区,操作系统也一定将缓冲区中的内容释放掉了,否则会造成内存泄漏。

2.进程等待 (1)进程等待的原因 进行进程等待的通常是父进程,父进程需要等待子进程执行结束之后再进行进程退出。

1.通过获取子进程退出的信息,来获得子进程退出的结果。 2.可以保证时序问题,子进程先退出,父进程后退出。 3.为避免子进程进入僵尸状态,父进程需要等子进程结束后回收子进程的资源。

(2)wait函数 我们在父进程中使用wait函数来进行进程的等待。下面我们将介绍两个函数来实现进程的等待。

返回值与参数 等待成功,wait将返回子进程的id,如果等待失败,wait会返回-1。 wait的参数status和waitpid的status表示一个意思,在下面会西索。

举例 父进程可以通过进程等待来达到回收僵尸进程的目的:

pid_t id=fork();

if(id==0)

{

int cnt=5;

while(cnt)

{

printf("child:%d is running,cnt is %d\n",getpid(),cnt);

cnt--;

sleep(1);

}

exit(0);

}

else

{

sleep(10);

printf("father wait begin\n");

pid_t ret=wait(NULL);

if(ret>0)

{

printf("father wait %d\n",ret);

}

else

{

printf("wait fail");

}

printf("wait success\n");

sleep(10);

}

这段代码的目的是:先让子进程运行5s,父进程休眠10s,这就导致了子进程在前5s是运行态,在后5s是僵尸状态(父进程在休眠,无法进行资源的回收)。当父进程进行进程等待的时候,回收了子进程的资源,此时子进程死亡。父进程在10s再退出。 当我们不进行进程等待的时候,父进程执行结束,子进程会变成孤儿进程被操作系统领养。 如果进行进程等待,子进程就不会被领养,资源被父进程回收后再死亡。 通过简单的命令行脚本我们可以进行观察。 wait其实有两种作用,1是等待子进程结束后回收,2是通过status获得子进程的信息。

(3)waitpid函数 返回值与参数 wait的返回值也是一个pid_t类型:

1.正常情况下,返回子进程的pid。 2.如果设置了选项WRONGHANG,而调用waitpid发现没有子进程可以被回收,则返回0。 3.如果嗲破蛹出错则返回-1。 waitpid有三个参数:

pid 一个进程可以有多个子进程,wait函数等待的是所有子进程执行结束,而waitpid可以通过参数pid来指定要结束的进程。 当pid为-1时,代表等待所有的进程结束,与wait相同。

pid_t ret=waitpid(id,NULL,0);

只需要将上面进程等待的代码改成这样,由于父进程中的fork返回值是子进程的id,赋值给了变量id,所以这里表示的是等待子进程执行结束。

status status的值 status是一个指针类型,是一个输出型参数。输出型参数的意义在于在waitpid中修改了status的值是通过指针修改的,对函数外界是有影响的。我们其实就是通过status来获得进程退出的结果的。 因此我们需要自己定义一个int类型的数据,并向status传入它的地址,然后通过该数据来获得进程退出的结果。 我们可以将子进程的退出码改为20,然后打印一下status的值来进行观察:

pid_t id=fork();

if(id==0)

{

int cnt=5;

while(cnt)

{

printf("child:%d is running,cnt is %d\n",getpid(),cnt);

cnt--;

sleep(1);

}

exit(10);

}

else

{

// sleep(10);

int status=0;

printf("father wait begin\n");

pid_t ret=waitpid(id,&status,0);

if(ret>0)

{

printf("father wait %d\n",ret);

printf("status is %d\n",status);

}

else

{

printf("wait fail");

}

printf("wait success\n");

// sleep(10);

}

此时我们发现status的值是2560,那么这个数字代表什么呢? 首先我们要明确,status是反映子进程退出时的状态的,而进程退出有三种情况:正常退出,结果正确;正常退出,结果错误;程序崩溃退出。所以status其实是反映这三种情况的。

status的构成 status指向的内容有32个比特位,但是只有低的16位有意义,高的16位没有意义。 程序崩溃即代码异常终止,本质上是收到了一个终止的信号,waitpid会首先判断是否是接收到终止信号导致进程结束,如果是则直接对status指向的内容进行修改。 其中终止信号占7位,coredump占一位。 如果不是程序异常终止,则status进行如下赋值: 因此我们就可以知道2560的由来了,退出码是10,status就是0000 1010 0000 0000的值刚好是2560。

通过status获得退出码 知道了status的构成,我们就可以通过移位操作,通过status来获得程序的退出码和退出信号。

(status>>8)&0xFF//获得退出码

status&(0x7F)//获得退出信号

了解了这一原理,我们还会得出这样的结论:bash就是通过wait方法获得的各个退出码,因此我们可以才能通过echo $?来获得各个进程的退出码。 我们发现如果要获得退出码还需要进行移位操作,非常的不方便,因此大佬们又引入了两个宏,来帮助我们获取退出码:

WIFEXITED(status):若为正常退出,则返回非0。 WEXITSTATUS(status):如果WIFEXITED为非0,则提取子进程的退出码。

if(WIFEXITED(status))

{

printf("father wait %d\n",ret);

printf("statusexit is %d\n",WEXITSTATUS(status));

}

此时可以打印到退出码10:

options waitpid的第三个选项options有两种取值:一种是0,一种是WNOHANG。 其中0表示的是阻塞等待,而WNOHANG表示的是非阻塞等待。 阻塞等待指的是父进程在等待子进程结束的期间内,不进行任何操作。而非阻塞等待指的是父进程在等待子进程结束的期间内不断调用waitpid的过程。(注意返回值条件,当设置WNOHANG且子进程没有结束时,返回值为0,否则返回子进程的pid)。 当取值为0,即为阻塞等待时,本质上其实就是将父进程由运行队列转移到等待队列。等子进程执行结束之后再将父进程由等待队列转移到运行队列。之前的例子都是阻塞赋值的例子。 我们也可以验证一下非阻塞赋值:

while(1)

{

pid_t ret=waitpid(id,&status,WNOHANG);

if(ret==0)

{

printf("do my things\n");

}

else if(ret>0)

{

printf("wait success\n");

break;

}

else

{

printf("wait failed\n");

}

sleep(1);

}

我们可以通过循环来使用非阻塞等待,帮助父进程完成其他内容。

3.总结 1.进程结束有三种原因。使进程结束有两种主要的方法。 2.进程等待有两个主要的函数,并且等待分为阻塞等待和非阻塞等待。 3…三连叭球球了。

相关推荐

剑网3红尘饮茶任务位置介绍 95级茶馆任务攻略
bt365验证不通过

剑网3红尘饮茶任务位置介绍 95级茶馆任务攻略

📅 08-15 👁️ 153
花了钱还要看广告?“套娃式”收费何时休? 海闻社讯 爱奇艺 时隔两周,再次登顶热搜,这次是因为基础会员不能跳过广告的问题。7月16日, 爱奇艺 在微博上回应称,基...
支付宝蚂蚁森林有人种樟子松了吗?
365app最新版安卓下载

支付宝蚂蚁森林有人种樟子松了吗?

📅 08-04 👁️ 6159