一些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.