linux-进程退出

进程终止

  • 进程终止
    • 进程终止原因
  • 进程返回值
    • echo $?
    • exit()和_exit()
  • 父进程获取进程退出码
    • wait()方法
    • waitpid()方法
    • 获取子进程的退出码(status)
  • 进程的非阻塞等待(WNOHANG)
  • 问题

进程终止

进程终止原因

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

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

进程终止时,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 }
~

运行结果如下:
在这里插入图片描述

exit()和_exit()

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

#include<stdlib.h>
void exit(int status);
#include<unistd.h>
void _exit(int status);

status是进程的退出状态码。

在这里插入图片描述
从该例子可以看出,exit封装了_exit,并且有更丰富的功能。

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

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

在这里插入图片描述

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 }   

在这里插入图片描述

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打印)。

在这里插入图片描述
将参数设置成:pid = -1,status=NULL,WNOHANG=0时,等价于wait使用。

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

有了上面的两个函数之后,我们就能获取子进程的退出码。
实际上status不能简单当作整形来看待,可以当位图来看。具体细节如下,我们只看低16位。
其中8-15位为进程的退出码(退出状态位)。
0~6位为终止信号的数字。
在这里插入图片描述

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

  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 }     

在这里插入图片描述

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

在子进程运行完之前,使用9号信号杀死它,结果是,状态位为0,信号位为9。
在这里插入图片描述

在这里插入图片描述

使用宏来获取子进程是否正常退出和退出码
代码修改部分如下:参考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       }

运行结果如下:
在这里插入图片描述
=程序发生异常,子进程的退出码就没有意义了(因为结果不对,或者子进程都可能没有跑完)。

