问题 外部禁用Linux程序的信号


在Linux上,是否有可能以某种方式禁用程序的信令 外部......也就是说,没有修改他们的源代码?

语境:

我叫C(还有一个Java)来自Linux上的bash脚本中的程序。我不希望我的bash脚本和脚本启动的其他程序(作为前台进程)中断。

虽然我可以用...

trap '' INT

...在我的bash脚本中禁用Ctrl C信号,这仅在程序控件恰好在bash代码中时才有效。也就是说,如果我在C程序运行时按下Ctrl C,C程序会被中断并退出!这个C程序正在进行一些关键操作,因此我不希望它被中断。我无法访问此C程序的源代码,因此C程序内的信号处理是不可能的。

#!/bin/bash

trap 'echo You pressed Ctrl C' INT 

# A C program to emulate a real-world, long-running program,
# which I don't want to be interrupted, and for which I 
# don't have the source code!
#
# File: y.c
# To build: gcc -o y y.c
#
# #include <stdio.h>
# int main(int argc, char *argv[]) {
#  printf("Performing a critical operation...\n");
#    for(;;); // Do nothing forever.
#  printf("Performing a critical operation... done.\n");
# }

./y

问候,

/ HS


2396
2017-12-23 01:57


起源

你能在最后使用&吗?所以你把编程发送到后台。例如猫文件& - Eric Fortis
您还应该意识到,通过按ctrl-c而无法停止程序的用户通常会有一种令人讨厌的倾向,以获得残酷并开始使用kill命令。因此,尽管你付出了努力,但要确保你处理掉整件事的可能性。 - Michael Kohne
@Eric Fortis我需要按顺序执行bash脚本中的步骤,因此无法在后台发送临时步骤。起初,我跳过你的建议......想,是的,为什么不把它发送到后台然后打电话给'等待'内置。但是&+ wait似乎没有帮助:无限循环C程序确实设法被中断,并且bash脚本也很快退出。 - Harry
@Michael Kohne这是一个很好的建议。我认为这个程序的观众将包括那些知道'杀戮意味着坏'但不一定意识到'Ctrl C在某些情况下也可能不好'的用户。虽然我将记录'no-Ctrl-C或其他 - 后果!'条款,但我想为什么不尝试通过陷阱和友情信息来阻止它。但如果这一切都转过来如果不可能,那么我可能只会依赖文档。 - Harry


答案:


过程信号掩码是继承的 exec,所以你可以简单地编写一个阻塞的小包装器程序 SIGINT 并执行目标:

#include <signal.h>
#include <unistd.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
        sigset_t sigs;

        sigemptyset(&sigs);
        sigaddset(&sigs, SIGINT);
        sigprocmask(SIG_BLOCK, &sigs, 0);

        if (argc > 1) {
                execvp(argv[1], argv + 1);
                perror("execv");
        } else {
                fprintf(stderr, "Usage: %s <command> [args...]\n", argv[0]);
        }
        return 1;
}

如果你编译这个程序 noint,你只需要执行 ./noint ./y

作为注释中的流行音符,信号处理也是继承的,因此您可以让包装器忽略信号而不是阻塞信号:

#include <signal.h>
#include <unistd.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
        struct sigaction sa = { 0 };

        sa.sa_handler = SIG_IGN;
        sigaction(SIGINT, &sa, 0);

        if (argc > 1) {
                execvp(argv[1], argv + 1);
                perror("execv");
        } else {
                fprintf(stderr, "Usage: %s <command> [args...]\n", argv[0]);
        }
        return 1;
}

(当然,对于腰带和牙套的方法,你可以做到这两点)。


11
2017-12-23 02:58



+1:我赞成你的回答,但有一个问题。如果孩子明确清除了自己的过程信号掩码怎么办?这不会使信号屏蔽父进程集吗? - sashang
忽略信号比阻止它更有意义: sigaction(SIGINT, &(struct sigaction){.sa_handler = SIG_IGN}, NULL);  这就是shell的作用 SIGTTOU 毕竟,对于它的孩子们。 - ephemient
@caf男孩,是一个地狱的答案。它似乎工作。在我进一步评估时,给你+1(特别是根据ephemient的评论)。只是fyi,我确实试着想一些基于包装器的解决方案,但是,我在考虑在内部进行系统(“...”)而不是execvp,因此无法使其工作。非常感谢。 - Harry
要停止子进程更改自己的信号掩码,可以使用LD_PRELOAD库并输入自己的信号函数来拦截标准库中的函数。 - camh
@Vlad: nohup 只是忽略了 SIGHUP不是 SIGINT。 - ephemient


“trap”命令是此过程的本地命令,从不适用于子进程。

要真正捕获信号,你必须使用a来破解它 LD_PRELOAD 钩。这是非繁琐的任务(你必须编译一个可加载的 _init()sigaction() 在里面),所以我不会在这里包含完整的代码。您可以在上面找到SIGSEGV的示例 Phack Volume 0x0b,Issue 0x3a,Phile#0x03

另外,试试吧 nohup 和 tail 招。

nohup  your_command &
tail -F nohup.out

2
2017-12-23 02:12



感谢你(清楚地)告诉我关于过程本地的陷阱,所以+1。但是咖啡馆的解决方案确实是我想要的。谢谢。 - Harry
@Harry,不用担心。你应该接受Caf的答案是正确的,以便其他人可以更容易地找到信息:) - ED这意味着我的帖子。哦,好吧,站点。 - richo


我建议您的C(和Java)应用程序需要重写,以便它可以处理异常,如果确实需要中断,电源故障等会发生什么......

我失败了,J-16是对的钱。用户是否需要与流程相互作用,或者只是看输出(他们甚至需要查看输出吗?)


0
2017-12-23 02:50



Richo,重写是不可能的......不是因为我不想,而是因为缺乏源代码。我相信上面的咖啡馆解决方案对我有用。谢谢。 - Harry


上面解释的解决方案对我来说不起作用,即使链接了Caf提出的两个命令。

但是,我终于成功地通过这种方式获得了预期的行为:

#!/bin/zsh
setopt MONITOR
TRAPINT() { print AAA }

print 1
( ./child & ; wait)
print 2

如果我按下Ctrl-C child 正在运行,它将等待它退出,然后将打印AAA和2。 child 不会收到任何信号。

子shell用于防止显示PID。

抱歉......这是针对zsh的,虽然问题是关于bash,但我不知道bash足以提供一个等效的脚本。


0
2017-07-09 18:30





这是为阻止它的程序启用Ctrl + C等信号的示例代码。

fixControlC.c

#include <stdio.h>
#include <signal.h>
int sigaddset(sigset_t *set, int signo) {
    printf("int sigaddset(sigset_t *set=%p, int signo=%d)\n", set, signo);
    return 0;
}

编译它:

gcc -fPIC -shared -o fixControlC.so fixControlC.c

运行:

LD_LIBRARY_PATH=. LD_PRELOAD=fixControlC.so mysqld

0
2017-07-02 09:31