fork+父子进程在理解+进程控制

news2024/11/28 0:51:53

索引

  • fork+父子进程在理解
  • 进程控制
      • 1.进程终止
      • 2.进程等待
      • 3.获取进程状态Status

fork+父子进程在理解

fork之后,内核做了什么?

  1. 将分配新的内存块和内核数据结构给子进程
  2. 将父进程部分数据结构拷贝至子进程
  3. 添加子进程到系统列表中
  4. fork返回开始调度器调度

fork父子进程共享所有的代码 != 子进程执行的后续代码,子进程执行的代码只能从fork之后开始,cpu读取指令时一般直接向eip寄存器要,然后eip中存储的指令++,指向下一条执行指令,而fork之后eip寄存器此时存储的是下一条指令,子进程拷贝父进程的eip,因此便只能从fork之后的代码执行了

#include<stdio.h>
  2 #include<unistd.h>
  3 int main()
  4 {
  5   printf("我是一个进程:pid: %d\n",getpid());
  6   fork();
  7   printf("我依旧是一个进程:pid:%d\n",getpid());
  8   return 0;
  9                                                                                                                                   
 10 }

在这里插入图片描述
从上述看出,foek之后父子进程共享代码,但也是只从fork之后子进程才会执行代码,由于进程具有独立性,代码和数据都必须独立,所以此时父子进程共享的代码只能读取,那么如果有一个进程非要修改数据呢?
此时父子进程会发生写实拷贝解决问题,刚开始父子进程的代码和数据都是只读的,但是发生写实拷贝之后,代码还是只读,但是此时可以写入数据,写实拷贝只是拷贝被写入的数据,没有写入的数据父子进程照样共享

在这里插入图片描述
为什么要写实拷贝?直接在创建子进程的时候,将数据分开不行吗?

  1. 父进程的数据,子进程不一定全用,即便全用,也不一定全部写入 ----浪费资源
  2. 最理想的情况,只有会被父子修改的数据,进行分离拷贝,不需要修改共享 ------技术上实现复杂(因为代码要被编译才能知道哪些数据是被写入的,哪些数据是没有被写入的)
  3. 如果fork之后就无脑拷贝给子进程,会增加fork的成本

fork调用失败可能原因

  1. 系统中有太多的进程
  2. 实际用户的进程数超过了限制

进程控制

1.进程终止

常见进程退出有三种情况:
1.代码跑完,结果正确
2.代码跑完,结果不正确
3.代码没跑完,程序异常了

在C/C++中在main函数最后都会有return 0;其实无脑return 0是不正确的行为,应该是return X,其中X表示进程退出码表征进程退出信息,进程的退出信息在将来进程退出的时候给父进程读取的,可以参考一下C语言中默认的错误码描述
在这里插入图片描述
可以用echo $?查看进程退出码

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<stdlib.h>
  4 #include<string.h>
  5 int main()
  6 {
  7 
  8   for(int i = 0; i < 100; i++){
  9     printf("%d: %s\n",i,strerror(i));
 10   }
 11   printf("\n");
 12   return 123;

在这里插入图片描述
echo虽然是一个内置命令不会生成子进程,但还是将其看做一个运行成功的程序,图中第一个echo $?表示的是我们自己的程序,此时退出码是123,第二个echo $?表示的是上一个命令echo本身的退出情况。

进程正常退出时:
1.从main返回
2.调用exit(系统调用)
3_exit

#include <unistd.h>
void _exit(int status);
status:定义了进程的终止状态
其中父进程通过wiat来获取状态
其中,status虽然是int,但是仅仅是有低八位可以被父进程使用,
所以如果exit(-1),在终端执行$?发现返回的是255
 10    printf("测试_exit函数\n");
 11   _exit(321);
 12   return 0;

在这里插入图片描述
还有一个函数与_exit相似,exit

#include <unistd.h>
void exit(int status);
exit最后也会调用_exit,只是在调用—_exit之前还做了其他工作
1.执行用户定义的清理函数
2.关闭所有打开的柳,所有的缓存数据均被写入
3.调用_exit

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
exit终止进程,刷新缓冲区,一秒钟之后消息被打印出来了,但是调用_exit的函数会直接终止进程,不会有任何刷新操作,所以一秒之后消息没有被刷新出来
关于进程终止,内核做了什么?
进程状态是Z的时候,父进程读取退出码信息后,将状态设置成X后释放代码和数据,其中操作系统会自己形成一个链表,将废弃的内核结构放在链表之中,当有新的进程形成内核数据结构时节省了开辟空间所需要的时间,直接将内核数据结构初始化即可。

2.进程等待

什么是进程等待?
进程等待是指一个进程暂停自己的执行,等待另一个进程执行完毕或发送信号给它,然后再继续执行,通常情况下,父进程会等待子进程完成,以确保子进程的结果能够被正确处理
为什么要进行进程等待?

  1. 子进程退出,如果父进程如果没有及时获取退出状态信息,该子进程的文件描述符和其他资源可能一直保留,经常造成内存泄漏
  2. 父进程再给子进程派发任务时,我们需要知道子进程运行完成后结果是对还是不对,是否是正常退出
  3. 父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息

如何进行进程等待
wait方法

#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int*status);
参数:输出型参数,获取子进程的退出状态,不关心则可以设置成null
返回值:成功则返回被等待进程的pid,失败返回-1
 16     while(1)
 17     {
 18       
 19       printf("我是子进程,我正在运行,我的pid: %d\n",getpid()    );
 20       sleep(1);
 21       flag++;
 22       if(flag == 5){
 23         exit(0);                                             
 24       }
 25 
 26     }
 27   }
 28   else 
 29   {
 30     printf("我是父进程pid: %d,,准备等待子进程!\n",getpid())    ;
 31    sleep(10);
 32     pid_t ret = wait(NULL);
 33     if(ret < 0) {
 34       printf("等待失败!\n");
 35     }else {
 36       printf("等待成功:result: %d\n",ret);
 37     }
 38     sleep(20);
 39 
 40   }
 41   return 0;
 42 }


