exec和execve等exec系统调用家族的功能有何区别?

50

最近我一直在学习系统编程课程,遇到了系统调用exec()execve()。到目前为止,我无法找到这两者之间的任何区别,即使在维基百科上也没有明确的解释,那么exec()execve()之间是否有区别呢?

请问有人能简要描述一下execl()execv()execle()execvp()等exec家族系统调用吗?


3
维基百科不是UNIX系统调用信息的主要来源,而是手册页面:对于exec*()函数族,您可以在此处阅读:http://man7.org/linux/man-pages/man3/execl.3.html。 - alk
请阅读高级Linux编程 - Basile Starynkevitch
2
可能是在C++中使用exec的不同版本是什么?的重复问题。 - Ciro Santilli OurBigBook.com
7个回答

79

系统调用中没有exec,通常将所有execXX调用作为一组引用。它们的本质相同:将一个新程序加载到当前进程中,并为其提供参数和环境变量。差异在于如何找到程序,如何指定参数以及环境从哪里来。

  • 名称中带有v的调用采用数组参数来指定新程序的argv[]数组。末尾的参数由包含NULL的数组元素指示。

  • 名称中带有l的调用将新程序的参数作为函数本身的可变长度参数列表接收。参数的结尾由(char *)NULL参数指示。您应始终包括类型转换,因为NULL可以是整数常量,而在调用可变参数函数时的默认参数转换不会将其转换为指针。

  • 名称中带有e的调用采用额外的参数(或在l的情况下使用多个参数)来提供新程序的环境;否则,程序将继承当前进程的环境。这与argv数组相同:对于execve(),使用数组;对于execle(),使用单独的参数。

  • 名称中带有p的调用将搜索PATH环境变量以查找程序(如果它没有目录,即不包含/字符)。否则,程序名称始终被视为可执行文件的路径。

  • FreeBSD 5.2添加了另一种变体:execvP(大写P)。这类似于execvp(),但是它不是从PATH环境变量获取搜索路径,而是显式地作为函数参数提供:

int execvP(const char *file, const char *search_path, char *const argv[]);

以上系统调用之间唯一的区别在于参数。这是真的吗?如果是这样,所有 exec 系列系统调用的最终结果都是执行程序(但使用不同的参数)吗? - buddhi weerasinghe
8
实际上,唯一的 系统调用execve(2),而所有其他的exec*函数都是对它的封装。 - Basile Starynkevitch
我知道,但是这个区别并不重要,除非你是一个内核开发者。 - Barmar
谢谢。这些exec*函数的受欢迎程度有区别吗?我没有看到很多例子,但似乎execlp()(也许是execvp())最常用? - Tim

26

使用man exec并阅读:

The execv(), execvp(), and execvpe() functions provide an array of pointers to 
null-terminated strings that represent the argument list available to the new program. 
The first argument, by convention, should point to the filename associated with the file 
being executed. The array of pointers must be terminated by a NULL pointer. 

execv

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

你可以将数组作为参数传递。

int execle(const char *path, const char *arg,
              ..., char * const envp[]);

几乎相同,但不是作为数组,而是作为值(字符串)列表,然后是指定环境的数组。

这里:

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

您正在调用一个文件,没有路径,因此它期望您在调用之前已经处于正确的 路径 中。

最后但同样重要:

int execve(const char *filename, char *const argv[],
                  char *const envp[]);

与之前类似,但现在您有两个数组,用于参数和环境变量。


3
上述系统调用之间唯一的区别在于参数,是这样吗?如果是,那么所有exec家族系统调用的最终结果都是执行一个程序(使用不同的参数)吗? - buddhi weerasinghe
3
只是不同的参数,别的都没有变化。 - Noam Rathaus

18

由于所有这些函数都属于 exec() 系列,让我根据附加字符将它们的含义进行区分:

1. execve():

p:不存在 => 将从 pathname 中获取要运行的程序的名称

v:存在 => 参数将作为 array 传递

e:存在 => 环境将从 envp argument 中获取

2. execle():

p:不存在 => 将从 pathname 中获取要运行的程序的名称

l:存在 => 参数将作为 list 传递

e:存在 => 环境将从 envp argument 中获取

3. execlp():

p:存在 => 将从指定的 filename 或系统将在 PATH 变量中搜索程序文件以获取要运行的程序的名称。

l:存在 => 参数将作为 list 传递

