[Linux入门]---进程等待

news2024/11/26 10:49:41

文章目录

  • 1.进程等待必要性
    • 1.1什么是进程等待?**
    • 1.2为什么需要进程等待?
  • 2.进程等待的方法
    • 2.1wait方法
    • 2.2waitpid方法
      • 2.2.1获取子进程status
      • 2.2.2options选项,父进程等待的三种方式

1.进程等待必要性

1.1什么是进程等待?**

通过系统调用wait/waitpid,来进行对子进程状态监测与回收的功能!

1.2为什么需要进程等待?

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

#define N 10

void RunChild()
{
    int cnt = 5;
    while (cnt)
    {
        printf("I am Child Process, pid: %d, ppid:%d\n", getpid(), getppid());
        sleep(1);
        cnt--;
    }
}

int main()
{
    //wait / waitpid
    for (int i = 0; i < N; i++)
    {
        pid_t id = fork();
        if (id == 0)
        {
            RunChild();
            exit(i);
            printf("create child process: %d success\n", id); // 这句话只有父进程才会执行
        }
    }
    sleep(10);

    return 0;
}

代码运行结果如下:
在这里插入图片描述
在这里插入图片描述
①僵尸进程无法被信号杀死,需要进程等待杀掉它,解决内存泄漏问题;②创建子进程,父进程(用户)需要知道子进程任务的完成的情况,可以通过进程等待子进程任务的完成的情况!

2.进程等待的方法

父进程通过调用wait/waitpid,对子进程资源的回收!

在这里插入图片描述

2.1wait方法

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

eg1:

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

#define N 10

void RunChild()
{
    int cnt = 5;
    while (cnt)
    {
        printf("I am Child Process, pid: %d, ppid:%d\n", getpid(), getppid());
        sleep(1);
        cnt--;
    }
}

int main()
{

    for (int i = 0; i < N; i++)
    {
        pid_t id = fork();
        if (id == 0)
        {
            RunChild();
            exit(i);
            printf("create child process: %d success\n", id); // 这句话只有父进程才会执行
        }
    }
    sleep(10);
    // 等待
    for(int i = 0; i < N; i++)
    {
        // wait当任意一个子进程退出的时候,wait回收子进程
        pid_t id = wait(NULL);
        if(id > 0)
        {
            printf("wait pid:%d success\n", id);
        }
    }
    return 0;
}

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

2.2waitpid方法

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

eg2:

 //pid_t id = wait(NULL);将上面的eg1的代码语句替换成以下一句,可以达到相同的效果
 pid_t id=waitpid(-1,NULL,0);

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

2.2.1获取子进程status

  • wait和waitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充。
  • 如果传递NULL,表示不关心子进程的退出状态信息。
  • 否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程。
  • status不能简单的当作整形来看待,可以当作位图来看待,具体细节如下图(只研究status低16比特
    位):
    在这里插入图片描述

eg3:

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

int main()
{ 
    pid_t id=fork();
    if(id < 0)
    {
      perror("fork");
      return 1;
    }
    else if(id == 0)
    {
      int cnt=3;
      while(cnt)
      {
        printf("I am child, pid:%d, ppid:%d, cnt:%d\n",getpid(),getppid(),cnt);
        cnt--;
        sleep(1);
      }
      exit(12);
    }
    else{
      int status = 0;

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

      if(ret==id)
      {
        printf("wait success, ret:%d, exit sig:%d, exit code:%d\n",ret,status&0x7F,(status>>8)&0xFF);
      }
    }
    return 0;
}

代码运行结果如下:
在这里插入图片描述
使用信号杀掉子进程的指令:

kill -l -pid
//其中-l为信号选项,可以选用任意异常信号终止进程

在这里插入图片描述
eg4:

//使用指令
kill -9 30773

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

//在子进程允许的代码中添加以下,让子进程异常退出
int* p=NULL;
*p=100;

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

①进程异常会被操作系统识别成信号杀掉;进程没有出现异常,信号码为0,这时我们只需要观察进程退出即可,子进程结束后代码和数据立即释放掉,PCB资源并没有被立即释放,操作系统需要从子进程PCB中获取信号码和退出码数据封装到status中,让父进程通过wait/waitpid在这里插入代码片系统接口函数获取!
②可以使用以下两个宏,一个判断进程是否正常终止,一个获取进程的退出码
WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
WEXITSTATUS(status):WIFEXITED非零,提取子进程退出码。(查看进程的退出码)

2.2.2options选项,父进程等待的三种方式

options选项若是为0,父进程阻塞等待子进程结束,等待方式:阻塞等待

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

