c - 请解释exec()函数及其族

什么是exec()功能及其家庭? 为什么使用此功能以及它如何工作?

请任何人解释这些功能。

user507401 asked 2019-09-08T17:28:15Z
6个解决方案
221 votes

简单地说,在UNIX中,您拥有流程和程序的概念。 进程是程序执行的过程。

UNIX“执行模型”背后的简单想法是你可以做两个操作。

第一个是fork/exec,它创建了一个包含当前程序副本的全新流程,包括其状态。 这些过程之间存在一些差异,使他们能够找出哪个是父母,哪个是孩子。

第二个是fork/exec,它用一个全新的程序取代当前流程中的程序。

通过这两个简单的操作,可以构建整个UNIX执行模型。


要为上面添加更多细节:

fork/execbash的使用体现了UNIX的精神,它提供了一种非常简单的方法来启动新进程。

fork/exec调用几乎与当前进程重复,几乎在所有方面都相同(并非所有内容都被复制,例如,某些实现中的资源限制,但想法是尽可能创建尽可能接近的副本)。 一个进程调用bash,而两个进程从它返回 - 听起来奇怪但它真的很优雅

新进程(称为子进程)获取不同的进程ID(PID),并将旧进程的PID(父进程)作为其父PID(PPID)。

因为这两个进程现在运行完全相同的代码,所以他们需要能够分辨哪个是哪个 - fork/exec的返回代码提供了这个信息 - 子得到0,父得到孩子的PID(如果bash失败) ,没有创建子项,父项获取错误代码)。 这样,父母知道孩子的PID,并且可以与之通信,杀死它,等待它等等(孩子总是可以通过调用ls找到其父进程)。

fork/exec调用使用新程序替换进程的整个当前内容。 它将程序加载到当前进程空间并从入口点运行它。

因此,通常依次使用fork/execbash来获得作为当前进程的子进程运行的新程序。 每当你尝试运行像ls这样的程序时,shell通常会这样做 - shell分叉,然后子程序将execve程序加载到内存中,设置所有命令行参数,标准I / O等等。

但它们不需要一起使用。 如果程序包含父代码和子代码(例如,您需要小心操作,每个实现可能都有限制),那么程序在没有以下bash的情况下调用fork/exec是完全可以接受的。 这对于守护进程使用了很多(现在仍然如此),它们只是在TCP端口上侦听并分叉自己的副本来处理特定请求,而父进程则回去监听。 对于这种情况,程序包含父代码和子代码。

同样地,知道他们已经完成并且只想运行另一个程序的程序不需要fork/exec,bashls这个孩子。 他们可以通过execve将孩子直接加载到他们当前的进程空间。

某些UNIX实现具有优化的fork/exec,它使用了他们称之为copy-on-write的方法。 这是在bash中延迟复制进程空间的技巧,直到程序尝试更改该空间中的某些内容。 这对于仅使用ls而不是execve的程序非常有用,因为它们不必复制整个进程空间。 在Linux下,exec只复制页面表和一个新的任务结构,exec()将执行“分离”两个进程的内存的繁琐工作。

如果在bash之后调用fork/exec(这是主要发生的情况),则会导致写入进程空间,然后将其复制到子进程。

Linux还有一个fork/exec,甚至更优化,几乎可以分享两个进程之间的所有内容。 因此,孩子可以做什么有一些限制,父母会停止,直到孩子打电话给bashls

必须停止父级(并且不允许子级从当前函数返回),因为这两个进程甚至共享相同的堆栈。 这对fork/exec的经典用例稍有效率,紧接着是bash

请注意,有一个完整的fork/exec电话(bash,lsexecve等)系列,但这里的exec意味着它们中的任何一个。

下图说明了典型的fork/exec操作,其中bash shell用于列出具有ls命令的目录:

+--------+
| pid=7  |
| ppid=4 |
| bash   |
+--------+
    |
    | calls fork
    V