在这里插入图片描述在这里插入图片描述

此时等待成功,原本父子进程的状态都是S,然后子进程变成Z,子进程退出之后就被回收,然后父进程接着运行
如果子进程不退出的话,那么父进程的代码会一直堵在wait()函数部分,因为wait默认是阻塞式等待,只要wait没有等到进程退出,就会一直卡在那。
waitpid方法

pid_ t waitpid(pid_t pid, int *status, int options);
参数:
	1.pid: 
		1、pid = -1,等待任意一个子进程,与wait等效
		2、pid > 0,等其进程ID与pid相等的子进程
	2.status:
		输出型参数,由操作系统填充
		如果传递NULL表示不关心子进程的退出状态
	3.options:
		1、WNOHANG:若pid指定的子进程没有结束,
		则waitpid()函数返回0,不予等待,
		2、若正常结束,则返回该子进程的id
		(如果加了这个参数就不是阻塞式等待了,就是非阻塞式等待)
返回值:
	1.当正常返回的时候waitpid返回收集到的子进程的进程id
	2.如果设置了选项WNOHANG,而调用中waitpid发现没有退
	出的子进程可以收集,则返回03.如果调用中出错,则返回-1,此时errno会被设置成相应的值以指示错误	
 8 int main()
  9 {
 10 
 11   pid_t id = fork();
 12   if(id == 0)
 13   {
 14     int flag = 1;
 15     //child
 16     while(1)
 17     {
 18       
 19       printf("我是子进程,我正在运行,我的pid: %d\n",getpid()    );
 20       sleep(1);                                              
 21       flag++;
 22       if(flag == 5){
 23         break;
 24       }
 25       
 26     }
 27     exit(12);                                                
 28   }
 29   else 
 30   {
 31     int status = 0;
 32 
 33     printf("我是父进程pid: %d,,准备等待子进程!\n",getpid())    ;
 34    sleep(1);
 35   //   pid_t ret = wait(NULL);
 36       // pid_t ret = waitpid(id,&status,0);
 37       pid_t ret = waitpid(id,&status,WNOHANG);
 38     if(ret < 0) {
 39       printf("等待失败!\n");
 40     }else {
 41       printf("等待成功:result: %d\n",ret);
 42     }
 43    
 44     sleep(2);
 45   }
 46   return 0;
    }


在这里插入图片描述

此时是非阻塞式等待,父进程运行到waitpid()时,子进程还未退出,当父进程运行完自己代码的时候,直接退出,子进程变成了孤儿进程,此时子进程会托管给1号进程也就是bash。

3.获取进程状态Status

先了解一下什么是信号
在Linux中,信号是一种用于进程间通信的机制,信号可以被一个进程发送给另一个进程,用于通知或中断目标进程的执行。
在Linux中kill命令用于向指定进程发送信号,从而控制进程的行为和状态

语法格式:
kill [signal] PID
signal:信号编号
PID:进程的PID
在这里插入图片描述

