纺织网站模板,企业级网站开发需求分析,域名iis网站添加,国外免费推广网站文章目录 1.进程替换原理2.进程替换函数2.1execl函数2.2execlp函数2.3execv函数2.4execvp函数2.5execle函数2.6execve函数2.7跨语言调用程序 3.总结 1.进程替换原理
一个程序替换的函数#xff1a;
#include unistd.h
int execl(const char *path, const char *arg,… 文章目录 1.进程替换原理2.进程替换函数2.1execl函数2.2execlp函数2.3execv函数2.4execvp函数2.5execle函数2.6execve函数2.7跨语言调用程序 3.总结 1.进程替换原理
一个程序替换的函数
#include unistd.h
int execl(const char *path, const char *arg, ...);示例1
#includestdio.h
#includesys/types.h
#includeunistd.h
#includesys/wait.h
#includestdlib.h
int main()
{printf(before: I am a process: pid: %d , ppid:%d\n,getpid(),getppid());sleep(5);execl(/usr/bin/ls,ls,-l,-a,NULL);printf(after: I am a process: pid: %d , ppid:%d\n,getpid(),getppid());
}示例2
#includestdio.h
#includesys/types.h
#includeunistd.h
#includesys/wait.h
#includestdlib.hint main()
{pid_t idfork();if(id 0){//childprintf(before: I am a process: pid: %d , ppid:%d\n,getpid(),getppid());sleep(5);execl(/usr/bin/ls,ls,-l,-a,NULL);printf(after: I am a process: pid: %d , ppid:%d\n,getpid(),getppid());exit(0);}//fatherpid_t retwaitpid(id,NULL,0);if(ret 0){printf(wait success, father pid:%d, ret id: %d\n,getpid(),ret);sleep(5);}return 0;
}代码运行结果如下 从上面的代码运行结果示例1中可以看出父进程使用execl程序替换函数会直接替换程序的代码和数据示例2中父进程创建子进程使用execl程序替换函数要替换子进程的代码和数据的时候触发只读权限写实拷贝OS会重新开辟空间存放替换新程序的代码和数据 问题1在两个示例中为什么没有执行以下代码语句 printf(after: I am a process: pid: %d , ppid:%d\n,getpid(),getppid());程序替换成功之后exec*后续的代码不会被执行替换失败呢?才可能执行后续代码。exec*函数只有失败返回值没有成功返回值!! 问题2CPU是如何得知程序的入口地址 Linux中形成的可执行程序是有格式的有一个描述信息的表该表里面就描述了当前可能程序分了哪些段包括代码段数据段等可执行入口地址就是表头地址CPU把可执行程序加载进来的时候首先获得表头地址可执行程序地址便可以执行程序
问题3程序替换有没有创建新的进程 程序替换没有创建新进程只是进行进程的程序代码和数据的替换工作
替换原理总结 用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支)子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程所以调用exec前后该进程的id并未改变。 2.进程替换函数 其实有六种以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[]);2.1execl函数
int execl(const char *path, const char *arg, ...);execl替换函数参数path中/usr/bin/ls作为路径传参需要找到可执行程序的位置execl替换函数以链表的方式把功能选项连接起来则是要确定如何执行可执行程序
#includestdio.h
#includesys/types.h
#includeunistd.h
#includesys/wait.h
#includestdlib.hint main()
{pid_t idfork();if(id 0){//childprintf(before: I am a process: pid: %d , ppid:%d\n,getpid(),getppid());sleep(5);//替换函数execl(/usr/bin/ls,ls,-l,-a,NULL);printf(after: I am a process: pid: %d , ppid:%d\n,getpid(),getppid());exit(0);}//fatherpid_t retwaitpid(id,NULL,0);if(ret 0){printf(wait success, father pid:%d, ret id: %d\n,getpid(),ret);sleep(5);}return 0;
}代码运行结果如下
使用命令行参数指令
#ls -a -l指令运行结果 简记execl替换函数arg参数与命令行参数传递一致命令行参数怎么写在execl函数中就怎么传只不过空格换逗号加NULL。
2.2execlp函数
int execlp(const char *file, const char *arg, ...);示例
int execl(const char *path, const char *arg, ...);#includestdio.h
#includesys/types.h
#includeunistd.h
#includesys/wait.h
#includestdlib.hint main()
{pid_t idfork();if(id 0){//childprintf(before: I am a process: pid: %d , ppid:%d\n,getpid(),getppid());sleep(5);//替换函数execlp(ls,ls,-l,-a,NULL);printf(after: I am a process: pid: %d , ppid:%d\n,getpid(),getppid());exit(0);}//fatherpid_t retwaitpid(id,NULL,0);if(ret 0){printf(wait success, father pid:%d, ret id: %d\n,getpid(),ret);sleep(5);}return 0;
}代码运行结果如下 execlp替换函数函数中名中p指的是环境变量PATH指令的默认路径执行进程时系统会去默认路径寻找执行系统提供的指令时不需要带上地址参数file中ls作为文件名传参需要在默认路径下找到对应的可执行程序ls,-l,-a,NULLexecl替换函数以链表的方式把功能选项连接起来则是要确定如何执行可执行程序
2.3execv函数
int execv(const char *path, char *const argv[]);execv替换函数名中的v是指vector数组把可执行程序的执行选项功能放在数组中在数组中以NULL为结尾参数path把可执行程序的位置作为参数传递参数argv把自定义选项功能数组作为参数传递 示例
#includestdio.h
#includesys/types.h
#includeunistd.h
#includesys/wait.h
#includestdlib.hint main()
{pid_t idfork();if(id 0){//childprintf(before: I am a process: pid: %d , ppid:%d\n,getpid(),getppid());sleep(5);//数组参数char* const myargv[]{ls,-a,-l,NULL};//替换函数execv(/usr/bin/ls,myargv);printf(after: I am a process: pid: %d , ppid:%d\n,getpid(),getppid());exit(0);}//fatherpid_t retwaitpid(id,NULL,0);if(ret 0){printf(wait success, father pid:%d, ret id: %d\n,getpid(),ret);sleep(5);}return 0;
}代码运行结果如下
2.4execvp函数
int execvp(const char *file, char *const argv[]);execvp替换函数名中的v是指vector数组把可执行程序的执行选项功能放在数组中在数组中以NULL为结尾函数中名中p指的是环境变量PATH执行系统程序时不需要带路径参数file把可执行程序的位置作为参数传递参数argv把自定义选项功能数组作为参数传递 示例
#includestdio.h
#includesys/types.h
#includeunistd.h
#includesys/wait.h
#includestdlib.hint main()
{pid_t idfork();if(id 0){//childprintf(before: I am a process: pid: %d , ppid:%d\n,getpid(),getppid());sleep(5);//数组参数char* const myargv[]{ls,-a,-l,NULL};//替换函数execvp(ls,myargv);printf(after: I am a process: pid: %d , ppid:%d\n,getpid(),getppid());exit(0);}//fatherpid_t retwaitpid(id,NULL,0);if(ret 0){printf(wait success, father pid:%d, ret id: %d\n,getpid(),ret);sleep(5);}return 0;
}代码运行结果如下
2.5execle函数
int execle(const char *path, const char *arg, ...,char *const envp[]);execle替换函数名是在‘execl’函数基础上新增加了envp参数。可以传递‘environ’环境变量参数libc中定义的全局变量environ指向环境变量表environ没有包含在任何头文件中,所以在使用时 要用extern声明。因为环境变量通常具有全局属性子进程继承父进程的环境变量也可以自己设置自定义新的环境变量数组传递给子进程 示例1
//ohtherExe.cpp
#include iostream
using namespace std;int main(int argc, char *argv[], char *env[])
{cout argv[0] begin running endl;cout 这是命令行参数: \n;for(int i0; argv[i]; i){cout i : argv[i] endl;}cout 这是环境变量信息: \n;for(int i 0; env[i]; i){cout i : env[i] endl; }coutxxxxxxxxxxxxxxxxxendl; return 0;
}//mycommand.c
//新增extern char** environ;//替换函数execle(./otherExe,otherExe,-a,-b,c,NULL,environ);代码运行结果为 示例2
//mycommand.c
//新增部分
char *const myenv[] {MYVAL1111,MYPATH/usr/bin/XXX,NULL
};
//替换函数为
execle(./otherExe, otherExe, -a, -w, -v, NULL, myenv);代码运行结果为
2.6execve函数
int execve(const char *path, char *const argv[], char *const envp[]);execve替换函数名中的v是指vector数组把可执行程序的执行选项功能放在数组中在数组中以NULL为结尾envp参数传递环境变量为参数。 示例 char* const myargv[]{otherExe,-a,-l,NULL};char* const myenv[]{MYVAL111111,MYPATH/usr/bin/xxx,NULL};extern char** environ;代码运行的结果为 注意 libc中定义的全局变量environ指向环境变量表environ没有包含在任何头文件中所以在使用时要用extern声明我们如果想给子进程传递环境变量有两种方式①新增环境变量在父进程中使用putenv函数自己设置的环境变量②彻底替换覆盖环境变量使用带“e”选项的exec函数execle/execve函数传递自己设置环境变量数组
2.7跨语言调用程序
在上面的例子中我们都是使用我们自己写的mycommand.c程序调用系统的程序指令那我们是否可以调用自己所写的程序呢答案是当然可以 示例1 python代码
#!/usr/bin/python3print(hello Python!)#includestdio.h
#includesys/types.h
#includeunistd.h
#includesys/wait.h
#includestdlib.hint main()
{pid_t idfork();if(id 0){//childprintf(before: I am a process: pid: %d , ppid:%d\n,getpid(),getppid());sleep(5);//替换函数execl(/usr/bin/python3, python3, test.py, NULL);printf(after: I am a process: pid: %d , ppid:%d\n,getpid(),getppid());exit(0);}//fatherpid_t retwaitpid(id,NULL,0);if(ret 0){printf(wait success, father pid:%d, ret id: %d\n,getpid(),ret);sleep(5);}return 0;
}代码运行结果如下 为什么我们可以用C语言程序调用Python可执行程序 无论什么语言的可执行程序运行起来之后变成了进程便可以通过在子进程中使用exec系列函数替换达到调用的效果
3.总结
①exec系列函数如果调用成功则加载新的程序从启动代码开始执行不再返回。如果调用出错则返回-1所以exec函数只有出错的返回值而没有成功的返回值。 ②命名理解记忆
l(list) : 表示参数采用列表
v(vector) : 参数用数组
p(path) : 有p自动搜索环境变量PATH
e(env) : 表示自己维护环境变量③只有execve函数是真正的系统调用其它五个函数最终都调用execve函数所以execve在man手册 第2节,其它函数在man手册第3节。这些函数之间的关系如下图所示