Linux fork、进程的退出和等待详解

初识fork函数

它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。

#include <unistd.h>
pid_t fork(void);


返回值:子进程中返回0,父进程返回子进程id,出错返回-1

最简单的fork使用示例

#include<stdio.h>
#include<unistd.h>
int main()
{pid_t pid=fork();if(pid<0){printf("创建出错\n");}else if(pid==0){printf("子进程创建成功\n");}else{printf("父进程\n");}printf("父进程\n");return 0;
}

父进程的判断可以不写,只写子进程和出错的判断就ok

代码详解

如果想要子进程运行完自己的部分后不执行原程序部分则需要加入退出部分

创建进程fork

#include<stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
int ret=fork();
if(ret<0)
{perror("fork");return 1;
}
else if(ret==0)
{printf("child_id : %d! , ret %d\n " ,getpid(),ret);
}
else //father ret>0  printf("father_id : %d! , ret %d\n " ,getpid(),ret);sleep(1);
return 0;}#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>int main() {pid_t pid;// 创建子进程pid = fork();if (pid < 0) {// fork失败fprintf(stderr, "fork failed");return 1;} else if (pid == 0) {// 子进程printf("This is the child process. PID: %d\n", getpid());} else {// 父进程printf("This is the parent process. Child PID: %d\n", pid);}// 父子进程都会执行以下代码printf("PID: %d - End of the program\n", getpid());return 0;
}

fork进程创建基本功能

  • fork() 调用在父进程中调用一次,但返回两次:一次在父进程中返回,一次在子进程中返回。
  • 在父进程中,fork() 返回子进程的进程ID。一般都是>0
  • 在子进程中,fork() 返回0。
  • 如果 fork() 调用失败,它返回一个负值。

fork工作原理的理解

fork的优点

  • 利用子进程计算一些数据
  • 让子进程和父进程分开执行不同代码块

写实拷贝

  • 通常,父子代码共享,父子再不写入时,数据也是共享的,当任意一方试图写入,便以写时拷贝的方式各自一份副本

fork调用失败的原因

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

进程退出与等待

进程退出场景

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

进程常见退出方法

正常终止(可以通过 echo $? 查看进程退出码):

  • 从main返回
  •  调用exit
  • _exit

异常退出:

  • ctrl + c,信号终止

_exit函数

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


参数:status 定义了进程的终止状态,父进程通过wait来获取该值

说明:虽然status是int,但是仅有低8位可以被父进程所用。所以_exit(-1)时,在终端执行$?发现返回值是255。

-1 的补码表示是:1111 1111转成10进制位255

注意事项:

  • _exit() 调用会立即终止进程,不会执行任何清理操作,例如不会刷新标准I/O缓冲区,不会调用任何终止处理程序
  • _exit() 会关闭进程打开的所有文件描述符(通过open等函数打开文件返回后的值)。
  • _exit() 调用后,进程所占用的资源(如内存、文件描述符等)将被操作系统释放。
  • _exit() 中的参数只需要修改低8位即可

exit函数

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

参数:status 定义了进程的终止状态,父进程通过wait来获取该值

exit最后也会调用_exit, 但在调用_exit之前,还做了其他工作:

1. 执行用户通过 atexit或on_exit定义的清理函数。
2. 关闭所有打开的流,所有的缓存数据均被写入
3. 调用_exit

由此可知,C语言用的是exit()进而能在退出前显示缓冲区内容

而系统调用_exit()则不会显示缓冲区内容

代码结果反应退出函数区别

代码

int main()
{
printf("hello");
exit(0);
}
运行结果:
[root@localhost linux]# ./a.out
hello[root@localhost linux]#
int main()
{
printf("hello");
_exit(0);
}
运行结果:
[root@localhost linux]# ./a.out
[root@localhost linux]#

区别

exit() 函数:

  • exit() 是C标准库函数,定义在 <stdlib.h> 头文件中。
  • 当调用 exit()时,它会刷新所有标准I/O流(例如 stdout),关闭所有标准库中打开的文件描述符,调用通过 atexit() 注册的所有退出处理函数,然后终止程序。
  • 因此,当你在调用 printf("hello") 后调用 exit(0),printf 的输出会被刷新到标准输出,你会在终端看到 “hello”。(exit()读取缓存文件)

