C源代码(已编译并运行Linux Centos 6.3)具有以下行:
execve(cmd, argv, envp);
execve
没有返回,但我想修改代码以了解它何时完成。所以我这样做:
if (child = fork()) {
waitpid(child, NULL, 0);
/*now I know execve is finished*/
exit(0);
}
execve(cmd, argv, envp);
当我这样做时,生成的程序可以在99%的时间内工作,但很少会出现奇怪的错误。
以上是否有问题?我希望上面的代码能像以前一样精确地运行(除了慢一点)。我是对的吗?
如果您想知道背景,修改后的代码为dash
。在execve
找出要运行的字符串之后,dash
调用用于运行简单命令。当我如上所述精确修改(在等待之后甚至没有运行任何东西)并在修改的破折号下重新编译和运行程序时,大多数时候它们运行正常。但是,重新编译一个特定的内核模块称为" biosutility"给我这个错误
cc1: error: unrecognized command line option "-mfentry"
答案 0 :(得分:1)
这是一种可能性。
实际上, dash
确实需要知道子进程何时终止。它必须收割孩子(通过wait
)以避免用僵尸填充进程表,并且无论如何它关心进程的退出状态。
现在,它知道它启动的进程的PID是什么,并且当它执行wait
时可以使用它来确定哪个进程终止,从而如何处理退出状态。
但是你正在做额外的fork
。所以dash
认为它开始了一些PID过程,比如368.但是你fork
一个新的孩子,比如说PID 723.然后你wait
为那个孩子,但是你忽略了状态代码。最后,您的流程已成功终止。那么dash
注意到进程368成功终止。即使它没有。
现在假设dash
实际上正在执行像
do_something && do_something_else
程序员指定如果do_something_else
失败,shell肯定不应该do_something
。可怕的事情可能发生。或者至少是神秘的东西。然而,你隐藏了这种失败。所以dash
高兴地点燃do_something_else
。 Etvoilà
嗯,这只是一个理论。我真的不知道,但它显示了可能发生的事情。
底线是dash
有一些机制可以让它知道子进程何时完成,如果你想挂钩子进程的退出处理,你最好搞清楚该机制如何工作,以便您可以挂钩。试图添加自己的附加机制几乎肯定会以泪流满面。
答案 1 :(得分:1)
仔细阅读 <{3}}以及execve(2)和fork(2)的文档。 execve
和&amp; fork
非常棘手, waitpid(2)难以理解。我强烈建议您阅读fork(网上免费提供,但您可以购买纸质书籍),这些有几章。
(不要害怕花几天阅读并理解这些系统调用,他们 棘手)
一些要点。
每个 Advanced Linux Programming 都可能失败,您总是处理其失败,至少通过system call显示一些错误消息并立即显示perror(3) - 。
exit(3) 系统调用通常永远不会返回,因为在失败时只返回 (成功时,它不会返回,因为调用程序已经被替换掉了!)因此大多数调用它(以及类似的execve(2)函数)通常都是这样的:
if (execve(cmd, argv, envp)) { perror (cmd); exit(127); };
/* else branch cannot be reached! */
习惯上在execve
失败时使用类似127的奇怪退出代码(通常未使用,除了上面这样),并且通常你不能做任何其他事情。
在fork
使用(几乎总是)时,您通常会在子进程中调用execve
。
exec(3)系统调用成功返回两次 (一次在父进程中,一次在子进程中)。这很难理解,阅读我给出的参考资料。它仅在失败时返回一次。因此,始终保持fork
的结果,因此典型代码为:
pid_t pid = fork ();
if (pid<0) { // fork has failed
perror("fork"); exit(EXIT_FAILURE);
}
else if (pid==0) { // successful fork in the child process
// very often you call execve in child, so you don't continue here.
// example code:
if (execve(cmd, argv, envp)) { perror (cmd); exit(127); };
// not reached!
};
// here pid is positive, we are in the parent and fork succeeded....
/// do something sensible, at some point you need to call waitpid and use pid
建议:在某些程序中使用fork(2),或者尝试strace -f bash -c 'date; pwd'
并研究输出。它提到了很多strace(1) ....
您的示例代码可能(有时)只需添加一些else
// better code, but still wrong because of unhandled failures....
if ((child = fork())>0) {
waitpid(child, NULL, 0);
/*now I know execve is finished*/
exit(0);
}
/// missing handling of `fork` failure!
else if (!child) {
execve(cmd, argv, envp);
/// missing handling of `execve` failure
}
但该代码仍然不正确,因为未处理失败。
答案 2 :(得分:0)
根据Rici的优秀评论和答案,我找到了问题的根本原因。
原始代码以退出的cmd
退出。我改变了,总是以0退出。这就是代码行为不同的原因。
以下修复程序未显示错误:
int status;
if (child = fork()) {
waitpid(child, &status, 0);
/*now we know execve is finished*/
if (WIFEXITED(status))
exit(WEXITSTATUS(status));
exit(1);
}
execve(cmd, argv, envp);
答案 3 :(得分:-1)
对于这个问题:“上面有什么问题吗?”
关于此代码:
if (child = fork()) {
waitpid(child, NULL, 0);
/*now I know execve is finished*/
exit(0);
}
execve(cmd, argv, envp);
fork()
函数有三种返回值:
-1
表示发生了错误=0
表示fork()
成功,子进程正在运行>0
表示fork()
成功,父进程正在运行execvp()
的调用(对于罕见的呼叫失败的情况)
perror( "execvp failed" );
exit( EXIT_FAILURE );
fork()
的调用会返回pid_t
。 调用之后,代码需要类似于:(使用child
作为pid变量)
if( 0 > child )
{
perror( "fork failed");
exit( EXIT_FAILURE );
}
else if( 0 == child )
{ // then child process
execve(cmd, argv, envp);
perror( "execvp failed" );
exit( EXIT_FAILURE );
}
//else
//{ // else parent process
waitpid(child, NULL, 0);
exit( EXIT_SUCCESS );
关于第二个问题,关于错误消息:
cc1: error: unrecognized command line option "-mfentry"
单词:unrecognized
拼写错误,因此这不是实际的错误消息。
此错误消息与您对dash
所做更改的问题无关。
但是,dash
不会直接调用任何编译操作,所以我怀疑这些问题完全不相关。
建议查看biosutility
实用程序的makefile,了解为什么将无效参数传递给cc1
。