-
2018-11-27 21:58:16
exec()函数
笔记来自《嗨翻C语言》进程与系统调用 第9章exec()函数的头文件是#include<unistd.h>
调用system函数时,操作系统必须解释命令字符串,然后决定运行哪些程序和怎样运行,问题就出在操作系统需要解释字符串上,比较容易出错。
从而exec()函数可以解决这个问题。
进程是存储器中运行的程序,在linux查看进程,输入ps -ef命令,操作系统使用进程标识符PID来标识进程。
exec()函数通过运行其他程序来替换当前进程,可以告诉exec()函数要使用哪些命令行参数和环境变量,新程序启动后PID和老程序一样,就像两个程序接力跑,把进程交接给了新程序。
exec函数有很多
- 列表函数 :execl()、execlp()、execle()
列表函数以参数列表的形式接受命令行参数
1程序
告诉exec()函数将运行什么程序,对execl()或execle()来说,它是程序的完整路径名;对execlp()来讲就是命令的名字,execlp()会根据它来查找程序。
2命令行参数
需要依次列出想使用的命令行参数。第一个命令行参数必须是程序名,也就是说列表版exec()的前两个参数是相同的字符串。
3NULL
需要在最后一个命令行参数后加上NULL,告诉函数没有其他参数了
4环境变量(如果有的话)
如果调用了以e结尾的exec()函数,还可以传递环境变量数组,像POWER=4、SPPED=17、PORT=OPEN…那样的字符串数组。
举个栗子:
execl("/home/flynn/clu","/home/flynn/clu",“paranoids”,“contract”,NULL)
execlp(“clu”,“clu”,“paranoids”,“contract”,NULL)
execle("/home/flynn/clu","/home/flynn/clu",“paranoids”,“contract”,NULL,env_vars)
注:env_vars是一个字符串数组,里面放了环境变量 - 数组函数:execv()、execvp()、execve()
如果已经把命令行参数保存在了数组里,就会发现这两个版本用起来更容易:
execv=参数数组/参数向量
execv("/home/flynn/clu",my_args);
execvp=参数数组/参数向量+在PATH中查找
execvp(“clu”,my_args)
注:参数需要保存在字符串数组my_args中
两者唯一的区别是execvp会在PATH中查找程序
如何记住exec函数
exec后面可以跟一到两个字符,但只能是l、v、p和e中的一个,代表你想使用的功能,如下表所示:
|使用 | 字符 |
|参数列表 | l |
| 参数数组/向量 | v |
|根据PATH查找 | p |
|环境变量 | e |
出错了怎么办
就算调用的程序发生错误,当前进程会继续运行。这点很有用,就算第二个进程启动失败,还是能够从错误中恢复出来,并向用户报告错误消息。
注意:
只要让exec()函数执行成功,就会修改进程。它会运行新程序替代你的程序,也就是,只要exec()函数一运行,你的程序就会停止运行。
系统调用是操作系统中的函数,当进行系统调用时,相当于调用程序外的代码。更多相关内容 - 列表函数 :execl()、execlp()、execle()
-
Linux中使用exec函数族详解及示例代码 | 嵌入式Linux应用开发篇 – 03
2021-01-06 10:38:261.exec函数族 exec 为 execute(执行),exec 函数族用来替换调用进程所执行的程序,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行,替换前后进程的 PID 不会改变。 exec函数族中包括6个... -
Linux 进程替换(exec函数)实现代码
2020-09-15 09:13:22主要介绍了Linux 进程替换(exec函数)实现代码的相关资料,需要的朋友可以参考下 -
PHP中exec函数和shell_exec函数的区别
2020-10-25 13:08:55主要介绍了PHP中exec函数和shell_exec函数的区别,这两个函数是非常危险的函数,一般情况都是被禁用的,当然特殊情况下也会使用,需要的朋友可以参考下 -
PHP中如何判断exec函数执行成功?
2020-12-19 13:07:31exec函数第一个参数是执行的命令,第二个参数是执行的结果,第三个参数是执行的状态。 <?php exec('ls', $log, $status); print_r($log); print_r($status); echo PHP_EOL; 执行这个php文件: 这里$log... -
Linux exec函数族
2022-03-03 20:51:04exec函数族内的函数可以根据指定的文件名或目录名找到可执行程序,并加载新的可执行程序,替换掉旧的代码区、数据区、堆区、栈区与其他系统资源。这里的可执行程序既可以是二进制文件,也可以是脚本文件。在执行exec...如果我们使用fork()函数创建一个子进程,则该子进程几乎复制了父进程的全部内容,也就是说,子进程与父进程在执行同一个可执行程序。那么我们能否让子进程不执行父进程正在执行的程序呢?
exec函数族提供了让进程运行另一个程序的方法。exec函数族内的函数可以根据指定的文件名或目录名找到可执行程序,并加载新的可执行程序,替换掉旧的代码区、数据区、堆区、栈区与其他系统资源。这里的可执行程序既可以是二进制文件,也可以是脚本文件。在执行exec函数族函数后,除了该进程的进程号PID,其他内容都被替换了。
exec函数族有6个函数,这些函数的函数名、函数功能、函数参数列表有相似之处,我们在使用的过程中一定要仔细区分这些函数的区别避免混淆。有关exec函数族的更多使用方法内容请查阅man手册。
所需头文件: #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[]) 函数参数: path:文件路径,使用该参数需要提供完整的文件路径 file:文件名,使用该参数无需提供完整的文件路径,终端会自动根据$PATH的值查找文件路径 arg:以逐个列举方式传递参数 argv:以指针数组方式传递参数 envp:环境变量数组 返回值: -1(通常情况下无返回值,当函数调用出错才有返回值-1)
这6个函数的函数功能类似,但是在使用语法规则上有细微区别。我们可以看出,其实exec函数族的函数都是exec+后缀来命名的,具体的区别如下:
区别1:参数传递方式(函数名含有l还是v)
- 若函数名内含有字母’l’(表示单词list),则表示该函数是以逐个列举的方式传参,每个成员使用逗号分隔,其类型为const char *arg,成员参数列表使用NULL结尾
- 若函数名内含有字母’v’(表示单词vector),则表示该函数是以指针数组的方式传参,其类型为char *const argv[],命令参数列表使用NULL结尾
区别2:查找可执行文件方式(函数名是否有p)
- 若函数名内没有字母’p’,则形参为path,表示我们在调用该函数时需要提供可执行程序的完整路径信息
- 若函数名内含有字母’p’,则形参为file,表示我们在调用该函数时只需给出文件名,系统会自动按照环境变量$PATH的内容来寻找可执行程序
区别3:是否指定环境变量(函数名是否有e)
- 若函数名内没有字母’e’,则使用系统当前环境变量
- 若函数名内含有字母’e’(表示单词environment),则可以通过形参envp[]传入当前进程使用的环境变量
这6个exec函数族的函数,execve()函数属于系统调用函数,其余5个函数属于库函数。
-
exec函数族的使用
2021-01-19 22:45:47fork创建一个新的进程就产生了一个新的PID,exec启动一个新程序,替换原有的进程,因此这个新的被exec执行的进程的PID不会改变,和调用exec函数的进程一样。 下面来看下exec函数族: #include int execl... -
【Linux】进程控制(exec函数族)的理解和使用
2022-03-20 19:05:11exec函数族使用的总结 有了exec函数族我们就可以用它来调用任何程序,也就是说,你还可以在C/C++,可以调用其他任何语言,比如python,Java等程序; 这些exec函数的接口函数本质是没有任何差别的,只是参数选项不同...文章目录
1. 进程程序替换是什么
我们直到一个进程被创建出来,OS会给它分配进程PCB,mm_struct,页表等信息,同时会将程序的代码和数据加载到物理内存是吧;
而进程程序替换就是:正在执行的进程本身的pcb,mm_struct,页表等信息不会发生改变,仅仅把一个新的程序代码和数据替换了原来进程的代码和数据;这就是进程程序的替换;
如下图:我们有个进程,在执行中,如果发生进程程序替换,只不过事把该进程的代码和数据从磁盘加载到了物理内存罢了,其他信息不会变化;
进程程序替换并不会创建新的进程,它只会加载程序的代码和数据,去替换原来的进程!!!
2. 进程替换函数–exec族
#include <unistd.h>` //exec函数族的头文件 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[]);
- 这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回;
- 如果调用出错则返回-1;
- 所以exec函数只有出错的返回值而没有成功的返回值;
这些函数原型看起来很容易混,但只要掌握了规律就很好记。
l(list) : 表示参数采用列表;
v(vector) : 参数用数组;
p(path) : 有p自动搜索环境变量PATH;
e(env) : 表示自己维护环境变量;
最简单的程序替换程序:
int main() { execl("/usr/bin/ls","ls","-a","-l",NULL); //程序替换函数 printf("这段代码是在execl执行成功是不会执行的\n"); return 0; }
而我们程序替换的本质:
而我们的程序替换函数,通常不父进程去执行,而是交给子进程执行,因为这也子进程可以做它的事,父进程也可以做自己的事,由于进程之间的独立性,即使子进程去执行execl函数时候,替换的也是子进程的代码和数据,而父进程的代码和数据是不会被影响的;
#include<stdio.h> #include<string.h> #include<stdlib.h> #include<unistd.h> #include<sys/wait.h> int main() { pid_t pid = fork(); if(pid == 0){ //child process execl("/usr/bin/ls","ls","-a","-l",NULL); //退出子进程 exit(0); } //父进程执行的代码 wait(NULL); printf("i am father[%d] ,do my thing ,不会被子进程的[%d]execl函数影响\n",getpid(),pid); return 0; }
请问:发生了子进程的程序替换,此时:父子进程的代码还是共享的码?
当然不会共享了,此时发生了写时拷贝,也就是会拷贝出一份代码和数据出来,然后子进程再被它的execl函数进行函数替换;
3. execl函数的使用
4. execv函数的使用
execv和execl也是没啥区别,只不过execv叭execl的以列表形式的传参,变成了以数组形式的传参;
int main() { if(fork() == 0){ // execl("/usr/bin/ls","ls","-a","-l",NULL); //等价下面的execv("/usr/bin/ls",argv); char*const argv[] = { "ls", "-a", "-l", NULL }; execv("/usr/bin/ls",argv); exit(1); } int waitRet = waitpid(-1,NULL,0); //阻塞等待所有子进程结束 if(waitRet< 0){ perror("wait error\n"); } printf("parent wait child success\n"); return 0; }
5. execlp函数的使用
execlp和execl的区别在于,execlp在第一个参数时候,不需要全路径,只需要写上执行命令的文件名即可,表示你需要执行谁,往后的参数也就是和execl的传参一样;
#include<stdio.h> #include<unistd.h>//使用fork,exec函数 #include<sys/wait.h>//使用waitpid #include<stdlib.h> //使用exit的头文件 int main() { if(fork() == 0){ // execl("/usr/bin/ls","ls","-a","-l",NULL); execlp("ls","ls","-a","-l",NULL); //等价上面的execl() //虽然这里的第一个参数和第二个参数都一样,但是含义不一样; //第一个参数表示iexeclp函数要执行命令的路径文件名, //第二个参数表示execlp在命令行上如何执行该命令 exit(1); } int waitRet = waitpid(-1,NULL,0); //阻塞等待所有子进程结束 if(waitRet< 0){ perror("wait error\n"); } printf("parent wait child success\n"); return 0; }
6. execvp函数的使用
这个函数execvp和execv的区别在于。execvp第一个传参时候,不需要给出要执行命令的绝对路径,只需要给出要执行命令的文件名即可;
#include<stdio.h> #include<unistd.h>//使用fork,exec函数 #include<sys/wait.h>//使用waitpid #include<stdlib.h> //使用exit的头文件 int main() { if(fork() == 0){ // execl("/usr/bin/ls","ls","-a","-l",NULL); //execlp("ls","ls","-a","-l",NULL); //等价上面的execl() char* const argv[] = { "ls", "-a", "-l", NULL }; //execv("/usr/bin/ls",argv); execvp("ls",argv);//等价上面的execv() exit(1); } int waitRet = waitpid(-1,NULL,0); //阻塞等待所有子进程结束 if(waitRet< 0){ perror("wait error\n"); } printf("parent wait child success\n"); return 0; }
7. 验证exec函数族执行自己写的程序
- makefile 文件,编译链接生成myload和myexe两个文件;
- 这两个文件的作用是:myload加载myexe的程序,验证execv函数执行自己写的程序是否可以;
.PHONY:all all:myload myexe # 该目标文件all不会被生成,是因为没有依赖方法 myload:myload.c gcc $^ -o $@ myexe:myexe.c gcc $^ -o $@ .PHONY:clean clean: rm -rf myload myexe
myexe.c文件的内容:
#include<stdio.h> int main(){ printf("i am myexe\n"); return 0; }
myload.c文件
#include<stdio.h> #include<unistd.h>//使用fork,exec函数 #include<sys/wait.h>//使用waitpid #include<stdlib.h> //使用exit的头文件 int main() { if(fork() == 0){ execl("./myexe","./myexe",NULL); exit(1); } int waitRet = waitpid(-1,NULL,0); //阻塞等待所有子进程结束 if(waitRet< 0){ perror("wait error\n"); } printf("parent wait child success\n"); return 0; }
测试结果:执行myoad,成功加载了myexe程序;
8. execle 和 execve函数的使用
这个函数只是比execl多了一个e,表示最后一个参数需要给execle传入自定义的环境变量数组;
它的使用情况是:假如你要给一个你需要执行新的程序,加载一些自定义的环境变量给新的程序时候,你可以使用该函数;
测试:
myexe.c文件:打印环境变量的值,这个文件假如自己执行自己的话,那么就打印默认的环境变量;
假如其他文件使用execle传参给myexe.c环境变量,myexe.c就会执行该execle传过来的环境变量;
#include<stdio.h> int main(){ extern char** environ; //系统提供的环境变量指针,指向环境变量的指针数组 int i = 0; for(;environ[i];i++){ printf("%s\n",environ[i]); } return 0; }
myload.c文件:使用execle函数,给该函数传递环境变量;使得myexe可执行程序可以获得该myload.c传递过去的环境变量;
#include<stdio.h> #include<unistd.h>//使用fork,exec函数 #include<sys/wait.h>//使用waitpid #include<stdlib.h> //使用exit的头文件 int main() { if(fork() == 0){ char* const env[] = { "MYENV0=123", "MYENV1=456", "MYENV2=789", NULL }; //char* const argv[] = { // "./myexe", // NULL //}; execle("./myexe","./myexe",NULL,env); //execlv("./myexe",argv,,env); //等价上execle的方式 exit(1); } int waitRet = waitpid(-1,NULL,0); //阻塞等待所有子进程结束 if(waitRet< 0){ perror("wait error\n"); } printf("parent wait child success\n"); return 0; }
测试结果:
9. exec函数族使用的总结
- 有了exec函数族我们就可以用它来调用任何程序,也就是说,你还可以在C/C++,可以调用其他任何语言,比如python,Java等程序;
- 这些exec函数的接口函数本质是没有任何差别的,只是参数选项不同罢了;
- 为什么有那么多种接口,本质满足不同引用场景而已;
- 这些函数之间的调用关系,本质都会调用系统调用函数execve;
-
【Linux】exec函数族
2022-03-23 18:27:56exec函数族就提供了一个在进程中执行另一个程序的方法。它可以根据指定的文件名或目录名找到可执行文件,并用他来取代当前进程的数据段、代码段和堆栈段。在执行完后,当前进程除了进程号外,其他内容都被替换。 ...fork()函数用于创建一个子进程,该子进程几乎复制了父进程的全部内容。我们能否让子进程执行一个新的程序呢?exec函数族就提供了一个在进程中执行另一个程序的方法。它可以根据指定的文件名或目录名找到可执行文件,并用他来取代当前进程的数据段、代码段和堆栈段。在执行完后,当前进程除了进程号外,其他内容都被替换。
头文件:#include <unistd.h>
函数原型:
int execl(const char*path, const char *arg, ...); //1. 查找方式:完整的文件目录路径;2. 参数传递方式:逐个列举(list)
int execv(const char*path, const char *const arg[]); //2. 参数传递方式:指针数组传递(vertor)
int execle(const char*path, const char *arg, ..., char *const envp[]); //3. 使用默认的环境变量(environment),在envp[]中指定当前进程使用的环境变量
int execve(const char*path, const char *const arg[], char *const envp[]);
int execlp(const char*file, const char *arg, ...); //1. 查找方式:文件名
int execvp(const char*file, const char *const arg[]);
exec函数名对应名称:
前四位:exec
第五位
l:参数传递为逐个列举方式
v:参数传递为构造指针数组的方式
第六位:
e:可传递新进程环境变量
p:可执行文件查找方式为文件名
函数返回值:-1:出错
如何使用文件名的方式查找可执行文件,同时使用参数列表的方式
这里使用的函数是execlp()
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> int main() { pid_t pid=fork(); if(pid==0) { if((ret=execlp("ps","ps","-ef",NULL))<0) //相当于调用了“ps -ef”命令 { printf("execlp error\n"); } }
使用完整的文件目录来查找对应的可执行文件。
这里使用的函数是execl()
#include <unistd.h> #include <stdio.h> #include <stdlib.h> int main() { if(fork()==0) { if(execl("/bin/ps","ps","-ef",NNULL)<0); //目录必须以“/”开头,否则将视其为文件名 { printf("execl error\n"); } } }
利用execle()将环境变量添加到新建的子进程中
#include <stdio.h> #include <unistd.h> #include <stdlib.h> int main() { char *envp[]={"PATH=/tmp","USER=harry",NULL}; //命令行参数列表,必须以NULL结尾 if(fork()==0) { if(execle("/usr/bin/env","env",NULL,envp)<0) //env是查看当前进程环境变量的命令 { printf("execle error\n"); } } }
使用execve()函数,通过构造指针数组的方式来传递参数
#include <stdio.h> #include <unistd.h> #include <stdlib.h> int main() { char *arg[]={"env",NULL}; char *envp[]={"PATH=/tmp","USER=harry",NULL}; if(fork()) { if(execve("/usr/bin/env",arg,envp)<0) { printf("execve error\n"); } } }
-
Linux下的exec函数族
2021-11-27 23:14:28创建子进程后,我们并不希望子进程执行父进程后续相同的内容,这时我们可以用exec函数族进行。 exec函数族 Linux下的exec函数族,是指6个以exec开头的函数。分别是 int execl(const char *path,const char *arg,...)... -
JavaScript中exec函数用法实例分析
2020-12-01 11:25:24本文实例讲述了JavaScript中exec函数用法。分享给大家供大家参考。具体如下: javaScript 中的 exec 函数,用正则表达式模式在字符串中运行查找,并返回包含该查找结果的一个数组。 rgExp.exec(str) 参数: ... -
(5)exec函数详解
2021-05-27 17:36:37一、为什么需要exec函数 (1)fork子进程是为了执行新程序(fork创建了子进程后,子进程和父进程同时被OS调度执行,因此子进程可以单独的执行一个程序,这个程序宏观上将会和父进程程序同时进行) (2)可以直接在子... -
exec函数详解
2018-05-17 17:30:59(1)exec函数说明fork函数是用于创建一个子进程,该子进程几乎是父进程的副本,而有时我们希望子进程去执行另外的程序,exec函数族就提供了一个在进程中启动另一个程序执行的方法。它可以根据指定的文件名或目录名... -
exec函数族
2021-02-15 23:42:11exec函数族一、exec函数族介绍二、exec函数族作用图解三、exec函数族 一、exec函数族介绍 exec函数族的作用是根据指定的文件名找到可执行文件,并用它来取代调用进程的内容,换句话说,就是在调用进程内部执行一个... -
了解C语言中的exec函数家族
2021-02-15 16:26:37exec函数家族用新进程替换当前正在运行的进程。它可以通过使用另一个C程序来运行一个C程序,它位于头文件unistd.h下。 exec系列中有许多成员,下面以示例显示。 execvp execvp:使用此命令,创建的... -
python exec函数怎么用?
2020-12-09 12:01:32但是在python中,有帮助我们执行复杂的python代码,功能十分强大,那就是exec()函数,今天我们就来聊聊exec()函数。1、exec 语法exec(object[, globals[, locals]])2、参数object:必选参数,表示需... -
linux系统进程 exec函数族的使用(fork之后)
2022-03-20 23:52:323 ##exec()函数可用在fork()函数之后,直接执行一个程序而省略子进程复制父进程的资源 4 ##调用exec不会创建一个新的pid 5 6 path:可执行文件的路径名字 7 arg:可执行程序所带的参数,第一个参数为可执行... -
EDA/PLD中的exec函数族的使用
2020-11-09 02:00:12fork创建一个新的进程就产生了一个新的PID,exec启动一个新程序,替换原有的进程,因此这个新的被exec执行的进程的PID不会改变,和调用exec函数的进程一样。 下面来看下exec函数族: #include int execl... -
Qt中exec函数的作用
2022-04-25 21:22:01Qt中的exec()方法到处可见,例如: QCoreApplicaton::exec() QApplication::exec() QDialog::exec() QThread::exec() QMenu::exec() 那么,这些exec()的作用是什么呢? 作用是开启一个循环,执行一个事件,相当于... -
Python之浅谈exec函数
2021-01-14 05:47:50在Python中,exec()是一个十分有趣且使用的内置函数,不同于eval()函数只能执行计算数学表达式的结果的功能,exec()能够动态地执行复杂的Python代码,能够十分强大。具体的介绍可以参考官方文档: ... -
23.Python中的exec函数
2022-04-06 20:08:13exec函数 exec是Python的内置函数,能够执行储存在字符串或文件中的 Python 语句。 eval()函数只能执行计算数学表达式的结果的功能,而exec()能够动态地执行复杂的Python代码, exec做的是编译和评估字符串中的语句... -
linux基础知识——exec函数
2020-11-21 20:32:351.exec函数 \qquadfork()函数在执行之后,父子进程其实还是执行同一个程序,不同的只是同一个程序的不同分支。如果要想让子进程执行另外一个不同的程序,这时候需要调用exec函数,这时候子进程的用户空间代码和数据... -
Linux -- exec函数的学习记录
2019-03-08 13:39:33Linux C编程中,调用另一个可执行文件或调用命令可以使用system函数和exec系列的函数。 下面关于exec系列函数的一些方法和使用做一个简单的记录。 exec系列函数。都是以execl开头或者execv开头(l表示列表list,v... -
exec函数族用法总结
2019-11-19 20:39:151、exec函数说明 fork()函数通过系统调用创建一个与原来进程(父进程)几乎完全相同的进程(子进程是父进程的副本,它将获得父进程数据空间、堆、栈等资源的副本。注意,子进程持有的是上述存储空间的“副本”,这... -
进程(四)exec函数
2021-03-20 16:57:46一、为什么要用exec族函数,有什么用 (1)一个父进程希望复制自己,使父、子进程同时执行不同的代码段。 这在网络服务进程种是...我们用fork函数创建新进程后,经常会在新进程中调用exec函数去执行另外一个程序。 . -
使用exec函数替换当前进程
2022-04-09 19:44:061 exec介绍 fork函数创建新的进程后,子进程往往要调用一种exec函数以执行另一个程序。当进程调用exec函数时,该进程执行的程序完全...有7种exec函数,统称为exec函数: #include <unistd.h> int execl(con -
exec函数与fork函数的配合使用
2021-07-26 14:55:381.实现功能 当父进程检测到输入为1的时候...可以看出通过fork函数产生子进程再调用exec函数来模拟处理客户端请求,父进程则继续等待下一个服务请求。从代码编译运行调试过程中,不难发现,父子进程是互不干扰的。 ... -
Linux学习日记15——exec函数族、回收子进程
2022-04-11 18:40:19一、exec函数族 1.1 函数族作用 1、简介 fork 创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种 exec 函数以执行另一个程序。当进程调用一种 exec 函数时,该进程的...