【问题标题】:Persistent signal handling持续信号处理
【发布时间】:2011-11-27 18:37:51
【问题描述】:

我正在尝试编写一个信号处理程序来捕获任意数量的连续 SIGINT 信号并防止程序退出。该程序是一个简单的文件服务器。处理程序设置一个全局标志,导致接受新连接的 while 循环结束,对 pthread_exit() 的调用确保 main 让当前连接在退出之前完成。当我按一次 ctrl-C 但第二次立即退出程序时,一切都像发条一样。

我首先尝试使用信号():

signal(SIGINT, catch_sigint);

...

static void catch_sigint(int signo)
{
    ...
    signal(SIGINT, catch_sigint);
}

我也用 sigaction 试过:

struct sigaction sigint_handler;
sigint_handler.sa_handler = catch_sigint;
sigemptyset(&sigint_handler.sa_mask);
sigint_handler.sa_flags = 0;
sigaction(SIGINT, &sigint_handler, NULL);

不确定如何“重新安装”这个我只是在处理程序中复制了这段代码,类似于使用 signal() 方法的处理程序。

这些都不如我所料。


附加信息:

该程序是一个简单的文件服务器。它接收来自客户端的请求,该请求只是一个由请求的文件名组成的字符串。它利用 pthreads 以便可以同时进行传输。收到 SIGINT 后,我希望服务器退出 while 循环并等待所有当前传输完成然后关闭。照原样,无论我如何编写信号处理程序,第二个 SIGINT 都会立即终止程序。

int serverStop = 0;

...

int main()
{
   /* set up the server -- socket(), bind() etc. */

   struct sigaction sigint_hadler;
   sigint_handler.sa_handler = catch_sigint;
   sigint_handler.sa_flags = 0;
   sigemptyset(&sigint_handler.sa_mask);
   sigaction(SIGINT, &sigint_handler, NULL);

   /* signal(SIGINT, catch_sigint); */

   while(serverStop == 0)
   {
      /* accept new connections and pthread_create() for each */
   }
   pthread_exit(NULL);
}

...

static void catch_sigint(int signo)
{
   serverStop = 1;

   /* signal(SIGINT, catch_sigint) */
}

我不认为任何其他代码可能是相关的,但请随时要求详细说明

【问题讨论】:

    标签: c linux pthreads signals


    【解决方案1】:

    在 Linux 上,您不必重新安装信号处理程序,使用 signal(默认为 implements BSD semantics)或 sigaction。

    当我按 ctrl-C 一次但第二次立即退出程序时。

    这不是因为您的处理程序被重置,而可能是因为您的信号处理程序正在做不应该做的事情。

    我将如何调试此问题:在 GDB 下运行程序并

    (gdb) catch syscall exit
    (gdb) catch syscall exit_group
    (gdb) run
    

    现在等待程序开始运行,然后按 Control-C。这会给你(gdb) 提示。现在继续程序,就好像它已经收到了SIGINT:signal SIGINT(这将调用你的处理程序)。再次重复“Control-C/signal SIGINT”序列。如果您在 exit 或 exit_group 系统调用中停止,请查看它来自哪里(使用 GDB where 命令)。

    更新:

    鉴于您发布的新代码,尚不清楚您在哪里调用 pthread_exit 以“确保 main 让当前连接在退出之前完成”。如前所述,您的主线程将在第一个 Control-C 上退出循环,然后继续调用 exit,这将等待其他线程完成。

    要么你没有显示你的实际代码,要么“第二个 Control-C”是一个红鲱鱼,你的第一个 Control-C 已经把你带走了(没有在其他线程中完成工作) .

    【讨论】:

    • 对不起,我试图尽可能多地省略其他程序逻辑,我把它省略了。在 while 循环之后立即有另一个 pthread_exit() 调用。
    【解决方案2】:

    注意:这主要是猜测。

    我很确定在主线程中调用 pthread_exit 是个坏主意。如果主线程已经退出,那么操作系统可能会尝试向其他线程发送后续信号。

    我建议不要在主线程中使用 pthread_exit,而是 pthread_join() 所有其他线程,然后正常退出。

    但确保其他线程不会收到信号也很重要。通常这是使用 sigprocmask(或者更准确地说是 pthread_sigmask,在 Linux 下相同)来屏蔽工作线程中的信号。这样可以确保信号永远不会传递给他们。

    请注意,为避免竞争条件,您应该在创建子线程之前在主线程中使用 pthread_sigmask,然后在主线程中再次设置信号掩码。这确保了没有窗口,无论多么小,子线程都可能收到不需要的信号。

    【讨论】:

    • 这是一个非常虚假的答案:在主线程中调用 pthread_exit 完全没问题,对于这个特定的信号处理程序,无论在哪个线程上调用信号处理程序都没有区别. SIGINT 的传递也不能保证在主线程上(虽然它继续存在)。
    • 目前我没有限制最大线程数(因为我知道我要发出多少客户端请求)所以我没有跟踪每个线程的 ID我相信需要使用pthread_join()。但是,您的解释使问题更加清楚。正如我的问题所说,我认为pthread_exit()in main 是让 main 等待,而不是在不终止其他线程的情况下退出 main。使用pthread_sigmask() 阻止每个线程内的 SIGINT 已解决问题。这可能不是最正确的解决方案,但现在可以。谢谢!
    【解决方案3】:

    我不确定是否理解。信号处理程序通常不应重新安装任何信号处理程序(包括其自身),因为信号处理程序在安装另一个信号处理程序之前一直有效。另请参阅 SA_NODEFER 标志到 sigaction 以便能够在信号处理期间捕获信号。

    信号处理程序应该很短。请参阅我对this question 的回答。它通常设置一个volatile sig_atomic_t 变量。

    什么不工作?不要在信号处理程序中进行复杂或持久的处理。 请出示您的代码...

    【讨论】:

    • “信号处理程序保持功能,直到安装另一个”。 BSD 信号以这种方式工作,而 SysV 则不能。
    • 但是这个问题被标记为 Linux,我很确定 Linux sigaction 就像我说的那样工作。
    • 是的,sigaction(没有SA_RESETHAND)不需要重置信号。对于signal,这取决于(请参阅我的答案和其中的指针;-)
    • 我主要关注来自csl.mtu.edu/cs441/www/NOTES/signal/install.html 的信息,其中提到在“2 个重要说明”下重新安装。我对 sigaction 方法不太熟悉。稍后将发布更多代码。
    猜你喜欢
    • 2014-05-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-07
    相关资源
    最近更新 更多