int main()
{
    pid_t id=fork();
    if(id < 0)
    {
      perror("fork");
      return 1;
    }
    else if(id == 0)
    {
      int cnt=3;
      while(cnt)
      {
        printf("I am child, pid:%d, ppid:%d, cnt:%d\n",getpid(),getppid(),cnt);
        //printf("I am child, pid:%d, ppid:%d",getpid(),getppid());
        cnt--;
        sleep(1);
      }
      sleep(20);
      exit(12);
    }
    else if(id>0){
      int status = 0;
      printf("I am parent, pid: %d 子进程,你知道我在等你吗?\n",getpid());
      pid_t ret = waitpid(id,&status,0);
      
      if(ret>0)
      {
        if(WIFEXITED(status))
        {
          printf("进程是正常跑完的,退出码:%d\n",WEXITSTATUS(status));
        }
        else{
          printf("进程出异常了\n");
        }
      }
    }
    return 0;
}

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

optiont选项若是为WNOHANG ,若pid指定的子进程没有结束,则waitpid()函数返回0,等待方式:进行非阻塞等待,配合循环使用(非阻塞轮询)

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

int main()
{  
    pid_t id=fork();
    if(id < 0)
    {
      perror("fork");
      return 1;
    }
    else if(id == 0)
    {
      int cnt=3;
      while(cnt)
      {
        //int* p=NULL;
        //*p=100;
        printf("I am child, pid:%d, ppid:%d, cnt:%d\n",getpid(),getppid(),cnt);
        //printf("I am child, pid:%d, ppid:%d",getpid(),getppid());
        cnt--;
        sleep(1);
      }
      //sleep(20);
      exit(12);
    }
    else if(id>0)
    {
      int status = 0;
      printf("I am parent, pid: %d 子进程,你知道我在等你呀!\n",getpid());
      while(1)//轮询
      {
          pid_t ret = waitpid(id,&status,WNOHANG);
          if(ret>0)
          {
            if(WIFEXITED(status))
            {
              printf("进程是正常跑完的,退出码:%d\n",WEXITSTATUS(status));
            }
            else{
              printf("进程出异常了\n");
            }
            break;
          }
          else if(ret<0)
          {
            printf("wait failed\n");
            break;
          }
          else if(ret == 0)
          {
              printf("你好了没?子进程还没有退出,我在等等...\n");
              sleep(1);
         }
      }
    }
    return 0;
}

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

optiont选项若是为WNOHANG ,若pid指定的子进程没有结束,则waitpid()函数返回0,等待方式:非阻塞轮询+父进程做自己的事情

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

#define TASK_NUM 10

typedef void(*task_t)();//函数指针
task_t tasks[TASK_NUM];//函数指针数组

void task1()
{
  printf("这是一个执行打印日志的任务,pid:%d\n",getpid());
}

void task2()
{
  printf("这是一个执行检测网络健康状态的一个任务,pid:%d\n",getpid());
}

void task3()
{
  printf("这是一个进行绘制图形界面的任务,pid:%d\n",getpid());
}

int AddTask(task_t t);//函数声明

//任务的管理代码
void InitTask()
{
  for(int i = 0; i < TASK_NUM; i++) tasks[i]=NULL;
  AddTask(task1);
  AddTask(task2);
  AddTask(task3);
}

int AddTask(task_t t)
{
  int pos=0;
  for(; pos < TASK_NUM; pos++)
  {
    if(!tasks[pos])break;//判断函数指针数组是否还有位置
  }
  if(pos == TASK_NUM)return -1;
  tasks[pos]=t;
  return 0;
}

void ExecuteTask()
{
  //执行任务
  for(int i = 0; i < TASK_NUM; i++)
  {
    if(!tasks[i])continue;
    tasks[i]();
  }
}
int main()
{
    pid_t id=fork();
    if(id < 0)
    {
      perror("fork");
      return 1;
    }
    else if(id == 0)
    {
      int cnt=3;
      while(cnt)
      {
        printf("I am child, pid:%d, ppid:%d, cnt:%d\n",getpid(),getppid(),cnt);

        cnt--;
        sleep(1);
      }
      exit(12);
    }
    else if(id>0){
      int status = 0;
      InitTask();//初始化和加载
      printf("I am parent, pid: %d 子进程,你知道我在等你呀!\n",getpid());
      while(1)//轮询
      {
          pid_t ret = waitpid(id,&status,WNOHANG);
          if(ret>0)
          {
            if(WIFEXITED(status))
            {
              printf("进程是正常跑完的,退出码:%d\n",WEXITSTATUS(status));
            }
            else{
              printf("进程出异常了\n");
            }
            break;
          }
          else if(ret<0)
          {
            printf("wait failed\n");
            break;
          }
          else if(ret == 0)
          {
              ExecuteTask();//父进程做自己的事情
              usleep(500000);
          }
      }
    }
    return 0;
}