wait/waitpid都有一个status参数,但是其不能当做一个简单的整形看待,当做位图看待,(只研究status低16比特位的情况);
在这里插入图片描述

 8 int main()
  9 {
 10 
 11   pid_t id = fork();
 12   if(id == 0)
 13   {
 14     int flag = 1;
 15     //child
 16     while(1)
 17     {
 18       
 19       printf("我是子进程,我正在运行,我的pid: %d\n",getpid()    );
 20       sleep(1);                                              
 21       flag++;
 22     /*  if(flag == 5){
 23         break;
 24       }*/
 25 
 26     }
 27     // exit(12);
 28   }
 29   else 
 30   {
 31     int status = 0;
 32 
 33     printf("我是父进程pid: %d,,准备等待子进程!\n",getpid())    ;                                                            
 34     
 35   //   pid_t ret = wait(NULL);
 36       // pid_t ret = waitpid(id,&status,0);
 37       pid_t ret = waitpid(id,&status,0);
 38       if(ret > 0){
 39         printf("wait success,ret : %d,我所等待的子进程的退出>    码 :%d,退出信号是 :%d\n",\
 40             ret,(status>>8)&0xFF,status&0xFF);
 41       }
 49   }
 50   return 0;
 51 }



在这里插入图片描述
上述代码子进程一直循环,父进程阻塞式等待,用kill-9杀死子进程,此时父进程收到的退出码为0,退出信号为9

LInux中还可以用宏来表示status输出型参数
Linux中还也可以用 WEXITSTATUS(status)宏表示进程的退出码
WIFEXITED(status):表示进程是否正常退出
WIFSIGNALED(status):非0表示被信号终止
WTERMSIG(status):获取终止信号的编号

总结:
退出码表示的是有没有跑完,结果对不对
退出信号表示的进程是否异常,进程一旦异常,我们只需要关心退出信号即可,关心退出码没有任何意义

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

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

相关文章

【人工智能】VScode中使用ChatGPT之Bito插件

文章目录 前言一、到官网下载VScode软件二、VScode软件安装步骤三、Bito插件下载与VScode软件中的使用四、注册Bito 前言 之前在VScode中使用ChatGPT中文版&#xff0c;后来要注册与收费&#xff0c;可采用一些ChatGPT中文版的替代插件。 后发现BitoAI插件功能同样强大&#…

SpringMVC框架详解

1.MVC概念 MVC 模式代表 Model-View-Controller&#xff08;模型-视图-控制器&#xff09; 模式。这种模式用于应用程序的分层开发。 Model&#xff08;模型&#xff09; - 模型代表一个存取数据的对象或 JAVA POJO&#xff0c;工程中的JavaBean。 View&#xff08;视图&…

阿里云服务器建站教程(5分钟网站上线)

使用阿里云服务器快速搭建网站教程&#xff0c;先为云服务器安装宝塔面板&#xff0c;然后在宝塔面板上新建站点&#xff0c;阿里云服务器网以搭建WordPress网站博客为例&#xff0c;来详细说下从阿里云服务器CPU内存配置选择、Web环境、域名解析到网站上线全流程&#xff1a; …

JAVA学习和题目

先上每日一题看看 1015. 可被 K 整除的最小整数 难度中等147收藏分享切换为英文接收动态反馈 给定正整数 k &#xff0c;你需要找出可以被 k 整除的、仅包含数字 1 的最 小 正整数 n 的长度。 返回 n 的长度。如果不存在这样的 n &#xff0c;就返回-1。 注意&#xff1a;…

async_simple编译测试(一)

前言 async_simple 阿里开源的轻量级 C 异步框架 提供了基于 C20 无栈协程(Lazy)、有栈协程(Uthread) 以及 Future/Promise 等异步组件&#xff0c;能够轻松完成 C 异步的开发&#xff0c;广泛应用于阿里的图计算引擎、时序数据库、搜索引擎等系统。 1&#xff1a;准备 下载地…

Microsoft Edge使用方法和心得

Microsoft Edge 使用方法和心得 本文目录&#xff1a; 一、写在前面的话 二、Edge浏览器简介 三、Microsoft Edge性能 3.1、睡眠标签 3.2、启动加速 3.3、清晰度提升 四、Microsoft Edge游戏特性 五、高效工作 5.1、标签页组 5.2、垂直标签 5.3、集锦 5.4、边栏 5…

Google I/O 2023 - Dart 3 发布,快来看看有什么更新吧

核心原文链接&#xff1a; https://medium.com/dartlang/announcing-dart-3-53f065a10635 自从 Flutter Forword 发布了 Dart 3α 预览 之后&#xff0c;大家对 Dart 3 的正式发布就一直翘首以待&#xff0c;这不仅仅是 Dart 版本号追上了 Flutter 版本号&#xff0c;更是 Dart…

【数据结构】- 教你一步完美应对面试官让你10分钟内实现带头双向循环链表(下)

文章目录 前言&#x1f31f;一、面试官让你十分钟内实现带头双向循环链表&#x1f31f;二、对链表的清晰认知&#x1f31f;三、根据上述步骤简单实现&#x1f30f;3.1结构:&#x1f30f;3.2查找(LTFind)LTEraseLTInsert&#xff1a;&#x1f30f;3.3头插&#xff1a;&#x1f4…

