简单的 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...

问题描述:

我有一个程序,它创建许多线程并运行,直到嵌入式计算机的电源关闭,或者用户使用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 捕捉终止信号以正常终止”中读到,显然操作系统会帮我处理清理工作。因此,我是否可以只用一个无限循环替换信号处理程序,让操作系统正常退出线程、取消分配内存等?

  1. 关于干净终止,还有其他信号需要我关注吗?这个主题“SIGINT 与其他终止信号有何关系?”有助于列出我可能关注的所有信号,但实际上有多少需要处理?

  2. 我的示例中的终止变量是否必须是 volatile 的?我见过许多示例,其中此变量是 volatile 的,而其他示例则不是。

  3. 我读到signal()现已弃用,并使用sigaction()。 是否有任何真正好的例子来展示如何从上一次signal()调用进行转换? 我对必须创建/传递的新结构以及它们如何组合在一起感到困扰。

  4. 第二次调用 是否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 );

参考:

  1. 《Linux 编程入门》第 4 版sigaction():在这本书中, “第 11 章:进程和信号”准确地 解释了您的代码。

  2. sigaction 文档,包括一个示例(快速学习)。

  3. 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`电源断开或发生一些非常糟糕的事情。

不,有一个限制!你无法捕获所有信号。有些信号无法捕获,例如SIGKILLSIGSTOP和都是终止信号。引用其中一段:

— 宏:intSIGKILL

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 秒。

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用