多线程编程指南

信号处理程序和异步信号安全

与线程安全类似的概念就是异步信号安全。异步信号安全操作可保证不会干扰正被中断的操作。

当信号处理程序操作干扰正被中断的操作时,就会引发异步信号安全问题。

例如,假设程序正在调用 printf(3S),且其调用程序调用 printf() 时出现了信号。在这种情况下,两个 printf() 语句的输出彼此关联。要避免关联输出,当 printf() 可能被信号中断时,处理程序不应直接调用 printf()

无法使用同步元语来解决此问题。 在信号处理程序与正被同步的操作之间执行的任何同步尝试都将立即产生死锁现象。

假设 printf() 借助互斥来保护自身。现在,假设调用 printf() 进而通过互斥锁持有锁定的线程被信号中断。

如果处理程序调用 printf(),则通过互斥锁持有锁定的线程将尝试再次利用互斥锁。尝试利用互斥锁将导致瞬间死锁。

为避免处理程序与操作之间出现干扰,请确保这种情况永远不会发生。或许您可以在关键时候屏蔽信号,或从内部信号处理程序中仅调用异步信号安全操作。

表 5–2 中列出了 POSIX 可确保异步信号安全的仅有例程。任何信号处理程序都可以安全地调用这些函数之一。

表 5–2 异步信号安全函数

_exit()

fstat()

read()

sysconf()

access()

getegid()

rename()

tcdrain()

alarm()

geteuid()

rmdir()

tcflow()

cfgetispeed()

getgid()

setgid()

tcflush()

cfgetospeed()

getgroups()

setpgid()

tcgetattr()

cfsetispeed()

getpgrp()

setsid()

tcgetpgrp()

cfsetospeed()

getpid()

setuid()

tcsendbreak()

chdir()

getppid()

sigaction()

tcsetattr()

chmod()

getuid()

sigaddset()

tcsetpgrp()

chown()

kill()

sigdelset()

time()

close()

link()

sigemptyset()

times()

creat()

lseek()

sigfillset()

umask()

dup2()

mkdir()

sigismember()

uname()

dup()

mkfifo()

sigpending()

unlink()

execle()

open()

sigprocmask()

utime()

execve()

pathconf()

sigsuspend()

wait()

fcntl()

pause()

sleep()

waitpid()

fork()

pipe()

stat()

write()