代码运行结果如下:
在这里插入图片描述
问题1: 父进程以非阻塞轮询+做执行自己的任务的方式等待子进程时,究竟是等待子进程重要,还是父进程执行其他任务重要?

答:当然是等待子进程回收资源重要,只不过子进程执行任务时,父进程会闲置造成资源浪费,为充分利用父进程资源,便设置一些任务让父进程去执行!

问题2: 父进程以非阻塞轮询+做执行自己的任务的方式等待子进程时,如果父进程执行其他任务过重,造成没有在子进程结束的时候立即回收,会不会对有什么影响呢?

答:父进程在等待子进程时,执行的任务都是轻量化的工作,如网络写入、记录日志、错误处理等工作,不会影响子进程的回收;如果父进程执行任务过多,没能在子进程结束的时候立即回收,当父进程执行其他任务结束时便会对子进程资源进行回收,不会有什么影响!这种延迟回收的方式,在父进程创建多个子进程时,等待所有子进程全部结束时再一起回收,反而会更好!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

结合长短期记忆网络(LSTM)和无迹卡尔曼滤波器(UKF)的技术在机器人导航和状态估计中的应用前景

结合长短期记忆网络(LSTM)和无迹卡尔曼滤波器(UKF)的技术在机器人导航和状态估计中具有广泛的应用前景。以下是关于这一主题的简要综述: 文章目录 结合LSTM和UKF的背景结合LSTM和UKF的优势应用实例研究现状MATLAB代码示例结合LSTM和UKF的背景 长短期记忆网络(LSTM)是一…

hadoop分布式搭建

hadoop的分布式搭建步骤&#xff1a; 第一步&#xff1a; 比如准备三台虚拟机&#xff0c;分别命名为master、node1、node2,并且确保都配置了java环境 &#xff0c;都关闭了防火墙&#xff0c;都设置了静态的IP地址&#xff0c;然后三台虚拟机的hosts文件都互相映射&#xff0…

atcoder abc370(dp,基环树/森林,倍增)

A 代码&#xff1a; #include <bits/stdc.h>using namespace std;int main() {int a, b;cin >> a >> b;if(a 1 && b 0) cout << "Yes" << endl;else if(a 0 && b 1) cout << "No" << en…

【Centos】Centos系统换yum源

【Centos】Linux&#xff0c;Centos系统换yum源 1、备份 mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak/etc/yum.repos.d/CentOS-Base.repo 是yum的配置文件 /etc/yum.repos.d/CentOS-Base.repo.bak 是我们备份的配置文件 2、下载yum源 这里…

这个开源的AI证件照项目又火了!有人靠它日入300+

今天在知识星球里看到一个球友靠一个AI证件照的项目赚到了第一桶金&#xff0c;看了看项目&#xff0c;还不错。整理下来分享给大家玩玩。 神器名叫HivisionIDPhotos&#xff0c;最近几天一直挂在GitHub热榜上&#xff0c;目前已狂揽3.2K星标。 除了能换背景&#xff0c;它还支…

你绝对想不到,ComfyUI竟然能这样转换线条图!

前言 使用ComfyUI将图像转为线条图&#xff1a;详细教程 在这个数码时代&#xff0c;图像处理技术已经像空气一样渗透进了我们的日常生活。今天&#xff0c;我想和大家分享一个既简单又高效的小妙招——用ComfyUI把图片变成线条图。 不管你是设计师、艺术家&#xff0c;还是…

深入掌握大模型精髓:《实战AI大模型》带你全面理解大模型开发!

今天&#xff0c;人工智能技术的快速发展和广泛应用已经引起了大众的关注和兴趣&#xff0c;它不仅成为技术发展的核心驱动力&#xff0c;更是推动着社会生活的全方位变革。特别是作为AI重要分支的深度学习&#xff0c;通过不断刷新的表现力已引领并定义了一场科技革命。大型深…

opencv羊群计数,动态目标检测跟踪

OpenCV&#xff08;开源计算机视觉库&#xff09;是一个功能强大的计算机视觉和图像处理库&#xff0c;广泛应用于各种视觉任务中&#xff0c;包括但不限于目标检测与跟踪。如果你正在考虑一个基于OpenCV的羊群计数项目&#xff0c;那么下面是对这样一个项目的概述&#xff1a;…

【动态规划】子序列问题二(数组中不连续的一段)

子序列问题二 1.最长定差子序列2.最长的斐波那契子序列的长度3.最长等差数列4.等差数列划分 II - 子序列 点赞&#x1f44d;&#x1f44d;收藏&#x1f31f;&#x1f31f;关注&#x1f496;&#x1f496; 你的支持是对我最大的鼓励&#xff0c;我们一起努力吧!&#x1f603;&am…

