尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
揭秘进程程序替换:加载器的核心原理
进程程序替换来初步了解一下进程程序替换加载器这些接口的作用为执行一个程序我们这里使用execl接口我们自己写的程序可以调用其他的程序我们调用了系统中的命令我们写的进程通过execl系统调用把系统的命令调用起来这种现象就是进程替换我们在bash使用ls命令ls命令也是进程所以应该是bash通过fork创建子进程阻塞等待这个子进程在子进程通过execl调用ls命令的程序这就是进程程序替换我们可以将这个系统调用放在子进程中使用进程替换的原理 - fork我们在调用execl系统调用之前这个进程还是我们自己提供给系统的程序进程 内核数据结构 代码数据所以此时数据和代码还属于我们自己的程序当运行execl系统调用这个函数给我们指明一个新的程序新的程序就是指明新的代码和数据所以通过execl指明一个新的程序那就把新程序的代码和数据以覆盖式的写入到数据和代码段中只替换代码设数据这个过程中PCB、虚拟地址空间、都没有变化页表映射关系可能会改变覆盖的数据代码的大小与原内存不一程序替换的过程中并没要创建新的进程只是把当前进程的代码和数据用新程序的代码数据以覆盖式的进行替换为什么程序后面部分的代码没有运行出来从execl函数开始本函数的代码和数据已经不在内存中了运行新程序的代码和数据运行完后已经进程退出即一旦程序替换成功就运行新程序代码就原始代码的后半部分已经不存在了如果进程程序替换失败了呢我们可以发现给一个错误的路径去替换程序替换失败进程就按原代码继续运行execl函数只有函数错误才会返回返回-1exec* 函数只有失败返回值没有成功返回值所以exec* 系列的函数不用做返回值判断只要返回就是失败而在execl后面的代码往往也是进程退出的语句因为execl存在的意义就是去替换程序完成某个任务而替换失败也代表着这个进程的任务失败后续直接exit(1)退出即可认识全部接口这些函数都有共享都是以exec为前缀的int execl(const *pathname,const char *argv,.../* (char *)NULL */);const *pathname-- 路径 程序名(/usr/bin/ls)作用告知进程要执行谁函数参数中...叫做可变参数列表函数传参默认是按定义的参数来传的但是我们传参的数量是不确定的就可以用可变参数列表 可以在C语言函数栈帧出了解那pathname后面的参数要怎么填写呢命令怎么写参数就怎么传ls命令在命令行上ls -a -l那么就以命令行运行格式以逗号为分隔符一个一个的传入excel(/usr/bin/ls,ls,-a,-l);所以这函数第一个参数是要知道我们要执行那个程序得到程序后考虑到程序可能有多个选项所以接下来要想怎么执行程序根据命令行输入方式传入类似把参数以链表的形式传入所以execl的l就是list的意思而我们写list链表其最后一定指向NULL此处同理函数传参最后要以NULL结尾我们知道execl会把自己的程序替换了有没有方法不替换我自己的程序让父进程安心做自己的事情让其他进程来做进程替换可以吗答案是可以我们完全可以让子进程来做程序替换不影响父进程的程序运行会不会影响父进程不会进程具有独立性数据和代码都发现写时拷贝子进程完全独立于父进程了子进程都是用自己的数据和代码而exec* 接口可以认为是加载器程序的加载实际上是进程的动态创建而我们学习了OS我们可以知道我们调用某个程序是在bash的基础上进行的也就是说bash加载程序实际上就是通过fork创建子进程在子进程替换相应的任务程序从而我们可以认为exec* 接口为加载器可以替换自己的程序吗可以只要是可以调度的进程都可以被替换我们可以在C语言中在子进程替换自己的C程序调用py文件这类脚本的文件我们调用py调用的不是脚本而是解释器所以在execl中应该是execl(/usr/bin/python3,python,other.py,NULL);bash同理execl(/usr/bin/bash,bash,other.sh,NULL);所以我们现在可以用C语言调用任何语言的程序这些程序都是进程都可以在C语言内子进程中进行进程程序替换知识不同语言的调用方式不一样验证进程程序替换时不产生新进程我们对替换的程序中做PID输出语句再在被替换的程序中对子进程被替换前做PID输出语句来看看替换前和替换后PID有没有变化可以来验证是否产生新进程可以看到进程程序替换时不产生新进程int execlp(const char *file,const char *argv,.../* (char *)NULL */);这个接口execlp其实p就是指PATH所以使用execlp替换接口我们不需要提供工作目录路径,只告诉要执行的文件名即可之后的就是和上面一样命令行如何写这里参数就这样写因为execlp会自动在环境变量PATH中查找指定的命令注意这里两个ls表达的语义是不一样的第一个表达是要执行的是谁后面的是表示要怎么执行这个程序int execv(const char *pathname,char *const argv[]);pathname参数的用法和execl用法一样execv中的v是vector(数组)参数是数组即提供一个命令行参数表就是一个指针数组所以execv的使用就是提供要调用程序的路径名称然后在调用execv之前提供一个char *const的指针数组(注意这个数组结尾也要NULL)execl与execv在传输的内容上完全一样只是传输的方式上不一样execl是链表传入execv是用数组传入的我们回想一下我们之前讲命令行参数的时候并没有讲是谁传给main函数的现在我们可以讲了父进程在通过子进程用exec接口替换程序的时候父进程如果有argv[]就用execv的形式把命令行参数传入到ls进程的main函数中其实argc是可以统计出来的所以说到底还是父进程传的int execvp(const char *file,char *const argv[]);现在看这个已经不用解释了对于参数同上面有类似file - 程序名argv[] - 命令行参数表int execvpe(const char *file,char *const argv[],char *const envp[]);相比于execvp来说execvpe多了一个参数char *const envp[]!这个参数就是环境变量我们实现一个程序用于输出程序的命令行参数和环境变量我们在父进程内通过子进程内设置一个环境变量和命令行参数通过execpe替换other程序看能否输出我们设置的环境变量和命令行参数表很明显可以我们在这里可以发现这里替换程序打印环境变量只打印父进程提供的环境变量没有打印从bash继承的系统环境变量所以使用execvpe的时候替换的程序要求使用全新的环境变量表程序接收父进程传入的环境变量表那就覆盖掉原本从父进程继承的环境变量表用最新的那想要以新增的环境变量表给子进程呢我们不使用execvpe我们子进程本来就可以获得bash继承的环境变量为什么子进程会知道父进程的环境变量父进程又没有给子进程传答案是虚拟地址空间虚拟地址空间中命令行参数和环境变量属于专属的区域子进程复制父进程的内核数据结构和代码数据子进程做程序替换替换的是子进程的代码和数据并影响环境变量区域的数据所以子进程通过虚拟地址同样可以获得父进程指向的环境变量二、其实exec接口加载新程序的时候在调用我们新程序的时exec接口底层会自动把指向环境变量的environ这个全局指针传递给新进程这里通过资料查询了解到——其实exec覆盖子进程虚拟地址空间是全覆盖的因为当前进程要变成一个全新的程序所以旧数据不能留所以环境变量区域也别清理了所以exec接口会默认自动的在传参处添加子进程的environ指针内核收到环境变量就会复制给新程序进程新进程启动environ就指向这份拷贝环境变量解决增量式传递环境变量可以了解一个新接口putenvputenv功能是新增环境变量在那个进程调用这个就在这个进程环境变量表中添加一个环境变量添加环境变量的字符串不能被销毁要在全局变量设置这个字符串那我们就是想用execvpe的方式传递新增环境变量方法如下通过循环来把要增加的环境变量添加到environ中在execvpe中传递environ全局变量(注意要extern char **environ定义告诉编译器变量在外部去链接别报错)这里传入environ其实我们使用exec接口没有使用环境变量传入编译器会自动转化成exece*传递environ综上增量添加环境变量方法通过putenv将环境变量添加到父进程自己的环境变量表中使用exec* 接口接口默认也会把父进程环境变量拷贝给新程序通过使用execvpe接口向environ中添加新环境变量在execvpe参数中传递environint execve(const char *pathname,char *const argv[],char *const envp[]);我们在上面的exec接口中可以发现少了一种exec接口execve我们可以看到execve是分开于其他接口因为这个execve是系统调用而其他的exec接口都是execve的语言封装说白了上面的那么多接口到最后都要调用execve系统调用都需要传pathnameargvenv这些参数所以我们那些不传env的exec接口没有传环境变量参数没传就默认调用environ编译器默认会补充环境变量参数有传递环境变量就传递我们提供的环境变量总结进程程序替换主要是通过调用接口实现程序的替换我们讲解了61个exec接口这里做语言级别的封装是为了面对上层替换的场景参数的不同形式有对应的接口这些接口到最后底层都会转化成execve的形式调用
RELATED

