向信号处理程序提供/传递参数

har*_*ari 57 c signals signal-handling

我可以向信号处理程序提供/传递任何参数吗?

/* Signal handling */
struct sigaction act;
act.sa_handler = signal_handler;
/* some more settings */
Run Code Online (Sandbox Code Playgroud)

现在,处理程序看起来像这样:

void signal_handler(int signo) {
    /* some code */
}
Run Code Online (Sandbox Code Playgroud)

如果我想做一些特殊的事情,即删除临时文件,我可以将这些文件作为参数提供给这个处理程序吗?

编辑0:谢谢你的回答.我们通常避免/不鼓励使用全局变量.在这种情况下,如果你有一个庞大的程序,在不同的地方可能会出现问题,你可能需要进行大量的清理工作.为什么API以这种方式设计?

Hen*_*olm 53

您不能将自己的数据作为参数传递给信号处理程序.相反,您必须将参数存储在全局变量中.(如果您在安装信号处理程序后需要更改这些数据,那真的非常小心).

回复编辑0:历史原因.信号是一个非常古老而且非常低级的设计.基本上你只是给内核一个地址一些机器代码,并要求它去这个特定的地址,如果发生这种情况.我们回到了这里的"便携式汇编程序"思维模式,其中内核提供了简单的基本服务,无论用户进程能够合理地预期为什么,它都必须自己完成.

此外,反对全局变量的通常参数在这里并不真正适用.信号处理程序本身是一个全局设置,因此没有相关的可能性为它提供几组不同的用户指定参数.(嗯,实际上它不是完全全局的,而只是线程全局的.但是线程API将包含一些线程局部存储机制,这正是你在这种情况下所需要的).

  • 信号处理程序不是每个线程的。更改信号的处理程序适用于所有线程。 (2认同)
  • 实际上,我不清楚从信号处理程序访问TLS是否有效。使用glibc / NPTL几乎肯定是“无效”的,因为可以在创建线程之后但在将控制权传递给start函数之前立即发送信号,而线程的TLS副本仍在初始化中。如果您此时小心地阻止信号,这可能还可以,但仍然是一种可疑的做法... (2认同)

R..*_*R.. 16

信号处理程序注册已经是一个等同于全局变量的全局状态.因此,使用全局变量将参数传递给它并不是更大的冒犯.然而,这是一个巨大的错误(几乎肯定是未定义的行为,除非你是专家!)无论如何都要从信号处理程序做任何事情.如果您只是阻止信号并从主程序循环中轮询它们,则可以避免所有这些问题.

  • 除非你是信号专家,否则从信号处理程序做任何事情都是非常危险的.相反,您可以使用信号处理程序来保存信号发生的标志,并从信号处理程序外部的各个点检查它,或者您可以阻止信号并使用`sigpending`或`sigwait`来轮询信号. (4认同)
  • 信号处理程序可以在任何时候中断程序.这意味着,除非您已经注意确保不会发生这种情况,否则它可能会与处于不一致状态的各种数据对象一起运行.例如,`FILE`中的缓冲区指针可能只是部分更新,或者打开的`FILE`s的链表可能有一半插入或中途删除的项目部分链接到它.或者内部`malloc`数据结构可能引用了中途释放的内存...如果信号处理程序调用依赖于该状态一致的函数,则会发生非常糟糕的事情! (4认同)
  • 顺便说一下,使用信号处理程序的一种经典的"安全"方法是打开自己的管道,并将文件描述符存储在全局的某个地方.信号处理程序可以写入管道(这是安全的),您可以等待主`select` /`poll`循环中的管道输入以及您正在等待输入的其他文件描述符. (4认同)
  • 哇,这对我来说是全新的.我已经看到代码在主程序终止之前清理像临时文件删除或子进程清理.另外当你说检查标志时,如果你得到SIGTERM怎么能工作呢?如果您在处理程序中没有处理该程序,您的程序是否会立即终止?为什么在处理程序中做事情是"危险的"? (2认同)
  • @R .. Linux上有signalfd()就是这么做的.还是个好主意.很少有人考虑使用管道在一个过程中进行通信*.使线程之间的通信更加容易和明确. (2认同)

Dav*_*ger 6

绝对地。您可以使用 sigqueue() 而不是通常的 kill() 将整数和指针传递给信号处理程序。

http://man7.org/linux/man-pages/man2/sigqueue.2.html


rak*_*007 5

这是一个非常老的问题,但我想我可以向您展示一个很好的技巧,它可以解决您的问题。无需使用sigqueue或其他任何工具。

我还不喜欢使用globals变量,因此在我的情况下,我不得不寻找一种巧妙的方法来发送一个无效的ptr(您可以稍后将其转换为适合您需要的任何内容)。

实际上,您可以这样做:

signal(SIGWHATEVER, (void (*)(int))sighandler); // Yes it works ! Even with -Wall -Wextra -Werror using gcc
Run Code Online (Sandbox Code Playgroud)

然后您的信号处理程序将如下所示:

int sighandler(const int signal, void *ptr) // Actually void can be replaced with anything you want , MAGIC !
Run Code Online (Sandbox Code Playgroud)

您可能会问:那么如何获取* ptr?

方法如下: 初始化时

signal(SIGWHATEVER, (void (*)(int))sighandler)
sighandler(FAKE_SIGNAL, your_ptr);
Run Code Online (Sandbox Code Playgroud)

在您的sighandler函数中

int sighandler(const int signal, void *ptr)
{
  static my_struct saved = NULL;

  if (saved == NULL)
     saved = ptr;
  if (signal == SIGNALWHATEVER)
     // DO YOUR STUFF OR FREE YOUR PTR
   return (0);
}
Run Code Online (Sandbox Code Playgroud)

  • 不错的技巧,但不幸的是,该标准尚未定义。参见6.3.2.3(8):“如果使用转换后的指针来调用类型与所引用类型不兼容的函数,则行为未定义”。 (3认同)