Linux 进程(九) 进程等待

        子进程退出,父进程如果不管不顾,就可能造成‘僵尸进程’的问题,进而造成内存泄漏,所以父进程回收子进程是必然要做的。
        另外,进程一旦变成僵尸状态,那就刀枪不入,“杀人不眨眼”的kill -9 也无能为力,因为谁也没有办法杀死一个已经死去的进程。
        最后,父进程派给子进程的任务完成的如何,我们需要知道。如,子进程运行完成,结果对还是不对,或者是否正常退出。
        父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息(子进程退出的信息包括进程退出码,我进程推出时候的信号)。

        进程等待的方法:

#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int*status);
返回值:
成功返回被等待进程pid,失败返回-1。
参数:
输出型参数,获取子进程退出状态,不关心则可以设置成为NULL

        waitpid方法:

pid_ t waitpid(pid_t pid, int *status, int options);
返回值:
当正常返回的时候waitpid返回收集到的子进程的进程ID;
如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
参数:
pid:
Pid=-1,等待任一个子进程。与wait等效。
Pid>0.等待其进程ID与pid相等的子进程。
status:
WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
options:
WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进
程的ID。

        下面通过一个小代码来验证:wait

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


int main()
{
   pid_t id = fork();
   if(id == 0)
   {
      int cnt = 5;
      while(cnt--)
      {
        printf(" i am child procrss,pid:%d,ppid:%d\n",getpid(),getppid());
        sleep(1);
      }
      printf("子进程退出\n");
      exit(1);
   }
   printf("父进程开始休眠,等待子进程\n");
   sleep(10);
   printf("父进程开始回收\n");

   pid_t rid = wait(NULL);
   if(rid > 0)
   {
    printf("wait success,rid:%d\n",rid);
   }

    printf("父进程回收结束\n");
    sleep(3);

  return 0;
}

        该代码,总共运行十三秒,前五秒父进程子进程一起运行,前五秒过后子进程退出。中间五秒父进程等待回收子进程,这时会看到子进程僵尸的状态,中间五秒后,父进程回收子进程。最后三秒,父进程休眠三秒,然后程序退出。

        注意:fork之后,父进程和子进程谁先运行是不确定的,由调度器说了算。

        修改一下代码,使用waitpid。

        

        通过上文对waitpid的描述,第一个参数传id,是子进程的id。第二个参数传递一个地址,这个记录进程退出时的退出码和收到的信号,第三个参数设置为0表示阻塞等待。

 

        通过上图我们可以看出,进程推出的退出状态时status = 256 。那么时为什么呢?

        status是一个整形,有三十二个bit位,前16个bit位不用,后十六个bit位的前八位表示进程退出时的退出码,后七位表示进程退出时收到的信号。子进程退出时设置的时exit(1),故而经过转化,得到的status是256。

        如果想直接拿到信号编号和进程退出码,还可以这样写。

printf("wait success! rid:%d , status:%d,exit singno:%d, exit code:%d\n",rid,status,status&0x7F,(status>>8)&0xFF);

        这样显示的就比较直观了。

       如果不想着麻烦的话,Linux系统同时给我们提供了两个宏让我们可以直接提取到进程的退出码。

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

        用法如下:

  if(WIFEXITED(status)){
printf("wait success! rid:%d , status:%d,exit code:%d\n",rid,status,WEXITSTATUS(status));} 

        

        同时waitpid的第三个参数设置为0表示阻塞调用,而设置为WNOHANG 表示非阻塞调用。

        阻塞调用表示:父进程一直等待当前子进程到结束,然后读取返回的退出码以及信号。

        非阻塞调用表示:父进程基于非阻塞的轮询访问子进程,访问一次子进程发现没有结果,然后就返回,不必等到子进程有结果才返回。其间父进程也可以去执行别的代码,而不是一直卡住等待子进程有结果。

        注意:为什么我们不能自己定义一个变量,子进程退出修改这个变量,然后给父进程呢?

        答案是不行的,进程间具有独立性。父进程创建子进程,当子进程对数据做修改时会发生写时拷贝。子进程的信息属于内核数据,父进程读取子进程属于读取内核数据,而操作系统不相信任何人,所以只能通过系统调用来读取。同时,用户定义的变量属于用户层面的,而子进程的信息属于内核数据,子进程不能直接对用户层面的信息做修改,还是要通过系统调用。

        下面是一个代码演示了基于非阻塞轮询访问子进程,其间父进程也可以执行别的代码。

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