相关推荐

C语言第二章

C语言第二章

一.变量1.做计算printf("1213%d",1213);2.算找零printf("100-23%d",100-23);3.为什么要引入变量每次计算时都需要输入数字,繁琐4.引入change函数int price0; printf("请输入金额(元):"); scanf("%d"…

📅 2026/9/15 14:20:48
优化 PostgreSQL 的 25 条实战经验:从能跑到跑的稳

优化 PostgreSQL 的 25 条实战经验:从能跑到跑的稳

这些年跑过几个千万级、亿级数据量的生产系统后,我意识到一件事:大部分 PostgreSQL 性能问题,加 CPU 和内存是解决不了的。 真正管用的,是理解 PostgreSQL 的“思考方式”——它怎么做查询规划、怎么利用索引、怎么处理并发。 下面…

📅 2026/10/6 12:47:03
基于 RA8P1 的触摸屏演示工程

基于 RA8P1 的触摸屏演示工程

我们是全国大学生电子设计竞赛信息科技前沿专题赛的参赛队伍。本文是参赛作品中触摸显示部分整理出的学习例程:一个基于瑞萨 RA8P1 的触摸屏演示应用,驱动 ST7796 液晶屏(480320)与 FT6336 电容触摸屏。文章重点介绍 RA8P1 平台资…

📅 2026/10/5 14:35:33
MORE NEWS

更多资讯

📰

应用商店合规下架潮:开发者应对策略与趋势分析

1. 应用商店生态变局观察最近业内同行们都在讨论一个现象:国内主流应用商店出现了一波明显的应用下架潮。作为从业十余年的移动应用开发者,我深切感受到这次调整带来的震动。不同于以往零星的下架事件,这次波及范围广、持续时间长&#xff0c…

📰

Flutter与OpenHarmony的Crontab定时任务开发实践

1. 项目背景与核心价值在跨平台开发领域,Flutter与OpenHarmony的结合正在开辟新的技术可能性。这个项目实现了Linux风格的定时任务调度系统(Crontab语法解析)在Dart环境中的完整移植,并特别针对鸿蒙系统进行了深度适配。作为开发者…

📰

text-to-cad 实战:从自然语言到参数化 CAD 模型的完整落地链路

1. 从一句话到三维模型:text-to-cad 到底在解决什么问题第一次听到 "text-to-cad" 这个词,很多人脑子里浮现的画面大概是:对着电脑说一句"给我画个带法兰的弯管接头",屏幕上就自动旋转出一个可以导出加工的 3…

📰

FFmpeg字幕嵌入GUI工具:批量硬编码SRT/ASS到MP4的生产级方案

简介:这是一款面向音视频后期从业者与字幕制作新手的本地化SRT字幕自动生成工具,解决人工听写效率低、在线识别需上传隐私视频、多文件批量处理困难等痛点。软件基于FFmpeg与语音识别模型构建,支持中英文双语识别,无需联网上传原视…

📰

iOS应用合规下架潮:开发者应对策略与技术方案

1. 事件背景与行业震动上周三早晨,不少iOS开发者一觉醒来发现自己的应用从中国区App Store神秘消失。这次下架潮波及范围远超往常,涉及游戏、工具、社交等多个品类,其中不乏月活百万级的应用。根据第三方监测数据,单日下架应用数量…

📰

S7-1214C Modbus TCP通信实战:从组态到排错

简介:这份资源面向自动化工程师与PLC初学者,聚焦西门子S7-1214C紧凑型PLC与机器人之间基于Modbus TCP的通信配置,解决以太网接口设置、主从站角色定义、寄存器地址映射及功能码使用等实际问题,适合具备一定Step 7编程基础、需要落…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