为什么要做计划跟踪:没有计划,就没有控制

日常工作中&#xff0c;我们每天都被大量的信息和任务填满&#xff0c;常常由于任务繁冗复杂&#xff0c;让人陷入一种无所适从的状态。 我们经常会看到很多如何安排工作计划的教程&#xff0c;比如&#xff1a; 要把大的项目分解为小目标&#xff0c;小目目标再分解为日常任务…

【技术干货】PCB焊盘设计之问题详解

SMT的组装质量与PCB焊盘设计有直接的关系&#xff0c;焊盘的大小比例十分重要。如果PCB焊盘设计正确&#xff0c;贴装时少量的歪斜可以再次回流焊纠正(称为自定位或自校正效应)&#xff0c;相反&#xff0c;如果PCB焊盘设计不正确&#xff0c;即使贴装位置十分准确&#xff0c;…

十分钟教你搭建类似ChatGPT的安卓应用程序

大家好&#xff0c;我是易安&#xff01; Chat GPT 是当今著名的人工智能工具&#xff0c;就像聊天机器人一样。Chat GPT会回答发送给它的所有查询。今天&#xff0c;我将通过集成 OpenAI API (ChatGPT)构建一个简单的类似 ChatGPT 的 android 应用程序&#xff0c;我们可以在其…

【docker_centos7】docker在centos7中如何安装??

Docker &#xff1f;&#xff1f;&#xff1f;What&#xff1f;&#xff1f;&#xff1f; Docker是一种开源的容器化平台&#xff0c;可以让开发者轻松地构建、打包、运输和部署应用程序。Docker容器是轻量级的、可移植的、自包含的软件包&#xff0c;其中包含了应用程序和其所…

Pytorch pth 格式转ONNX 格式

目录 背景介绍 安装依赖库 转换成onnx 格式 ONNX 转换成TensorRT 格式 背景介绍 PyTorch 训练的模型&#xff0c;需要在Jetson nano 上部署&#xff0c;jetson 原生提供了TensorRT 的支持&#xff0c;所以一个比较好的方式是把它转换成ONNX 格式&#xff0c;然后在通过ONNX …

给Debian 11系统,添加右键时,使用其它程序打开】

VS Code 添加到文件管理器的右键菜单中 在 Debian 系统中&#xff0c;nautilus-actions 软件包已经被移除了。因此&#xff0c;如果你想将 VS Code 添加到文件管理器的右键菜单中&#xff0c;你需要使用 nautilus-admin 工具。下面是详细步骤&#xff1a; 打开终端应用程序。运…

弃用 Docker 后!哪几种超好用的容器工具能替代?

2020 年底&#xff0c;Kubernetes 发布了公告&#xff0c;官方宣布从 v1.24 开始停止对 Docker 的支持。然而&#xff0c;Kubernetes 弃用 docker 对我们的影响不应过于恐慌。 有以下几种工具可以替代 Docker &#xff1f;并且相比 Docker 都有哪些优点&#xff1f; containe…

JRebel插件热部署快速入门教程

文章目录 引入插件安装插件激活打开激活窗口激活插件 插件使用设置项目热更新热更新说明演示热更新 引入 Jrebel能够非常方便的帮助我们进行项目的热更新&#xff0c;尤其是前端也嵌在后端工程中的单体项目&#xff0c;热更新能减少一半的开发时间&#xff0c;这里我们演示一下…

分布式缓存Redis 面试突击

Redis分布式锁 单机版 关于 synchronized 和trylock的区别 前者 是 不见不散 我一定要等到你 后者是 过时不候 我尝试一下 获取不到就算了 可以设置一个时间 这个时间范围内获取不到就算了 用缓存两个目的&#xff1a;高性能与高并发 高性能&#xff1a;减少了查询…

使用Linkage Mapper制作环境连接图

✅创作者&#xff1a;陈书予 &#x1f389;个人主页&#xff1a;陈书予的个人主页 &#x1f341;陈书予的个人社区&#xff0c;欢迎你的加入: 陈书予的社区 &#x1f31f;专栏地址: Linkage Mapper解密数字世界链接 文章目录 引言一. 简介1.1 Linkage Mapper 概述1.2 环境连接…

基于html+css的图展示55

准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…

DJ5-8 磁盘存储器的性能和调度

目录 5.8.1 磁盘性能简述 1、磁盘的结构和布局 2、磁盘的类型 3、磁盘数据的组织和格式 4、磁盘的访问过程 5、磁盘访问时间 5.8.2 磁盘调度算法 1、先来先服务 FCFS 2、最短寻道时间优先 SSTF 3、扫描算法&#xff08;电梯算法&#xff09;SCAN 4、循环扫描算法 …