如何通过子进程取消警报()信号?

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 规范。

【讨论】:

以上是关于如何通过子进程取消警报()信号?的主要内容,如果未能解决你的问题,请参考以下文章

如何通过子进程之间的共享内存共享信号量?

使用 c 安排警报

php如何把自身进程设置为系统进程

fork 和 signal:如何将信号从父进程发送到特定的子进程

使用警报让父进程等待子进程几秒钟

golang父进程通过管道向子进程传递数据