易基因:Adv Sci:ACE等揭示产前不良环境暴露通过DNA羟甲基化变化介导子代自闭症|国人佳作

大家好&#xff0c;这里是专注表观组学十余年&#xff0c;领跑多组学科研服务的易基因。 自闭症谱系障碍&#xff08;Autism spectrum disorder&#xff0c;ASD&#xff09;是一种神经发育障碍&#xff0c;以社交沟通障碍和刻板行为为主要特征。许多研究证明&#xff0c;妊娠期…

2024上半年上汽收入2770多亿,为啥没长城900多亿赚得多?

【科技明说 &#xff5c; 车评头条】 看了上汽集团和长城汽车两家的最新H1财报后&#xff0c;你是不是发现了点什么&#xff1f; 上汽集团披露2024年半年报。公司上半年实现合并营业收入2770.86亿元&#xff0c;同比下降12.43%&#xff1b;净利润66.28亿元&#xff0c;同比下…

vb.net发送邮件:如何高效地实现邮件发送?

vb.net发送邮件怎么配置服务器&#xff1f;怎么用vb.net发邮件&#xff1f; 如何高效地实现vb.net发送邮件&#xff0c;确保邮件能够快速、稳定地送达&#xff0c;是许多开发者面临的挑战。AokSend将深入探讨vb.net发送邮件的最佳实践&#xff0c;帮助您提升邮件发送的效率和可…

投屏开发调试技能-pcm数据转wav格式文件源码实战分享

背景 在学习投屏相关音视频开发时候&#xff0c;经常验证一些声音卡顿问题时候&#xff0c;需要对音频数据可能需要保存到本地&#xff0c;一般可能是pcm格式的数据&#xff0c;但是pcm格式的数据是不可以用音乐播放器直接进行播放&#xff0c;需要专门的工具&#xff0c;而且…

14种实际上有效的AI营销方法(专业推荐)

当有人提到人工智能时&#xff0c;你会感到头晕目眩吗&#xff1f;这是可以理解的。LinkedIn和Twitter&#xff08;好吧……现在叫X&#xff09;充斥着刚刚涌现的AI专家和科技达人们&#xff0c;他们在分享最新的27步算法攻略和自动化整个市场营销程序的操作。 这对大多数基层…

一款rust语言AI神器cursor在ubuntu环境下的安装启动教程

虽然cursor目前只支持英文但是它强大的代码联想能力以及问答能力&#xff0c;可以高效的提高编码效率。 如下步骤所有的前提是你的ubuntu上面已经安装了rust以及其必须的extensions。 1 下载 到官网https://www.cursor.com下载指定版本的软件。 下载到本地以后会生成如下软件…

如何通过网络找到自己想要的LabVIEW知识?

学习LabVIEW或其他编程技术时&#xff0c;无法依赖某一篇文章解决所有问题。重要的是通过多种途径获取灵感&#xff0c;并学会归纳总结&#xff0c;从而逐渐形成系统性的理解。这种持续学习和总结的过程是技术提升的基础。通过网络找到所需的LabVIEW知识可以通过以下几个步骤进…

WEB渗透权限维持篇-MSSQL后门

往期文章WEB渗透权限维持篇-DLL注入\劫持-CSDN博客 WEB渗透权限维持篇-CLR-Injection-CSDN博客 WEB渗透权限维持篇-计划任务-CSDN博客 WEB渗透权限维持篇-DLL注入-修改内存中的PE头-CSDN博客 WEB渗透权限维持篇-DLL注入-进程挖空(MitreT1055.012)-CSDN博客 WEB渗透权限维…

轻松上手LangChain:新手必读的入门指南

导语 在人工智能领域的不断发展中&#xff0c;语言模型扮演着重要的角色。特别是大型语言模型&#xff08;LLM&#xff09;&#xff0c;如ChatGPT&#xff0c;已经成为科技领域的热门话题&#xff0c;并受到广泛认可。在这个背景下&#xff0c;LangChain作为一个以LLM模型为核…

打造高效业务架构:价值流在企业转型中的应用指南

从流程到价值流的业务架构转型 随着企业面对数字化转型带来的激烈市场竞争&#xff0c;优化业务架构成为每个企业管理者必须面对的核心挑战。传统的业务流程优化方法往往难以应对复杂的客户需求和日益增加的业务复杂性。《价值流指南》由The Open Group发布的企业数字化转型专…

K-Means算法详解与实战应用.

在数据分析的众多工具中&#xff0c;K-Means聚类算法以其简单、直观和高效的特点&#xff0c;成为了探索数据集结构的常用方法。本文将带你深入了解K-Means算法的原理&#xff0c;并展示如何在实际项目中运用这一强大的聚类工具。 一 算法原理 K-Means是一种迭代聚类算法&…