进程替换库函数
1.程序替换预备工作上级目录…下的fork目录下的makefile文件拷贝到当前目录并且命名为Makefile把proc1替换为myexec1.1 现象和原理先看现象可以看到执行了main函数第一句代码接着就执行的是ls -a -l这时候回想fork的两种用法创建子进程一种是指向父进程的部分任务另一种是执行全新的任务就是调用exec系列函数实现的程序替换可以理解为是main函数在exec之后的代码都被替换为了另一个进程的代码堆、栈、数据也被替换掉其实我们知道进程是内核数据结构PCB页表进程地址空间代码/数据而要替换数据/代码其实也就是改页表的虚拟地址和物理地址的映射关系程序替换的本质把数据和代码加载拷贝到内存中也就是IO程序要运行得先加载到内存中只有OS软硬件资源的管理者有权把数据从一个硬件转移到另一个硬件也就是说OS必须提供相应的系统调用完成加载的过程C/C程序第一个执行的函数不是main函数任何程序启动时先执行OS的加载器的代码扫描程序在什么路径找到程序后调用exec替换数据和代码在linux系统下任何程序之前都会先执行加载器(ld)也可以看到exec系列的函数之后的代码不会被执行因为代码已经被替换了执行的是新的代码exec系列的函数成功运行时没有返回值接着我们演示一下forkexec子进程执行exec系列的代码父进程执行自己的代码我们知道子进程创建的时候会拷贝父进程的页表/进程地址空间在指向exec时写入堆/栈/数据/代码发生写时拷贝父子进程分离创建一个进程让该进程可以执行完全不同的程序最经典的引用场景是bash命令行解释器bash最核心的指令是forkexec把输入的命令也就是从命令行输的字符串喂给execbash以子进程的方式运行所以要执行的命令1.2 用法运行任何进程第一件事是把对应的可执行文件从磁盘加载到内存中提供文件路径及名称第二件事就是如何运行比如ls的-a-l选项等exec系列的库函数如下1.2.1 execltop -d 1 -n 4表示每隔1s刷新一次一共刷新4次用于周期性监控进程创建子进程替换为top指令exec系列函数后面的代码都被替换了如果替换成功系统指令或者自己写的程序一般运行结束都会主动退出如果替换失败执行后续指令一般exit填入的值表示出现异常从man手册可以看到正常运行结束exec系列函数是没有返回值的如果出现异常返回值是-1并且设置errno测试如下传入错的path1.2.2 execlpexeclp和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命令行是可以执行很多命令的我们可以把表放到全局每次创建子进程根据情况修改参数表传入execv1.2.4 execvpexecvp和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外壳类似点击一个功能forkexec调用该功能vs2022图形化界面的程序配置文本编辑器调试器编译器三个命令打开代码替换编辑器编译代码替换编译器想使用什么功能forkexec1.2.5 execvpe看到下面可以通过路径运行cmd.out但是子进程的环境变量只有该路径子进程要调用exit返回都找不到exit在哪里所以替换失败根本原因不是路径不对也不是理论不对而是依赖把进程的环境变量只改成了当前路径一旦调用任何系统中的函数就出错下面就是调用make调不了cmd.out文件用到动态库终端等环境变量找不到自然出错所以我们在使用exce系列函数带e的是全新定义环境变量覆盖原有的环境变量需要保留父进程环境变量的基础上叠加我们需要的环境变量而在传入路径的时候可以正常运行因为此时直接按路径找不依赖我们设置的envp直接忽略我们设置的envp但有意思的是argv和envp还是传给了cmd的main函数作为参数所以打印的环境变量还是只有当前路径ctrlr可以实现搜索在调用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 指是否自定义环境变量牛刀小试通过fork和exec系统调用可以产生新进程,下列有关fork和exec系统调用说法正确的是? [多选]A.fork生成的进程是当前进程的一个相同副本B.fork系统调用与clone系统调用的工作原理基本相同C.exec生成的进程是当前进程的一个相同副本D.exec系统调用与clone系统调用的工作原理基本相同ABclone是Linux底层创建进程/线程fork基于clone实现下面哪些属于Fork后子进程保留了父进程的什么[多选]A.环境变量B.父进程的文件锁pending alarms和pending signalsC.当前工作目录D.进程号ACB.信号相关信息各进程独立以下代码最终的打印结果是什么intmain(){inta0;a;execl(/usr/bin/pwd,pwd,NULL);printf(%d\n,a);exit(1);}A.0B.1C.2D.以上都不对D以下描述正确的有A.execl函数可以直接指定可执行程序文件的名称而不需要路径B.execle函数可以直接指定可执行程序文件的名称而不需要路径C.execl函数和execle函数的区别是是否自定义设置环境变量D.execl函数和execlp函数的区别是是否自定义设置环境变量C以下描述正确的有[多选]A.程序替换成功后运行完新程序依然会运行原有的代码B.程序替换成功后运行完新程序则程序直接退出C.程序替换成功后原进程退出创建新的进程运行新程序D.程序替换成功后原进程没有退出使用原进程运行新程序BD