简单的Linux信号处理

我有一个程序创build了许multithreading并运行,直到电源closures到embedded式计算机,或者用户使用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捕获信号优美的终止” ,这显然是操作系统将为我处理清理。 因此,我可以只用一个无限循环replace信号处理程序,让操作系统正常退出线程,取消分配内存等。

  2. 是否还有其他信号需要关注清洁终止? 此线程“SIGINT如何与其他终止信号相关?” ,列出我可能关心的所有信号是有用的,但实际需要处理多less?

  3. 我的示例中的终止variables是否必须是易失性的? 我已经看到很多例子,这个variables是不稳定的,其他的不是。

  4. 我读过signal()现在已经被弃用了,并且使用了sigaction() 。 有没有真正的好例子来显示如何从以前的signal()调用转换? 我在创build/传递的新结构方面遇到问题,以及它们如何组合在一起。

  5. 第二个调用signal()必要?
    有什么类似的,我需要关心sigaction()

要清楚,所有我试图完成有我的:主循环运行,直到ctrl c或电源断开或发生了一些非常糟糕的事情。

Solutions Collecting From Web of "简单的Linux信号处理"

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

[Q-5]是否需要第二个发signal() ? 有什么类似的,我需要关心sigaction()

为什么你在程序终止之前将它设置为default-action对我来说是不清楚的。 我想下面的段落会给你一个答案:

处理信号

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

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

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

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

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

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

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

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

不,有一个限制! 你不能捕捉所有的信号。 有些信号不可捕捉,例如SIGKILLSIGSTOP ,都是终止信号。 引用一个:

宏:int SIGKILL

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

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

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

我希望这个答案会帮助你。

为了使用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; } 

1.是否需要处理任何信号?

  • 在你发布的链接的特定情况下,是的。 涉及网络的软件的运行需要特定的操作,以示例的方式关闭套接字的警告客户端,以便不干扰其运行。
  • 在你的特定情况下,你不需要处理任何信号,这个过程就会被优雅地清除:你的操作系统会为你做。

2.是否还有其他信号需要我们关注清洁终止?

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

  • 所有这些终止信号需要处理,如果你不想让你的软件突然终止。

3.我的示例中的终止变量是否必须是易失性的?

  • 绝对不。

4.我读过signal() ,现在不推荐使用,并使用sigaction()

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

  • Signal()对我来说工作正常,但如果您想要任何示例: IBM示例

这是没有必要使用信号。 标准的终止不需要被捕获。 你可能有抓住它的理由,但这将取决于你的应用程序,而不是O / S所要求的任何东西。

就信号而言,一般来说,你现在应该使用sigaction来表示信号,而不是一个标准化问题。

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

W. Richard Stevens所着的“UNIX环境下的高级编程”就是为什么以及如何处理信号的一个很好的例子。

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

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

其次 – 这个while (!terminate) sleep(2); 是不是很好 – 在最坏的情况下,它可能使用户(甚至系统)不耐烦,不得不等待2s,并发送给你的程序一个SIGKILL,你不能处理。

恕我直言,这里最好的解决方案将使用signalfd和选择 ,所以你可以终止你的程序,而不必等待2s。