再谈Linux 进程:进程等待、进程替换与环境变量

目录

1.进程等待

为什么需要进程等待?

相关系统调用:wait()和waitpid()

wait():

waitpid():

解析子进程状态(status)

2.进程替换

为什么需要进程替换?

相关系统调用:exec函数家族

3.环境变量

​编辑

​编辑


        本文将对Linux环境下的进程:包括进程创建、终止与进程等待、替换进行讲解,作者使用XShell连接配置为CentOs 7.6的主机进行演示,希望能帮助你更好理解操作系统的运行原理!

1.进程等待

        在操作系统中,进程等待是指父进程主动等待子进程结束执行,并回收子进程资源的过程。这是进程管理中的重要机制,主要用于解决子进程结束后资源释放和状态获取的问题。

为什么需要进程等待?

  1. 回收子进程资源
            子进程结束后,其内核数据结构PCB不会立即释放,会变成僵尸进程(ps:僵尸进程不能被杀死,只能通过进程等待解决!)父进程通过等待机制回收子进程的残留资源,避免内存泄漏和系统资源浪费。

  2. 获取子进程退出状态
            父进程可以通过等待机制获取子进程的执行结果(退出的状态值、被终止收到的信号),进行后续处理。

相关系统调用:wait()和waitpid()

        先来看看man-pages的wait介绍:

wait():

  • 作用:阻塞当前父进程,直到任意一个子进程结束或被信号中断。
  • 参数
    status
    是一个指向整数的指针,用于存储子进程的退出状态(可通过宏解析)。
  • 返回值
    • 成功时返回结束的子进程的PID;
    • 失败时返回 -1
//以下是对进程等待的测试
int testwait()
{pid_t id=fork();if(id<0){perror("fork");return 1;}else if(id==0){//childint cnt=5;while(cnt){printf("I am child,pid:%d,ppid:%d,cnt:%d\n",getpid(),getppid(),cnt);cnt--;sleep(1);}exit(0);}else {//parentint cnt=10;while(cnt){printf("I am father,pid:%d,ppid:%d,cnt:%d\n",getpid(),getppid(),cnt);cnt--;sleep(1);}//这里先不介绍status
-----------------------------------------------------------------------------------------pid_t ret=wait(NULL);//这里wait返回的就是子进程创建成功后返回的pidif(ret==id){printf("success!\n");}
-----------------------------------------------------------------------------------------}return 0; 
}//补充:如果创建了多个子进程,又如何通过wait获得子进程的运行状态呢?
//在---画出的区域可以这样修改:
//对于多个子进程,只需要将wait遍历即可for(i=0;i<n;i++){pid_t ret=wait(NULL);if(ret>0){printf("wait %d success\n",ret);}}

waitpid():

  • 作用:等待指定的子进程(更灵活,可设置非阻塞等待)。
  • 参数
  • pid
    • pid =  -1:等待任意子进程;
    • pid > 0:等待 PID 为 pid 的子进程(一般设定为指定等待子进程的PID);
    • status:同上,用于获取子进程状态。
    • options
      • 0(默认选择):阻塞模式,若子进程未结束则一直等待(阻塞状态)
      • WNOHANG: 非阻塞模式,若子进程未结束则立即返回0
  • 返回值
    • 子进程未结束:根据options返回 0(非阻塞)或阻塞等待;
    • 子进程结束:返回该子进程的 PID;
    • 出错:返回 -1
