Signals

什麼是中斷的系統呼叫?

  • March 30, 2019

我正在閱讀APUE,而中斷的系統呼叫一章讓我感到困惑。

我想根據本書寫下我的理解,請指正。

  1. >

早期 UNIX 系統的一個特點是,如果程序在“慢”系統呼叫中被阻塞時擷取到信號,則係統呼叫會被中斷。系統呼叫返回錯誤並 errno設置為EINTR。這是在假設發生信號並且程序擷取它的情況下完成的,很有可能發生了一些應該喚醒阻塞的系統呼叫的事情。

所以說早期的UNIX 系統有一個特性:如果我的程序使用系統呼叫,它會被中斷/停止,如果程序在任何時候捕捉到一個信號。(預設處理程序也算作擷取嗎?)

例如,如果我有一個read讀取 10GB 數據的系統呼叫,當它讀取時,我發送任何一個信號(例如kill -SIGUSR1 pid),然後read會失敗並返回。


  1. >

為了防止應用程序不得不處理中斷的系統呼叫,4.2BSD 引入了自動重啟某些中斷的系統呼叫。自動重新啟動的系統呼叫是ioctlreadreadvwritewritevwaitwaitpid。正如我們所提到的,前五個函式只有在執行速度較慢的設備時才會被信號中斷。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 實現而異。Linuxsignal(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 */ ;

引用自:https://unix.stackexchange.com/questions/509375