分叉和Execlp

7

我正在尝试使用fork和execlp编写一个程序,其中父进程的地址空间被"ls"命令替换。

#include<stdio.h>
main()
{
    int pid,j=10,fd;
    pid=fork();
    if(pid==0)
    {
        printf("\nI am the child\n");
        execlp("/bin/ls","ls",NULL);
        printf("\nStill I am the child\n");

    }
    else if (pid > 0)
    {
        printf("\n I am the parent\n");
        wait();
    } 
}

当我执行程序时,子进程的最后一行是:
printf("\nStill I am the child\n");

is not printed. Why?

5个回答

16

exec系列函数在成功之后不会返回。

http://pubs.opengroup.org/onlinepubs/009604499/functions/exec.html

exec系列函数将把当前进程镜像替换为一个新的进程镜像。新的进程镜像将从一个被称为新进程镜像文件的常规可执行文件构造而成。由于新进程镜像覆盖了调用进程镜像,因此成功执行exec后不会有返回值。

如果exec函数之一返回到调用进程图像,则表示出现错误;返回值应为-1,并设置errno以指示错误。


4
exec函数不仅仅会执行您的命令,它们实际上会将进程的执行上下文替换为您选择的可执行文件(在本例中为/bin/ls)。
换句话说,由于ls函数通过终止其进程(通过“exit”或返回主函数或其他方式)来结束,因此在ls执行结束时,您的子进程将被终止。
您实际上可以使用此printf调用打印一些错误,例如:
 if(pid==0)
    {
        printf("\nI am the child\n");
        execlp("/bin/ls","ls",NULL);
        printf("\nError: Could not execute function %s\n", "/bin/ls");
        _exit(0); //make sure you kill your process, it won't disappear by itself. 
    }

2
原因很简单:只有在发生错误时, exec() 函数才会返回。关于相同的内容,请参考 exec() 函数的 man 页面。
当调用 exec() 函数时,究竟发生了什么:
execl() 不会创建新进程 - 它修改 VADS 和相关内容 - 此外,执行上下文也被修改。
旧的执行上下文不再使用 - 创建一个新的执行上下文。
为新加载的应用程序创建一个全新的上下文,并将控制传递给调度程序 - 调度程序使用新可用的执行上下文恢复相同的子进程 - 使用此方法,在用户空间中向新应用程序的入口点执行跳转 - 新应用程序在相同的子进程中开始执行。
系统堆栈被新的硬件上下文覆盖,以便在用户空间中恢复新程序的 main()。
子进程中旧应用程序的执行上下文和代码/数据/堆/栈完全被销毁 - 不再可用。
只有当 execve() 或 execl() 无法在当前进程中加载新应用程序时,execve() 或 execl() 才会返回到当前进程/代码 - 这意味着,只有当完成 execv()/execl()/family of calls 时出现错误时,execv()/execvl() 或其系列调用才会返回。
注意:必须验证 exec() 系统调用 API 的返回值是否存在错误/错误代码 - 根据错误/错误代码,您可以终止当前进程或采取其他操作。

0

根据execlp的文档,函数execlp()之后不会被执行,因此您的printf()语句“Still I'm the child”也不会被执行...!!


-3
  1. 你正在使用 int 类型来存储进程 ID,但实际上,应该使用 pid_t 来存储进程 ID。
  2. 当你使用 exec 系列函数时,被调用进程的整个地址空间会替换调用进程。因此,现在新进程中没有最后一个 printf 语句,实际上甚至进程的 ID 也没有改变。

有一些好的观点,但是新进程中的pid肯定不同。此外,exec只有在成功时才替换进程,否则它将继续执行exec调用后的程序。 - techdude

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