#define NUM 5
typedef void(*fun_t)();
fun_t task[NUM];

void printflog()
{
  printf("this is log task\n");
}

void printfnet()
{
  printf("this is net task\n");
}

void printfnpc()
{
  printf("this is npc task\n");
}

void inittask()
{
  task[0] = printflog;
  task[1] = printfnet;
  task[2] = printfnpc;
  task[3] = NULL;
}

void excutetask()
{
  for(int i = 0;task[i];i++) task[i]();
}

int main()
{
  inittask();
  pid_t id = fork();
  if(id == 0)
  {
    int cnt = 5;
    while(cnt)
    {
      printf("i am child process,pid:%d ,ppid:%d\n",getpid(),getppid());
      sleep(1);
      cnt--;
    }
    exit(111);

  }

  int status = 0;
  while(1)
  {
    pid_t rid = waitpid(id,&status,WNOHANG); 
    if(rid > 0){

      printf("wait success! rid:%d , status:%d,exit  
 code:%d\n",rid,status,WEXITSTATUS(status));
      break;
    }
    else if(rid == 0)
    {
      printf("father say: child is runnin,do other thing\n");
      printf("##################begin######################\n\n");
      excutetask();
      printf("##################end######################\n");
    }
    else{
      perror("waitpid");
      break;
    }
    sleep(1);
  }

  return 0;
}

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

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

相关文章

线性代数笔记6 1.4

学习视频&#xff1a; 2.2 矩阵运算&#xff08;二&#xff09;_哔哩哔哩_bilibili 包括内容&#xff1a; p23 3.2 线性相关线性无关 p24 3.3 向量的秩&#xff08;一&#xff09; p25 3.3 向量的秩&#xff08;二&#xff09; p26 4.1 线性方程组 p27 4.2 线性方程组有解…

一篇文章带你了解基于 Jenkins 流水线方式部署的好处

在软件开发过程中&#xff0c;部署是将代码从开发环境转移到生产环境的关键步骤。传统的部署方式可能涉及多个手动步骤和容易出错的过程。然而&#xff0c;基于 Jenkins 流水线方式部署可以带来许多好处&#xff0c;包括提高效率、一致性和可靠性。本文将探讨基于 Jenkins 流水…

【Spring实战】21 Spring Data REST 常用功能详细介绍

文章目录 1. 资源导出&#xff08;Resource Exporting&#xff09;2. 查询方法&#xff08;Query Methods&#xff09;3. 分页和排序&#xff08;Pagination and Sorting&#xff09;4. 关联关系&#xff08;Associations&#xff09;5. 事件&#xff08;Events&#xff09;6. …

Parallels虚拟机启动后,Mac主机无法上网怎么办

文章目录 1.问题2.解决 1.问题 部分用户在运行Parallels Desktop的Windows 11打开后&#xff0c;Windows上网没有问题 &#xff0c;但是Mac主机不能访问带域名的网站&#xff0c;而访问带ip的网站没问题&#xff0c;退出parallels虚拟机以后&#xff0c;mac网络恢复正常。 2.…

【管理篇 / 恢复】❀ 07. macOS下用命令刷新固件 ❀ FortiGate 防火墙

【简介】随着苹果电脑的普及&#xff0c;很多管理员都会通过苹果电脑对飞塔防火墙进行管理。当防火墙需要命令状态下刷新固件时&#xff0c;在macOS下用命令刷新固件&#xff0c;将会是一个小小的挑战。 首先是硬件的连接&#xff0c;USB配置线的USB一头&#xff0c;接入MAC的U…

【亚马逊云科技】自家的AI助手 - Amazon Q

写在前面&#xff1a;博主是一只经过实战开发历练后投身培训事业的“小山猪”&#xff0c;昵称取自动画片《狮子王》中的“彭彭”&#xff0c;总是以乐观、积极的心态对待周边的事物。本人的技术路线从Java全栈工程师一路奔向大数据开发、数据挖掘领域&#xff0c;如今终有小成…

高并发下的计数器实现方式:AtomicLong、LongAdder、LongAccumulator

一、前言 计数器是并发编程中非常常见的一个需求&#xff0c;例如统计网站的访问量、计算某个操作的执行次数等等。在高并发场景下&#xff0c;如何实现一个线程安全的计数器是一个比较有挑战性的问题。本文将介绍几种常用的计数器实现方式&#xff0c;包括AtomicLong、LongAd…

IO进程线程Day4

1> 创建出三个进程完成两个文件之间拷贝工作&#xff0c;子进程1拷贝前一半内容&#xff0c;子进程2拷贝后一半内容&#xff0c;父进程回收子进程的资源 #include <myhead.h> //使用三个进程完成两个文件的拷贝工作 //两个子进程分别拷贝文件的上下两部分 //父进程回…

基础知识:晶振的驱动功率测量方法

驱动功率 驱动功率是指振荡电路工作时晶体谐振器的功耗。 保持晶体谐振器低于驱动功率是很重要的。超过驱动功率&#xff0c;可能会引起频率和等效串联电阻的意外变化。 按如下方法计算驱动功率 : 驱动功率 I &#xff65;R1 I&#xff1a;驱动功率 [有效值] R1&#xff1a…

【Mybatis系列】Mybatis判断问题

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

简单的MySQL高可用还不快来学

MHA高可用 传统的MySQL主从架构会存在单点故障问题 MySQL集群高可用方案 单主&#xff1a;keepalived MHA MMM 多主&#xff1a;MySQL cluster PXC 1 MHA 1.1 MHA简介 MHA&#xff08;Master High Availability Manager and tools for MySQL&#xff09;目前在MySQL高可…

Focal Loss

1、样本不均衡的 问题 与 方案 Focal loss 用于解决上述 样本不均衡的问题 : \quad 1、正负样本数量不均衡 \quad 2、易分类的样本和难分类的样本数量不均衡

【Linux】常用的基本命令指令②

前言&#xff1a;前面我们学习了Linux的部分指令&#xff0c;今天我们将接着上次的部分继续将Linux剩余的基本指令. &#x1f496; 博主CSDN主页:卫卫卫的个人主页 &#x1f49e; &#x1f449; 专栏分类:Linux的学习 &#x1f448; &#x1f4af;代码仓库:卫卫周大胖的学习日记…

基于gamma矫正的照片亮度调整(python和opencv实现)

import cv2 import numpy as npdef adjust_gamma(image, gamma1.0):invGamma 1.0 / gammatable np.array([((i / 255.0) ** invGamma) * 255 for i in np.arange(0, 256)]).astype("uint8")return cv2.LUT(image, table)# 读取图像 original cv2.imread("tes…

使用qtquick调用python程序

一. 内容简介 使用qtquick调用python程序 二. 软件环境 2.1vsCode 2.2Anaconda version: conda 22.9.0 2.3pytorch 安装pytorch(http://t.csdnimg.cn/GVP23) 2.4QT 5.14.1 新版QT6.4,&#xff0c;6.5在线安装经常失败&#xff0c;而5.9版本又无法编译64位程序&#xf…

【算法】递归算法理解(持续更新)

这里写目录标题 一、递归算法1、什么情况下可以使用递归&#xff1f;2、递归算法组成部分3、案例&#xff1a;求n的阶乘4、编写一个递归函数来计算列表包含的元素数。5、通过递归找到列表中最大的数字。6、通过递归的方式实现二分查找算法。 一、递归算法 递归&#xff08;Rec…

pytorch07:损失函数与优化器

目录 一、损失函数是什么二、常见的损失函数2.1 nn.CrossEntropyLoss交叉熵损失函数2.1.1 交叉熵的概念2.2.2 交叉熵代码实现2.2.3 加权重损失 2.2 nn.NLLLoss2.2.1 代码实现 2.3 nn.BCELoss2.3.1 代码实现 2.4 nn.BCEWithLogitsLoss2.4.1 代码实现 三、优化器Optimizer3.1 什么…

【Nodejs】基于node http模块的博客demo代码实现

目录 package.json www.js db.js app.js routes/blog.js controllers/blog.js mysql.js responseModel.js 无开发&#xff0c;不安全。 这个demo项目实现了用Promise异步处理http的GET和POST请求&#xff0c;通过mysql的api实现了博客增删改查功能&#xff0c;但因没有…

elementui loading自定义图标和字体样式

需求&#xff1a;页面是用了很多个loading&#xff0c;需要其中有一个字体大些&#xff08;具体到图标也一样的方法&#xff0c;换下类名就行&#xff09; 遇见的问题&#xff1a;改不好的话会影响其他的loading样式&#xff08;一起改变了&#xff09; 效果展示 改之前 改之…