exec族函数函数的作用:
1.一个父进程希望复制自己,使父、子进程同时执行不同的代码段。这在网络服务进程中是常见的——父进程等待客户端的服务请求。当这种请求到达的时候,父进程调用fork,使子进程处理此请求。父进程则继续等待下一个服务请求到达。
2.一个进程要执行一个不同的程序。这对shell是非常常见的情况。在这种情况下,子进程从fork返回后立即调用exec.
exec族函数包含以下:
#include <unistd.h>
extern char **environ;
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 execvpe(const char *file, char *const argv[],char *const envp[]);
返回值:
exec函数族的函数执行成功后不会返回,调用失败时,会设置errno并返回-1,然后从原程序的调用点接着往下执行。
参数说明:
path:可执行文件的路径名字
arg:可执行程序所带的参数,第一个参数为可执行文件名字,没有带路径且arg必须以NULL结束
file:如果参数file中包含/,则就将其视为路径名,否则就按 PATH环境变量,在它所指定的各目录中搜寻可执行文件。
exec族函数参数极难记忆和分辨,函数名中的字符会给我们一些帮助:
l : 使用参数列表
p:使用文件名,并从PATH环境进行寻找可执行文件
v:应先构造一个指向各参数的指针数组,然后将该数组的地址作为这些函数的参数。
e:多了envp[]数组,使用新的环境变量代替调用进程的环境变量
下面我们逐个介绍
1.execl:(l表示list),包括execl、execlp、execle,要求将新程序的每个命令行参数都说明为 一个单独的参数。这种参数表以空指针结尾。
函数原型:
int execl(const char *path, const char *arg, ...);
//文件echoarg.c
#include <stdio.h>
int main(int argc,char *argv[])
{
int i = 0;
for(i = 0; i < argc; i++)
{
printf("argv[%d]: %s\n",i,argv[i]);
}
return 0;
}
//文件execl.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//函数原型:int execl(const char *path, const char *arg, ...);
int main(void)
{
printf("before execl\n");
if(execl("./newpro","newpro","abc","efg",NULL) == -1)
{
printf("execl failed!\n");
perror("Fault:");
}
printf("after execl\n");
return 0;
}
结果:
在调用execl()
函数后,当前进程的映像会被新程序(newpro
)的映像所替代,控制权会完全转移到新程序中。因此,如果execl()
函数成功执行,它将不会返回,后续的代码也不会被执行。
只有当execl()
函数调用失败时(返回值为-1),才会输出"execl failed!"这一行,并打印出具体的错误信息。
2.execlp:函数会根据环境变量PATH
的设置来查找可执行文件的路径,而不需要指定完整的文件路径。
函数原型:
int execlp(const char *file, const char *arg, ...);
执行ls-l指令:此时我们不需要输入绝对路径,它从PATH环境进行寻找可执行文件
//文件execlp.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//函数原型:int execl(const char *path, const char *arg, ...);
int main(void)
{
printf("before execl\n");
if(execlp("ls","ls","-l",NULL) == -1)
{
printf("execl failed!\n");
perror("Fault:");
}
printf("after execl\n");
return 0;
}
执行结果:
3.execle:
函数是exec
函数族的另一个成员,用于执行另一个程序,并允许您指定新程序的环境变量。与execlp()
函数不同的是,execle()
函数可以显式地指定新程序的环境变量数组。
下面是execle()
函数的函数原型:
int execle(const char *path, const char *arg,..., char * const envp[]);
//文件execle.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//函数原型:int execle(const char *path, const char *arg,..., char * const envp[]);
char *env_init[] = {"AA=aa","BB=bb",NULL};
int main(void)
{
printf("before execle****\n");
if(execle("./echoenv","echoenv",NULL,env_init) == -1)
{
printf("execle failed!\n");
}
printf("after execle*****\n");
return 0;
}
1 //文件echoenv.c
2 #include <stdio.h>
3 #include <unistd.h>
4 extern char** environ;
5 int main(int argc , char *argv[])
6 {
7 int i;
8 char **ptr;
9 for(int i =0;i<argc;i++){
10 printf("canshu =%s\n",argv[i]);
11
12 }
13 for(ptr = environ;*ptr != 0; ptr++)
14 printf("%s\n",*ptr);
15 return 0;
16 }
~
运行结果:
主程序部分调用了 execle
函数来执行 ./echoenv
可执行文件,同时传递了参数 echoenv
和自定义的环境变量 env_init
。
env_init
是一个字符串数组,其中每个字符串都表示一个环境变量。在这个例子中,定义了两个环境变量 "AA=aa"
和 "BB=bb"
,并通过最后一个元素为 NULL
来标记环境变量数组的结尾。
执行 execle
函数后,当前进程会被替换为 ./echoenv
的程序。在新程序中,通过访问全局变量 environ
,可以获取到父进程传递过来的环境变量。然后新程序使用一个循环遍历输出所有的环境变量。
extern char** environ;
是一个声明语句,它声明了一个名为 environ
的全局变量,该变量是一个指向字符指针的指针。
在C语言中,extern
关键字用于声明一个全局变量或函数,表示该变量或函数是在其他文件中定义的。这样做可以在当前文件中引用其他文件中定义的全局变量或函数。
environ
是一个全局变量,它是一个指向字符串指针的指针数组。它包含了当前进程的环境变量。每个字符串指针都指向一个以 key=value
格式表示的环境变量。
通过声明 extern char** environ;
,你可以在当前文件中引用 environ
变量,以访问和操作环境变量。例如,在你给出的代码中,通过遍历 environ
可以输出所有的环境变量。
需要注意的是,environ
变量通常是由操作系统在程序启动时设置的,并且它是一个只读变量。因此,你不能直接修改 environ
数组中的内容,但可以通过其他方式来操作环境变量,比如使用标准库中的函数来获取、设置或删除环境变量。
4.execv:是一个用于执行指定路径下可执行文件的函数。该函数会将当前进程替换为指定路径下的可执行文件,并传递给新程序一个参数列表。
int execv(const char *path, char *const argv[]);
path
:表示要执行的可执行文件的路径名。argv
:参数列表,是一个字符串数组,其中每个元素都是一个参数。最后一个元素必须为NULL
,用于标记参数列表的结束//文件execv.c #include <stdio.h> #include <stdlib.h> #include <unistd.h> //函数原型:int execl(const char *path, const char *arg, ...); int main(void) { char *argv[]={"ls",NULL,NULL}; printf("before execl\n"); if(execvp("/bin/ls",argv) == -1) { printf("execl failed!\n"); perror("Fault:"); } printf("after execl\n"); return 0; }
运行结果:
5:execvp:是一个用于执行指定可执行文件的函数。与 execv
函数相比,execvp
函数的参数有所不同,但功能类似。execvp
函数会在系统的 PATH 环境变量指定的路径中搜索可执行文件。当调用execvp
函数时,系统将自动搜索可执行文件并执行它。新程序接收到的命令行参数将由 argv
提供。可以通过遍历 argv
数组来获取传递给新程序的参数。
int execvp(const char *file, char *const argv[]);
//文件execvp.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//函数原型:int execl(const char *path, const char *arg, ...);
int main(void)
{
char *argv[]={"ls","-l",NULL};
printf("before execl\n");
if(execvp("ls",argv) == -1)
{
printf("execl failed!\n");
perror("Fault:");
}
printf("after execl\n");
return 0;
}
运行结果:(实现ls-l)
这个函数有兴趣的可以研究下,目前没用过
int execvpe(const char *file, char *const argv[],char *const envp[]);
20230815