什麼是中斷的系統呼叫?
我正在閱讀APUE,而中斷的系統呼叫一章讓我感到困惑。
我想根據本書寫下我的理解,請指正。
- >
早期 UNIX 系統的一個特點是,如果程序在“慢”系統呼叫中被阻塞時擷取到信號,則係統呼叫會被中斷。系統呼叫返回錯誤並
errno
設置為EINTR
。這是在假設發生信號並且程序擷取它的情況下完成的,很有可能發生了一些應該喚醒阻塞的系統呼叫的事情。所以說早期的UNIX 系統有一個特性:如果我的程序使用系統呼叫,它會被中斷/停止,如果程序在任何時候捕捉到一個信號。(預設處理程序也算作擷取嗎?)
例如,如果我有一個
read
讀取 10GB 數據的系統呼叫,當它讀取時,我發送任何一個信號(例如kill -SIGUSR1 pid
),然後read
會失敗並返回。
- >
為了防止應用程序不得不處理中斷的系統呼叫,4.2BSD 引入了自動重啟某些中斷的系統呼叫。自動重新啟動的系統呼叫是
ioctl
、read
、readv
、write
、writev
、wait
和waitpid
。正如我們所提到的,前五個函式只有在執行速度較慢的設備時才會被信號中斷。wait
並且waitpid
總是在捕捉到信號時被中斷。由於這會導致一些應用程序在操作中斷時不希望重新啟動操作,因此 4.3BSD 允許程序在每個信號的基礎上禁用此功能。所以在引入自動重啟之前,我必須自己處理中斷的系統呼叫。我需要編寫如下程式碼:
中斷系統呼叫的問題是我們現在必須明確地處理錯誤返回。典型的程式碼序列(假設一個讀取操作並假設我們想要重新啟動讀取,即使它被中斷)將是:
again: if ((n = read(fd, buf, BUFFSIZE)) < 0) { if (errno == EINTR) goto again; /* just an interrupted system call */ /* handle other errors */ }
但是現在我不用寫這種程式碼了,因為有自動重啟機制。
因此,如果我的理解都是正確的,那麼我現在應該關心什麼/為什麼要關心中斷的系統呼叫..?似乎系統/作業系統會自動處理它。
信號處理程序對系統呼叫的中斷僅發生在各種阻塞系統呼叫的情況下,並且當系統呼叫被程序員明確建立的信號處理程序中斷時發生。
此外,在阻塞系統呼叫被信號處理程序中斷的情況下,自動系統呼叫重新啟動是一個可選功能。
SA_RESTART
您可以通過在建立信號處理程序時指定標誌來選擇自動重新啟動系統呼叫。如(例如)Linux signal(7)手冊頁中所述: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)).
正如上面引用的最後一句話所暗示的,即使您選擇使用此功能,它也不適用於所有系統呼叫,並且它適用的系統呼叫集因 UNIX 實現而異。Linux
signal(7)
手冊頁記錄了許多在使用該SA_RESTART
標誌時會自動重新啟動的系統呼叫,但還繼續記錄了從未重新啟動的各種系統呼叫,即使您在建立處理程序時指定了該標誌,包括:* "Input" socket interfaces, when a timeout (SO_RCVTIMEO) has been set on the socket using setsockopt(2): accept(2), recv(2), recvfrom(2), recvmmsg(2) (also with a non-NULL timeout argu‐ ment), and recvmsg(2). * "Output" socket interfaces, when a timeout (SO_RCVTIMEO) has been set on the socket using setsockopt(2): connect(2), send(2), sendto(2), and sendmsg(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).
對於這些系統呼叫,使用 APUE 中描述的形式的循環手動重新啟動是必不可少的,例如:
while ((ret = some_syscall(...)) == -1 && errno == EINTR) continue; if (ret == -1) /* Handle error */ ;