int testwaitplus()
{pid_t id=fork();if(id<0){perror("fork");return 1;}else if(id==0){//childint cnt=20;while(cnt){printf("I am child,pid:%d,ppid:%d,cnt:%d\n",getpid(),getppid(),cnt);cnt--;sleep(1);}exit(0);}else {//parentint cnt=10;while(cnt){printf("I am father,pid:%d,ppid:%d,cnt:%d\n",getpid(),getppid(),cnt);cnt--;sleep(1);}//pid_t ret=wait(NULL);//这里wait返回的就是子进程创建成功后返回的pidwhile(1){int status=0;// pid_t ret=waitpid(-1,&status,0);pid_t ret=waitpid(-1,&status,WNOHANG);if(ret>0){printf("success!\n");printf("exit successfully,pid:%d\n,exit_code:%d,exit_signal:%d\n",ret,(status>>8)&0xFF,status&0x7F);//还可以用宏if(WIFEXITED(status))//判断是否异常退出{printf("进程正常退出,无异常!exit_code:%d\n",WEXITSTATUS(status));}exit(0);}else if(ret<0){printf("wait failed\n");}else {printf("子进程还未退出,waiting...\n");}sleep(1);}}return 0; 
}

解析子进程状态(status)

        这里的status到底是什么?这里详细介绍一下这个玩意:

status本质上就是一个整形:4个字节对应32个bit位:

0000 0000 0000 0000 0000 0000 0000 0000

前16位不考虑,后十六位进行写入标记;
0000 0000 0000 0000

后七位表示异常信号,倒数第八位(标红)标识core dump,前八位表示退出码,自己想要查看两码可以进行位操作,也可以用宏:
WIFEXITED(status): 若为正常终止子进程返回的状态,则为真
WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码

其实本质上就是进行的位操作:

(status>>8)&0xFF(0xFF十六进制)

status&0x7F(0x7F十六进制)

2.进程替换

        在操作系统中,进程替换是指用一个新的程序(可执行文件或脚本)替换当前进程的内存空间、代码和数据,使进程转而执行新程序的过程。这一过程不会创建新进程,而是直接覆盖当前进程的上下文,因此进程的PID保持不变,但执行的内容被完全替换。

为什么需要进程替换?

  1. 执行外部程序
    例如,在Shell中输入ls命令时,Shel
    l会通过进程替换让当前子进程执行ls程序。
  2. 程序升级或切换功能
    一个进程在运行中需要切换到另一个功能模块时,可通过替换自身来实现。

相关系统调用:exec函数家族

       进程替换通过exec系列函数实现,共有 7个函数,统称为exec函数族。它们的作用是加载并执行一个新程序,替换当前进程的内存空间。

函数原型与区别

上述六个都是库函数,都是基于系统调用execve实现的:

给出总结: 

函数名参数形式是否搜索 PATH 环境变量能否自定义环境变量
execl可变参数列表(以 NULL 结尾)
execlp可变参数列表是(根据 PATH 查找程序)
execle可变参数列表是(传入环境变量数组)
execv字符指针数组(argv
execvp字符指针数组
execvpe字符指针数组
execve字符指针数组是(系统调用)

核心区别:

参数传递方式:带 的函数(类似链表)通过逗号分隔的可变参数列表传递参数,最后以NULL结尾;带 的函数(类似数组)通过字符指针数组传递参数,类似main函数的参数形式。

是否搜索 PATH:带 的函数会根据系统环境变量PATH查找程序路径,无需指定绝对路径。

环境变量控制带 的函数可以自定义环境变量,否则继承当前进程的环境变量。

底层系统调用execve 是唯一的系统调用,其他函数均是对它的封装。

exec函数的特点:

执行成功后不返回
        若 exec 调用成功,当前进程的代码、数据、堆、栈等会被新程序完全替换,进程从新程序的入口点开始执行,
不会返回原程序。(也没有办法返回)仅当 exec 调用失败时,才会返回 -1,并继续执行原程序后续代码。

进程 ID 不变
        替换前后进程的PID保持不变,因为替换的是进程的 “内容”,而非进程本身。

文件描述符继承
        原进程打开的文件描述符在EXEC后默认保持打开状态。

环境变量继承
        原进程所拥有的环境变量在EXEC后默认保持相同。

不是说进程替换是将新的程序所带有的代码数据,替换掉原先的代码数据吗?PID保持不变,其他全部都被替换掉了,但是!实际上只有内存管理部分大换血,而环境变量,文件描述符包括IO部分完全相同,或者说原封不动地继承了下去,两个部分解耦合。

void test1()
{//单个进程进行进程替换printf("this is a begin:pid:%d,ppid:%d\n",getpid(),getppid());execl("/usr/bin/ls","ls","-a","-l",NULL); printf("this is a end:pid:%d,ppid:%d\n",getpid(),getppid());//可以看到进程替换后代码和数据进行了替换,pid不变,execl后面的代码不再执行
}extern char** environ;void test2()
{//对exec系统调用函数进行使用char* const arr[]={"ls","-a","-l",NULL };//execlp("testcpp","testcpp",NULL);//execv("/usr/bin/ls",arr);execle("./testcpp","testcpp",NULL,environ);
}

3.环境变量

        抱歉抱歉,环境变量姗姗来迟,前面已经提到了它,现在来补充认识一下吧:

         环境变量是操作系统中存储的一系列键值对,用于控制系统和应用程序的行为。它们在进程的上下文中生效,影响进程的运行环境...

环境变量本质:

存储形式:以 NAME=VALUE 的格式存储,例如PATH=/home/usr/testfile。

进程关联:每个进程启动时会继承其父进程的环境变量,并可修改自身的环境变量(但通常不影响父进程)。

存储位置:进程的环境变量存储在内存中的环境变量表(由指针environ指向)(使用时记得extern char** environ),可通过main函数的第三个参数envp访问。

常见环境变量:

变量名作用描述典型值
PATH命令搜索路径,多个路径用冒号 : 分隔/usr/local/sbin:/usr/local/bin:...
HOME当前用户主目录/home/username
USER当前用户名username
SHELL用户默认 Shell 路径/bin/bash
LANG系统语言和区域设置en_US.UTF-8
PWD当前工作目录(由 Shell 动态更新)/home/username/projects
TERM终端类型(影响命令行显示效果,如颜色、光标控制)xterm-256color
EDITOR默认文本编辑器vim 或 nano
JAVA_HOMEJava 运行环境路径(供 Java 程序查找 JRE/JDK)/usr/lib/jvm/java-17-openjdk
PATH(特殊)注意:部分程序(如 systemd)使用 PATH 以外的变量(如 PATH 需显式设置)-

 查看环境变量:

set//可以查看所有环境变量,包括本地变量
env//查看环境变量

    查看单个环境变量:

    echo $PATH
    echo $USER
    ....

    设置环境变量:

    临时设置(仅当前 Shell 会话有效)

    export NAME=VALUE
    export MY_VAR="hello world"  # 声明为环境变量(可被子进程继承)
    MY_VAR="hello"  # 仅为当前 Shell 的局部变量(不被子进程继承)可以用set查到,echo打印
    #因为echo是内建命令,不会将环境变量继承给子进程#

     删除环境变量:

    unset TEST
    #将TEST环境变量永久删除#

    环境变量与进程的关系

    1. 继承性:子进程会自动继承父进程的环境变量但父进程无法感知子进程对环境变量的修改;进程替换(exec函数族)时,默认继承当前进程的环境变量,除非经过 execle/execve 显式传递新的环境变量数组。

    2. 作用域:全局变量:通过 export 声明或写入系统配置文件,可被所有子进程继承局部变量:未用 export 声明的变量,仅在当前 Shell 进程内有效,不被子进程继承。

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

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

    相关文章

    基于深度学习的无线电调制识别系统

    基于深度学习的无线电调制识别系统 本项目实现了一个基于深度学习的无线电调制识别系统&#xff0c;使用LSTM&#xff08;长短期记忆网络&#xff09;模型对不同类型的 无线电信号进行自动分类识别。该系统能够在不同信噪比(SNR)条件下&#xff0c;准确识别多种调制类型&#…

    Python 爬虫之requests 模块的应用

    requests 是用 python 语言编写的一个开源的HTTP库&#xff0c;可以通过 requests 库编写 python 代码发送网络请求&#xff0c;其简单易用&#xff0c;是编写爬虫程序时必知必会的一个模块。 requests 模块的作用 发送网络请求&#xff0c;获取响应数据。 中文文档&#xf…

    随机森林(Random Forest)学习

    随机森林是一种基于集成学习的机器学习算法&#xff0c;属于Bagging&#xff08;Bootstrap Aggregating&#xff09;方法的一种扩展。它通过组合多个决策树来提升模型的泛化能力和鲁棒性&#xff0c;广泛用于分类、回归和特征选择任务。 1.随机森林核心思想 1.1少数服从多数 在…

    从 0 到 1!Java 并发编程基础全解析,零基础入门必看!

    写在前面 博主在之前写了很多关于并发编程深入理解的系列文章&#xff0c;有博友反馈说对博主的文章表示非常有收获但是对作者文章的某些基础描述有些模糊&#xff0c;所以博主再根据最能接触到的基础&#xff0c;为这类博友进行扫盲&#xff01;当然&#xff0c;后续仍然会接…

    el-input宽度自适应方法总结

    使用 style 或 class 直接设置宽度 可以通过内联样式或 CSS 类来直接设置 el-input 的宽度为 100%&#xff0c;使其自适应父容器的宽度 <template><div style"width: 100%;"><el-input style"width: 100%;" v-model"input">…

    解决 Supabase “permission denied for table XXX“ 错误

    解决 Supabase “permission denied for table” 错误 问题描述 在使用 Supabase 开发应用时&#xff0c;你可能会遇到以下错误&#xff1a; [Nest] ERROR [ExceptionsHandler] Object(4) {code: 42501,details: null,hint: null,message: permission denied for table user…

    java每日精进 5.20【MyBatis 联表分页查询】

    1. MyBatis XML 实现分页查询 1.1 实现方式 MyBatis XML 是一种传统的 MyBatis 使用方式&#xff0c;通过在 XML 文件中编写 SQL 语句&#xff0c;并结合 Mapper 接口和 Service 层实现分页查询。分页需要手动编写两条 SQL 语句&#xff1a;一条查询分页数据列表&#xff0c;…

    linux taskset 查询或设置进程绑定CPU

    1、安装 taskset larkubuntu&#xff1a;~$ sudo apt-get install util-linux larkubuntu&#xff1a;~$ taskset --help 用法&#xff1a; taskset [选项] [mask | cpu-list] [pid|cmd [args...]] 显示或更改进程的 CPU 关联性。 选项&#xff1a; -a&#xff0c; --all-tasks…

    Python应用字符串格式化初解

    大家好!在 Python 编程中&#xff0c;字符串格式化是一项基础且实用的技能。它能让你更灵活地拼接字符串与变量&#xff0c;使输出信息更符合需求。本文将为和我一样的初学者详细介绍 Python 字符串格式化的常用方法。 定义: 字符串格式化就是将变量或数据插入到字符串中的特定…

    EasyRTC嵌入式音视频通信SDK一对一音视频通信,打造远程办公/医疗/教育等场景解决方案

    一、方案概述​ 数字技术发展促使在线教育、远程医疗等行业对一对一实时音视频通信需求激增。传统方式存在低延迟、高画质及多场景适配不足等问题&#xff0c;而EasyRTC凭借音视频处理、高效信令交互与智能网络适配技术&#xff0c;打造稳定低延迟通信&#xff0c;满足基础通信…

    SEO长尾词优化精准布局

    内容概要 长尾关键词作为SEO策略的核心要素&#xff0c;其价值在于精准捕捉细分需求与低竞争流量入口。相较于短尾词的高泛化性&#xff0c;长尾词通过语义扩展与场景化组合&#xff0c;能够更高效地匹配用户搜索意图&#xff0c;降低优化成本的同时提升转化潜力。本文将从词库…

    【MySQL】第7节|Mysql锁机制与优化实践以及MVCC底层原理剖析

    锁等待分析 我们通过检查InnoDB_row_lock相关的状态变量来分析系统上的行锁的争夺情况 示例场景 假设有两个用户同时操作账户表 accounts&#xff08;主键为 id&#xff09;&#xff1a; 1. 用户A&#xff1a;执行转账&#xff0c;锁定账户 id1 并等待3秒&#xff1a; BEG…

    基于规则引擎与机器学习的智能Web应用防火墙设计与实现

    基于规则引擎与机器学习的智能Web应用防火墙设计与实现 引言&#xff1a;智能防御的必然选择 在2023年OWASP最新报告中&#xff0c;传统Web应用防火墙&#xff08;WAF&#xff09;对新型API攻击的漏报率高达67%&#xff0c;而误报导致的正常业务拦截损失每年超过2.3亿美元。面…

    GIM发布新版本了 (附rust CLI制作brew bottle流程)

    GIM 发布新版本了&#xff01;现在1.3.0版本可用了 可以通过brew upgrade git-intelligence-message升级。 初次安装需要先执行 brew tap davelet/gim GIM 是一个根据git仓库内文件变更自动生成git提交消息的命令行工具&#xff0c;参考前文《GIM: 根据代码变更自动生成git提交…

    PyQt5高效布局指南:QTabWidget与QStackedWidget实战解析

    &#x1f50d; 问题背景 当界面控件过多时&#xff0c;直接平铺会导致窗口拥挤、用户体验下降。PyQt5提供了两种高效容器控件&#xff1a; QTabWidget&#xff1a;选项卡式布局&#xff0c;支持直接切换不同功能模块QStackedWidget&#xff1a;堆栈式布局&#xff0c;需配合导…

    《2.2.1顺序表的定义|精讲篇》

    上一节学习了线性表的逻辑结构&#xff0c;线性表需要实现哪些基本运算/操作&#xff1f;在本节中&#xff0c;我们将学习顺序表的定义、顺序表的特性&#xff0c;以及如何用代码来实现顺序表。下个小节我们会介绍基于顺序存储&#xff08;这种存储结构&#xff09;如何用代码具…

    【 大模型技术驱动智能网联汽车革命:关键技术解析与未来趋势】

    大模型技术驱动智能网联汽车革命&#xff1a;关键技术解析与未来趋势 关键词总结&#xff1a; 大模型技术&#xff1a;LLM、VLM、MLLM、Transformer架构核心场景&#xff1a;智能驾驶、智能座舱、智能网联关键技术&#xff1a;端到端系统、BEVOCC网络、多模态融合、强化学习挑…

    Rocketmq broker 是主从架构还是集群架构,可以故障自动转移吗

    RocketMQ Broker的架构与故障转移机制 RocketMQ的Broker架构同时采用了主从架构和集群架构&#xff0c;并且支持故障自动转移。下面详细说明&#xff1a; 一、架构类型 1. 集群架构 RocketMQ天然支持分布式集群部署 一个RocketMQ集群包含多个Broker组(每组有主从) 不同Bro…

    从零开始建立个人品牌并验证定位变现性的方法论——基于开源AI大模型、AI智能名片与S2B2C商城生态的实证研究

    摘要&#xff1a;本文提出一种融合开源AI大模型、AI智能名片与S2B2C商城小程序源码的"最小测试闭环"方法论&#xff0c;通过技术赋能实现个人品牌定位的精准验证与变现路径优化。以某美妆领域自由职业者为例&#xff0c;其通过开源AI大模型完成能力图谱构建与资源匹配…

    SQL进阶之旅 Day 2:高效的表设计与规范:从基础到实战

    【SQL进阶之旅 Day 2】高效的表设计与规范&#xff1a;从基础到实战 开篇 在数据库开发中&#xff0c;一个良好的表设计不仅能够提高查询效率&#xff0c;还能避免冗余数据和一致性问题。本文作为"SQL进阶之旅"系列的第2天&#xff0c;将重点介绍高效的表设计与规范…