简单的Linux信号处理

时间:2013-07-30 08:33:22

标签: c++ c linux signals

我有一个程序可以创建许多线程并运行,直到嵌入式计算机的电源关闭,或者用户使用kill ctrl c 来终止这个过程。

这是一些代码以及main()的外观。

static int terminate = 0;  // does this need to be volatile?

static void sighandler(int signum) { terminate = 1; }

int main() {
  signal(SIGINT, sighandler);
  // ...
  // create objects, spawn threads + allocate dynamic memory
  // ...
  while (!terminate) sleep(2);
  // ...
  // clean up memory, close threads, etc.
  // ...
  signal(SIGINT, SIG_DFL);  // is this necessary?
}

我想知道一些事情:

  1. 是否需要进行信号处理? 我在这个帖子"Linux C catching kill signal for graceful termination"中读到,显然操作系统会为我处理清理工作。 因此,我可以只用无限循环替换信号处理程序,让操作系统优雅地退出线程,取消分配内存等吗?

  2. 关于清洁终止,我还需要关注其他信号吗?这个帖子"How does SIGINT relate to the other termination signals?"对于列出我可能关注的所有信号,但实际需要处理多少信息很有用?

  3. 我的示例中的terminate变量是否必须是volatile?我已经看到很多这个变量是易变的例子,而其他的变量则不是。

  4. 我读过signal()现已弃用,并使用sigaction()。是否有任何非常好的示例来展示如何从之前的signal()电话转换?我遇到了我必须创建/传递的新结构以及它们如何组合在一起的问题。

  5. 是否需要第二次调用signal()? 是否有类似的事情我需要关注sigaction()

  6. 要清楚,我正在努力完成让我的:主循环运行,直到 ctrl c 或电源断开或发生了一些非常糟糕的事情。

5 个答案:

答案 0 :(得分:28)

  

[问题3] 我的示例中的terminate变量是否必须为volatile?我'已经   看过很多这个变量是易变的例子,还有其他的   它不是。

标记terminate应为volatile sig_atomic_t

因为可以异步调用处理函数。也就是说,可能会在程序中的任何位置调用处理程序,这是不可预测的。如果两个信号在非常短的时间间隔内到达,则一个处理程序可以在另一个处声明volatile sig_atomic_t被认为是更好的做法,这种类型总是以原子方式访问,避免中断访问变量的不确定性。 volatile告诉编译器不要优化并将其注册。 (阅读:Atomic Data Access and Signal Handling详细说明) 还有一个参考:24.4.7 Atomic Data Access and Signal Handling。 此外,7.14.1.1-5中的C11标准表明只能从信号处理程序访问volatile sig_atomic_t的对象(访问其他对象具有未定义的行为)。

  

[问题4] 我已阅读signal()现已弃用,并使用sigaction()。是   有任何非常好的例子来展示如何转换   之前的signal()电话?我遇到了新结构的问题   我必须创建/传递以及它们如何组合在一起。

以下示例(以及评论中的链接)可能会有所帮助:

// 1. Prepare struct 
struct sigaction sa;
sa.sa_handler =  sighandler;

// 2. To restart functions if interrupted by handler (as handlers called asynchronously)
sa.sa_flags = SA_RESTART; 

// 3. Set zero 
sigemptyset(&sa.sa_mask);

/* 3b. 
 // uncomment if you wants to block 
 // some signals while one is executing. 
sigaddset( &sa.sa_mask, SIGINT );
*/ 

// 4. Register signals 
sigaction( SIGINT, &sa, NULL );

