linux-进程退出

简介: 进程终止进程终止原因进程终止原因有三种情况。代码运行完毕,结果正确代码运行完毕,结果不正确代码异常终止

进程终止

进程终止原因

进程终止原因有三种情况。

  • 代码运行完毕,结果正确
  • 代码运行完毕,结果不正确
  • 代码异常终止

进程终止时,OS要释放进程申请的向相关内核数据结构和对应的数据和代码。(就是释放系统资源

进程返回值

进程运行后,一般都有返回值,比如说main函数里面的return 0。

而0一般表示:success。非0表示:程序运行的结果不正确。

echo $?

命令行符号的意义是:获取最近一个进程,执行完毕时的退出码。

  1 #include<stdio.h>
  2 
  3 
  4 int main()
  5 {
  6   printf("hello world\n");
  7   return 111;                                                                                                                                                           
  8 }
~

运行结果如下:

e9953c000c5c43f0a48fce91df43ddf3.png

exit()和_exit()

这两个函数都可以让进程终止,并且返回指定的status。

#include<stdlib.h>

void exit(int status);

#include<unistd.h>

void _exit(int status);

status是进程的退出状态码。


142f9507328b481e8a68fdb2fd61ef48.png

从该例子可以看出,exit封装了_exit,并且有更丰富的功能。

如下所示:

打印结尾没有’\n’(\n的作用是输出换行,刷新缓冲区),也能照样打印出该结果来。

 10 int main()
 11 {
 12   printf("hello world");
 13   exit(100);
 14 }         


ac8ce0eb814346dd90bdbd46654c8c7d.png

return 退出:

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

父进程获取进程退出码

wait()方法

pid_t wait(int* status);

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

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

如下代码:父进程在阻塞式等待(一直等待wait的退出码),子进程执行完,父进程才开始执行wait下面的内容。

 1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<sys/wait.h>
  4 #include<sys/types.h>
  5 #include <stdlib.h>
  6 int main()
  7 {
  8 
  9   pid_t id = fork();
 10 
 11 
 12   if(id==0)
 13   {
 14     //子进程
 15     int cnt = 5;
 16     while(cnt--)
 17     {
 18       printf("我是子进程,我的pid = %d, ppid = %d , %d\n",getpid(), getppid(), cnt);
 19       sleep(1);
 20     }
 21     exit(1);
 22   }
 23   else if(id>0)
 24   {
 25     printf("我是父进程:---------------\n");  
 26     pid_t ret = wait(NULL);
 27     if(ret>0)
 28     {
 29       printf("等待成功,返回值为:%d \n", ret);
 30     }
 31   }
 32   return 0;
 33 }   

1cf15787161e4a05895bce79ea2be272.png

waitpid()方法

升级版的wait方法。

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

参数:

pid = -1,等待任何一个子进程,与wait等效。

pid > 0,等待其进程ID与pid相等的子进程。

status:输出型参数:

WIFEXITED(status):若为正常终止子进程返回的状态,则为真。(查看进程是否正常退出)

WEXITSTATUS(status):若WIFEXITED非零,提取子进程的退出码。(查看进程的退出码)

options:

WNOHANG:默认为0,表示不使用这个参数。若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID。(用来完成非阻塞等待的接口)


返回值:

正常返回时返回收集到子进程的ID。

如果选项设置了WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0。

如果调用中出错,则返回-1,这时errno会被设置成相应的值指示错误所在。(可以使用perror打印)。

288f1ba90a244cd79182374149a3efb7.png

获取子进程的退出码(status)

有了上面的两个函数之后,我们就能获取子进程的退出码。

实际上status不能简单当作整形来看待,可以当位图来看。具体细节如下,我们只看低16位。

其中8-15位为进程的退出码(退出状态位)。

0~6位为终止信号的数字。

cb252a7ec1ad48788dd35f2f9487f0be.png

获取子进程正常退出的退出码:

  1 #include<stdio.h>
  2 #include<unistd.h>  
  3 #include<sys/wait.h> 
  4 #include<sys/types.h>
  5 #include <stdlib.h>
  6 int main()          
  7 {                                                                                                                                                                       
  8   pid_t id = fork();
  9   if(id==0)
 10   {
 11     //子进程
 12     int cnt = 5;
 13     while(cnt--)
 14     {
 15       printf("我是子进程,我的pid = %d, ppid = %d , %d\n",getpid(), getppid(), cnt);
 16       sleep(1);
 17     }
 18     exit(1);   //退出时,返回号码。
 19   }
 20   else if(id>0)
 21   {
 22     int status = 0;
 23     printf("我父进程,我的pid = %d, ppid = %d \n",getpid(), getppid());
 24     pid_t ret = waitpid(id,&status, 0);  //阻塞式等待,一直等,直到子进程退出
 25     if(ret>0)
 26     {        
      printf("等待成功,返回值为ret:%d  子进程退出码:%d ,信号位为:%d \n", ret    , (status>>8)&0xFF,(status&0x7f));//获取高8位,低7位为信号位
 28     }                                                                                            
 29   }                                                                                              
 30   return 0;
 31 }     

fe082274868f433581d5f57cd22d9bbb.png

若进程非正常退出,它的信号位就可以看出是出什么错了

在子进程运行完之前,使用9号信号杀死它,结果是,状态位为0,信号位为9。

eaad37280a1a4cc39438cea7927dc8aa.png

9f9948118faf44198bf11ef565183e05.png

使用宏来获取子进程是否正常退出和退出码

代码修改部分如下:参考waitpid参数介绍部分

 24     if(ret>0)
 25     {
 26       if(WIFEXITED(status))
 27       {
 28         //子进程正常退出                                                        
 29         printf("子进程执行完毕,子进程的退出码位:%d \n",WEXITSTATUS(status));
 30       }
 31       else{
 32         printf("子进程异常退出:%d \n",WIFEXITED(status));
 33       }

运行结果如下:

53997a7769ee4effa4ea74b7e99ec8be.png

=程序发生异常,子进程的退出码就没有意义了(因为结果不对,或者子进程都可能没有跑完)。

进程的非阻塞等待(WNOHANG)

就是父进程不能光等,父进程在等待子进程退出码的同时,也可以运行一些东西。

下面为代码示例:(注意使用c++11标准编译)

#include <iostream>
#include<vector>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdio.h>
#include<stdlib.h>
//定义非阻塞等待父进程干的事情
typedef void (*hander_t)();  //hander_t = void(*)()
std::vector<hander_t> handlers; 
void fun_1()
{
  printf("work_1\n");
}
void fun_2()
{
  printf("work_2\n");
}
void Load()
{
  handlers.push_back(fun_1);
  handlers.push_back(fun_2);
}
int main()
{
  pid_t id = fork();
  if(id==0)
  {
    int cnt = 5;
    while(cnt--)
    {
      printf("I am child, my pid = %d, my ppid = %d, %d \n",getpid(),getppid(),
          cnt);
      sleep(1);
    }
    exit(11);
  }
  else
  {
    int quit = 0;
    while(1)
    {
      int status = 0;
      pid_t ret = waitpid(-1,&status,WNOHANG);
      if(ret>0)
      {
        printf("等待成功,退出码为:%d \n",WEXITSTATUS(status));
        quit = 1;
        break;
      }
      else{
          if(ret==0)
          {
            printf("子进程还没结束,我还得继续干活\n");
            if(handlers.empty()) Load();
            for(hander_t iter : handlers)
            {
              iter();
            }
          }
          if(ret<0)
          {
            printf("child running failed, signal = %d \n", status&&0x7f);
            quit = 1;
            break;
          }
      }
      sleep(1);
    }
 }
}

运行结果就是在等待子进程的退出码的过程中,父进程也在做一些其他的事情。

问题

  1. 为什么要用wait/waitpid函数获取退出码呢?全局变量不可以吗?
    进程具有独立性,如果数据发生写时拷贝,那么全局变量就会变成两份数据,父进程拿不到。

wait/waitpid系统调用函数如何读取退出码的呢?

因为子进程退出时,变成僵尸进程(资源被释放),但是其pcb仍然保留在内存中,函数通过访问pcb来获取进程的退出码。

962ddfc7e2584efea13b79b06bdec054.png

目录
相关文章
|
12月前
|
存储 Linux API
【Linux进程概念】—— 操作系统中的“生命体”,计算机里的“多线程”
在计算机系统的底层架构中,操作系统肩负着资源管理与任务调度的重任。当我们启动各类应用程序时,其背后复杂的运作机制便悄然展开。程序,作为静态的指令集合,如何在系统中实现动态执行?本文带你一探究竟!
【Linux进程概念】—— 操作系统中的“生命体”,计算机里的“多线程”
|
算法 Linux 调度
深入理解Linux操作系统的进程管理
本文旨在探讨Linux操作系统中的进程管理机制,包括进程的创建、执行、调度和终止等环节。通过对Linux内核中相关模块的分析,揭示其高效的进程管理策略,为开发者提供优化程序性能和资源利用率的参考。
417 32
|
10月前
|
并行计算 Linux
Linux内核中的线程和进程实现详解
了解进程和线程如何工作,可以帮助我们更好地编写程序,充分利用多核CPU,实现并行计算,提高系统的响应速度和计算效能。记住,适当平衡进程和线程的使用,既要拥有独立空间的'兄弟',也需要在'家庭'中分享和并行的成员。对于这个世界,现在,你应该有一个全新的认识。
351 67
|
9月前
|
Web App开发 Linux 程序员
获取和理解Linux进程以及其PID的基础知识。
总的来说,理解Linux进程及其PID需要我们明白,进程就如同汽车,负责执行任务,而PID则是独特的车牌号,为我们提供了管理的便利。知道这个,我们就可以更好地理解和操作Linux系统,甚至通过对进程的有效管理,让系统运行得更加顺畅。
258 16
|
9月前
|
Unix Linux
对于Linux的进程概念以及进程状态的理解和解析
现在,我们已经了解了Linux进程的基础知识和进程状态的理解了。这就像我们理解了城市中行人的行走和行为模式!希望这个形象的例子能帮助我们更好地理解这个重要的概念,并在实际应用中发挥作用。
166 20
|
8月前
|
监控 Shell Linux
Linux进程控制(详细讲解)
进程等待是系统通过调用特定的接口(如waitwaitpid)来实现的。来进行对子进程状态检测与回收的功能。
190 0
|
8月前
|
存储 负载均衡 算法
Linux2.6内核进程调度队列
本篇文章是Linux进程系列中的最后一篇文章,本来是想放在上一篇文章的结尾的,但是想了想还是单独写一篇文章吧,虽然说这部分内容是比较难的,所有一般来说是简单的提及带过的,但是为了让大家对进程有更深的理解与认识,还是看了一些别人的文章,然后学习了学习,然后对此做了总结,尽可能详细的介绍明白。最后推荐一篇文章Linux的进程优先级 NI 和 PR - 简书。
252 0
|
8月前
|
存储 Linux Shell
Linux进程概念-详细版(二)
在Linux进程概念-详细版(一)中我们解释了什么是进程,以及进程的各种状态,已经对进程有了一定的认识,那么这篇文章将会继续补全上篇文章剩余没有说到的,进程优先级,环境变量,程序地址空间,进程地址空间,以及调度队列。
151 0
|
8月前
|
Linux 调度 C语言
Linux进程概念-详细版(一)
子进程与父进程代码共享,其子进程直接用父进程的代码,其自己本身无代码,所以子进程无法改动代码,平时所说的修改是修改的数据。为什么要创建子进程:为了让其父子进程执行不同的代码块。子进程的数据相对于父进程是会进行写时拷贝(COW)。
210 0
|
12月前
|
存储 网络协议 Linux
【Linux】进程IO|系统调用|open|write|文件描述符fd|封装|理解一切皆文件
本文详细介绍了Linux中的进程IO与系统调用,包括 `open`、`write`、`read`和 `close`函数及其用法,解释了文件描述符(fd)的概念,并深入探讨了Linux中的“一切皆文件”思想。这种设计极大地简化了系统编程,使得处理不同类型的IO设备变得更加一致和简单。通过本文的学习,您应该能够更好地理解和应用Linux中的进程IO操作,提高系统编程的效率和能力。
533 34