linux系統編程之信號(七):被信號中斷的系統調用和庫函數處理方式
一些IO系統調用執行時, 如 read 等待輸入期間, 如果收到一個信號,系統將中斷read, 轉而執行信號處理函數. 當信號處理返回後, 系統遇到了一個問題: 是重新開始這個系統調用, 還是讓系統調用失敗?早期UNIX系統的做法是, 中斷系統調用, 並讓系統調用失敗, 比如read返回 -1, 同時設置 errno 為 EINTR中斷了的系統調用是沒有完成的調用, 它的失敗是臨時性的, 如果再次調用則可能成功, 這並不是真正的失敗, 所以要對這種情況進行處理, 典型的方式為:
while (1)
{
n = read(fd, buf, BUFSIZ);
if (n == -1 && errno != EINTR) {
printf("read error\n");
break;
}
if (n == 0) {
printf("read done\n");
break;
}
}
這樣做邏輯比較繁瑣, 事實上, 我們可以從信號的角度來解決這個問題, 安裝信號的時候, 設置 SA_RESTART屬性, 那麼當信號處理函數返回後, 被該信號中斷的系統調用將自動恢復.
示例程序:
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <error.h>
#include <string.h>
#include <unistd.h>
void sig_handler(int signum)
{
printf("in handler\n");
sleep(1);
printf("handler return\n");
}
int main(int argc, char **argv)
{
char buf[100];
int ret;
struct sigaction action, old_action;
action.sa_handler = sig_handler;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
/* 版本1:不設置SA_RESTART屬性
* 版本2:設置SA_RESTART屬性 */
//action.sa_flags |= SA_RESTART;
sigaction(SIGINT, NULL, &old_action);
if (old_action.sa_handler != SIG_IGN) {
sigaction(SIGINT, &action, NULL);
}
bzero(buf, 100);
ret = read(0, buf, 100);
if (ret == -1) {
perror("read");
}
printf("read %d bytes:\n", ret);
printf("%s\n", buf);
return 0;
}
當sa_flags不設置:SA_RESTART時:
結果:
設置後:
當被中斷後,重新執行
man幫助說明:
Interruption of system calls and library functions by signal handlers
If a signal handler is invoked while a system call or library
function call is blocked, then either:
* the call is automatically restarted after the signal handler
returns; or
* the call fails with the error EINTR.
Which of these two behaviors occurs depends on the interface and
whether or not the signal handler was established using the
SA_RESTART flag (see sigaction(2)). The details vary across UNIX
systems; below, the details for Linux.
If a blocked call to one of the following interfaces is interrupted
by a signal handler, then the call will be automatically restarted
after the signal handler returns if the SA_RESTART flag was used;
otherwise the call will fail with the error EINTR:
* read(2), readv(2), write(2), writev(2), and ioctl(2) calls on
"slow" devices. A "slow" device is one where the I/O call may
block for an indefinite time, for example, a terminal, pipe, or
socket. (A disk is not a slow device according to this
definition.) If an I/O call on a slow device has already
transferred some data by the time it is interrupted by a signal
handler, then the call will return a success status (normally,
the number of bytes transferred).
* open(2), if it can block (e.g., when opening a FIFO; see
fifo(7)).
* wait(2), wait3(2), wait4(2), waitid(2), and waitpid(2).
* Socket interfaces: accept(2), connect(2), recv(2), recvfrom(2),
recvmsg(2), send(2), sendto(2), and sendmsg(2), unless a
timeout has been set on the socket (see below).
* File locking interfaces: flock(2) and fcntl(2) F_SETLKW.
* POSIX message queue interfaces: mq_receive(3),
mq_timedreceive(3), mq_send(3), and mq_timedsend(3).
* futex(2) FUTEX_WAIT (since Linux 2.6.22; beforehand, always
failed with EINTR).
* POSIX semaphore interfaces: sem_wait(3) and sem_timedwait(3)
(since Linux 2.6.22; beforehand, always failed with EINTR).
The following interfaces are never restarted after being interrupted
by a signal handler, regardless of the use of SA_RESTART; they always
fail with the error EINTR when interrupted by a signal handler:
* Socket interfaces, when a timeout has been set on the socket
using setsockopt(2): accept(2), recv(2), recvfrom(2), and
recvmsg(2), if a receive timeout (SO_RCVTIMEO) has been set;
connect(2), send(2), sendto(2), and sendmsg(2), if a send
timeout (SO_SNDTIMEO) has been set.
* Interfaces used to wait for signals: pause(2), sigsuspend(2),
sigtimedwait(2), and sigwaitinfo(2).
* File descriptor multiplexing interfaces: epoll_wait(2),
epoll_pwait(2), poll(2), ppoll(2), select(2), and pselect(2).
* System V IPC interfaces: msgrcv(2), msgsnd(2), semop(2), and
semtimedop(2).
* Sleep interfaces: clock_nanosleep(2), nanosleep(2), and
usleep(3).
* read(2) from an inotify(7) file descriptor.
* io_getevents(2).
The sleep(3) function is also never restarted if interrupted by a
handler, but gives a success return: the number of seconds remaining
to sleep.