在C中使用Exeve loader时如何终止子进程

在C中使用Exeve loader时如何终止子进程,c,linux,process,C,Linux,Process,我是C的初学者,我正在努力理解C中的execve函数调用子进程来加载和运行可执行对象文件 我们知道,execve只有在出现诸如无法找到文件名之类的错误时才会返回调用程序,因此它被调用一次,并且永远不会返回 这里是我的问题,如果我们分叉一个子进程来调用execve,但由于execve从未返回,如果一切正常,它将始终执行某些操作,这意味着子进程将永远不会终止,那么父进程如何收获这个子进程呢?下面是示例代码 if ((pid = Fork()) == 0) { /* Child runs user j

我是C的初学者,我正在努力理解C中的
execve
函数调用子进程来加载和运行可执行对象文件

我们知道,
execve
只有在出现诸如无法找到文件名之类的错误时才会返回调用程序,因此它被调用一次,并且永远不会返回

这里是我的问题,如果我们分叉一个子进程来调用
execve
,但由于
execve
从未返回,如果一切正常,它将始终执行某些操作,这意味着子进程将永远不会终止,那么父进程如何收获这个子进程呢?下面是示例代码

if ((pid = Fork()) == 0) { /* Child runs user job */
   if (execve(argv[0], argv, environ) < 0) {      -------->line 2
      printf("%s: Command not found.\n", argv[0]);
      exit(0);
   }
}

/* Parent waits for foreground job to terminate */
if (waitpid(pid, &status, 0) < 0) {  ------------> but the child process never terminated
   printf("waitpid error");
}
if((pid=Fork())==0){/*Child运行用户作业*/
if(execve(argv[0],argv,environ)<0){----------->第2行
printf(“%s:未找到命令。\n”,argv[0]);
出口(0);
}
}
/*父级等待前台作业终止*/
if(waitpid(pid,&status,0)<0){------>但子进程从未终止
printf(“waitpid错误”);
}
因此,在第2行中,
execve(argv[0],argv,environ)
从未返回,因此子进程从未终止?

来自以下手册页:

成功时,execve()不返回,错误时返回-1,然后 errno设置正确


要从子进程接收返回值,需要使用

在简单的情况下,无论您是否成功调用
exec
,子进程最终都将终止。所以这很容易…只要让父级
等待它使用
fork创建的每个子级

但是,有一种技术可以将故障从
exec
传播到父级。该技术的工作原理如下:

  • 创建一个管道
  • 叉子
  • 在子进程中,关闭管道的读取端。在exec上将写端标记为关闭
  • 在子对象中调用
    exec
    。如果失败,则向管道写入失败消息,然后退出
  • 在父进程中,关闭管道的写入端并从读取端读取
    如果
    exec
    成功,父进程将不读取任何数据,只获取EOF。如果失败,父级将读取错误消息

    您的程序
    foo
    ,将启动一些子进程来运行其他程序
    bar
    , 您希望它通过基本的系统调用
    fork
    execve

    让我们调用您的初始
    foo
    流程p1。(这代表一些人。)

    首先,你要打电话。 创建p1的子进程,该子进程正在运行另一个
    foo
    将该子进程称为p1.1

    p1.1正在运行
    foo
    。但您要运行
    。因此,在p1.1中,
    foo
    立即调用。 这将p1.1正在运行的
    foo
    实例替换为
    bar
    实例。然后你的 子进程p1.1正在运行
    bar
    ,如您所愿

    要清楚这一点:-

    execve(path/to/bar…
    不会在的新子进程中启动
    bar
    p1.1,让p1.1仍然运行
    foo的post fork实例。相反,
    execve(path/to/bar…)
    流程p1.1中的
    foo
    实例和
    bar
    实例。在
    fork
    之后,但在
    execve
    之前, 我们有:

    execve
    之后,我们有:

    p1[foo] -> p1.1[bar]
    
    不是:

    您可以看到,
    execve
    无法将success返回给其调用者p1.1[foo], 因为如果
    execve
    成功,那么p1.1[foo]就不再存在。 当然,
    execve
    不能将success返回给p1[foo],因为p1[foo]没有调用它

    因为execve永远不会返回,所以如果一切正常,它将始终执行某些操作

    否。
    execve
    将p1.1[foo]替换为p1.1[bar],并且不会返回,因为调用者不再存在。然后p1.1[bar]运行,直到终止

    p1.1[bar]迟早会以以下方式之一终止 任何程序终止:它将运行到正常的
    退出
    ,否则它将 被信号杀死,或者它可能会自动调用
    abort

    父进程(p1)如何收获子进程(p1.1)

    首先,没有必要。一旦p1[foo]启动了p1.1,它就可以, 如果这就是你想要的,那就忘掉p1.1,继续做其他的事情吧 如果有,最后退出。如果p1在p1.1之前终止,则p1.1 变成了一个。 一个孤儿的过程会在孩子出生后立即被父母采纳。所以 如果同时没有任何东西终止它,则在系统关闭时,
    init
    终止时将收获p1.1

    但很有可能,您不想抛弃孤儿,您确实希望
    foo
    知道孩子
    bar
    的退出状态。那么,, p1[foo]迟早要打电话学习p1.1 结束,然后采取相应行动

    同时,p1[foo]可能正在使用一些 形式。和/或p1[foo]可能 在p1.1[bar]仍未结束时,注意经过的时间。p1[foo]可能通过以下方式之一或其他方式决定 p1.1[bar]陷入困境,已经持续太久,决定将p1.1本身。 当p1.1被杀死时——不管是谁干的——或者是由它自己的意愿结束,
    wait/waitpid
    将把该信息返回给p1[foo],然后它就会被杀死 可能会自行退出,或继续做其他事情

    在您询问的评论中:

    我们能否将[
    execve
    ]设计为:如果子进程终止,则返回1

    这样的系统调用当然可以设计,并且已经存在,但它不能 是一个非阻塞系统调用,它取代了调用过程,这就是
    execve
    是这将是一个阻塞系统调用,运行调用的子进程 处理并返回子进程
    p1[foo] -> p1.1[bar]
    
    p1[foo] -> p1.1[foo] -> p1.1.1[bar]