简单的 Linux 信号处理
- 2024-11-08 09:04:00
- admin 原创
- 29
问题描述:
我有一个程序,它创建许多线程并运行,直到嵌入式计算机的电源关闭,或者用户使用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 捕捉终止信号以正常终止”中读到,显然操作系统会帮我处理清理工作。因此,我是否可以只用一个无限循环替换信号处理程序,让操作系统正常退出线程、取消分配内存等?
关于干净终止,还有其他信号需要我关注吗?这个主题“SIGINT 与其他终止信号有何关系?”有助于列出我可能关注的所有信号,但实际上有多少需要处理?
我的示例中的终止变量是否必须是 volatile 的?我见过许多示例,其中此变量是 volatile 的,而其他示例则不是。
我读到
signal()
现已弃用,并使用sigaction()
。 是否有任何真正好的例子来展示如何从上一次signal()
调用进行转换? 我对必须创建/传递的新结构以及它们如何组合在一起感到困扰。第二次调用 是否
signal()
必要?
是否有类似的事情需要我关注sigaction()
?
要清楚的是,我所要做的就是让主循环一直运行,直到ctrl
`c`电源断开或发生一些非常糟糕的事情。
解决方案 1:
[Q-3]我的例子中的变量
terminate
必须是 吗volatile
?我见过许多例子,其中这个变量是 volatile 的,而其他例子则不是。
该标志terminate
应为volatile sig_atomic_t
:
因为处理程序函数可以异步调用。也就是说,处理程序可能会在程序中的任何点被不可预测地调用。如果两个信号在很短的时间间隔内到达,则一个处理程序可以在另一个处理程序内运行。并且,声明被认为是更好的做法volatile sig_atomic_t
,这种类型总是以原子方式访问,避免对中断对变量的访问的不确定性。volatile
告诉编译器不要优化并将其放入寄存器中。(阅读:原子数据访问和信号处理以了解详细说明)。
另一个参考:24.4.7 原子数据访问和信号处理volatile sig_atomic_t
。此外,7.14.1.1-5 中的 C11 标准表明只能从信号处理程序访问的对象(访问其他对象具有未定义的行为)。
[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 );
参考:
《Linux 编程入门》第 4 版
sigaction()
:在这本书中, “第 11 章:进程和信号”准确地 解释了您的代码。sigaction 文档,包括一个示例(快速学习)。
GNU C 库:信号处理*我从1
开始,目前我正在阅读3 GNU 库
[Q-5]第二次调用 是
signal()
必要的吗? 我需要关注 的类似问题吗sigaction()
?
我不清楚为什么在程序终止之前将其设置为默认操作。我认为以下段落可以给你答案:
处理信号
调用 signal 只会为一次信号建立信号处理。在调用信号处理函数之前,库会重置信号,以便如果再次出现相同的信号,则执行默认操作。重置信号处理有助于防止无限循环,例如,如果在信号处理程序中执行的操作再次引发相同的信号。如果希望每次出现信号时都使用处理程序,则必须在处理程序中调用 signal 来恢复它。恢复信号处理时应谨慎。例如,如果您不断恢复
SIGINT
处理,可能会失去中断和终止程序的能力。
该signal()
函数仅定义下一个接收到的信号的处理程序,之后将恢复默认处理程序。因此,如果程序需要使用非默认处理程序继续处理信号,则必须调用信号处理程序。signal()
阅读讨论以供进一步参考:何时重新启用信号处理程序。
[Q-1a]是否需要任何信号处理?
是的,Linux 会为您进行清理。例如,如果您不关闭文件或套接字,Linux 会在进程终止后进行清理。但 Linux 可能不必立即执行清理,这可能需要一些时间(可能是为了保持系统性能高或一些其他问题)。例如,如果您不关闭 tcp 套接字并且程序终止,内核将不会立即关闭套接字以确保所有数据都已传输,如果可能,TCP 会保证交付。
[Q-1b]因此,我可以用一个无限循环替换信号处理程序,让操作系统正常退出线程、取消分配内存等吗?
不,操作系统仅在程序终止后执行清理。当进程执行时,分配给该进程的资源不会被操作系统认领。(操作系统无法知道您的进程是否处于无限循环中 -这是一个无法解决的问题)。如果您希望在进程终止后操作系统为您执行清理操作,那么您不需要处理信号(即使您的进程被信号异常终止)。
[问]我所要做的就是让主循环一直运行,直到
ctrl
`c`电源断开或发生一些非常糟糕的事情。
不,有一个限制!你无法捕获所有信号。有些信号无法捕获,例如SIGKILL
和SIGSTOP
和都是终止信号。引用其中一段:
— 宏:int
SIGKILL
此
SIGKILL
信号用于立即终止程序。它无法被处理或忽略,因此始终是致命的。也无法阻止此信号。
所以你不能制作一个不能被打断的程序(不被打断的程序)!
我不确定,但也许你可以在 Windows 系统中做类似的事情:通过编写 TSR(某种内核模式挂钩)。我记得在写论文的时候,有些病毒甚至无法从任务管理器中终止,但我还相信它们会通过管理员权限欺骗用户。
希望这个回答能对你有帮助。
解决方案 2:
相反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;
}
解决方案 3:
1. 是否需要任何信号处理?
就您发布的链接的特定情况而言,是的。与网络有关的软件的运行需要特殊操作,例如警告客户端套接字已关闭,以免干扰其运行。
在您的特定情况下,您不需要处理任何信号以使流程顺利完成:您的操作系统将为您完成此操作。
2. 关于干净终止,还有其他什么信号需要我关注吗?
首先,看一下他的页面:GNU 库信号
终止信号是您要关注的。但是,请看一下 SIGUSR1 和 SIGUSR2,即使您永远不会在任何软件中找到它们,除非出于调试目的。如果您不希望软件突然终止,则需要处理所有这些终止信号。
3. 我的例子中的终止变量必须是易失性的吗?
绝对不是。
4. 我读到
signal()
现在这个已经被弃用了,可以使用sigaction()
Sigaction()
是 POSIX,而信号是 C 标准。Signal()
对我来说很好,但如果你想要任何例子:IBM 示例
解决方案 4:
没有必要为此使用信号。标准终止不需要被捕获。您可能有理由捕获它,但这取决于您的应用程序,而不是操作系统要求的任何东西。
就信号而言,一般来说你应该使用 sigaction 而不是 signal,它解决了标准化问题。
信号处理程序必须编写为可重入的。这并不要求您的终止变量是易失性的,但这可能取决于您如何使用它!
W. Richard Stevens 的书“UNIX 环境中的高级编程”很好地说明了为什么以及如何处理信号。
不,您不必在应用程序终止之前放回默认处理程序,您的处理程序仅对您的应用程序有效,因此如果您只是终止应用程序,则不需要它。
解决方案 5:
首先 - 如果您不知道是否应该处理任何信号,那么您可能不需要处理。信号需要在某些特定情况下进行处理,例如关闭套接字、在退出之前向其他连接的进程发送一些消息,或者处理来自 write() 的 SIGPIPE 信号(可能还有很多)。
其次 - 这while (!terminate) sleep(2);
不是很好 - 在最坏的情况下,它可能会让用户(甚至系统)不耐烦地等待 2 秒并向您的程序发送 SIGKILL,而您无法处理。
我认为这里最好的解决方案是使用 signalfd 和 select,这样您就可以终止程序而不必等待 2 秒。
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件