简单的Linux信号处理

It'*_*ete 30 c c++ linux signals

我有一个程序可以创建许多线程并运行,直到电源关闭到嵌入式计算机,或者用户使用killctrlc终止进程.

这是一些代码以及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?
}
Run Code Online (Sandbox Code Playgroud)

我想知道一些事情:

  1. 是否需要任何信号处理?
    我在这个帖子中读到了"Linux C catch kill信号以便正常终止",显然操作系统会为我处理清理工作.因此,我可以只用无限循环替换信号处理程序,让操作系统优雅地退出线程,取消分配内存等吗?

  2. 关于清洁终止,我还需要关注其他任何信号吗?这个主题"SIGINT如何与其他终止信号相关?" ,列出我可能关注的所有信号,但有多少实际需要处理是有用的?

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

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

  5. 第二次电话是signal()必要的吗?
    是否有类似我需要关注的东西sigaction()

要清楚,我正在努力实现我的:主循环运行,直到其中一个ctrlc或电源断开或发生了一些非常糟糕的事情.

Gri*_*han 28

[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 );
Run Code Online (Sandbox Code Playgroud)

引用:

  1. 开始Linux编程,第4版:在本书中,您的代码sigaction()在"第11章:进程和信号"中得到了很好的解释.
  2. sigaction的文档,其中包括一个例子(快速学习).
  3. GNU C库:信号处理
    *我从1开始,现在我正在阅读3 GNU库

[Q-5]第二次电话是signal()必要的吗?是否有类似我需要关注的东西sigaction()

为什么在程序终止之前将其设置为default-action对我来说不明确.我想以下段落会给你一个答案:

处理信号

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

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

阅读讨论以供进一步参考:何时重新启用信号处理程序.

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

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

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

不,操作系统只在程序终止后执行清理.当一个进程执行时,操作系统不会声明分配给该进程的资源.(操作系统无法知道您的进程是否处于无限循环中 - 这是一个无法解决的问题).如果您希望在进程终止后OS执行清理操作,那么您不需要处理信号(即使您的进程因信号异常终止).

[问]我正在努力完成我的:主循环运行,直到其中一个ctrlc或电源断开或发生了一些非常糟糕的事情.

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

- Macro:int SIGKILL

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

所以你不能制作一个不能被打断的程序(一个不间断的程序)!

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

我希望这个答案会对你有所帮助.


Som*_*ude 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;
}
Run Code Online (Sandbox Code Playgroud)

  • 如果您为作者提供学分,则可能需要更正书名中的拼写错误. (4认同)

小智 5

1.是否需要进行信号处理?

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

2.关于清洁终止,是否还需要关注其他信号?

  • 首先,看看他的页面:GNU库信号 终止信号就是你所关注的.但是看看SIGUSR1和SIGUSR2,即使你不会在任何软件中找到它们,除了调试目的.

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

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

  • 绝对不.

我读过signal()现在已弃用,并且正在使用sigaction()

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

  • Signal()对我来说很好,但是如果你想要任何例子:IBM Example