进程替换库函数
2026/5/5 3:02:41 网站建设 项目流程

1.程序替换

预备工作

上级目录(…)下的fork目录下的makefile文件拷贝到当前目录并且命名为Makefile

把proc1替换为myexec

1.1 现象和原理

先看现象,可以看到执行了main函数第一句代码,接着就执行的是ls -a -l

这时候回想fork的两种用法,创建子进程一种是指向父进程的部分任务,另一种是执行全新的任务,就是调用exec系列函数实现的程序替换,可以理解为是main函数在exec之后的代码都被替换为了另一个进程的代码,堆、栈、数据也被替换掉,其实我们知道进程是内核数据结构(PCB+页表+进程地址空间)+代码/数据,而要替换数据/代码,其实也就是改页表的虚拟地址和物理地址的映射关系


程序替换的本质,把数据和代码加载(拷贝)到内存中,也就是IO,程序要运行,得先加载到内存中,只有OS(软硬件资源的管理者)有权把数据从一个硬件转移到另一个硬件,也就是说OS必须提供相应的系统调用完成加载的过程,C/C++程序,第一个执行的函数不是main函数,任何程序启动时,先执行OS的加载器的代码,扫描程序在什么路径,找到程序后,调用exec,替换数据和代码,在linux系统下,任何程序之前都会先执行加载器(ld)

也可以看到exec系列的函数之后的代码不会被执行,因为代码已经被替换了,执行的是新的代码,exec系列的函数成功运行时没有返回值

接着我们演示一下fork+exec
子进程执行exec系列的代码,父进程执行自己的代码,我们知道子进程创建的时候会拷贝父进程的页表/进程地址空间,在指向exec时写入堆/栈/数据/代码,发生写时拷贝,父子进程分离

创建一个进程,让该进程可以执行完全不同的程序,最经典的引用场景是bash(命令行解释器),bash最核心的指令是fork+exec,把输入的命令,也就是从命令行输的字符串喂给exec,bash以子进程的方式运行所以要执行的命令

1.2 用法

运行任何进程第一件事是把对应的可执行文件从磁盘加载到内存中(提供文件路径及名称),第二件事就是如何运行(比如ls的-a,-l选项等)

exec系列的库函数如下

1.2.1 execl


top -d 1 -n 4表示每隔1s刷新一次,一共刷新4次,用于周期性监控进程
创建子进程替换为top指令

exec系列函数后面的代码都被替换了,如果替换成功,系统指令或者自己写的程序一般运行结束都会主动退出;如果替换失败,执行后续指令(一般exit填入的值表示出现异常)

从man手册可以看到,正常运行结束,exec系列函数是没有返回值的,如果出现异常,返回值是-1,并且设置errno


测试如下,传入错的path

1.2.2 execlp

execlp和execl的区别是,我们看到下图,一共参数是path,一个参数是file,只要带p就是我们不需要传可执行文件所在的路径,只需要传文件名即可,系统会自动去环境变量PATH表明的路径下查找


1.2.3 execv

我们看到execv和execl的区别是在传第二个及以后参数时,execl是列举,execv的v是vector,把参数存到一个向量表(指针数组)构建一个参数表,接着传入该向量

char* const pconst修饰的是p,也就是指针本身,指针本身不能被修改,不能执行其它元素,但可以修改*p,也就是p指向的内容

const char* pconst修饰的是*p,也就是p指向的内容不能被修改,但是p本身可以修改,也就是p可以改指向,指向其它元素

char* const argv[]是数组指针,每个元素是char* const argv[i],const修饰的是指针,指针本身不能修改,argv[0]=“ls”,指向内容也不能修改(常量字符串),也就是数组本身不能被修改

但其实"/usr/bin/ls"本身是可执行程序,有main函数,main函数有参数,int main(int argc, char* const argv[]),其实execv(const char* path, char* const argv[]),这个argv就是传给ls内的main函数的,argv[0]就是程序名,argv后面的就是选项

那我们在bash命令行输入指令ls -a -l等,argv是bash帮我们传的,也是用exec系列的替换函数,如果是execv,将ls -a -l等构建一个参数表,传入execv

但此处是硬编码的,我们知道,bash命令行是可以执行很多命令的,我们可以把表放到全局,每次创建子进程,根据情况修改参数表传入execv

1.2.4 execvp

execvp和execv的区别是,多了一个p也就是第一个参数只需要传入可执行文件名,而不需要传入可执行文件所在文件路径

测试

但打印结果不带颜色,其实执行/usr/bin路径下的ls也不带颜色

我们bash命令行使用的ls其实是封装过的

在传参的时候带上颜色配置即可

