请解释exec()函数及其系列

这个exec()函数和它的家族是什么? 为什么使用这个函数,它是如何工作的?

请任何人解释这些function。

简单地说,在UNIX中,你有过程和程序的概念。 一个进程是一个程序执行的东西。

UNIX“执行模式”背后的简单理念是有两个操作你可以做。

第一个是fork() ,它创build一个包含当前程序副本(包括其状态)的全新进程。 允许他们弄清楚哪个是父母,哪个是孩子的过程之间有一些区别。

第二个是exec() ,它用一个全新的程序replace当前进程中的程序。

从这两个简单的操作中,可以构build整个UNIX执行模型。


在上面添加更多的细节:

fork()exec()体现了UNIX的精神,因为它提供了一个非常简单的方法来启动新的进程。

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

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

因为这两个进程现在运行完全相同的代码,所以他们需要能够判断哪个是哪个 – fork()的返回代码提供了这个信息 – subprocess得到0,父进程得到subprocess的PID(如果fork()失败,没有创buildsubprocess,父进程得到一个错误代码)。 这样,父节点知道孩子的PID,并且可以与它通信,杀死它,等待它等等(孩子总是可以通过调用getppid()来find父进程)。

exec()调用用一个新程序replace整个过程的当前内容。 它将程序加载到当前进程空间并从入口点运行它。

所以, fork()exec()通常被用来依次获得一个新的程序作为当前进程的subprocess运行。 当你试图运行程序如find – shell fork时,Shell通常会这样做,然后子程序将find程序加载到内存中,设置所有命令行参数,标准I / O等等。

但是他们不需要一起使用。 例如,如果程序同时包含父代码和子代码(你需要小心你所做的事情,每个实现可能有限制),那么程序调用fork()是完全可以接受的。 守护进程只使用TCP端口侦听,并且在父节点返回侦听的同时,分发自己的副本以处理特定的请求,这已经被使用了很多(现在仍然是这样)。 对于这种情况,该程序包含父代码子代码。

类似的,程序知道他们已经完成,只是想运行另一个程序,不需要fork()exec() ,然后wait()/waitpid() 。 他们可以直接使用exec()将subprocess加载到当前进程空间中。

一些UNIX实现有一个优化的fork() ,它使用了他们所谓的copy-on-write。 这是延迟fork()进程空间复制的一个技巧,直到程序试图改变该空间中的某些东西。 这对那些只使用fork()而不使用exec()程序很有用,因为它们不需要复制整个进程空间。 在Linux下, fork()只是创build一个页表和一个新的任务结构的副本, exec()将做“分离”两个进程的内存的繁重工作。

如果在fork调用exec (主要是这种情况),那么会导致写入进程空间,然后将其复制到subprocess。

Linux也有一个vfork() ,甚至更优化,它分享两个进程之间的一切 。 因此,孩子可以做什么有一定的限制,并且在孩子调用exec()_exit()之前,父母会暂停。

由于两个进程共享同一个堆栈,因此必须停止父进程(并且不允许subprocess从当前函数返回)。 这对fork()紧跟在exec()之后的经典用例稍微有效一些。

请注意, exec调用( execlexecleexecve等)的整个系列,但在这里上下文的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 

exec系列的函数使得你的进程执行一个不同的程序,取代它正在运行的旧程序。 也就是说,如果你打电话

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

那么ls程序就是用进程id,当前工作目录和调用execl的进程的用户/组(访问权限)执行的。 之后,原来的程序不再运行。

要开始一个新的过程,使用fork系统调用。 要执行一个程序而不replace原来的,你需要fork ,然后exec

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

  • l:参数作为string列表传递给main()
  • v:参数作为string数组传递给main()
  • p:path/ s来search新的正在运行的程序
  • e:环境可以由调用者指定

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

  • int execl(const char * path,const char * arg,…);
  • int execlp(const char *文件,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 

exec经常和fork一起使用,我也看到你也问过,所以我会在脑子里讨论这个。

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

在你的程序和exec发生这种情况的方式是,操作系统内核检查你传递给程序参数(第一个参数)的文件是否可以被当前用户(用户ID exec调用的进程),如果是,则用当前进程的虚拟内存replace新进程的虚拟内存映射,并将在exec调用中传递的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。

执行function及其家族是什么?

exec函数族是所有执行文件的函数,如execlexeclpexecleexecvexecvp 。它们都是execve前端,并提供了不同的调用方法。

为什么使用这个function

执行(启动)文件(程序)时使用Exec函数。

它是如何工作的

他们通过覆盖当前过程映像的工作而启动。 他们用已经启动的新进程replace(通过结束)当前正在运行的进程(称为exec命令的进程)。

有关更多详情, 请参阅此链接 。

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