当前位置: 首页 > article >正文

7. 进程控制-进程替换

目录

1. 进程替换

1.1 单进程版:

1.2 进程替换的原理

1.3 多进程版-验证各种程序替换接口

2. 进程替换的各种接口

2.1 execl

2.2 execlp

2.3 execv

2.4 execvp

2.5 execle


 

1. 进程替换

上图为程序替换的接口,之后会详细介绍。

1.1 单进程版:

        最简单的看看程序替换的效果:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>int main()
{printf("before: i am a process, pid: %d, ppid: %d\n", getpid(), getppid());execl("/usr/bin/ls", "ls", "-a", "-l", NULL);printf("after: i am a process, pid: %d, ppid: %d\n", getpid(), getppid());return 0;
}

        我们可以看到,before...执行了,然后执行了execl里的内容,最重要的是after...的内容没有被执行。这就叫程序替换。

1.2 进程替换的原理

       让我们分析一下上面代码的执行过程,刚开始肯定执行本代码,也就是before,然后execl,关键就在于这个接口,这个接口是执行了 ls -l -a,但并不是创建新进程去执行这个指令,而是在这个进程上,直接替换代码和数据,从新程序的main处开始运行,新程序的code和data替换掉老的。

        也就是说我们运行起来的 myproc 这个进程,在运行到execl时,这个进程的代码和数据被换成了 execl 中的命令内容的代码和数据。

1.3 多进程版-验证各种程序替换接口