+--------+             +--------+
| pid=7  |    forks    | pid=22 |
| ppid=4 | ----------> | ppid=7 |
| bash   |             | bash   |
+--------+             +--------+
    |                      |
    | waits for pid 22     | calls exec to run ls
    |                      V
    |                  +--------+
    |                  | pid=22 |
    |                  | ppid=7 |
    |                  | ls     |
    V                  +--------+
+--------+                 |
| pid=7  |                 | exits
| ppid=4 | <---------------+
| bash   |
+--------+
    |
    | continues
    V
paxdiablo answered 2019-09-08T17:30:35Z
27 votes

exec()系列中的函数具有不同的行为:

  • l:参数作为字符串列表传递给main()
  • v:参数作为字符串数组传递给main()
  • p:搜索新运行程序的路径
  • e:可以由呼叫者指定环境

你可以混合它们,因此你有:

  • 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(3)手册页:

man 3 exec  # if you are running a UNIX system
T04435 answered 2019-09-08T17:32:25Z
15 votes

fork系列函数使您的进程执行不同的程序,替换它正在运行的旧程序。 即,如果你打电话

execl("/bin/ls", "ls", NULL);

然后fork程序使用进程id,当前工作目录和调用fork的进程的用户/组(访问权限)执行。之后,原始程序不再运行。

要启动新进程,请使用fork系统调用。 要在不更换原件的情况下执行程序,需要fork,然后exec

Fred Foo answered 2019-09-08T17:33:04Z
6 votes

exec经常与FILE *一起使用,我看到你也问过这个问题,所以我会考虑到这一点。

FILE *将当前进程转换为另一个程序。 如果你曾经看过神秘博士,那就像他再生一样 - 他的旧身体被一个新的身体所取代。

你的程序和FILE *发生这种情况的方式是,操作系统内核检查的许多资源是否可以由当前用户(用户ID)执行,以查看您作为程序参数(第一个参数)传递给int的文件。 进行exec调用的过程)如果是,它将当前进程的虚拟内存映射替换为新进程的虚拟内存,并将2517023627527519493调用中传递的argvenvp数据复制到此新虚拟内存映射的区域中。 此处还可能发生其他一些事情,但为程序打开的调用exec的文件仍将为新程序打开,并且它们将共享相同的进程ID,但调用exec的程序将停止(除非exec失败)。

这样做的原因是通过将新程序分成两个步骤,你可以在这两个步骤之间做一些事情。 最常见的做法是确保新程序将某些文件作为特定文件描述符打开。 (请记住,文件描述符与FILE *不同,但是内核知道的值为int)。 这样做你可以:

int X = open("./output_file.txt", O_WRONLY);

pid_t fk = fork();
if (!fk) { /* in child */
    dup2(X, 1); /* fd 1 is standard output,
                   so this makes standard out refer to the same file as X  */
    close(X);

    /* I'm using execl here rather than exec because
       it's easier to type the arguments. */
    execl("/bin/echo", "/bin/echo", "hello world");
    _exit(127); /* should not get here */
} else if (fk == -1) {
    /* An error happened and you should do something about it. */
    perror("fork"); /* print an error message */
}
close(X); /* The parent doesn't need this anymore */

这完成了运行:

/bin/echo "hello world" > ./output_file.txt

从命令shell。

nategoose answered 2019-09-08T17:34:06Z
4 votes

什么是exec功能及其家族。

exec函数系列是用于执行文件的所有函数,例如execl,execlp,execle,execvexecvp。它们都是execve的前端,并提供了不同的调用方法。

为什么使用这个功能

当您要执行(启动)文件(程序)时,将使用Exec函数。

它是如何工作的。

它们通过使用您启动的过程映像覆盖当前过程映像来工作。 它们用已启动的新进程替换(通过结束)当前正在运行的进程(调用exec命令的进程)。

有关详细信息:请参阅此链接。

Reese Moore answered 2019-09-08T17:35:20Z
3 votes

exec(3,3p)函数将当前进程替换为另一个进程。 也就是说,当前进程停止,而另一个进程运行,接管原始程序拥有的一些资源。

Ignacio Vazquez-Abrams answered 2019-09-08T17:35:50Z
translate from https://stackoverflow.com:/questions/4204915/please-explain-the-exec-function-and-its-family