Linux进程控制(进程退出+进程等待)

news2024/11/23 12:43:21

目录

一、子进程创建

1.1 fork函数深入

1.2 写时拷贝

二、进程退出

2.1.1 进程退出码概念

2.1.2 系统退出码文字描述

 2.1.3 _exit和exit函数

2.1.4 查看退出码

三、进程等待

3.1 进程等待解决僵尸进程

3.2 进程等待方法

3.2.1 wait

 3.2.2 waitpid()

 四、阻塞与非阻塞等待

4.1 阻塞等待

4.2 非阻塞等待

4.3 多次非阻塞等待(轮询) 

 4.4 非阻塞等待好处


一、子进程创建

1.1 fork函数深入

fork初识在我另一篇博客:fork初识

当调用fork函数以后,OS会给子进程:

①.分配新的内存块和内核数据结构给子进程
②.将父进程部分数据结构内容拷贝至子进程
③.添加子进程到系统进程列表当中
④.fork返回,开始调度器调度

如何理解父子进程fork函数返回值不同?

1.为什么有两个返回值?

当一个函数返回的时候说明函数任务已经完成,当fork返回之前,子进程已经被创建好了,子进程会拷贝父进程代码和部分数据,两次返回其实就是父子进程都有了fork函数,所以return会分流

2.为什么父子进程返回值不同?

父子进程的对应关系时1:n ,相同的代码,返回值不同,这样就可以让父子进程完成父子进程的对应代码(确定自己的身份)

1.2 写时拷贝

子进程创建会拷贝父进程代码和部分数据,它们会共享部分数据!但是如果父或子进程想修改数据,操作系统会进行写实拷贝!它会拷贝数据在另一块内存空间存储!


二、进程退出

2.1.1 进程退出码概念

main函数return值是退出码,退出码的作用时让我们判断进程任务是否成功完成!当退出码为0时,进程任务完成,当退出码为!0,进程任务没有完成!

2.1.2 系统退出码文字描述


图一


图二


 2.1.3 _exit和exit函数

代码跑完后用return返回退出码,代码没跑完也可以直接返回退出码终止程序!

我们可以调用exit(C库函数) 或者_exit(系统调用) ! 两者区别是前者会在退出前刷新缓冲区,后者不会!



 具体用法:

#include <stdio.h>
#include <stdlib.h>

int main ()
{
   printf("程序的开头....\n");
   
   printf("退出程序....\n");
   exit(0);//==_exit(0);

   printf("程序的结尾....\n");

   return(0);
}


2.1.4 查看退出码

利用echo 可以获取上一个进程的退出码!


三、进程等待

3.1 进程等待解决僵尸进程

前面我们提到僵尸进程,子进程等待父进程回收自己的资源与退出信息,如果父进程一直不获取子进程退出信息,子进程就会进入僵尸状态,僵尸进程无法被手动杀死!从而造成内存泄漏!我们利用进程等待方式解决僵尸进程!

3.2 进程等待方法


3.2.1 wait

返回值:
成功返回被等待进程pid,失败返回-1。
参数:
输出型参数,获取子进程退出状态,不关心则可以设置成为NULL

下面我们来看看代码效果:

#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)
  {
    int cnt=5;
    while(1)
    {
      printf("我是子进程,我的id=%d,父进程id=%d,cnt=%d\n",getpid(),getppid(),cnt);
      sleep(1);
      cnt--;
      if(cnt==0)
      {
        exit(0);//进程退出
      }
    }
  }
  else
  {
    sleep(10);
    pid_t ret=wait(NULL);//回收
    sleep(3);
    printf("wait success:%d\n",ret);//打印回收结果
  }
  return 0;
}

 


 3.2.2 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。


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

位图:

信号码:


正常退出:

#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)
  {
    int cnt=5;
    while(1)
    {
      printf("我是子进程,我的id=%d,父进程id=%d,cnt=%d\n",getpid(),getppid(),cnt);
      sleep(1);
      cnt--;
      if(cnt==0)
      {
        exit(10);//进程退出
      }
    }
  }
  int status=0;//进程退出状态,以位图存储信息
  pid_t ret=waitpid(id,&status,0);
  if(id>0)
  {
    printf("wait success:%d   sig code=%d    child exit code=%d\n",ret,status&0X7F,(status>>8)&0xFF);
  }
  return 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)
  {
    int cnt=5;
    while(1)
    {
      printf("我是子进程,我的id=%d,父进程id=%d,cnt=%d\n",getpid(),getppid(),cnt);
      sleep(1);
      cnt--;
       //野指针!
       int *a=NULL;
       *a=100;
      if(cnt==0)
      {
        exit(10);//进程退出
      }
    }
  }
  int status=0;//进程退出状态,以位图存储信息
  pid_t ret=waitpid(id,&status,0);
  if(id>0)
  {
    printf("wait success:%d   sig code=%d    child exit code=%d\n",ret,status&0X7F,(status>>8)&0xFF);
  }
  return 0;
}


 四、阻塞与非阻塞等待

4.1 阻塞等待

