我有一个程序,该程序可以创建许多线程并运行,直到关闭嵌入式计算机的电源,或者用户使用kill或ctrl``c终止该过程为止。
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? }
我想知道几件事:
是否需要信号处理? 我在这个线程中读到“ Linux C为正常终止捕获了终止信号”,显然,操作系统将为我处理清理工作。因此,我可以仅用无限循环替换信号处理程序,然后让OS正常退出线程,取消分配内存等吗?
关于干净终止,还有其他需要关注的信号吗?该线程“ SIGINT与其他终止信号有何关系?” ,对于列出我可能关心的所有信号很有用,但是实际上需要处理多少个信号?
在我的示例中,terminate变量是否必须是volatile?我看过很多例子,其中该变量是易失性的,而另一些则不是。
我读过,signal()现在已弃用,请使用sigaction()。有没有真正好的例子来说明如何从上一个signal()通话转换?我在必须创建/通过的新结构以及它们如何组合在一起方面遇到了麻烦。
signal()
sigaction()
第二个电话signal()需要吗? 是否有类似的事情需要我关注sigaction()?
明确地说,我要做的就是让我的:main循环运行,直到其中一个ctrl``c断开或电源断开或发生真正的不良情况。
[Q-3]terminate我的示例中的变量是否必须为volatile?我看过很多例子,其中该变量是易失性的,而另一些则不是。
terminate
volatile
标志terminate应为volatile sig_atomic_t:
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 );
参考资料:
[Q-5] 是否需要再次致电signal()?是否有类似的事情需要我关注sigaction()?
我不清楚为什么在程序终止之前将其设置为默认操作。我认为以下段落将为您提供答案:
处理信号 信号调用仅对 一个 信号出现建立信号处理。在调用信号处理函数之前, 库会重置信号,以便在再次出现相同信号时执行默认操作 。重置信号处理有助于防止无限循环,例如,如果在信号处理程序中执行的操作再次引发相同的信号。如果希望您的处理程序每次出现时都用于信号,则必须在处理程序中调用signal来恢复它。您应谨慎恢复信号处理。例如,如果您继续恢复SIGINT处理,则可能会失去中断和终止程序的能力。
处理信号
信号调用仅对 一个 信号出现建立信号处理。在调用信号处理函数之前, 库会重置信号,以便在再次出现相同信号时执行默认操作 。重置信号处理有助于防止无限循环,例如,如果在信号处理程序中执行的操作再次引发相同的信号。如果希望您的处理程序每次出现时都用于信号,则必须在处理程序中调用signal来恢复它。您应谨慎恢复信号处理。例如,如果您继续恢复SIGINT处理,则可能会失去中断和终止程序的能力。
SIGINT
该signal()函数仅定义下一个接收到的信号的处理程序, 之后将恢复默认处理程序 。因此, 如果程序需要使用非默认处理程序继续处理信号 ,则有必要调用信号处理signal() 程序。
阅读讨论以获取更多参考:何时重新启用信号处理程序。
[Q-1a] 是否需要信号处理?
是的,Linux将为您进行清理。例如,如果您不关闭文件或套接字,则Linux将在进程终止后进行清理。但是Linux可能没有必要立即执行清理,并且可能需要一些时间(可能是为了保持较高的系统性能或其他一些问题)。例如,如果您不关闭tcp- socket并且程序终止,则内核将不会立即关闭套接字以确保所有数据都已传输,TCP尽可能保证传送。
[Q-1b] 因此,我可以仅用无限循环替换信号处理程序,然后让OS正常退出线程,取消分配内存等吗?
不,操作系统仅在程序终止后才执行清理操作。进程执行时,分配给该进程的资源不会被OS占用。(操作系统无法知道您的进程是否处于无限循环中-这是无法解决的问题)。如果您希望进程终止后,操作系统为您执行清理操作,那么您就不需要处理信号(即使您的进程被信号异常终止)。
[Q] 我要完成的所有工作都是为了使我的:main循环运行,直到ctrl``c或断开电源或发生真正的不良情况。
不,有限制!您无法捕获所有信号。某些信号是不可捕获的,例如SIGKILL并且SIGSTOP都是终止信号。引用一:
SIGKILL
SIGSTOP
[—宏:int SIGKILL](http://www.gnu.org/software/libc/manual/html_node/Termination- Signals.html) 该SIGKILL信号用于导致程序立即终止。它 不能被处理或忽略,因此总是致命的。 它也 没有 能够阻止这个信号。
Signals.html)
该SIGKILL信号用于导致程序立即终止。它 不能被处理或忽略,因此总是致命的。 它也 没有 能够阻止这个信号。
我不确定,但也许您可以在Windows系统中执行以下操作:通过编写TSR(某种内核模式挂钩)。我从论文撰写时就记得某些病毒甚至无法从任务管理器中终止,但我也相信它们会通过管理员权限欺骗用户。
希望这个答案对您有帮助。