int main()
{//多进程版的程序替换pid_t id = fork();if(id == 0){printf("before: i am a process, pid: %d, ppid: %d\n", getpid(), getppid());sleep(5);execl("/usr/bin/top", "top", NULL);printf("after: i am a process, pid: %d, ppid: %d\n", getpid(), getppid());exit(0);}//fatherpid_t ret = waitpid(id, NULL, 0);if(ret > 0)printf("wait success, father pid: %d, ret id: %d\n", getpid(), ret);sleep(5);return 0;

程序一运行起来就有两个进程,其中一个是子进程,替换后就结束了,然后父进程5秒后也退出。

有几个现象结论:

  1. 子进程执行程序替换,不会影响父进程父进程在等待子进程结束,没有直接top完就退出
  2. execl时,替换数据时发生写时拷贝,此时替换的代码也发生了写时拷贝!子进程的代码被替换,但是父进程的还在执行。
  3. 程序替换有无创建新进程呢?进程最多都是两个,所以没有!

程序替换成功之后,exec*之后的代码不会被执行;如果替换失败呢?才可能执行后续的代码,所以exec*系列的函数,只有失败返回值,没有成功返回值,因为成功了就替换成功了,显示被替换后的代码的结果。

我们的CPU如何知道程序的入口地址在哪?代码区这么大,程序替换后的main在哪里呢?

2. 进程替换的各种接口

通过对接口参数的学习,我们可以回答上面的问题。

文章一开头的图片就是这一系列的各种接口,有6个,但是其实有7个,剩下那个不在这个手册里。

我们这里介绍5个,剩下的就都知道怎么用了。

2.1 execl

exec是一样的,l 代指 list。

所以这一系列的函数的第一个参数:你要执行一个程序的第一件事情是什么?就是先找到这个程序!这个参数就是决定如何找到程序的,什么路径下的什么程序!

找到这个程序之后,接下来要怎么办?就是如何执行这个程序,主要就是要不要涵盖选项,涵盖哪些?所以从第二个参数开始,命令行中怎么输入,就怎么输入!只不过都要加上""

execl("usr/bin/ls", "ls", "-l", NULL);

最后的NULL是必须要加的。

2.2 execlp

        我们发现它的第一个参数是file,不是path,说明第一个参数只用写明文件就行,不需要指明路径(当然指明也是可以的),它会自动去PATH环境变量中去找这个指令。

execlp("ls", "ls", "-l", NULL);

2.3 execv

        这个接口的最后一个是 v,表示vector,这种不外乎是将传参的形式改变了,之前是传list,现在是传个vector。

char* const myargv[] = {"ls","-l","-a",NULL
};execv("/usr/bin/ls", myargv);

exec 会在内部把命令行参数传给ls,exec就是代码层面的加载器,把可执行程序导到内存里。

2.4 execvp

一带p就不用写path,只写file就可以。带v就是把传的参数列表换成vector

2.5 execle

这个e是我们没见过的,它的意思是环境变量,意味着我们可以自己往里传环境变量,前面的命令行参数我们知道可以在内部传给指令,那这个环境变量会传给替换后的代码吗?

在验证这个问题之前,先来讲一些前置知识:

1. 怎么用一个makefile生成两个可执行文件?

我们都知道makefile会把它碰到的第一个文件定位目标文件,那怎么能有两个目标文件呢?总得有个先后吧,所以我们可以这样处理:

.PHONY:all
all:otherExe mycommandotherExe:otherExe.cppg++ -o $@ $^ -std=c++11
mycommand:mycommand.cgcc -o $@ $^ std=c99
.PHONY:clean
clean:rm -f mycommand otherExe

接下来我们实现一下:一个可执行程序调用另一个可执行程序

mycommand.c 中可以通过exec系列接口调用其他可执行程序,当然这个可执行程序也得是编译好的,所以需要一个makefile编译两个文件。

int main()
{pid_t id = fork();if(id == 0){printf("before: i am a process, pid: %d, ppid: %d\n", getpid(), getppid());execl("./otherExe", "otherExe", NULL);}return 0;
}

这里有一个小问题:我们在调用 otherExe时,是 ./otherExe 这样执行,为什么我们传的第二个参数是otherExe,而不是 ./otherExe 呢?

  • 因为前面已经说明它在哪里了,这里就不用再写一遍了

既然我一个C程序可以调用C++程序,那能不能调用其他语言写的程序呢?

让我们来验证一下:

脚本语言(所谓的脚本语言就是bash从文件中一行一行的执行):test.sh:

#!/usr/bin/bashecho "hello 1"
echo "hello 1"
echo "hello 1"
echo "hello 1"
echo "hello 1"
echo "hello 1"
echo "hello 1"
ls -a -l

要注意直接./test.sh是不能执行的。

int main()
{pid_t id = fork();if(id == 0){printf("before: i am a process, pid: %d, ppid: %d\n", getpid(), getppid());//execl("./otherExe", "otherExe", NULL);execl("/usr/bin/bash", "bash", "test.sh", NULL);}return 0;
}

这样就可以在C程序中执行脚本代码,还可以发现其他什么 .py 这种python文件也一样可以运行。

无论是我们的可执行程序,还是脚本,为什么能跨语言调用呢???

这是因为:所有的语言写成的运行起来,本质都是进程!!!只要是进程,就能用exec系列接口调用

回到正题:将这个的目的就是为了验证execle时,要传环境变量,因为我们自己写的程序是可以让他输出环境变量、命令行参数的,所以要用我们自己写的程序验证是否传入成功。

这时就需要观察:由一个程序形成的环境变量如何导给另一个程序(需要我们自己写程序并且一个调用另一个)

先来验证一下命令行参数:我们前面的结论说,命令行参数会在接口内部传给那个命令。

mycommand.c:

int main()
{pid_t id = fork();if(id == 0){printf("before: i am a process, pid: %d, ppid: %d\n", getpid(), getppid());char *const myargv[] = {"otherExe","-a","-b","-c",NULL};execv("./otherExe", myargv);}return 0;
}

它会把这些个命令行参数传给 otherExe,然后otherExe.cpp:

int main(int argc, char *argv[])
{cout << argv[0] << " begin running" << endl;for(int i=0; argv[i]; i++){cout << i << ":" << argv[i] << endl;}cout << argv[0] << " stop running" << endl;return 0;
}

他会接收到命令行参数,然后打印出来,事实上的效果:

可以看到命令行参数确实传递成功了。

接下来验证一下环境变量:

将otherExe修改成:

int main(int argc, char *argv[], char* env[])
{cout << argv[0] << " begin running" << endl;cout << "这是命令行参数" << endl;for(int i=0; argv[i]; i++){cout << i << ":" << argv[i] << endl;}cout << "这是环境变量" << endl;for(int i=0; env[i]; i++){cout << i << ":" << env[i] << endl;}cout << argv[0] << " stop running" << endl;return 0;
}

我们可以看到:我们调用execv,只传了命令行参数,都没传环境变量,就能打印出来。

环境变量是什么时候给进程的呢?

环境变量也是数据,在创建子进程的时候,环境变量已经被子进程继承下去了!!!

所以即便没传,也有上面继承下来的环境变量。

所以,程序替换中,环境变量信息不会被替换!会一路继承。

bash创建一个子进程,会传给它环境变量,./mycommand也会创建进程,程序替换后调用./otherExe,也会创建进程,一路继承。

如果不想让bash创建环境变量,非要在mycommand中创建环境变量也是可以的:

  • putenv("PRIVATE_ENV=2025516");
  • extern char** environ;
    execle("./otherExe", "otherExe", "-a", "-b", NULL, environ);

    这个environ就是父进程环境变量

自定义环境变量也是可以的,但是这样传过去就只有自己定义的环境变量了,其他的就看不到了。

所以我们想给子进程传递环境变量,该怎么传递??

  1. 新增环境变量
    1. 父进程地址空间中直接putenv
    2. bash直接添加
  2. 彻底替换

相关文章:

7. 进程控制-进程替换

目录 1. 进程替换 1.1 单进程版&#xff1a; 1.2 进程替换的原理 1.3 多进程版-验证各种程序替换接口 2. 进程替换的各种接口 2.1 execl 2.2 execlp 2.3 execv 2.4 execvp 2.5 execle 1. 进程替换 上图为程序替换的接口&#xff0c;之后会详细介绍。 1.1 单进程版&am…...

理解 C# 中的各类指针

前言 变量可以理解成是一块内存位置的别名&#xff0c;访问变量也就是访问对应内存中的数据。 指针是一种特殊的变量&#xff0c;它存储了一个内存地址&#xff0c;这个内存地址代表了另一块内存的位置。 指针指向的可以是一个变量、一个数组元素、一个对象实例、一块非托管内存…...

真题卷001——算法备赛

蓝桥杯2024年C/CB组国赛卷 1.合法密码 问题描述 小蓝正在开发自己的OJ网站。他要求用户的密码必须符合一下条件&#xff1a; 长度大于等于8小于等于16必须包含至少一个数字字符和至少一个符号字符 请计算一下字符串&#xff0c;有多少个子串可以当作合法密码。字符串为&am…...

Qt图表库推荐指南与分析

目录 一、核心图表库横向对比1. Qt Charts2. QCustomPlot3. QWT (Qt Widgets for Technical Applications)4. KD Chart二、性能与功能对比矩阵三、选型策略与组合方案1. 通用型需求:2. 技术型场景:3. 企业级开发:四、未来趋势与避坑指南1. 协议风险:2. 技术兼容性:3. 性能…...

【Dv3Admin】工具视图配置文件解析

在开发后台管理系统时,处理复杂的 CRUD 操作是常见的需求。Django Rest Framework(DRF)通过 ModelViewSet 提供了基础的增删改查功能,但在实际应用中,往往需要扩展更多的功能,如批量操作、权限控制、查询优化等。dvadmin/utils/viewset.py 模块通过继承并扩展 ModelViewS…...

Vue3中实现轮播图

目录 1. 轮播图介绍 2. 实现轮播图 2.1 准备工作 1、准备至少三张图片&#xff0c;并将图片文件名改为数字123 2、搭好HTML的标签 3、写好按钮和图片标签 ​编辑 2.2 单向绑定图片 2.3 在按钮里使用方法 2.4 运行代码 3. 完整代码 1. 轮播图介绍 首先&#xff0c;什么是…...

C#中UI线程的切换与后台线程的使用

文章速览 UI线程切换示例 后台线程使用示例 两者对比适用场景Application.Current.Dispatcher.InvokeTask.Factory.StartNew 执行同步性Application.Current.Dispatcher.InvokeTask.Factory.StartNew 一个赞&#xff0c;专属于你的足迹&#xff01; UI线程切换 在WPF应用程序…...

微信小程序 自定义图片分享-绘制数据图片以及信息文字

一 、需求 从数据库中读取头像&#xff0c;姓名电话等信息&#xff0c;当分享给女朋友时&#xff0c;每个信息不一样 二、实现方案 1、先将数据库中需要的头像姓名信息读取出来加载到data 数据项中 data:{firstName:, // 姓名img:, // 头像shareImage:,// 存储临时图片 } 2…...

优艾智合机器人助力半导体智造,领跑国产化替代浪潮

在全球半导体产业加速自动化转型的背景下&#xff0c;传统物流已成为制约智能化升级的关键瓶颈。作为中国移动机器人行业的领军企业&#xff0c;优艾智合&#xff08;YOUIBOT&#xff09;自2017年起就敏锐洞察到"半导体设备国产化"的紧迫需求&#xff0c;依托在工业移…...

关于 TCP 端口 445 的用途以及如何在 Windows 10 或 11 上禁用它

TCP 端口 445 主要用于直接通过 TCP/IP 访问 Microsoft 网络,无需使用 NetBIOS 层。此服务自 Windows 2000 和 Windows XP 开始在 Windows 中提供。在 Windows NT/2K/XP 中,SMB(Server Message Block)协议用于文件共享等。它在 Windows NT 中运行在 NetBT(NetBIOS over TC…...

C语言_coredump深度解析

在 C/C++ 开发中,Core Dump 是解决程序崩溃问题的重要手段。本文将系统介绍 Core Dump 的概念、用途、常见原因及调试方法,结合具体代码示例,帮助开发者快速定位和解决问题。 一、Core Dump 是什么? Core Dump(核心转储)是操作系统在进程异常终止时,将进程的内存镜像、…...

全栈项目中是否可以实现统一错误处理链?如果可以,这条链路该如何设计?需要哪些技术支撑?是否能同时满足性能、安全性和用户体验需求?

在复杂系统中&#xff0c;错误一旦出现&#xff0c;可能不断扩散&#xff0c;直到让整个系统宕机。尤其在一个全栈项目中&#xff0c;从数据库到服务器端逻辑、再到前端用户界面&#xff0c;错误可能在任意一个环节产生。如果我们不能在全栈范围内实现统一的错误处理机制&#…...

Twitter数据采集新选择:twitterapi.io全面评测与实战指南

之前我在CSDN上分享过如何高效获取Twitter数据&#xff1a;Apify平台上的推特数据采集解决方案_tweet scraper v2 (pay per result)-CSDN博客&#xff0c;当时介绍了如何利用Apify平台抓取Twitter数据。虽然Apify提供了不错的解决方案&#xff0c;但在实际项目中我遇到了一些瓶…...

排序01:多目标模型

用户-笔记的交互 对于每篇笔记&#xff0c;系统记录曝光次数、点击次数、点赞次数、收藏次数、转发次数。 点击率点击次数/曝光次数 点赞率点赞次数/点击次数 收藏率收藏次数/点击次数 转发率转发次数/点击次数 转发是相对较少的&#xff0c;但是非常重要&#xff0c;例如转发…...

Dify中使用插件LocalAI配置模型供应商报错

服务器使用vllm运行大模型&#xff0c;今天在Dify中使用插件LocalAI配置模型供应商后&#xff0c;使用工作流的时候&#xff0c;报错&#xff1a;“Run failed: PluginInvokeError: {"args":{},"error_type":"ValueError","message":&…...

初识计算机网络。计算机网络基本概念,分类,性能指标

初识计算机网络。计算机网络基本概念&#xff0c;分类&#xff0c;性能指标 本系列博客源自作者在大二期末复习计算机网络时所记录笔记&#xff0c;看的视频资料是B站湖科大教书匠的计算机网络微课堂&#xff0c;祝愿大家期末都能考一个好成绩&#xff01; 视频链接地址 一、…...

【Python 操作 MySQL 数据库】

在 Python 中操作 MySQL 数据库主要通过 pymysql 或 mysql-connector-python 库实现。以下是完整的技术指南&#xff0c;包含连接管理、CRUD 操作和最佳实践&#xff1a; 一、环境准备 1. 安装驱动库 pip install pymysql # 推荐&#xff08;纯Python实现&#xff0…...

标贝科技:大模型领域数据标注的重要性与标注类型分享

当前&#xff0c;大模型作为人工智能领域的前沿技术&#xff0c;其强大的泛化能力和复杂任务处理能力&#xff0c;依赖于海量数据的训练。而数据标注&#xff0c;作为连接原始数据与大模型训练的关键桥梁&#xff0c;在这一过程中发挥着举足轻重的作用。​ 大模型的训练依赖海…...

C++ QT图片查看器

private:QList<QString> fs;int i;void MainWindow::on_btnSlt_clicked() {QStringList files QFileDialog::getOpenFileNames(this,"选择图片",".","Images(*.png *.jpg *.bmp)");qDebug()<<files;ui->picList->clear();ui-…...

数据集-目标检测系列- 杨桃 数据集 Starfruit>> DataBall

数据集-目标检测系列- 杨桃 数据集 Starfruit&#xff1e;&#xff1e; DataBall * 相关项目 1&#xff09;数据集可视化项目&#xff1a;gitcode: https://gitcode.com/DataBall/DataBall-detections-100s/overview 2&#xff09;数据集训练、推理相关项目&#xff1a;GitH…...

【Linux网络】网络套接字编程

套接字编程 一&#xff0c;理解端口号二&#xff0c;初识TCP/UDP协议三&#xff0c;网络字节序四&#xff0c;UDP套接字编程常用API4.1 struct sockaddr类型4.2 socket接口4.3 bind接口4.4 recvfrom4.5 sendto 五&#xff0c;TCP套接字常用API5.1 listen接口5.2 accept接口5.3 …...

【data】上海膜拜数据

数据初始样貌 一、数据预处理 1. 数据每5分钟栅格统计 时间数据的处理 path"mobike_shanghai.csv" dfpd.read_csv(path) # 获取时间信息&#xff0c;对于分钟信息&#xff0c;5分钟取整 def time_info(df,col): df[datetime] pd.to_datetime(df[col])df[wee…...

文件相关操作

文本文件 程序运行时产生的数据都属于临时数据&#xff0c;程序一旦运行结束都会被释放 通过文件可以将数据持久化 C的文件操作需要包含头文件 文件分类 文本文件&#xff1a;文件以文本的ASCII码形式存储在计算机中 二进制文件&#xff1a;文件以文本的二进制形式存储在计算…...

DDS(数据分发服务) 和 P2P(点对点网络) 的详细对比

1. 核心特性对比 维度 DDS P2P 实时性 微秒级延迟&#xff0c;支持硬实时&#xff08;如自动驾驶&#xff09; 毫秒至秒级&#xff0c;依赖网络环境&#xff08;如文件传输&#xff09; 架构 去中心化发布/订阅模型&#xff0c;节点自主发现 完全去中心化&#xff0c;节…...

Web 架构之攻击应急方案

文章目录 一、引言二、常见 Web 攻击类型及原理2.1 SQL 注入攻击2.2 跨站脚本攻击&#xff08;XSS&#xff09;2.3 分布式拒绝服务攻击&#xff08;DDoS&#xff09; 三、攻击检测3.1 日志分析3.2 入侵检测系统&#xff08;IDS&#xff09;/入侵防御系统&#xff08;IPS&#x…...

探索嵌入式硬件的世界:技术、应用与未来趋势

目录 一、什么是嵌入式硬件&#xff1f; 二、嵌入式硬件的核心组件与架构 1. 微处理器与控制器 2. 存储器设备 3. 输入/输出接口 4. 电源管理模块 5. 时钟芯片与时序控制 三、嵌入式硬件的设计原则与技术难点 1. 低功耗与能耗优化 2. 小型化与高度集成 3. 高可靠性和…...

【LeetCode 热题 100】动态规划 系列

&#x1f4c1; 70. 爬楼梯 状态标识&#xff1a;爬到第i层楼梯时&#xff0c;有多少种方法。 状态转移方程&#xff1a;dp[i] dp[i-1] dp[i-2]&#xff0c;表示从走一步和走两步的方式。 初始化&#xff1a;dp[1] 1 , dp[2] 2。 返回值&#xff1a;dp[n]&#xff0c;即走到…...

[特殊字符] Maven配置阿里云镜像终极指南(2024最新版)

文章目录 &#x1f31f; 为什么要配置镜像仓库&#xff1f;&#xff08;血泪教训&#xff09;&#x1f6e0;️ 准备工作&#xff08;必看&#xff01;&#xff09;&#x1f680; 三步搞定镜像配置&#xff08;抄作业版&#xff09;步骤1&#xff1a;打开settings.xml步骤2&…...

计网实验笔记(一)CS144 Lab1

Lab0 ByteStream : 实现一个在内存中的 有序可靠字节流Lab1 StreamReassembler&#xff1a;实现一个流重组器&#xff0c;一个将字节流的字串或者小段按照正确顺序来拼接回连续字节流的模块Lab2 TCPReceiver&#xff1a;实现入站字节流的TCP部分。Lab3 TCPSender&#xff1a;实…...

使用 OpenCV 将图像中标记特定颜色区域

在计算机视觉任务中&#xff0c;颜色替换是一种常见的图像处理操作&#xff0c;广泛用于视觉增强、目标高亮、伪彩色渲染等场景。本文介绍一种简单而高效的方式&#xff0c;基于 OpenCV 检测图像中接近某种颜色的区域&#xff0c;并将其替换为反色&#xff08;对比色&#xff0…...