【Linux】进程篇Ⅱ:进程开始、进程终止、进程等待

news2024/11/17 2:54:39

文章目录

  • 五、fork 函数,创建进程
      • 写时拷贝
  • 六、进程终止
      • 1. 退出码
      • 2. 如何终止程序
  • 七、进程等待
      • 1. 概念
      • 2. wait 函数
        • waitpid 函数 🔺
      • 3. 阻塞等待


五、fork 函数,创建进程

#include <unistd.h>

pid_t fork(void);

返回值:

  • 子进程 返回 0
  • 父进程 返回 子进程 id
  • 出错 返回 -1

进程调用 fork,当控制转移到内核中的 fork 代码后,内核做:

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

fork 之前,父进程独立执行,
fork 之后,父子两个执行流分别执行 fork 后面的代码。

注意,fork之后,谁先执行完全由调度器决定。

写时拷贝

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

在这里插入图片描述

fork常规用法

  • 一个父进程希望复制自己,使父子进程同时执行不同的代码段。例如,父进程等待客户端请求,生成子进程来处理请求。
  • 一个进程要执行一个不同的程序。例如子进程从fork返回后,调用exec函数。

fork调用失败的原因

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

六、进程终止

如何理解进程退出?

OS内少了一个进程,OS就要释放进程对应的内核数据结构 + 代码和数据(如果有独立的)

进程结束情况分类:

a. 执行完了,正常退出

  • 结果正确
  • 结果不正确

b. 崩溃了,进程异常 (通过 信号 的方式,后面更新)

  • 崩溃的本质: 进程因为某些原因,导致进程收到了来自操作系统的信号 kill -9

1. 退出码

我们说,进程是有退出码的。

比如 main 函数中的 return x; 正常执行完了

  • 结果正确,退出码为 0,
  • 结果不正确, 1、2、3、4 则表示不同的原因(供用户进行进程退出健康状态的判定)

echo $? 可以 查看 最近执行的进程的 退出码

echo $?

2. 如何终止程序

正常终止有三种方式:

① main 函数 return。

  • 其他函数return,仅仅代表该函数返回
  • 进程执行,本质是main执行流执行!

void exit(int status) 函数退出

  • 头文件 #include <stdlib.h>
  • status 代表就是进程的退出码
  • 等价于 main return XXX
  • 会刷新缓冲区、关闭流等
  • 其实 exit 函数 也是封装调用的 _exit 函数

void _exit(int status) 函数退出

  • 头文件 #include <stdlib.h>
  • status 代表就是进程的退出码
  • 直接退出程序,不处理缓冲区等

在这里插入图片描述

由此我们可以推测出的就是:
	缓冲区,一定不在 OS 内,不然肯定都能刷新了,事实上并没有
这是因为用户层和 OS 之间,还有一个 C库。(后面更新)

异常退出

  • ctrl + C 信号终止(信号!!后面更新)

七、进程等待

1. 概念

进程等待 就是 通过系统调用,获取 子进程 退出码 或者 退出信号 的方式,顺便释放内存问题。

进程等待的作用:

  • 避免内存泄漏

  • 获取子进程执行的结果(如果必要)

    • 对于进程退出的结果查验,我们通过的还是上述提过的 信号 + 退出码 的方案。

等待,实际上就是 父进程 对 子进程 的等待

2. wait 函数

pid_t wait(int *status)

头文件: #include <sys/wait.h>
返回值:等待成功则返回 子进程 的 PID

waitpid 函数 🔺

pid_t waitpid(pid_t pid, int *status, int options);

头文件 #include <sys/wait.h>

返回值:

  • > 0,success(返回的是对应子进程的 pid)
  • ==-1,failed(一般不会失败,pid 传错了会导致失败)
    == 0,在选项设置了 WNOHANG 的情况下,发现没有已退出的子进程可收集

参数 pid

  • >0,表示等待指定的子进程
  • ==-1,表示等待任一个子进程,与 wait 等效

参数 status:是一个输出型参数

  • 我们会想得到,子进程的退出状态,即 信号 + 返回码
    • 如何用一个整数 返回两个整数…?实际上这里的 status 是类似位图作用的。信号和返回码都不超过一定值,用一个整数位存储足够啦!(见下文)
  • WIFEXITED(status):若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
  • WEXITSTATUS(status):若 WIFEXITED 非零,提取子进程退出码。(查看进程的退出码)

参数 options

  • 0,默认父进程进行 阻塞等待
  • WNOHANG,父进程进行 非阻塞轮询

🌰使用举例:

int status = 0;
pid_t ret_id = waitpid(id, &status, WNOHANG);
if(ret_id < 0)
{
    printf("waitpid error!\n");
    exit(1);
}
else if(ret_id == 0)	// 子进程还没结束
{
    RunTask();
    sleep(1);
    continue;
}
else					// 子进程结束了
{
    if(WIFEXITED(status))	// 正常退出
    {
        printf("wait success, child exit code: %d\n", WEXITSTATUS(status));
    }
    else					// 异常退出
    {
        printf("wait success, child exit signal: %d\n", status & 0x7F);
    }
    break;
}

