前端做网站如何调接口,wordpress中ajax请求,企业网站脚本语言,网址是什么文章目录 替换原理站在进程的角度站在程序的角度初体验及理解原理 替换函数函数解释命名理解exec系列函数与main函数之间的关系在一个程序中调用我们自己写的程序 替换原理 创建子进程的目的是什么#xff1f; -想让子进程执行父进程代码的一部分 执行父进程对应的磁盘代码… 文章目录 替换原理站在进程的角度站在程序的角度初体验及理解原理 替换函数函数解释命名理解exec系列函数与main函数之间的关系在一个程序中调用我们自己写的程序 替换原理 创建子进程的目的是什么 -想让子进程执行父进程代码的一部分 执行父进程对应的磁盘代码中的一部分 -想让子进程执行一个全新的程序 让子进程想办法加载磁盘上指定的程序,执行新程序的代码和数据-进程的程序替换 站在进程的角度
没有创建新的进程PCB尤其是pid是没有变化的只是程序的替换
站在程序的角度
这个程序被加载到内存中了-称exec函数为加载器既然我自己的代码能加载到新的程序,操作系统也是 先创造进程数据结构(PCB),在再需要的时候加载代码和数据
初体验及理解原理 int execl(const char *path, const char *arg, ...); 将指定的程序加载到内存中,让制定进程进行执行 需要解决两个问题:1.找到程序 2.如何执行(cmd 选项一 选项二) 后面的三个点:可变参数列表:可以给函数不同数量的参数,例如:printf\scanf 示范程序:
#includestdio.h
#includeunistd.hint main()
{//.c-exe-load-process-运行-执行我们现在所写的程序printf(process is running...\n);//load-exeexecl(/usr/bin/ls/*要执行哪一个程序*/, ls, -a,-l,NULL/*你想怎么执行*/);//所有的exel程序都必须以NULL结尾,证明你把参数传完了//但是此时就不能打印出下面的句子
//因为他也是代码,是在execl的,execl执行完毕的时候,代码已经被完全覆盖,开始执行新的程序的代码了,所以printf就无法执行了!
//但是如果调用失败了,就是没有替换成功,就会打印出来下面的句子printf(process running done...\n);
}由上述的例子我们可以看出这个函数的特点: 只有失败才会返回-1(只需要做错误处理,即:只要返回值,就一定是调用错误了) perror(execl);可以打印错误原因. 调用成功时则不返回,因为此时已经替换了,后续的代码已经没有意义了,没有必要返回值了.(成功了就和接下来的代码无关了) 用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变
由虚拟地址空间页表来保证进程的独立性,一旦有执行流想替换代码或者数据,就发生写时拷贝.-说明写时拷贝在代码区也可以发生的!
但是我们一般不直接在父进程进行程序替换,而是创建新的子进程进行程序替换. 于是我们优化上面的代码:
#includestdio.h
#includestdlib.h
#includeunistd.h
#includeassert.h
##includesys/types.h
#includesys/wait.h
int main()
{printf(process is running...\n);pid_t id fork();assert(id ! -1);if (id 0){//这里的替换会影响父进程吗//不会。进程具有独立性,在执行exec时会发生写时拷贝//类比命令行怎么写这里就怎么传参sleep(1);execl(/usr/bin/ls, ls, -a, -l, --colorauto, NULL);exit(-1);//must failed}int status 0;pid_t ret waitpid(id,status,0);if (ret 0){printf(wait success: \nexit code: %d,sig: %d\n,(status8)0xFF,status0x7F);}}替换函数
其实有六种以exec开头的函数,统称exec函数:
#include unistd.h
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ...,char *const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);int execve(const char *path, char *const argv[], char *const envp[]); 其中,execve是系统调用,其余的都是封装,是为了让我们有更多的选择性 函数解释
这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。 如果调用出错则返回-1 所以exec函数只有出错的返回值而没有成功的返回值 不用对该函数进行返回值判断,只要继续向后运行一定是失败的
命名理解
这些函数原型看起来很容易混,但只要掌握了规律就很好记。 l(list) : 表示参数采用列表 v(vector) : 参数用数组 可以将所有的执行参数放入数组中,统一传递,而不用进行使用可变参数方案. char* const argv_[] {-ls,-a,-l,--colorauto,NULL
};
execv(usr/bin/ls,argv_);p(path) : 有p自动搜索环境变量PATH 带p字符的函数,不用告诉我程序的路径,你只要告诉我程序的名字,我会自动在环境变量PATH中进行可执行程序的查找. execlp(ls, ls, -a, -l, --colorauto, NULL); 这里前面的ls是告诉系统我要执行谁,第二个是告诉系统我想要怎么执行 e(env) : 表示自己维护环境变量
#includestdio.h
#includestdlib.h
#includeunistd.h
#includeassert.h
##includesys/types.h
#includesys/wait.h
int main()
{printf(process is running...\n);pid_t id fork();assert(id ! -1);if (id 0){sleep(1);char* const envp_[] {(char*)MYENV1122233334444,NULL;}extern char** environ;//声明系统默认的环境变量列表execle(./mybin,mybin,NULL,environ);//这里是只传了系统默认的环境变量putenv((char*)MYENV444333222111);//将指定环境变量导入系统中environ指向对应的环境变量表中execle(./mybin,mybin,NULL,envp_);//这里是只传了你自定义的环境变量execle(./mybin,mybin,NULL,environ);//实际上,默认环境变量你不传,子进程也能获取. 这里是传了默认的环境变量加上你自己添加的环境变量exit(1);}int status 0;pid_t ret waitpid(id,status,0);if (ret 0){printf(wait success: \nexit code: %d,sig: %d\n,(status8)0xFF,status0x7F);}}exec系列函数与main函数之间的关系 exec*系列函数的意义是: 将我们的程序加载到内存中 int main(int argc,char* argv[],char* env[])所以应该是先加载到内存中,之后才是调用main函数,main函数中的argc argc[]分别是exec函数中的前面的两个参数,env[]是从exec函数的环境变量参数中加载出来的 exec调用举例如下:
#include unistd.h
int main()
{char* const argv[] { ps, -ef, NULL };char* const envp[] { PATH/bin:/usr/bin, TERMconsole, NULL };execl(/bin/ps, ps, -ef, NULL);// 带p的可以使用环境变量PATH无需写全路径execlp(ps, ps, -ef, NULL);// 带e的需要自己组装环境变量execle(ps, ps, -ef, NULL, envp);execv(/bin/ps, argv);// 带p的可以使用环境变量PATH无需写全路径execvp(ps, argv);// 带e的需要自己组装环境变量//没有带e的话会默认传入系统默认的环境变量execve(/bin/ps, argv, envp);exit(0);
}在一个程序中调用我们自己写的程序
首先,我们需要在makefile文件中同时创建两个文件并且同时删除两个文件,那该怎么书写呢?
.PHONY:all
all: mybin myexecmybin:mybin.cgcc -o $ $^ -stdc99
myexec:myexec.cgcc -o $ $^ -stdc99 .PHONY:clean
clean:rm -f myexec mybinmyexec.c
#includestdio.h
#includestdlib.h
#includeunistd.h
#includeassert.h
##includesys/types.h
#includesys/wait.h
int main()
{printf(process is running...\n);pid_t id fork();assert(id ! -1);if (id 0){execl(./mybin, mybin, NULL);exit(-1);}int status 0;pid_t ret waitpid(id,status,0);if (ret 0){printf(wait success: \nexit code: %d,sig: %d\n,(status8)0xFF,status0x7F);}}可以使用程序替换调用任何后端语言所对应的可执行!!! 事实上,只有execve是真正的系统调用,其它五个函数最终都调用 execve,所以execve在man手册 第2节,其它函数在man手册第3节。这些函数之间的关系如下图所示。 下图exec函数族 一个完整的例子: