如何通过子进程取消警报()信号?
Posted
技术标签:
【中文标题】如何通过子进程取消警报()信号?【英文标题】:How to cancel an alarm() signal via a child process? 【发布时间】:2012-04-19 23:45:09 【问题描述】:对于一项任务,我正在创建一个时间感知外壳。如果命令运行超过设定的时间,shell 会分叉并执行命令并杀死它们。例如。
input# /bin/ls
a.out code.c
input# /bin/cat
Error - Expired After 10 Seconds.
input#
现在,我的问题是:如果在程序的处理过程中发生错误,即exevce返回-1时,有没有办法防止警报启动?
由于子进程是单独运行的,经过数小时的试验和研究,我还没有找到任何讨论甚至暗示这类任务的东西,我觉得这可能是不可能的。如果确实不可能,我该如何防止发生类似以下的事情......
input# /bin/fgdsfgs
Error executing program
input# Error - Expired After 10 Seconds.
对于上下文,这是我目前正在使用的代码,我自己尝试这样做的尝试已被删除。提前感谢您的帮助!
while(1)
write(1, prompt, sizeof(prompt)); //Prompt user
byteCount = read(0, cmd, 1024); //Retrieve command from user, and count bytes
cmd[byteCount-1] = '\0'; //Prepare command for execution
//Create Thread
child = fork();
if(child == -1)
write(2, error_fork, sizeof(error_fork));
if(child == 0) //Working in child
if(-1 == execve(cmd,arg,env)) //Execute program or error
write(2, error_exe, sizeof(error_exe));
else if(child != 0) //Working in the parent
signal(SIGALRM, handler); //Handle the alarm when it goes off
alarm(time);
wait();
alarm(0);
【问题讨论】:
【参考方案1】:根据man page:
说明
alarm() 函数应使系统在经过 seconds 指定的实时秒数后为进程生成 SIGALRM 信号。处理器调度延迟可能会阻止进程在信号生成后立即对其进行处理。
如果秒为 0,则取消挂起的警报请求(如果有)。
警报请求不堆叠;以这种方式只能调度一个 SIGALRM 生成。如果尚未生成 SIGALRM 信号,则调用将导致重新安排生成 SIGALRM 信号的时间。
alarm() 与 setitimer()、ualarm() 或 usleep() 中的任何一个之间的交互是未指定的。
所以,要取消警报:alarm(0)
。它甚至出现在您的示例代码中。
主要问题
顺便说一句,你在这里错过了一个重要的部分:
if(child == 0) //Working in child
if(-1 == execve(cmd,arg,env)) //Execute program or error
write(2, error_exe, sizeof(error_exe));
_exit(EXIT_FAILURE); // EXIT OR A FORKED SHELL WILL KEEP GOING
else if(child != 0) //Working in the parent
【讨论】:
对了,我代码中的alarm(0)成功取消了子进程执行成功时的报警;因此,当在 x 秒计时器到期之前执行命令时,警报不会响起。不幸的是,这不是进程无法执行时发生的行为。 编辑:感谢第二个提示! 我在回答后才意识到。但是当execve()
失败时缺少exit()
似乎是暗示。
如果execve
失败,您必须_exit
而不是exit
。后者通常是不安全的。
@R..:好吧,如果没有任何与atexit()
挂钩的操作,那么它们之间的区别是微妙的。但无论如何我都会编辑答案,因为正如你所说,在这种情况下它是一个更好的选择(+1 为你)。【参考方案2】:
wait()
系统调用带有一个参数;为什么不使用源文件中的正确标头和未声明函数的编译器警告(最好是错误)进行编译?或者,如果您收到此类警告,请在提交代码以供在 *** 等地方进行审核之前注意它们。
您不需要测试来自execve()
(或任何exec*()
函数)的返回状态;如果返回,则失败。
在失败时写一个错误是好的。如果子进程也退出会更好,这样它就不会回到while (1)
循环,与你的主shell竞争输入数据。
if (child == 0)
execve(cmd, arg, env);
write(2, error_exe, sizeof(error_exe));
exit((errno == ENOEXEC) ? 126 : 127);
事实上,孩子不出门是造成问题的主要原因;由于孩子尚未退出,因此等待不会返回,直到警报响起。显示的退出状态旨在匹配 POSIX shell 规范。
【讨论】:
以上是关于如何通过子进程取消警报()信号?的主要内容,如果未能解决你的问题,请参考以下文章