_exit() 系统调用:

  • _exit() 是一个系统调用,定义在 <unistd.h> 头文件中。
  • 当调用 _exit() 时,它会立即终止程序,而不会刷新标准I/O流,也不会调用任何退出处理函数。
  • 因此,当你在调用 printf("hello") 后调用 _exit(0)printf 的输出缓冲区不会被刷新,“hello” 不会被打印到终端。(_exit()不读取缓存文件)

不推荐使用_exit(),使用exit()和return满足日常需求

return退出

return是一种更常见的退出进程方法。执行return n等同于执行exit(n),因为调用main的运行时函数会将main的返回值当做 exit的参数。

注意:

exit() 函数和 return 语句通常不能同时使用,因为它们都会导致程序终止。

wait方法

#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会被设置成相应的值以指示错误所在;



参数:
       1. pid:

  •  Pid=-1,等待任一个子进程。与wait等效。
  •  Pid>0.等待其进程ID与pid相等的子进程。

       2.status:

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

       3.options(阻塞方式):

  • WNOHANG:如果子进程尚未终止,waitpid() 函数不会阻塞父进程,而是立即返回 -1,并设置 errno 为 ECHILD。这通常用于非阻塞等待即父进程可以在等待子进程的同时继续执行其他任务。
  • WUNTRACED:如果子进程是暂停状态(例如,因为它正在等待输入或正在后台运行),waitpid() 函数会返回子进程的进程ID,并设置 status 变量。
  • WNOHANG 和 WUNTRACED 结合使用(WNOHANG|WUNTRACED):如果指定的子进程尚未终止,waitpid() 函数不会阻塞调用进程,而是立即返回 -1,并设置 errno 为 ECHILD。如果指定的子进程是暂停状态,waitpid() 函数会返回子进程的进程ID,并将子进程的退出状态和信号信息存储在 status 变量中。(组合使用意味多种情况多种选项,不单单只适合一种情况)
  • WCONTINUED:如果子进程是暂停状态,并且被信号唤醒(例如,因为它等待的输入已到达),waitpid() 函数会返回子进程的进程ID,并设置 status 变量。

当第三个参数设置为 0 时,它等同于 WNOHANG 和 WUNTRACED 的组合。这意味着 waitpid() 函数不会阻塞父进程,并且如果子进程是暂停状态,它会返回子进程的进程ID,并设置 status 变量。如果子进程尚未终止,waitpid() 函数不会阻塞父进程,而是立即返回 -1,并设置 errno 为 ECHILD


等待函数注意事项

  • 如果子进程已经退出,调用wait/waitpid时,wait/waitpid会立即返回,并且释放资源,获得子进程退出信息。
  • 如果在任意时刻调用wait/waitpid,子进程存在且正常运行,则进程可能阻塞。
  • 如果不存在该子进程,则立即出错返回。

代码结果反应等待函数

#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <unistd.h>int main() {pid_t pid = fork();if (pid < 0) {perror("fork failed");return 1;} else if (pid == 0) {// 子进程代码printf("子进程创建成功,倒数3s\n");int n = 3;while (n--) {printf("%d ", n);}printf("\n");exit(2);  // 子进程正常退出} else {// 父进程代码int status;pid_t ret = waitpid(pid, &status, 0);  // 等待子进程结束if (ret == -1) {perror("waitpid failed");return 1;}if (WIFEXITED(status))// 子进程正常退出{printf("子进程的退出状态: %d\n", WEXITSTATUS(status));}else if (WIFSIGNALED(status))// 子进程因信号终止 { printf("子进程被信号终止,信号: %d\n", WTERMSIG(status));}}return 0;
}

结果

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

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

相关文章

每日一题,力扣leetcode Hot100之11. 盛最多的水

解法一&#xff1a; 双层循环遍历找结果&#xff0c;相当于是一个暴力求解方法&#xff0c;两层嵌套循环的时间复杂度是O(N2&#xff09;&#xff0c;所以有一些样例测试时间超时 class Solution:def maxArea(self, height: List[int]) -> int:max_result0for i in range(…

