huyc

导航

signal(7)

SIGNAL(7) 2008-10-15

NAME

       signal —— 信号概述

DESCRIPTION

Linux支持POSIX可靠信号(以下称之为“标准信号”)和POSIX实时信号。

信号处理方式
每个信号都有一个当前的处理方式,该方式决定了进程被递送信号时该如何处理。
下表的每个表项都在“Action”列指出了信号的默认处理方式,像这样:
       Term   默认动作为终止进程。
       Ign    默认动作是忽略信号。
       Core   默认动作是终止进程并转储(参见core(5))。
       Stop   默认动作是停止进程。
       Cont   默认动作是继续进程执行,如果进程当前处于停止状态。
一个进程可以使用sigaction或signal改变信号的处理方式。使用这些系统调用,一个进程可以选择以下行为之一在信号递送时触发:执行默认动作;忽略信号;用信号处理程序捕获信号。一个程序定义的函数将在递送对应信号时自动触发。(默认情况下,信号处理程序将在正常的进程栈中被调用,也可以安排信号处理程序使用一个替代栈,参见sigaltstack(2)以了解如何做和应该怎么做。)
信号处理方式是一个进程属性,多线程应用下,指定信号的处理方式对于所有线程都是一样的。
一个通过fork创建的子进程继承父进程的信号处理方式。当调用execve时,处理方式为handle的将被重置为default,处理方式为ignore的将继续保持ignore。
发送一个信号
以下系统调用或库函数允许发送一个信号:
raise(3)        发送一个信号到调用线程。(thread)
kill(2)         发送一个信号到指定进程,到进程组的所有成员,到系统中的所有进程。(process)
killpg(2)       发送一个信号到指定进程组的所有成员。(process)
pthread_kill(3) 在同一个进程中,发送一个信号给指定的POSIX线程。(thread)
tgkill(2)       发送一个信号给指定进程的指定线程。(这个系统调用用于实现pthread_kill(3))(thread)
sigqueue(2)     发送一个带附加数据的实时信号给指定进程。(process)
等待捕获信号
以下系统调用挂起调用进程或线程,直到捕获一个信号(或一个不可处理的信号终止进程):
pause(2)        挂起执行,直至任何信号被捕获。
sigsuspend(2)   临时改变信号掩码,且挂起执行,直至一个非掩码的信号被捕获。
同步响应信号
不同于通过信号handler异步捕获信号,同步的的方式响应信号也是可以的,这样做要求阻塞执行直到被递送信号,而后,内核返回信号的信息给调用者。一般通过两种方式来做这个:
* sigwaitinfo(2),sigtimedwait(2),和sigwait(3)阻塞执行直到指定信号集合里面的一个信号被递送。它们都返回递送信号的信息。
* signalfd(2)返回一个文件描述符,它可用于读取递送给调用者的信号信息。每次从文件描述符read都将阻塞,直到signalfd调用时指定的的信号集内的信号之一递送给调用者。read返回的后缓冲区内包含一个描述信号的结构。
信号掩码和未决信号
一个信号将被阻塞,这意味着在解除阻塞之前它不能被递送。在生成之后,被递送之前的这段时间就说该信号是未决的。
进程内的每个线程有一个独立的信号掩码,它指定一个线程当前阻塞的信号集。一个线程可以通过pthread_sigmask(3)操纵它的信号掩码。传统的单线程应用中,通过sigprocmask操纵信号掩码。
一个fork的子进程继承父进程的信号掩码;信号掩码可以跨越execve。
一个信号可以生成(未决)并传递给整个进程(如使用kill(2))或传递给指定线程(如,某些信号,像SIGSEGV和SIGFPE,作为特定机器指令的执行结果而生成是特定于线程的,如需指定线程则使用pthread_kill(3))。一个针对进程的信号也许被递送到任意一个没有阻塞当前信号的线程。如果没有阻塞该信号的线程多于一个,则内核随意挑选一个。
一个线程可以用sigpending获取一个未决信号集。此集合将取进程和调用线程未决信号集的并集。
一个fork初始化的子进程未决信号集合为空集。未决信号集可以跨越execve。
标准信号
Linux支持的标准信号罗列于下。几个信号值依赖于体系结构,在Value列中列出。(给出三组值,第一组对ALPHA和SPARC有效,第二组对ix86,ia64,PPC,S390,ARM,SH有效,最后一组MIPS有效。“-”表示该体系没有对应信号)
第一组信号在POSIX.1-1990标准中描述。
       Signal     Value     Action   Comment
