# 系統程式設計 - Interrupted System Call [TOC] ## 課程影片 ### W16 2 (00:00 ~ 17:24) {%youtube eFQBt1zlOg8 %} ## 問題:系統呼叫被信號中斷時怎麼辦? 也就是系統呼叫到一半時,突然出現一個信號,於是這個系統呼叫做到一半時就跳去執行信號處理函式。等到信號處理函式完之後,那要拿這個做到一半的系統呼叫怎麼辦? ## `man` 的說法:重新開始,或回傳 `EINTR` 查詢 [`signal(7)`](https://man7.org/linux/man-pages/man7/signal.7.html) 的手冊: ```shell $ man 7 signal ``` 當中的 **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`.* 也就是說:這個被信號中斷的系統呼叫可能會在信號處理函式結束後「從頭開始重新執行」,或者是直接回傳 `EINTR` 表示錯誤。這會取決於使用 `sigaction` 時,是否使用了 `SA_RESTART` 這個 flag: > *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.* 並且提到:這些細節在不同平台會有不同的行為。 ### 狀況一:設定了 `SA_RESTART` 的信號在特定系統呼叫發生時 不過以 Linux 來說,大致上的原則是:對於「某些」會阻塞的系統呼叫,如果在使用 `sigaction()` 註冊處理函式時有設定 `SA_RESTART` 這個 flag,那麼信號處理函式結束之後,就會把這個系統呼叫從頭開始執行。除了這個狀況之外,其他系統呼叫會失敗,並且回傳 `EINTR`: > If a blocked call to one of the following interfaces is interrupted by a signal handler, then the call is automatically restarted after the signal handler returns if the `SA_RESTART` flag was used; otherwise the call fails with the error `EINTR`: 底下也接著列出這個「某些」系統呼叫是哪些東西。如:`wait()` 系列的函式。 ### 狀況二:被打斷也一定不會重新開始的系統呼叫 同一個章節也有提到:有一些系統呼叫,就算被設定成 `SA_RESTART` 的信號中斷他,他也不會重新開始,而是一被信號打斷時,無論如何都會回傳 `EINTR`。最明顯的是如 ` usleep()` 這類的系統呼叫。如果被中斷就重新開始,那一直被中斷不就一直重新開始計時,沒有結束的一天嗎? 另外一個例子是有逾時設定的 IPC 或 `socket`。這聽起來也很合理:如果對方在時間內有傳訊息過來,結果自己因為被信號中斷而錯過了這個訊息,那它不應該期待對方在沒收到任何資訊的狀況下會自動重送這個訊息。