VMware 安装完,设备管理器中没有虚拟网卡(vmnet0、wmnet1、vmnet8) / 虚拟网络编辑器中没有桥接模式

问题&#xff1a;VMware 安装完&#xff0c;设备管理器中没有虚拟网卡(vmnet0、wmnet1、vmnet8) / 虚拟网络编辑器中没有桥接模式 1、确认 Device Install Service 和 Device Setup Manager 没有被禁用 Device Install Service 和 Device Setup Manager是 Windows 操作系统中…

GraphRAG参数与使用步骤 | 基于GPT-4o-mini实现更便宜的知识图谱RAG

首先给兄弟朋友们展示一下结论&#xff0c;一个文本18万多字&#xff0c;txt文本大小185K&#xff0c;采用GraphRAG,GPT-4o-mini模型&#xff0c;索引耗时差不多5分钟&#xff0c;消耗API价格0.15美元 GraphRAG介绍 GraphRAG是微软最近开源的一款基于知识图谱技术的框架&#…

“论系统安全架构设计及其应用”,写作框架,软考高级论文,系统架构设计师论文

论文真题 随着社会信息化进程的加快&#xff0c;计算机及网络已经被各行各业广泛应用&#xff0c;信息安全问题也变得愈来愈重要。它具有机密性、完整性、可用性、可控性和不可抵赖性等特征。信息系统的安全保障是以风险和策略为基础&#xff0c;在信息系统的整个生命周期中提…

73、Flink 的 DataStream API 生产实践总结

0、汇总 1.可以使用 Maven 命令、CURL 命令、IDEA 手动创建 Flink 项目&#xff1b;2.可以使用 Maven Shade 插件将必需的依赖项打包进应用程序 jar 中&#xff1b;3.应该在 Flink 集群的 lib 文件夹内配置需要的&#xff08;核心&#xff09;依赖项&#xff1b;4.应该将程序中…

【Godot4.2】GodotXML插件 - 解析和生成XML

概述 近期在研究基于Godot的XML和SVG解析&#xff0c;并且在昨天&#xff08;2024年7月20日&#xff09;编写了一个简易的SVG文件解析器。 在群友的提示下&#xff0c;知道早就存在GodotXML这样的解析器。所以今天就来测试使用并准备研究学习源代码了。和以往一样&#xff0c…

GD32 MCU是如何进入中断函数的

用过GD32 MCU的小伙伴们都知道&#xff0c;程序是顺序执行的&#xff0c;但当有中断来的时候程序会跳转到中断函数&#xff0c;执行完中断函数后程序又继续回到原来的位置继续执行&#xff0c;那么你们知道MCU是如何找到中断函数入口的吗&#xff1f; 今天我们就以GD32F303系列…

Flex和Bison

Flex和Bison是Linux和Unix环境下两个非常强大的工具&#xff0c;分别用于生成词法分析器和语法分析器。它们在编译器设计、文本处理等领域有着广泛的应用。下面我将详细介绍Flex和Bison的基本概念、功能、用法以及它们之间的关系。 一、Flex 1. 基本概念 Flex&#xff08;其…

JavaScript进阶之作用域解构箭头函数

目录 一、作用域1.1 局部作用域1.2 全局作用域1.3 作用域链1.4 垃圾回收机制1.5 闭包1.6 变量提升 二、函数进阶2.1 函数提升2.2 函数参数2.3 箭头函数&#xff08;重要&#xff09; 三、解构赋值3.1 数组解构3.2 对象解构&#xff08;重要重要&#xff09; 一、作用域 1.1 局…

【BUG】已解决:ModuleNotFoundError: No module named‘ pip‘

已解决&#xff1a;ModuleNotFoundError: No module named‘ pip‘ 目录 已解决&#xff1a;ModuleNotFoundError: No module named‘ pip‘ 【常见模块错误】 【解决方案】 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博主英杰…

揭开黑箱:目标检测中可解释性的重要性与实现