e:不存在 => 环境将从 caller's environ 中获取

4. execvp():

p:存在 => 将从指定的 filename 或系统将在 PATH 变量中搜索程序文件以获取要运行的程序的名称。

v:存在 => 参数将作为 array 传递

e:不存在 => 环境将从 caller's environ 中获取

5. execv():

p:不存在 => 将从 pathname 中获取要运行的程序的名称

v:存在 => 参数将作为 array 传递

e : not present => 环境变量将从调用者的环境变量获取

6.execl():

p : not present => 要运行的程序名称将从路径名中获取

l : present => 参数将作为列表传递

e : not present => 环境变量将从调用者的环境变量获取


4

主要思路

exec()函数族用新的进程镜像替换现有进程镜像。这与fork()系统调用明显不同,后者会使得父进程和子进程在内存中共存。

exec()函数族

int execv (const char *filename, char *const argv[])

文件名是新进程映像的文件。

argv是一个以空字符结尾的字符串数组。该数组的最后一个元素必须为null指针。

int execl (const char *filename, const char *arg0, …)

与execv相同,但参数以单个字符串(由逗号分隔)提供,而不是数组/向量。

int execve (const char *filename, char *const argv[], char *const env[])

与execv相同,但它允许为新的进程映像指定环境变量。
int execle (const char *filename, const char *arg0, …, char *const env[])

与execl相同,但它允许指定新进程镜像的环境变量。
int execvp (const char *filename, char *const argv[])

与execv函数相同,但如果文件名不含斜杠,则会搜索标准环境变量PATH以查找该文件名。
以下是标准环境变量列表: https://www.gnu.org/software/libc/manual/html_node/Standard-Environment.html#Standard-Environment
int execlp (const char *filename, const char *arg0, …)

这个功能和Excel函数相同,唯一不同的是它会像execvp函数一样执行文件名搜索。

注意

在Linux系统中,如果您在Shell或终端上键入envprintenv,您将获得标准环境变量列表。


3
这些函数有不同的参数要求。
  • 函数execl,execlp和execle需要将新程序的每个命令行参数作为单独的参数来指定。

  • 对于execv、execvp和execve函数,我们必须构建一个指向参数的指针数组,并且该数组的地址是这三个函数的参数。

  • execve和execle函数允许我们传递指向环境字符串指针数组的指针。另外四个函数使用调用进程中的environ变量将现有环境复制到程序中。

  • 字母p表示该函数需要一个文件名参数,并使用PATH环境变量查找可执行文件。
  • 字母l表示该函数需要一个参数列表,并且与字母v互斥,后者表示它使用argv[]向量。
  • 字母e表示该函数需要一个envp[]数组,而不是使用当前环境。

  • 新程序从调用进程继承以下其他特性。

    Process ID and the Parent Process ID
    Real user ID and Real Group ID
    Supplementary group IDs
    Process group ID
    Session ID
    Controlling terminal
    Time left until alarm clock
    Current working directory
    Root directory
    File mode creation mask
    File locks
    Process signal mask
    Pending signals
    Resource limits
    Values for tms_utime, tms_stime, tms_cutime, and tms_cstime.
  • 在执行过程中,真实用户 ID 和真实组 ID 保持不变,但有效 ID 可能会发生变化,这取决于所执行程序文件的设置用户 ID 和设置组 ID 位的状态。

2
为了回答您问题的第一部分,特别是在Linux环境下,只有一个系统调用,即execve(而不是exec)。所谓“exec家族”的其余部分(execlexecleexecvexecveexecvp等)都是GLIBC对内核系统调用execve的包装器。请注意,保留HTML标签。

0

在exec家族中,有一些函数在其功能和调用方式方面略有不同:

  1. 函数名称中包含字母p的函数(如execvpexeclp)接受一个程序名,并在当前执行路径中搜索该名称的程序;不包含字母p的函数必须提供要执行的程序的完整路径。

  2. 函数名称中包含字母v的函数(如execvexecvpexecve)将新程序的参数列表作为以NULL结尾的字符串指针数组接受。函数名称中包含字母l的函数(如execlexeclpexecle)使用C语言的varargs机制接受参数列表。

  3. 函数名称中包含字母e的函数(如execveexecle)接受一个附加参数,即环境变量数组。该参数应为以NULL结尾的字符串指针数组,每个字符字符串都应为VARIABLE=value形式。

源代码


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接