可以看到.c文件编译得到的myexec可执行文件中创建子进程可以替换为.cpp编译得到的可执行文件,也可以调用系统指令,linux下都是elf(xecutable and Linkable Format)

shell脚本

也可以给test.sh➕️可执行权限,直接运行


脚本语言没有编译链接的概念,解释器是由C/C++编写,在命令行启动,解释器变成进程,把脚本中的命令一行一行读取进行执行

运行python文件

python, shell, php, c/c++运行时都是进程,运行时是进程的,都可以进行exec程序替换,是系统概念(不是语言概念)

运行一个程序,把编译器替换为自己的编译器,把代码读进来,就能直接编译代码,编译器也是命令,也有选项,gcc/g++ -o -c -E vs IDE环境,写好代码,点击,fork创建子进程,exec系列函数替换为编译器,读取并编译代码,所有平台(比如windows)下都有类似的函数;安装软件,给一个图形化界面,软件可以配很多程序,图形化界面底层和shell外壳类似,点击👆一个功能,fork+exec调用该功能,vs2022图形化界面的程序,配置文本编辑器,调试器,编译器(三个命令),打开代码,替换编辑器;编译代码,替换编译器;想使用什么功能,fork+exec

1.2.5 execvpe

看到下面,可以通过路径运行cmd.out,但是子进程的环境变量只有该路径,子进程要调用exit返回都找不到exit在哪里,所以替换失败根本原因不是路径不对,也不是理论不对,而是依赖!把进程的环境变量只改成了当前路径,一旦调用任何系统中的函数就出错,下面就是,调用make调不了;cmd.out文件用到动态库,终端等,环境变量找不到,自然出错

所以,我们在使用exce系列函数,带e的是全新定义环境变量,覆盖原有的环境变量,需要保留父进程环境变量的基础上叠加我们需要的环境变量

而在传入路径的时候可以正常运行,因为此时直接按路径找,不依赖我们设置的envp,直接忽略我们设置的envp;但有意思的是argv和envp还是传给了cmd的main函数作为参数,所以打印的环境变量还是只有当前路径

ctrl+r可以实现搜索

在调用excevpe的时候,使用系统环境变量(environ)无法执行,因为cmd.out不在系统环境变量下,追加cmd.out所在路径到系统环境变量就可以

运行成功,如下所示

那么我们在使用bash的时候,命令行的参数,包括argv甚至env都是bash创建子进程替换为要执行的可执行文件,并把命令行参数和环境变量传递给可执行程序,子进程的系统环境变量也是继承于父进程(共用页表,地址空间,写入时发生写时拷贝)

在bash改的PATH,会作为环境变量参数传递给子进程,子进程执行exec替换为myexec,接着myexec创建子进程,替换执行cmd,环境变量参数传给cmd

在环境变量追加cmd的路径

运行结果

只保留追加后的PATH

环境变量为空也能运行,怪了,cmd.out也不在当前目录下

如果execl函数不传环境变量,用的是继承于父进程的环境变量


l 指传递参数的时候进行罗列
v 指将参数作为vector进行传递
e 指是否自定义环境变量

牛刀小试

  1. 通过fork和exec系统调用可以产生新进程,下列有关fork和exec系统调用说法正确的是? [多选]
    A.fork生成的进程是当前进程的一个相同副本
    B.fork系统调用与clone系统调用的工作原理基本相同
    C.exec生成的进程是当前进程的一个相同副本
    D.exec系统调用与clone系统调用的工作原理基本相同

AB
clone是Linux底层创建进程/线程,fork基于clone实现

  1. 下面哪些属于,Fork后子进程保留了父进程的什么?[多选]
    A.环境变量
    B.父进程的文件锁,pending alarms和pending signals
    C.当前工作目录
    D.进程号

AC
B.信号相关信息各进程独立

  1. 以下代码最终的打印结果是什么
intmain(){inta=0;a++;execl("/usr/bin/pwd","pwd",NULL);printf("%d\n",a++);exit(1);}

A.0
B.1
C.2
D.以上都不对

D

  1. 以下描述正确的有:
    A.execl函数可以直接指定可执行程序文件的名称而不需要路径
    B.execle函数可以直接指定可执行程序文件的名称而不需要路径
    C.execl函数和execle函数的区别是是否自定义设置环境变量
    D.execl函数和execlp函数的区别是是否自定义设置环境变量

C

  1. 以下描述正确的有:[多选]
    A.程序替换成功后,运行完新程序,依然会运行原有的代码
    B.程序替换成功后,运行完新程序,则程序直接退出
    C.程序替换成功后,原进程退出,创建新的进程运行新程序
    D.程序替换成功后,原进程没有退出,使用原进程运行新程序

BD

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询