<强>的引用:

  1. Beginning Linux Programming, 4th Edition:在本书中,您的代码恰好用sigaction()解释了&#34;第11章:流程和信号&#34;。
  2. sigaction documentation,包括一个例子(快速学习)。
  3. GNU C Library:Signal Handling
    *我是从1开始的,现在正在阅读3 GNU-library
  4.   

    [问题5] 第二次调用signal()是否必要?我需要关注sigaction()类似的东西吗?

    为什么在我不清楚程序终止之前将其设置为default-action。我认为以下段落会给你一个答案:

      

    Handling Signals

         

    信号呼叫建立信号处理仅发生一次信号。在调用信号处理功能之前,库会重置信号,以便在再次出现相同信号时执行默认操作。例如,如果在信号处理程序中执行的动作再次引发相同的信号,则重置信号处理有助于防止无限循环。如果希望每次出现处理程序时都使用处理程序,则必须在处理程序中调用signal来恢复它。您应该谨慎恢复信号处理。例如,如果您不断恢复SIGINT处理,则可能会失去中断和终止程序的能力。

    signal()函数仅定义下一个接收信号的处理程序,之后恢复默认处理程序。因此,如果程序需要使用非默认处理程序继续处理信号,则信号处理程序必须调用signal()

    阅读讨论以供进一步参考:When to re-enable signal handlers

      

    [问题1a] 是否需要进行信号处理?

    是的,Linux会为你做清理工作。例如,如果您不关闭文件或套接字,Linux将在进程终止后进行清理。但Linux可能不需要立即执行清理,可能需要一些时间(可能是为了保持系统性能高或其他一些问题)。例如,如果您没有关闭tcp-socket并且程序终止,则内核不会立即关闭套接字以确保所有数据都已传输,TCP保证在可能的情况下交付。

      

    [Q-1b] 因此,我可以只用无限循环替换信号处理程序,让操作系统优雅地退出线程,取消分配内存等吗?

    不,操作系统只在程序终止后执行清理。在执行流程时,分配给该流程的资源不会被操作系统声明。 (操作系统无法知道您的进程是否处于无限循环中 - this is an unsolvable problem)。如果您希望在过程终止后OS执行清理操作,那么您就不需要处理信号(即使您的过程因信号异常终止)。

      

    [问] 所有我试图完成让我的:主循环运行,直到 ctrl c 或权力断开连接或发生了一些非常糟糕的事情。

    不,有限制!你无法捕获所有信号。某些信号不可捕获,例如SIGKILLSIGSTOP都是终止信号。引用一个:

      

    — Macro: int SIGKILL

         

    SIGKILL信号用于立即终止程序。它无法处理或忽略,因此总是致命的。阻止此信号也可能

    所以你不能a program that cannot be interrupted (an uninterrupted program)

    <子> 我不确定但是你可以在Windows系统中做这样的事情:编写TSR(某种内核模式挂钩)。我记得从我的论文时间开始,即使是任务经理也无法终止某些病毒,但我也相信他们会通过管理员权限欺骗用户。

    我希望这个答案可以帮到你。

答案 1 :(得分:6)

使用sigaction代替,您可以使用如下函数:

/*
 * New implementation of signal(2), using sigaction(2).
 * Taken from the book ``Advanced Programming in the UNIX Environment''
 * (first edition) by W. Richard Stevens.
 */
sighandler_t my_signal(int signo, sighandler_t func)
{
    struct sigaction nact, oact;

    nact.sa_handler = func;
    nact.sa_flags = 0;
    # ifdef SA_INTERRUPT
    nact.sa_flags |= SA_INTERRUPT;
    # endif
    sigemptyset(&nact.sa_mask);

    if (sigaction(signo, &nact, &oact) < 0)
        return SIG_ERR;

    return oact.sa_handler;
}

答案 2 :(得分:5)

  

<强> 1。是否需要进行信号处理?

  • 在您发布的链接的特定情况下,是的。涉及网络的软件运行需要特定的操作,例如通过示例关闭套接字的警告客户端,以免干扰其运行。
  • 在您的特定情况下,您无需处理任何信号,以便流程清晰明了:您的操作系统将为您完成。
  

<强> 2。关于清洁终止,我还需要关注其他任何信号吗?

  • 首先,看一下他的页面:The GNU Library Signals 终止信号就是您所关注的。但是看看SIGUSR1和SIGUSR2,即使你在任何软件中也找不到它们,除了调试目的。

  • 如果您不希望软件突然终止,则需要处理所有这些终止信号。

  

第3。我的示例中的terminate变量是否必须是volatile?

  • 绝对不是。
  

<强> 4。我读过signal()现已弃用,并使用sigaction()

  • Sigaction()是POSIX,而信号是C标准。

  • Signal()适用于我,但如果您想要任何示例:IBM Example

答案 3 :(得分:4)

没有必要为此使用信号。标准终止不会需要被捕获。您可能有理由抓住它,但这取决于您的应用程序,而不是O / S所需的任何内容。

就信号而言,一般情况下你应该使用sigaction这些天没有信号,它会解决标准化问题。

必须将信号处理程序编写为可重入。这不要求你的终止变量是易变的,但它可能会取决于你如何使用它!

W. Richard Stevens的书"Advanced Programming in the UNIX Environment"有一个很好的例子说明为什么以及如何处理信号。

不,你不必在应用程序终止之前放回默认处理程序,你的处理程序只对你的应用程序有效,所以如果你只是杀掉应用程序,那就不需要了。

答案 4 :(得分:0)

首先 - 如果你不知道,你是否应该处理任何信号,那么你可能不会。信号需要在某些特定情况下处理,例如关闭套接字,在退出之前向其他连接的进程发送一些消息,或者处理来自write()的SIGPIPE信号(可能还有更多)。

其次 - 这个while (!terminate) sleep(2);不是很好 - 在最坏的情况下,它可能会让用户(甚至系统)不耐烦等待2s并向你的程序发送一个你无法处理的SIGKILL。 / p>

恕我直言,这里的最佳解决方案是using signalfd and select,因此您无需等待2即可终止程序。