──────────────────────────────────────────────────────────────────────
SIGHUP 1 Term 在控制终端检测到挂起,或控制进程死亡
SIGINT 2 Term 键盘中断,INT
SIGQUIT 3 Core 键盘中断,QUIT
SIGILL 4 Core 非法指令
SIGABRT 6 Core abort(3)递送退出信号
SIGFPE 8 Core 浮点异常
SIGKILL 9 Term kill信号
SIGSEGV 11 Core 引用无效内存
SIGPIPE 13 Term 管道的另外一端已经关闭
其中,信号SIGKILL和SIGSTOP不能被捕获,阻塞或忽略。
下一组信号没在POSIX.1-1990但在SUSv2和POSIX.1-2001中描述。 
       Signal       Value     Action   Comment
────────────────────────────────────────────────────────────────────
SIGBUS 10,7,10 Core Bus error (bad memory access)
SIGPOLL Term 轮询事件(System V)。与SIGIO同义
SIGPROF 27,27,29 Term 分析定时器超时
SIGSYS 12,-,12 Core 参数错误(SVR 4
SIGTRAP 5 Core 跟踪/断点的调试陷阱
SIGURG 16,23,21 Ign 套接字上的紧急条件(4.2BSD)
SIGVTALRM 26,26,28 Term 虚拟定时器(4.2BSD)
SIGXCPU 24,24,30 Core 超过CPU时间上限(4.2BSD)
SIGXFSZ 25,25,31 Core 超过文件大小上限(4.2BSD)
Linux 2.2之后的版本,SIGSYS,SIGXCPU,SIGXFSZ和(除SPARC和MIPS之外的架构)SIGBUS的默认行为是中断进程(没有转储)。(其他一些Unix系统SIGXCPU和SIGXFSZ的默认动作是终止进程)Linux 2.4符合POSIX.1-2001中对信号的要求,终止并转储。
最后的一堆信号。
       Signal       Value     Action   Comment
────────────────────────────────────────────────────────────────────
SIGIOT 6 Core IOT陷阱,SIGABRT的同义词
SIGEMT 7,-,7 Term
SIGSTKFLT -,16,- Term 协处理器堆栈错误(unused)
SIGIO 23,29,22 Term I/O事件(4.2BSD)
SIGCLD -,-,18 Ign SIGCHLD的同义词
SIGPWR 29,30,19 Term 电源断电(System V)
SIGINFO 29,-,- SIGPWR的同义词
SIGLOST -,-,- Term 文件锁丢失
SIGWINCH 28,28,20 Ign 窗口resize(4.3BSD,SunOS)
SIGUNUSED -,31,- Term 未用信号(可能是SIGSYS)
(信号29在ALPHA上是SIGINFO/SIGPWR但在SPARC上是SIGLOST。)
SIGEMT不是POSIX.1-2001指定的,但仍然出现在绝大多数的Unix系统上,典型的默认动作是终止并转储进程。
SIGPWR(没有在POSIX.1-2001中指定)在其他Unix中典型的默认动作是忽略。
SIGIO(没有在POSIX.1-2001)在少数其他Unix中的默认动作是忽略。
实时信号
Linux支持最初在POSIX.1b定义的实时信号扩展(现在已经收录到POSIX.1-2001)。实时信号支持的信号范围定义在SIGRTMIN和SIGRTMAX之间。POSIX.1-2001要求一个实现至少支持_POSIX_RTSIG_MAX个实时信号。
Linux内核支持32个不同的实时信号,33~64。然而,glibc的POSIX线程实现内部使用两个(NPTL)或三个(Linux线程)实时信号(参见pthreads(7)),SIGRTMIN(34或35)也作了相应的调整。因为可用的实时信号范围依赖glibc的线程实现(随着可用内核与glibc的不同这个变化还可以发生在运行时),为确保实时信号的范围依赖系统,程序应该从不引用硬编码的数值来标记信号,而应该引用符号SIGRTMIN+n,当然,也要在运行时确保SIGRTMIN+n不会超过SIGRTMAX。
与标准信号不同,实时信号没有预定义的含义:实时信号可以用于进程定义的目的。(毕竟,Linux线程实现就使用了前三个实时信号)
未处理的实时信号默认终止接收进程。
实时信号有以下特点:
1. 多个实时信号实例可以排队。相比而言,如果当前阻塞了多个标准信号实例,只有一个实例排队。
2. 如果信号是用sigqueue发送的,可以随后附带一个值(要么整数要么指针)。如果接收进程为这个信号调用sigaction注册处理程序时带了SA_SIGINFO标记,则处理函数将获得传递过来的数值,该值位于信号处理程序的第二个参数,siginfo_t结构的si_value字段中。此外,si_pid和si_uid字段可以被用于获得发送此信号的进程的PID和实际用户ID。
3. 实时信号的递送顺序是可预见的。多个同样的实时信号以发送的顺序逐个递送。如果不同的实时信号被发送到一个进程,则它们从低值信号开始递送。(也就是说,小值信号具有高优先级)相比而言,一个进程有多个未决信号,它们的递送顺序是未指定的。
如果某进程上的多个标准信号和实时信号都是未决的,POSIX没有指定应该先递送哪个。Linux下,和很多其他实现一样,优先递送标准信号。
根据POSIX,一个实现应该允许一个进程至少_POSIX_SIGQUEUE_MAX个实时信号排队。然而Linux有些不同,对于Linux 2.6.7之后的版本,Linux规定整个系统的所有进程的实时信号排队上限值。此值可以在/proc/sys/kernel/resig-max文件中找到并修改。一个相关文件/proc/sys/kernel/rtsig-nr内可以找到当前排队了多少个实时信号。Linux 2.6.8下,/proc接口被RLIMIT_SIGPENDING资源限制替代,它指定每个用户的信号排队的限制,参见setrlimit(2)了解详细。
异步信号安全的函数
一个调用sigaction或signal注册的信号处理函数必须非常小心,因为程序的任何一个执行点都可能被打断。POSIX有“安全函数”的概念。如果一个信号打断了不安全的函数,并且信号处理函数还调用了一个不安全的函数,则程序的行为就是未定义的。
POSIX.1-2004(POSIX.1-2001技术勘误2)要求一个实现保证以下函数可以在信号处理函数中被安全调用:
View Code
_Exit()
_exit()
abort()
accept()
access()
aio_error()
aio_return()
aio_suspend()
alarm()
bind()
cfgetispeed()
cfgetospeed()
cfsetispeed()
cfsetospeed()
chdir()
chmod()
chown()
clock_gettime()
close()
connect()
creat()
dup()

dup2()
execle()
execve()
fchmod()
fchown()
fcntl()
fdatasync()
fork()
fpathconf()
fstat()
fsync()
ftruncate()
getegid()
geteuid()
getgid()
getgroups()
getpeername()
getpgrp()
getpid()
getppid()
getsockname()

getsockopt()
getuid()
kill()
link()
listen()
lseek()
lstat()
mkdir()
mkfifo()
open()
pathconf()
pause()
pipe()
poll()
posix_trace_event()
pselect()
raise()
read()
readlink()
recv()
recvfrom()

recvmsg()
rename()
rmdir()
select()
sem_post()
send()
sendmsg()
sendto()
setgid()
setpgid()
setsid()
setsockopt()
setuid()
shutdown()
sigaction()
sigaddset()
sigdelset()
sigemptyset()
sigfillset()

sigismember()
signal()
sigpause()
sigpending()
sigprocmask()
sigqueue()
sigset()
sigsuspend()
sleep()
sockatmark()
socket()
socketpair()
stat()
symlink()
sysconf()
tcdrain()
tcflow()
tcflush()
tcgetattr()
tcgetpgrp()
tcsendbreak()
tcsetattr()
tcsetpgrp()
time()
timer_getoverrun()
timer_gettime()
timer_settime()
times()
umask()
uname()
unlink()
utime()
wait()
waitpid()
write()
POSIX.1-2008从该列表中移除了fpathconf(),pathconf()和sysconf(),并添加了下面这些函数: 
View Code
execl()
execv()
faccessat()
fchmodat()
fchownat()
fexecve()
fstatat()
futimens()
linkat()
mkdirat()
mkfifoat()
mknod()
mknodat()
openat()
readlinkat()
renameat()
symlinkat()
unlinkat()
utimensat()
utimes()
信号处理程序打断系统调用和库函数
如果一个信号处理程序在系统调用或库函数阻塞时被调用,则:
* 要么该调用在信号处理程序返回后自动重启
* 要么该调用出错返回EINTR
两个行为的发生取决于接口,以及信号处理函数注册时是否使用SA_RESTART标记(参见sigaction(2))。具体细节在各个Unix都不尽相同,下面是Linux下的细节。
如果一个阻塞调用以下接口时被信号打断,则调用将在信号处理程序返回后自动重启,如果其注册时应用了SA_RESTART标记。否则调用将以EINTR错误失败:
* read(2),readv(2),write(2),writev(2)和ioctl(2)在慢速设备上调用。慢速设备可以阻塞I/O一段不确定的时间,例如,一个终端,管道,套接字。(根据定义,磁盘不是慢速设备)如果I/O调用在打断之前已经往慢速设备写入了一些数据,则调用将返回一个成功状态(也就是返回传输的字节数)。
* open(2),如果它也被阻塞了(比如,打开一个FIFO,参见fifo(7))
* wait(2),wait3(2),wait4(2),waitid(2),waitpid(2)。
* 套接字接口:accept(2),connect(2),recv(2),recvfrom(2),recvmsg(2),send(2),sendto(2)和sendmsg(2),除非套接字上设置了超时(见下)。
* 文件锁接口:flock(2)和fcntl(2)在调用F_SETLKW时。
* POSIX消息队列接口:mq_receive(3),mq_timedreceive(3),mq_send(3)和mq_timedsend(3)。
* futex(2)调用FUTEX_WAIT(since  Linux  2.6.22,在这之前总是返回EINTR)
* POSIX信号量接口:sem_wait(3)和sem_timedwait(3)(since Linux 2.6.22,在这之前,总是返回EINTR)
下面的接口在被打断后从不重启,即便设置了SA_RESTART。当被信号打断后,他们总是返回EINTR:
* 套接字接口,当套接字用setsockopt设置了超时:accept(2),recv函数族设置SO_RCVTIMEO,connect(2)和send函数族设置SO_SNDTIMEO。
* 用于等待信号的接口:pause(2),sigsuspend(2),sigtimedwait(2)和sigwaitinfo(2)。
* 文件描述符复用接口:epoll_wait(2),epoll_pwait(2),poll(2),ppoll(2),select(2)和pselect(2)。
* System V的IPC接口:msgrcv(2),msgsnd(2),semop(2)和semtimedop(2)。
* sleep接口:clock_nanosleep(2),nanosleep(2)和usleep(3)。
* read(2)从inotify(7)文件描述符中读时。
* io_getevents(2)。
sleep(3)函数被信号打断后从不重启,但是会返回一个成功值,表示剩余的秒数。
系统调用或库函数被停止信号打断
Linux下,即便没有信号处理函数,某些阻塞的接口依然会在进程被停止信号停止后失败,并在SIGCONT唤醒后返回EINTR。这些行为既不符合POSIX.1,也没有在其他系统遇见。
表现这种行为的Linux接口有:
* 套接字接口,使用setsockopt设置了超时:accept(2),recv函数族设置SO_RCVTIMEO,connect(2)和send函数族设置SO_SNDTIMEO。
* epoll_wait(2),epoll_pwait(2)。
* semop(2),semtimedop(2)。
* sigtimedwait(2),sigwaitinfo(2)。
* read(2) 从inotify(7)文件描述符读。
* Linux  2.6.21及其之前的版本:futex(2)调用FUTEX_WAIT,sem_timedwait(3),sem_wait(3)。
* Linux 2.6.8及其之前的版本:msgrcv(2),msgsnd(2)。
* Linux 2.4及其之前的版本:nanosleep(2)。
CONFORMING TO
       POSIX.1, except as noted.
BUGS
SIGIO和SIGLOST取值相同。后来在内核源码中被注释掉了,但一些软件的build程序依然认为信号29是SIGLOST。
SEE ALSO
       kill(1), getrlimit(2), kill(2), killpg(2), setitimer(2),  setrlimit(2),
       sgetmask(2), sigaction(2), sigaltstack(2), signal(2), signalfd(2), sig‐
       pending(2), sigprocmask(2), sigqueue(2), sigsuspend(2), sigwaitinfo(2),
       abort(3), bsd_signal(3), longjmp(3), raise(3), sigset(3), sigsetops(3),
       sigvec(3), sigwait(3), strsignal(3), sysv_signal(3), core(5),  proc(5),
       pthreads(7)
COLOPHON
       This  page  is  part of release 3.23 of the Linux man-pages project.  A
       description of the project, and information about reporting  bugs,  can
       be found at http://www.kernel.org/doc/man-pages/.

posted on 2011-10-20 14:22  huyc  阅读(8226)  评论(0编辑  收藏  举报