status 记录 信号 和 退出码 的方式:
00000000 00000000 00000000 00000000

  • 高 16 位不需要
  • 低 16 位中,次低的 8 位(黑体比特位),储存 退出状态
  • 最低 7 位(斜体比特位),储存 终止信号
  • 第8位,是 core dump 标志(后面更新,信号篇)

想要自己访问 信号 和 退出码,我们用如下方式:

// 退出码
(status>>8) & 0xFF;
// 终止信号
 status & 0x7F;

所以,

父进程 如何拿到 子进程 的退出码和终止信号的呢?

实际上,进程的 PCB 中,就有这两个成员值。

struct task_struct
{
	// ...
	int exit_code;
	int exit_signal;
	
	task_struct* parent;
};
  • 当子进程执行完毕时 main 函数的退出码,写入 PCB 的 exit_code 里
  • 如果出异常,OS 会把遇到异常时的信号编号写到 PCB 的 exit_signal 里
  • 进程退出之后,OS 会将进程 PCB 维护起来,通过系统调用接口 waitpid / wait,就可以让用户拿到这些数据。

3. 阻塞等待

父进程在 wait 的时候,如果子进程没有退出,父进程只能一直调用 waitpid 进行等待,这就叫 阻塞等待

  • 可以理解为父进程从 R 状态,开始等待,进入 S 状态。
  • 这个等待不在运行队列,可以理解为在一个只有它一个节点的等待队列里等待。
  • 等待的同时,子进程的 CPB 中找到并挂接到父进程,直到子进程结束,父进程再回到运行队列(R 状态)

如果我们不想 阻塞等待,即 不想在 waitpid 处卡住呢?

  • 非阻塞轮询(WNOHANG,waitpid 的最后一个参数)
  • 故,我们在日常这样情况的时候,说 这个程序 夯住了

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

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

相关文章

动态SQL 语句-更复杂的查询业务需求也能轻松拿捏

文章目录 动态SQL 语句-更复杂的查询业务需求动态SQL-官方文档为什么需要动态SQL动态SQL-基本介绍基本介绍动态SQL 必要性解决方案分析 动态SQL 常用标签动态SQL-案例演示if 标签应用实例where 标签应用实例choose/when/otherwise 应用实例forEach 标签应用实例trim 标签应用实…

【C语言进阶篇】 你真的学会数组了嘛?数组笔试题万字解析(上)

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏:《C语言初阶篇》 《C语言进阶篇》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 &#x1f4cb; 前言&#x1f4ac; 数组笔试题解析&#x1f4ad; 一维数组笔试题✅ 一维数组笔试题解析✅ 一维数…

ShardingSphere 源码模块介绍

目录 Agent Db-Protocol Dialect-Exception Distribution Features Infra JDBC Kernel Mode Proxy Agent agent 模块提供了基于 Java Agent 实现的可观察框架 logging&#xff1a; 用于记录 ShardingSphere 的日志&#xff0c;支持文件metricrs&#xff1a;用于收集…

C++那些事之高性能SIMD

C那些事之高性能SIMD 最近在看相关向量化的内容&#xff0c;看起来有点头大&#xff0c;借此机会&#xff0c;学习一下高性能SIMD编程。 SIMD全称single-instruction multiple-data,单指令多数据。 在传统的计算机架构中&#xff0c;CPU一次只能处理一个数据元素。但是&#xf…

【机器学习】Feature scaling and Learning Rate (Multi-variable)