揭开黑箱&#xff1a;目标检测中可解释性的重要性与实现 在深度学习的目标检测任务中&#xff0c;模型的准确性虽然重要&#xff0c;但模型的决策过程是否透明也同样关键。可解释性&#xff08;Explainability&#xff09;是指模型能够为其预测结果提供清晰、可理解的解释。本…

SpringCloud--负载均衡

目录 前言 一.负载均衡的引入 1.1问题引入 1.2代码修改实现 二.负载均衡介绍 2.1实现负载均衡 2.2负载均衡策略 2.3LoadBalancer 原理 学习专栏&#xff1a;http://t.csdnimg.cn/tntwg 前言 在前面的Eureka当中&#xff0c;我们虽然实现了从注册中心中获取url&#xf…

桌面小宠物发布一周,第一次以独立开发者的身份赚到了100块

收入数据(AppStore一周收入统计) AppStore付费工具榜第七 应用简介 桌面新宠(NewPet)&#xff0c;是我耗时半年开发的一款桌面宠物。我是被 QQ 宠物影响的那批人&#xff0c;上学时天天给 QQ 宠物喂食&#xff0c;很可惜它现在不在了。所以&#xff0c;我开发的初衷是想要在电…

将SQL中的占位符替换成参数

将SQL中的占位符替换成参数 描述 描述 此方法是将SQL中的${}或#{}替换为直接拼接到SQL中或直接替换为?的形式。具体详情看下面代码。 import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern;/*** author HuYu* date 2023-09-21* since 1.0**…

编码和解码

编码 编码的原理 编码是将信息&#xff08;如文本、图像、音频等&#xff09;转换成计算机可以处理和存储的二进制格式&#xff08;0和1&#xff09;的过程。不同的编码方案定义了不同的信息转换规则&#xff1a; 字符编码&#xff1a;用于将字符转换为数字。常见的有&#…

WPF 解决: DataGrid 已定义列,但是还是会显示模型的所有属性的问题

AutoGenerateColumns 属性 AutoGenerateColumns&#xff1a;这个属性决定 DataGrid 是否根据数据源中的属性自动生成列。如果设置为 true&#xff0c;DataGrid 会根据数据源中的属性自动生成列。如果设置为 false&#xff0c;则 DataGrid 不会自动生成列&#xff0c;开发者需要…

Mysql-查询

1.基本查询 //查询所有内容 select * from 表名;//查询指定字段 select 字段1&#xff0c;字段2&#xff0c;字段3.....from 表名;//查询时给字段起别名 select 字段1 as 别名1 , 字段2 as 别名2 ... from 表名&#xff1b;//去重查询 select distinct 字段列表 from 表名; …

假设我写了一段C++循环代码,我希望对这段代码做 profiling,计算出每次循环需要消耗的指令 cycle 数。我应该如何实现这种 profiling?

为了对你编写的C循环代码进行profile并计算每次循环消耗的指令周期数&#xff0c;可以采用以下步骤&#xff1a; 使用硬件性能计数器 使用 rdtsc 指令 rdtsc&#xff08;读时间戳计数器&#xff09;指令可以返回一个64位时间戳计数器的值&#xff0c;这个计数器从系统启动时开…

解决显存不足问题:深度学习中的 Batch Size 调整【模型训练】

解决显存不足问题&#xff1a;深度学习中的 Batch Size 调整 在深度学习训练中&#xff0c;显存不足是一个常见的问题&#xff0c;特别是在笔记本等显存有限的设备上。本文将解释什么是 Batch Size&#xff0c;为什么调整 Batch Size 可以缓解显存不足的问题&#xff0c;以及调…

杰发科技AC7840——SENT数据解析及软件Sent发送的实现

0. 测试环境 AC7840官方Demo板&#xff1b; 图莫斯0503 DSlogic U2Basic 使用引脚 输出脚&#xff1a;PB1 时钟&#xff1a;PB2&#xff0c;其他引脚可以不初始化&#xff0c;不接线 1. 数据解析 以下是SENT数据的格式&#xff08;1tick以3us为例&#xff09;&#…