阻塞等待指的时wait/waitpid会一直等到子进程进程退出,然后拿到结果!上面我们写的代码都是阻塞等待,子进程不退出,我们的函数就拿不到结果!

4.2 非阻塞等待

不管子进程有没有结束,waitpid都会立刻返回结果!

子进程退出了,返回值>0;

子进程没退出,返回值=0;

出错返回值=-1;

#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)
  {
    int cnt=5;
    while(1)
    {
      printf("我是子进程,我的id=%d,父进程id=%d,cnt=%d\n",getpid(),getppid(),cnt--);
      sleep(1);
      if(cnt==0)
      {
        exit(10);//进程退出
      }
    }
  }
   int status=0;//进程退出状态,以位图存储信息
   pid_t ret=waitpid(id,&status,WNOHANG);//以非阻塞等待
   if(ret>0)
   {
    printf("wait success:%d   sig code=%d    child exit code=%d\n",ret,status&0X7F,(status>>8)&0xFF);
   }
   else if(ret==0)
   {
    printf("wait success child process is running....\n");
    sleep(5);//拿到结果后睡眠,让子进程先退出
   }
   else
   {
    printf("wait error...\n");
   }
  return 0;
}


4.3 多次非阻塞等待(轮询) 

轮询:父进程一直监测子进程退出结果,如果子进程没退出,父进程依然可以执行自己的代码!如果退出,父进程获取子进程退出结果!

#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)
  {
    int cnt=5;
    while(1)
    {
      printf("我是子进程,我的id=%d,父进程id=%d,cnt=%d\n",getpid(),getppid(),cnt--);
      sleep(1);
      if(cnt==0)
      {
        exit(10);//进程退出
      }
    }
  }
  while(1)
  {
    int status=0;//进程退出状态,以位图存储信息
   pid_t ret=waitpid(id,&status,WNOHANG);
   if(ret>0)//子进程退出
   {
    printf("wait success:%d   sig code=%d    child exit code=%d\n",ret,status&0X7F,(status>>8)&0xFF);
    break;
   }
   else if(ret==0)//子进程没退出
   {
    //父进程做自己的工作
    printf("wait success child process is running....\n");
    sleep(1);
   }
   else//出错
   {
    printf("wait error...\n");
    break;
   }
  }
  return 0;
}


 4.4 非阻塞等待好处

非阻塞等待不会让父进程一直等待到子进程退出,不会占用父进程所有精力,父进程可以在轮询期间完成自己的任务!


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

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

相关文章

seata部署指南(v1.6.1)

Seata 搭建 db模式版本 V1.6.1一、 简介二、下载三、建表&#xff08;仅db&#xff09;四、配置 seata server 参数4.1、V1.4.2之前方式4.2、V1.4.2 之后推荐方式(seataServer.properties)五、配置Server5.1、 修改 appplication.yml5.1.1、 修改 appplication.yml seata.store…

文件操作(File类)

文章目录一、初识文件二、File类构造方法常用方法一、初识文件 我们目前是如何存储数据的?弊端是什么? int a 1; int[] arr new int[5];我们这些数据是在内存中存储的&#xff0c;是不能够长久保存的。 那么&#xff0c;我们的计算机当中有没有一块硬件可以长久存储数据…

PostgreSQL(一)Windows安装

目录一、下载二、安装PostgreSQL三、安装StackBuilder四、打开PostgreSQL管理工具pgAdmin五、打开命令行一、下载 下载地址&#xff1a; https://www.enterprisedb.com/downloads/postgres-postgresql-downloads 下载后安装包如下&#xff1a; 二、安装PostgreSQL 双击打开安…

DataX使用入门

DataX 是阿里云 DataWorks数据集成 的开源版本&#xff0c;在阿里巴巴集团内被广泛使用的离线数据同步工具/平台。DataX 实现了包括 MySQL、Oracle、OceanBase、SqlServer、Postgre、HDFS、Hive、ADS、HBase、TableStore(OTS)、MaxCompute(ODPS)、Hologres、DRDS 等各种异构数据…

Java 日志框架 Log4J

文章目录引言什么是Log4JLog4J三大组件Log4J日志级别Log4J基本使用自定义配置文件Appender示例FileAppenderDailyRollingFileAppenderRollingFileAppenderJDBCAppender自定义Logger引言 Java 日志框架 JUL 在这篇文章中已经向大家介绍了我们为什么要使用日志文件、常见的日志…

张力调节(精密调节气阀应用)

跳舞轮对应张力调节范围&#xff0c;我们可以通过改变气缸的气压方式间接改变&#xff0c;张力跳舞轮在收放卷闭环控制上的详细应用&#xff0c;可以参看下面的文章链接&#xff0c;这里我们主要讨论精密可调气阀的模拟量编程问题。 PLC张力控制&#xff08;开环闭环算法分析&…

【实践向】当移除了三级缓存……

本文会手把手带你一起把使用二级缓存替换三级缓存&#xff0c;看下移除了三级缓存&#xff0c;只有二级缓存会出什么问题&#xff0c;用实践回答那个被问了无数次的“为什么要有三级缓存&#xff1f;”以及“二级缓存解决不了循环依赖问题吗&#xff1f;”等类似问题(&#xff…