Feature scaling and Learning Rate 1、数据集2、学习率2.1 α \alpha α 9.9e-72.2 α \alpha α 9e-72.3 α \alpha α 1e-7 3、特征缩放3.1 特征缩放的原因3.2 Z-score 归一化3.3 预测3.4 损失等值线 导入所需的库 import numpy as np np.set_printoptions(precision…

【C++】类和对象-C++运算符重载

运算符重载 1.加号运算符重载 代码&#xff1a; #include <iostream> using namespace std; /******************************************/ //加号运算符重载class Person { public:int m_A;int m_B;//1、成员函数重载号(不能与下面方式2同时存在&#xff0c;否则代码报…

在docker中没有vi如何修改docker中的文件

今天在做学成在线的项目&#xff0c;遇到了一个问题&#xff0c;就是死活登不上xxl-job&#xff0c;按照之前遇到的nacos的问题&#xff0c;我怀疑很大概率是和当时的ip设置有关&#xff0c;不知道nacos的ip怎么修改的同学&#xff0c;可以看看这篇文章&#xff1a;关于docker中…

电子词典

项目要求&#xff1a; 1.登录注册功能&#xff0c;不能重复登录&#xff0c;重复注册。用户信息也存储在数据库中。 2.单词查询功能 3.历史记录功能&#xff0c;存储单词&#xff0c;意思&#xff0c;以及查询时间&#xff0c;存储在数据库 4.基于TCP&#xff0c;支持多客户…

【AI网站分享】

AI网站分享 1 AI应用2 AI 写作3 AI 编程4 AI设计5 AI作图6 AI训练模型7 AI影音编辑8 AI效率助手 网站链接&#xff1a; https://tools.haiyong.site/ai/ 网站中的内容大致可以分为八类&#xff1a;AI应用、AI写作、 AI 编程、 AI设计、 AI作图、AI训练模型、 AI影音编辑、 AI效…

线程属性——线程分离应用

文章目录 相关函数初始化释放线程属性的资源获取线程分离的状态属性设置线程分离的状态属性获取线程的栈的大小线程分离应用 相关函数 可以通过man pthread_attr_然后按两次table键查询和属性相关的函数 初始化 释放线程属性的资源 获取线程分离的状态属性 设置线程分离的状…

C# VS2022+WinForm+Oracle19.3+存储过程,SQL和代码分离

【我的目的】&#xff1a;SQL和代码分别存放在不同的地方&#xff0c;便于随时修改SQL的内容&#xff0c;也便于修改SQL的书写格式 方案1&#xff1a;把SQL存放在DataSet.xsd中实现SQL和代码分离 方案2&#xff1a;用存储过程实现SQL和代码分离 我最倾向方案1&#xff0c;利用…

链路 聚合

静态链路聚合&#xff1a;多数内网使用 。非物理直连建议与BFD联动 动态链路聚合LACP&#xff1a;是公有协议、内网、二层专线接口都能使用&#xff0c;现网多数使用此方式链路 聚合 PAGP&#xff1a;思科私有协议&#xff0c;只支持思科设备使&#xff0c;现网多数不用

Windows驱动开发

开发Windows驱动程序时&#xff0c;debug比较困难&#xff0c;并且程序容易导致系统崩溃&#xff0c;这时可以使用Virtual Box进行程序调试&#xff0c;用WinDbg在主机上进行调试。 需要使用的工具&#xff1a; Virtual Box&#xff1a;用于安装虚拟机系统&#xff0c;用于运…

谨防虚假发货!了解如何辨别真假发货单号

随着电子商务的发展&#xff0c;快递行业成为了一个不可忽视的重要环节。然而&#xff0c;虚假发货单号的出现给快递行业带来了一定的困扰。为了解决这个问题&#xff0c;一些快递批量查询高手软件开始应用于虚假发货单号的分析。本文将介绍这些软件如何分析出虚假发货单号&…

应用开发者的疑问:大模型是银弹吗?

被当成银弹的大模型 ChatGPT 火了之后&#xff0c;大模型似乎被当成了真正的银弹&#xff0c;所有的体验问题都想通过大模型解决&#xff1a; 能不能和大模型对话订机票&#xff1f;自然语言生成 SQL&#xff0c;简化报表分析工作&#xff1f;大模型帮老年人操作软件&#xff…

nosql之redis集群

文章目录 一.redis集群1.单节点redis服务器带来的问题2.集群redis3.集群的优势4.redis集群的实现方法5.redis群集的三种模式5.1 主从复制5.2 哨兵5.3 集群 二.Redis 主从复制1.主从复制概念2.主从复制的作用3.主从复制流程4.搭建Redis 主从复制4.1 安装 Redis4.2 修改 Redis 配…

RBF神经网络原理和matlab实现

1.案例背景 1.1 RBF神经网络概述 径向基函数(Radical Basis Function,RBF)是多维空间插值的传统技术,由Powell于1985年提出。1988年, Broomhead和 Lowe根据生物神经元具有局部响应这一特点,将 RBF引入神经网络设计中,产生了RBF神经网络。1989 年&#xff0c;Jackson论证了…

开源项目-erp企业资源管理系统(毕设)

哈喽,大家好,今天给大家带来一个开源项目-erp企业资源管理系统,项目通过ssh+oracle技术实现。 系统主要有基础数据,人事管理,采购管理,销售管理,库存管理,权限管理模块 登录 主页 基础数据 基础数据有商品类型,商品,供应商,客户,仓库管理功能

Python零基础入门(十)——模块与包

系列文章目录 个人简介&#xff1a;机电专业在读研究生&#xff0c;CSDN内容合伙人&#xff0c;博主个人首页 Python入门专栏&#xff1a;《Python入门》欢迎阅读&#xff0c;一起进步&#xff01;&#x1f31f;&#x1f31f;&#x1f31f; 码字不易&#xff0c;如果觉得文章不…

【C语言-扫雷游戏全功能详解】

目录 理解扫雷原理 梳理扫雷过程 9*9 棋盘 初始化棋盘 显示棋盘 ​编辑 布置雷 排查雷 统计x,y坐标周围有几个雷 使用递归函数来实现周围没地雷时展开多个 判断成功排除后剩下的方格数是否等于地雷数 排查函数 梳理编写代码思路 头文件game.h 库函数所需要头文件 …