一尘不染

简单的Linux信号处理

linux

我有一个程序,该程序可以创建许多线程并运行,直到关闭嵌入式计算机的电源,或者用户使用killctrl``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为正常终止捕获了终止信号”
    ,显然,操作系统将为我处理清理工作。因此,我可以仅用无限循环替换信号处理程序,然后让OS正常退出线程,取消分配内存等吗?

  2. 关于干净终止,还有其他需要关注的信号吗?该线程“ SIGINT与其他终止信号有何关系?” ,对于列出我可能关心的所有信号很有用,但是实际上需要处理多少个信号?

  3. 在我的示例中,terminate变量是否必须是volatile?我看过很多例子,其中该变量是易失性的,而另一些则不是。

  4. 我读过,signal()现在已弃用,请使用sigaction()。有没有真正好的例子来说明如何从上一个signal()通话转换?我在必须创建/通过的新结构以及它们如何组合在一起方面遇到了麻烦。

  5. 第二个电话signal()需要吗?
    是否有类似的事情需要我关注sigaction()

明确地说,我要做的就是让我的:main循环运行,直到其中一个ctrl``c断开或电源断开或发生真正的不良情况。


阅读 343

收藏
2020-06-02

共1个答案

一尘不染

[Q-3]terminate我的示例中的变量是否必须为volatile?我看过很多例子,其中该变量是易失性的,而另一些则不是。

标志terminate应为volatile sig_atomic_t

因为处理函数可以异步调用。也就是说,处理程序可能在程序中的任何地方被意外地调用。如果两个信号在很短的间隔内到达,则一个处理程序可以在另一个处理程序中运行。最好声明为volatile sig_atomic_t,始终以原子方式访问此类型,以避免不确定是否中断对变量的访问。volatile告诉编译器不要进行优化并将其放入寄存器。(有关详细信息,请阅读:原子数据访问和信号处理)。
一种参考:24.4.7原子数据访问和信号处理。此外,7.14.1.1-5中的C11标准指示只能volatile sig_atomic_t从信号处理程序访问对象的(访问其他对象具有未定义的行为)。

[Q-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. 《 Linux编程入门》,第4版:在本书中,sigaction()“第11章:进程和信号”中很好地解释了您的代码。
  2. sigaction的文档,其中包括一个例子(快速学习)。
  3. GNU C库:信号处理
    *我从1开始,目前我正在阅读3 GNU库

[Q-5] 是否需要再次致电signal()?是否有类似的事情需要我关注sigaction()

我不清楚为什么在程序终止之前将其设置为默认操作。我认为以下段落将为您提供答案:

处理信号

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

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

阅读讨论以获取更多参考:何时重新启用信号处理程序

[Q-1a] 是否需要信号处理?

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

[Q-1b] 因此,我可以仅用无限循环替换信号处理程序,然后让OS正常退出线程,取消分配内存等吗?

不,操作系统仅在程序终止后才执行清理操作。进程执行时,分配给该进程的资源不会被OS占用。(操作系统无法知道您的进程是否处于无限循环中-这是无法解决的问题)。如果您希望进程终止后,操作系统为您执行清理操作,那么您就不需要处理信号(即使您的进程被信号异常终止)。

[Q] 我要完成的所有工作都是为了使我的:main循环运行,直到ctrl``c或断开电源或发生真正的不良情况。

不,有限制!您无法捕获所有信号。某些信号是不可捕获的,例如SIGKILL并且SIGSTOP都是终止信号。引用一:

[—宏:int SIGKILL](http://www.gnu.org/software/libc/manual/html_node/Termination-

Signals.html)

SIGKILL信号用于导致程序立即终止。它 不能被处理或忽略,因此总是致命的。 它也 没有 能够阻止这个信号。

我不确定,但也许您可以在Windows系统中执行以下操作:通过编写TSR(某种内核模式挂钩)。我从论文撰写时就记得某些病毒甚至无法从任务管理器中终止,但我也相信它们会通过管理员权限欺骗用户。

希望这个答案对您有帮助。

2020-06-02