Str*_*keW 6 c linux signals async-safe
我只是想知道是否可以在信号处理程序中调用非异步安全函数。
引用 Linux 手册页 signal(7):
如果信号中断不安全函数的执行,并且处理程序调用不安全函数,则程序的行为是未定义的。
和TLPI:
SUSv3 指出,表 21-1(异步安全函数列表)中未列出的所有函数都被认为对于信号而言是不安全的,但指出只有当信号处理程序的调用中断了某个函数的执行时,该函数才是不安全的。不安全函数,并且处理程序本身也调用不安全函数。
我对上面引用的解释是,只有当信号处理程序没有中断非异步安全函数时,从信号处理程序调用非异步安全函数才是安全的。
例如,我安装了一个 SIGINT 处理程序,它调用一个不安全的函数,crypt(3)该函数被认为是不可重入的,即不安全的。
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sa.sa_handler = handler;
sigaction(SIGINT, &sa, NULL);
Run Code Online (Sandbox Code Playgroud)
我还在printf()中调用了无限循环main(),并且只运行主线程。
printf()问题是对于这个简单的例子,当处理程序中断执行并调用不安全函数时,我没有看到任何不好的事情发生。AFAK,printf()将获取控制台锁并具有内部缓冲区来执行缓冲 I/O,但其状态在本例中是一致的。虽然crypt()返回一个静态分配的字符串,但它不与其他函数或线程共享。
我误解了什么吗?我希望有人澄清一下,让信号处理程序中断主程序中不安全函数的执行并且本身也调用不安全函数总是不安全的,或者在某些情况下这样做是安全的(例如上面的简单示例)?
是的,确实,在所有情况下从信号处理程序内部调用非异步信号安全函数都是不安全的(除非您深入研究实现代码 -libc例如,也许您的编译器为其生成代码);那么你也许可以证明调用这样的函数实际上是安全的;但这样的证明可能是不可能的,或者需要花费数月或数年的时间,即使在静态分析器 \xc3\xa0 la Frama-C的帮助下,......并且需要研究所有实现细节。
具体来说,很可能crypt是内部调用malloc(对于某些内部数据等......)。标准malloc函数显然具有一些全局状态(例如,与先前 -d 内存区域相关的存储桶链表向量free,以供将来调用重用malloc)。
请记住,Unix 信号可以出现在用户空间中的每个机器指令上(不仅是在 C 序列点上,它们具有一些明确定义的语义)(系统调用就是一条SYSENTER指令)。如果运气不好,在更新全局状态的少数机器指令中可能会出现信号malloc。然后,将来从信号处理程序间接调用malloc-eg可能会造成严重破坏(即未定义的行为)。这样的不幸可能不太可能发生(但评估其概率实际上是不可能的),但您应该针对它进行编码。
详细信息在很大程度上是特定于实现的,具体取决于您的编译器和优化标志、libc内核、处理器架构等......
您可能不关心异步信号安全功能,因为打赌灾难不会发生。对于调试目的来说,这可能是可以接受的(例如,很多时候,但并非总是如此,printf信号处理程序内部实际上在大多数时间都可以工作;并且GCC编译器在信号处理程序内部使用其“异步不安全” libbacktrace库)用于代码用 包装#ifndef NDEBUG,但它不适合生产代码;如果您确实必须在处理程序中添加此类代码,请在注释中提及您知道您错误地调用了非异步信号安全函数,并准备好被未来从事相同工作的同事诅咒代码库。
处理这种情况的典型技巧是简单地volatile sig_atomic_t在信号处理程序中设置一个标志(阅读 POSIX signal.h文档)并在处理程序外部的某个循环中的某个安全位置检查该标志,或者写入(2)一个-或几个字节到先前在应用程序初始化时设置的管道(7),并让该管道的读取端定期轮询(2),然后由事件循环或其他线程读取。
(我举了malloc 一个例子,但您可以想到其他广泛使用的非异步信号安全函数,甚至实现特定的例程,例如 32 位处理器上的 64 位算术等...)。
| 归档时间: |
|
| 查看次数: |
2446 次 |
| 最近记录: |