进程的非阻塞等待(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函数获取退出码呢?全局变量不可以吗?
    进程具有独立性,如果数据发生写时拷贝,那么全局变量就会变成两份数据,父进程拿不到。

  2. wait/waitpid系统调用函数如何读取退出码的呢?
    因为子进程退出时,变成僵尸进程(资源被释放),但是其pcb仍然保留在内存中,函数通过访问pcb来获取进程的退出码。
    在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/220679.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【多线程】-- 12 线程协作之生产者消费者问题及解决办法

多线程 9 线程协作 “生产者消费者问题” ——并非二十三种设计模式之一 9.1 生产者消费者问题 “线程通信” 应用场景&#xff1a;生产者和消费者问题 假设仓库中只能存放一件产品&#xff0c;生产者将生产出来的产品放入仓库&#xff0c;消费者将仓库中产品取走消费如果…

linux 服务的JDK安装

1.jdk安装 1.1 jdk下载 官网下载地址&#xff1a;https://www.oracle.com/java/technologies/downloads/ 这里我使用的是rpm的方式&#xff0c;所以下载rpm的包。如下图&#xff1a;1.2 jdk的安装 下载完成后&#xff0c;会有一个以rpm结尾的包&#xff0c;例如&#xff1a;jd…

【恋上数据结构】哈夫曼树学习笔记

哈夫曼树 哈夫曼编码&#xff08;Huffman Coding&#xff09; 哈夫曼编码&#xff0c;又称为霍夫曼编码&#xff0c;它是现代压缩算法的基础 假设要把字符串 [ABBBCCCCCCCCDDDDDDEE] 转成二进制编码进行传输。 可以转成 ASCII 编码 (6569&#xff0c;10000011000101) &…

牛客算法题【HJ96 表示数字】golang实现

题目 HJ96 表示数字 golang实现 package mainimport ("fmt""unicode" )func main() {s : ""var s_o stringvar char_pre, r runefor {n, _ : fmt.Scan(&s)if n 0 {break} else {for _, r range s {if unicode.IsDigit(r) {if !unicode.…

前端模拟新闻列表ajax请求 mocky

效果图&#xff1a; <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title> </head><style>ul {display: flex;flex-wrap: wrap;justify-content: space-between;}ul::after{content: ;width: 30%;}a…

L1-004:计算摄氏温度

题目描述 给定一个华氏温度F&#xff0c;本题要求编写程序&#xff0c;计算对应的摄氏温度C。计算公式&#xff1a;C5(F−32)/9。题目保证输入与输出均在整型范围内。 输入格式&#xff1a;输入在一行中给出一个华氏温度。 输出格式&#xff1a;在一行中按照格式“Celsius C”…

创建vue3数学符号选择器(vue3+elementPlus+ts)

本文包含两种效果&#xff1a; 效果一&#xff1a;数学符号只能选择一次&#xff0c;选中的数学符号高亮 效果二&#xff1a;相同的数学符号可以选择多次&#xff0c;当前选中的数学符号高亮 首先创建math.ts定义常见数学符号数组 : export const symbols [{ id: 1, value: …

数据结构与算法-D2D3线性表之顺序表

线性表&#xff1a;包含若干数据元素的一个线性序列&#xff0c;特征如下&#xff1a; 1&#xff09;对非空表&#xff0c;a0是表头&#xff0c;无前驱&#xff1b; 2&#xff09;an-1是表尾&#xff0c;无后继&#xff1b; 3&#xff09;其他元素仅且仅有一个前驱&#xff0c;…

CSS面经(未完待续)

1. CSS选择器及其优先级 !important > 行内样式 > id选择器 > 类/伪类/属性选择器 > 标签/伪元素选择器 > 子/后台选择器 > *通配符 2. 重排和重绘是什么&#xff1f;浏览器的渲染机制是什么&#xff1f; 重排(回流)&#xff1a;当增加或删除dom节点&…

Centos7上安装Redis

第一步&#xff1a;安装Redis依赖 yum install -y gcc tcl //需要使用管理员权限第二步&#xff1a;下载上传安装包并解压 下载地址redis中文官网 上传成功后解压 输入tar -zxvf &#xff08;redis版本&#xff09;,即可解压成功 进入redis目录&#xff0c;运行编译命令&am…

人工智能学习3(特征变换:特征数值化)

编译工具&#xff1a;PyCharm 有些编译工具不用写print可以直接将数据打印出来&#xff0c;pycharm需要写print才会打印出来。 文章目录 编译工具&#xff1a;PyCharm 概念1.特征类型分类型二值型顺序型数值型 2.特征数值化练习13.特征数值化练习24.特征二值化使用sklearn库自…

信号是怎么搞到电磁波上面去的呢?

在之前的文章中&#xff0c;我们曾多次讲到电磁波的美妙&#xff0c;但是有了电磁波就可以通信了吗&#xff1f; No&#xff0c;我们要把信息加载到电磁波上&#xff0c;这个电磁波就可以作为信息的载体来工作了。可是信号是怎么加载到电磁波上的呢&#xff1f; 今天我们一起…

Javafx实现浏览器

浏览器是一种计算机程序&#xff0c;主要用于显示互联网上的网页。通过浏览器&#xff0c;用户可以访问各种网站、搜索引擎、在线应用程序、社交媒体等。常见的浏览器包括Google Chrome、Mozilla Firefox、Safari、Microsoft Edge、Opera等。浏览器的功能不仅限于浏览网页&…

无线网卡填坑记

没想到我安装无线网卡这么波澜起伏~ 起因 近来刚在电脑上玩完了 Dishonored 2&#xff0c;紧接着继续着我的刺客信条之旅。总是觉得键盘鼠标玩起来不爽&#xff0c;还是手柄玩这种游戏才舒服。突然&#xff0c;灵光一现&#xff0c;我想到正好有闲置的 Switch 掌机没怎么玩&am…

【代码随想录】算法训练计划39

dp 1、62. 不同路径 题目&#xff1a; 求路径方案多少个 思路&#xff1a; 这道题就有点dp了哈 func uniquePaths(m int, n int) int {//dp&#xff0c;写过,代表的是多少种// 初始化dp : make([][]int, m)for i : range dp {dp[i] make([]int, n)dp[i][0] 1 // 代表到…

【数据结构】图<简单认识图>

对于下面的内容&#xff0c;大家着重观察和理解图即可&#xff0c;可以直接绕过一些文字性的概念&#xff0c;对图有一个大概的认识。 图 简单认识图图的定义有向图和无向图完全图无向完全图有向完全图 图的基本存储结构邻接矩阵存储邻接矩阵的优点 网络的邻接矩阵邻接表无向图…

看懂lscpu的输出

文章目录 1. lscpu1.1 Architecture1.2 逻辑核心数1.3 缓存1.4 CPU型号1.5 NUMA架构1.5.1 CPU多核架构1.5.2 多CPU Socket架构 2. cat /proc/cpuinfo2.1 关键字段 1. lscpu 通过lscpu查看当前系统的CPU信息。 [hadoopserver3 ~]$ lscpuArchitecture: x86_64 …

混音编曲软件tudio One 6.5.1 保姆级安装教程

根据软件大数据显示De-Esser驯服人声嘶嘶声和其他高频声音&#xff0c;和其他 Studio One 中新的去实体插件一样高效且直观易用&#xff0c;使用“收听”按钮查找有问题的频率&#xff0c;然后使用相关的旋钮和 S-Mon 功能拨入 S-Reduce 量即可。实际上我们可以这样讲工作流和协…

Linux(15):SELinux 初探

什么是 SELinux SELinux 是【Security Enhanced Linux】的缩写&#xff0c;字面上的意义就是安全强化的 Linux。 SELinux 是由美国国家安全局(NSA)开发的&#xff0c;开发原因&#xff1a;因为很多企业界发现&#xff0c;通常系统出现问题的原因大部分都在于【内部员工的资源…

Redis的三种消息队列实现方式

目录 前言 List实现消息队列 PubSub消息队列 Stream消息队列 三种实现方式对比 前言 为什么要使用Redis的消息队列&#xff1f; 成本低&#xff0c;对于RabbitMQ或是Kafka来说&#xff0c;已经是重量级的消息队列。 Redis的三种实现方式&#xff1a; List结构&#xff1…