“Flash配置不当漏洞”详解

危害 可被用来进行跨域访问&#xff0c;可能会导致“跨站点伪造请求”或“跨站点跟踪”&#xff08;“跨站点脚本编制”的变体&#xff09;之类的攻击&#xff0c;从而导致其它用户的信息被非法读取。 导致不受信任的第三方域的flash也能访问当前域的资源&#xff0c;绕过同源策…

一、构建自己的图像分类数据集(Datawhale组队学习)

文章目录安装配置环境图像采集采集函数爬取一类图片爬取多类图片一些参考类别的关键词制作图像分类数据集的注意事项删除多余文件删除系统自动生成的多余文件删除gif格式的图像文件删除非三通道的图像统计图像尺寸、比例分布采用的数据集统计数据集的基本信息可视化图像尺寸分布…

Embarcadero Dev-C++第一次使用注意事项

Embarcadero Dev-C第一次使用注意事项 Embarcadero Dev-C简介 2000年左右&#xff0c;Bloodshed software开发了Dev-C &#xff0c;提供轻量、免费、开源的C/CIDE。Dev-C是最适合初学C语言的IDE之一&#xff0c;但至2015年Dev C5.11&#xff0c;停止了更新维护了。 后来&…

springboot,vue电影院售票系统

开发工具&#xff1a;IDEA服务器&#xff1a;Tomcat9.0&#xff0c; jdk1.8项目构建&#xff1a;maven数据库&#xff1a;mysql5.7系统用户前台和管理后台两部分&#xff0c;项目采用前后端分离前端技术&#xff1a;vue elementUI服务端技术&#xff1a;springbootmybatis项目功…

pom文件中自定义的repository不生效

问题描述 对应的pom中依赖获取失败 pom文件依赖配置如下: <dependencies><dependency><groupId>it.geosolutions</groupId><artifactId>geoserver-manager</artifactId><version>1.7.0</version><exclusions><excl…

verilog学习笔记- 15)动态数码管显示实验

目录 简介&#xff1a; 实验任务&#xff1a; 硬件设计&#xff1a; 程序设计&#xff1a; 下载验证&#xff1a; 简介&#xff1a; 由于一般的静态驱动操作虽然方便&#xff0c;但占用的 I/0 口较多&#xff0c;例如要驱动6 位 8 段数码管&#xff0c;以静态驱动方式让数…

值得收藏的30道Python基础练手题(附详解)

今天给大家分享30道Python练习题&#xff0c;建议大家先独立思考一下解题思路&#xff0c;再查看答案。 1. 已知一个字符串为 “hello_world_JMzz”&#xff0c;如何得到一个队列 [“hello”,”world”,”JMzz”] &#xff1f; 使用 split 函数&#xff0c;分割字符串&…

ESP8266 ArduinoIDE 闪存文件操作系统

一、闪存文件系统基本操作 esp8266 的采用 SPIFFS 嵌入式文件系统&#xff0c;在内部 Flash 为 4M&#xff0c;其中 1M 用于存储程序&#xff0c;其他的空间有一部分用于系统&#xff0c;3M 中剩下的大部分空间可以用来存放文件。 其中这个空间大小是可以自定义的&#xff0c;…

【Node.js实战】一文带你开发博客项目之安全(sql注入、xss攻击、md5加密算法)

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;也会涉及到服务端 &#x1f4c3;个人状态&#xff1a; 在校大学生一枚&#xff0c;已拿多个前端 offer&#xff08;秋招&#xff09; &#x1f680;未…

Netty进阶

三. Netty 进阶 1. 粘包与半包 1.1 粘包现象 服务端代码 public class HelloWorldServer {static final Logger log LoggerFactory.getLogger(HelloWorldServer.class);void start() {NioEventLoopGroup boss new NioEventLoopGroup(1);NioEventLoopGroup worker new Ni…

想要全面了解DevOps,从概念、实现相关工具到如何落地,看这篇就够了

我们总是在提DevOps&#xff0c;敏捷管理&#xff0c;但大家未必真的明白什么是DevOps。本文是将向大家介绍到底什么是DevOps&#xff0c;DevOps的初衷到底是为了解决什么问题&#xff1f;它能够如何实现&#xff1f;能够带来哪些价值&#xff1f;来让大家对DevOps有一个全面的…

Vscode++Opencv+Anaconda+Python安装教程

最近在学习opencv的时候vscode一直报这个错&#xff1a; Import “cv2” could not be resolved Pylance (reportMissingImports) 但是在Jupyter上面可以运行&#xff0c;一直没找到解决方法&#xff0c;无奈只好重装。 一&#xff1a;安装AnacondaPython 进入Anaconda官网 w…

计网必会:封装、协议、解封装

文章目录解释什么是封装什么是协议解封装是啥封装过程拓展知识需要了解解释 什么是封装 封装就是给初始的数据增加“数据”&#xff0c;让原始数据的信息量扩大&#xff0c;方便层与层间的交流&#xff0c;所谓封装&#xff0c;就是包装的意思&#xff0c;中文博大精深哈哈哈…