c语言的execeve函数,exec族函数

本文详细介绍了Linux中的exec族函数,包括它们的六种形式:execl、execlp、execle、execv、execvp和execve。这些函数用于在子进程中替换当前的执行映像,执行新的程序。区别在于文件路径查找方式、命令行参数传递和环境变量的设定。通过示例代码展示了如何在不同的场景下使用这些函数,强调了参数的传递和父子进程的关系。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

通常我们使用了fork函数创建了一个子进程后,子进程会调用exec族函数执行另外一个程序。由于fork函数新建的子进程和父进程几乎一模一样(不过,后来引入了写时复制技术避免了这一点),因此需要使用exec函数使得子进程执行一个新的可执行程序。

我们之所以称exec为族函数是因为它有6种不同的形式,主要区别体现在参数上:

01#include < unistd.h >

02extern char **environ;

03int execl(const char *path,const char *arg, ...);

04int execlp(const char *file,const char *arg, ...);

05int execle(const char *path,const char *arg,

06...,char *const envp[]);

07int execv(const char *path,char *const argv[]);

08int execvp(const char *file,char *const argv[]);

09int execve(const char *filename,char *const argv[],

10char *const envp[]);

总体来说,exec族函数的参数分为三类:文件路径,命令行参数,环境变量。不论那个exec函数,最终都是将上述三类参数传递给可执行程序中的

main函数。这里有一个新的概念,即环境变量,它定义了用户的工作环境,包括用户的主目录,终端的类型,当前用户,当前目录等信息。事实上,main函

数的完整形式应该是:

1int main(int argc,char *argv[],char **envp);

通常我们并没有显示指定环境变量,这是因为main函数每次都默认使用系统自定义的全局变量environ。另外,通过在终端输入env命令,也可查看当前的所有环境变量信息。可以看到,main函数中的argv[]和envp与exec函数的参数有对应关系。

初次接触exec族函数,易被这6种不同的调用形式搞得混乱。下面我们将依次从上述三个参数的角度来区分这些函数的细微区别。

1.可执行文件的路径

以p结尾的exec函数可以直接传递可执行程序的文件名,此时这类函数会自动在PATH环境变量所指定的目录下查找这个可执行文件;非p结尾的exec函数则必须显示指定可执行程序的完整路径;

2.命令行参数

以execl开始的exec函数必须以列表的形式传递所有的命令行参数,并且最终以NULL作为命令行参数的结束;而以execv开始的exec函数则以字符串指针数组的形式传递所有命令行参数;

3.环境变量

以e结尾的exec函数必须显示指定环境变量;而非p结尾的exec函数则使用默认的环境变量,也就是main函数中的环境变量;

虽然exec函数有6种不同的调用形式,但是每个函数实现的功能都是一样的,即完成新的可执行程序的装入。下面的这个程序很好的演示了不同exec函数的使用方法:

01#include < unistd.h >

02#include < stdio.h >

03

04int main()

05{

06char *envp[]={

07"PATH = /tmp",

08"USER = edsionte",

09NULL

10};

11

12char *argv_execv[] = {"echo","excuted by execv", NULL};

13char *argv_execvp[] = {"echo","executed by execvp", NULL};

14char *argv_execve[] = {"env", NULL};

15

16if (fork() == 0)

17if(execl("/bin/echo","echo","executed by execl", NULL) < 0)

18perror("Err on execl");

19if(fork() == 0)

20if(execlp("echo","echo","executed by execlp", NULL) < 0)

21perror("Err on execlp");

22if(fork() == 0)

23if(execle("/usr/bin/env","env", NULL, envp) < 0)

24perror("Err on execle");

25if(fork() == 0)

26if(execv("/bin/echo", argv_execv) < 0)

27perror("Err on execv");

28if(fork() == 0)

29if(execvp("echo", argv_execvp) < 0)

30perror("Err on execvp");

31if(fork() == 0)

32if(execve("/usr/bin/env", argv_execve, envp) < 0)

33perror("Err on execve");

34

35printf("goodbye!\n");

36return 0;

37}

38//结果:

39edsionte@edsionte-desktop:~/edsionte$ ./sys_process

40goodbye!

41edsionte@edsionte-desktop:~/edsionte$ executed by execl

42excuted by execv

43executed by execvp

44executed by execlp

45PATH = /tmp

46USER = edsionte

47PATH = /tmp

48USER = edsionte

这个程序分别使用不同的exec函数调用了echo或env命令,并根据所传递的不同参数显示不同结果。该程序可能每次运行后的显示顺序都有所不同,这是进程的并发性所导致的。

值得注意的是,我们通常所说的命令行参数是以argv数组的第一个元素开始的,而第0号元素是可执行程序的名称。以第一个if语句为例,execl

函数为/bin目录下的echo程序传递了两个参数:”echo”和”executed by

execl”。由于我们通过execl函数已经装入了/bin/echo程序,因此在调用execl函数时,第一个参数”echo”可以替换成任意字符

串,但是这并不代表就不需要传递该参数。

我们也可以让exec函数去执行我们自己编写的可执行程序。下面这个例子很好的演示了父进程和子进程的关系:

01/*

02*Author: edsionte

03*Email:  edsionte@gmail.com

04*Time:   2011/03/14

05*/

06

07#include < stdio.h >

08#include < unistd.h >

09#include < stdlib.h >

10

11int main()

12{

13int pid;

14int val = 0;

15

16pid = fork();

17if (pid == 0) {

18//child proces;

19printf("[Child %d] I will be a new process!\n", getpid());

20if (execl("./sleeping","first arg","20", NULL) == -1) {

21printf("execl error!\n");

22exit(1);

23}

24printf("[Child %d] I never go here!\n", getpid());

25}else if (pid > 0) {

26printf("[Parent %d] I am running!\n", getpid());

27/*

28wait(&val);

29printf("[Parent %d] All my child were leaving..\n", getpid());

30*/

31}else {

32printf("fork error!\n");

33exit(1);

34}

35

36printf("[process %d] I am leaving..\n", getpid());

37return 0;

38}

39//sleeping.c程序

40#include < stdio.h >

41#include < unistd.h >

42

43int main(int argc,char** argv)

44{

45int sec;

46

47sec =atoi(argv[1]);

48while (sec != 0) {

49sleep(1);

50printf("[New Child %d] I am running and my parent is %d\n", getpid(), getppid());

51sec--;

52}

53

54return 0;

55}

该程序使用了与进程有关的四个最基本的系统调用函数:fork(),exec(),wait()和exit()。对于该程序的具体分析如下:

1.父进程通过fork函数创建子进程,然后父进程打印自己的pid;

2.子进程首先打印自己的pid,再通过execve函数装入可执行程序sleeping,并通过execve函数向sleeping传递了一个参数“20”;

3.在sleeping程序中,将主函数中传递的参数作为计数器,并依次循环睡眠20s。并且循环打印当前的sleeping进程的pid以及其父进程pid;

4.由于父进程先于子进程退出,则sleeping进程成为孤儿进程,因此在sleeping打印的父进程pid为1;如果去掉程序中的注释部分,则父进程等待子进程退出后再退出,那么sleeping所打印的父进程pid即为父进程的pid;

5.如果子进程执行exec函数成功,则主程序中最后一条语句就不会被子进程执行。说明子进程在执行了exec函数后就与父进程完全脱离。另外,在子进程执行过程中也可以通过ps命令查看当前的进程存在情况;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值