マルチスレッドのプログラミング

シグナルハンドラと「非同期シグナル安全」

スレッド安全と似た概念に、「非同期シグナル安全」があります。「非同期シグナル安全」操作は、割り込まれている操作を妨げないことが保証されています。

「非同期シグナル安全」に関する問題が生じるのは、現在の操作がシグナルハンドラによる割り込みで動作を妨げる可能性があるときです。

たとえば、printf(3C) の呼び出し中にシグナルが発生し、このシグナルのハンドラが printf() を呼び出すとします。その場合は、2 つの printf() 文の出力が混ざり合ってしまいます。この問題を避けるには、printf() がシグナルに割り込まれる可能性があるときは、シグナルハンドラが printf() を呼び出さないようにします。

この問題は、同期プリミティブを使用しても解決できません。シグナルハンドラと同期対象の操作の同期をとろうとすると、たちまちデッドロックが発生します。

たとえば、printf() が自分自身を相互排他ロックで保護していると仮定します。あるスレッドが printf() を呼び出している最中に、つまり相互排他ロックを保持した状態にある時に、シグナルにより割り込まれたとします。

このハンドラが printf() を呼び出す場合、すでに相互排他ロックを保持するスレッドが、新たに相互排他ロックを保持しようとします。その結果、即座にデッドロックが発生します。

ハンドラと操作の干渉を回避するには、そうした状況が決して発生しないようにします。たとえば、危険領域でシグナルをマスクしたり、シグナルハンドラ内部では「非同期シグナル安全」操作以外は使用しないようにする方法があります。

表 5–2 に、POSIX が「非同期シグナル安全」を保証しているルーチンを示します。どのようなシグナルハンドラも、これらの関数を安全に呼び出すことができます。

表 5–2 「非同期シグナル安全」関数

_Exit()

fpathconf()

read()

sigset()

_exit()

fstat()

readlink()

sigsuspend()

abort()

fsync()

recv()

sockatmark()

accept()

ftruncate()

recvfrom()

socket()

access()

getegid()

recvmsg()

socketpair()

aio_error()

geteuid()

rename()

stat()

aio_return()

getgid()

rmdir()

symlink()

aio_suspend()

getgroups()

select()

sysconf()

alarm()

getpeername()

sem_post()

tcdrain()

bind()

getpgrp()

send()

tcflow()

cfgetispeed()

getpid()

sendmsg()

tcflush()

cfgetospeed()

getppid()

sendto()

tcgetattr()

cfsetispeed()

getsockname()

setgid()

tcgetattr()

cfsetospeed()

getsockopt()

setpgid()

tcsendbreak()

chdir()

getuid()

setsid()

tcsetattr()

chmod()

kill()

setsockopt()

tcsetpgrp()

chown()

リンク()

setuid()

time()

clock_gettime()

listen()

shutdown()

timer_getoverrun()

close()

lseek()

sigaction()

timer_gettime()

connect()

lstat()

sigaddset()

timer_settime()

creat()

mkdir()

sigdelset()

times()

dup()

mkfifo()

sigemptyset()

umask()

dup2()

open()

sigfillset()

uname()

execle()

pathconf()

sigismember()

ulink()

execve()

pause()

sleep()

utime()

fchmod()

pipe()

signal()

wait()

fchown()

poll()

sigpause()

waitpid()

fcntl()

posix_trace_event()

sigpending ()

write()

fdatasync()

pselect()

sigprocmask()

 

fork()

raise()

sigqueue()