Linux被中斷的系統(tǒng)如何調(diào)用詳解
前言
慢系統(tǒng)調(diào)用,指的是可能永遠(yuǎn)無(wú)法返回,從而使進(jìn)程永遠(yuǎn)阻塞的系統(tǒng)調(diào)用,比如無(wú)客戶連接時(shí)的accept、無(wú)輸入時(shí)的read都屬于慢速系統(tǒng)調(diào)用。
在Linux中,當(dāng)阻塞于某個(gè)慢系統(tǒng)調(diào)用的進(jìn)程捕獲一個(gè)信號(hào),則該系統(tǒng)調(diào)用就會(huì)被中斷,轉(zhuǎn)而執(zhí)行信號(hào)處理函數(shù),這就是被中斷的系統(tǒng)調(diào)用。
然而,當(dāng)信號(hào)處理函數(shù)返回時(shí),有可能發(fā)生以下的情況:
- 如果信號(hào)處理函數(shù)是用signal注冊(cè)的,系統(tǒng)調(diào)用會(huì)自動(dòng)重啟,函數(shù)不會(huì)返回
- 如果信號(hào)處理函數(shù)是用sigaction注冊(cè)的
- 默認(rèn)情況下,系統(tǒng)調(diào)用不會(huì)自動(dòng)重啟,函數(shù)將返回失敗,同時(shí)errno被置為EINTR
- 只有中斷信號(hào)的SA_RESTART標(biāo)志有效時(shí),系統(tǒng)調(diào)用才會(huì)自動(dòng)重啟
下面我們編寫代碼,分別驗(yàn)證上述幾種情形,其中系統(tǒng)調(diào)用選擇read,中斷信號(hào)選擇SIGALRM,中斷信號(hào)由alarm產(chǎn)生。
使用signal
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>
void handler(int s)
{
printf("read is interrupt by signal handler\n");
return;
}
int main()
{
char buf[10];
int nread = 0;
signal(SIGALRM, handler);
alarm(2);
printf("read start\n");
nread = read(STDIN_FILENO, buf, sizeof(buf));
printf("read return\n");
if ((nread < 0) && (errno == EINTR))
{
printf("read return failed, errno is EINTR\n");
}
return 0;
}

使用sigaction + 默認(rèn)情況
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>
void handler(int s)
{
printf("read is interrupt by signal handler\n");
return;
}
int main()
{
char buf[10];
int nread = 0;
struct sigaction act;
sigemptyset(&act.sa_mask);
act.sa_handler = handler;
act.sa_flags = 0; //不給SIGALRM信號(hào)設(shè)置SA_RESTART標(biāo)志,使用sigaction的默認(rèn)處理方式
//act.sa_flag |= SA_INTERRUPT; //SA_INTERRUPT是sigaction的默認(rèn)處理方式,即不自動(dòng)重啟被中斷的系統(tǒng)調(diào)用
//實(shí)際上,不管act.sa_flags值為多少,只要不設(shè)置SA_RESTART,sigaction都是按SA_INTERRUPT處理的
sigaction(SIGALRM, &act, NULL);
alarm(2);
printf("read start\n");
nread = read(STDIN_FILENO, buf, sizeof(buf));
printf("read return\n");
if ((nread < 0) && (errno == EINTR))
{
printf("read return failed, errno is EINTR\n");
}
return 0;
}

使用sigaction + 指定SA_RESTART標(biāo)志
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>
void handler(int s)
{
printf("read is interrupt by signal handler\n");
return;
}
int main()
{
char buf[10];
int nread = 0;
struct sigaction act;
sigemptyset(&act.sa_mask);
act.sa_handler = handler;
act.sa_flags = 0;
act.sa_flags |= SA_RESTART; //給SIGALRM信號(hào)設(shè)置SA_RESTART標(biāo)志
sigaction(SIGALRM, &act, NULL);
alarm(2);
printf("read start\n");
nread = read(STDIN_FILENO, buf, sizeof(buf));
printf("read return\n");
if ((nread < 0) && (errno == EINTR))
{
printf("read return failed, errno is EINTR\n");
}
return 0;
}

由于對(duì)被中斷系統(tǒng)調(diào)用處理方式的差異性,因此對(duì)應(yīng)用程序來(lái)說(shuō),與被中斷的系統(tǒng)調(diào)用相關(guān)的問(wèn)題是:
- 應(yīng)用程序無(wú)法保證總是知道信號(hào)處理函數(shù)的注冊(cè)方式,以及是否設(shè)置了SA_RESTART標(biāo)志
- 可移植的代碼必須顯式處理關(guān)鍵函數(shù)的出錯(cuò)返回,當(dāng)函數(shù)出錯(cuò)且errno等于EINTR時(shí),可以根據(jù)實(shí)際需求進(jìn)行相應(yīng)處理,比如重啟該函數(shù)
int nread = read(fd, buf, 1024);
if (nread < 0)
{
if (errno == EINTR)
{
//read被中斷,其實(shí)不應(yīng)該算作失敗,可以根據(jù)實(shí)際需求進(jìn)行處理,比如重寫調(diào)用read,也可以忽略它
}
else
{
//read真正的讀錯(cuò)誤
}
}
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
LNMP下FTP服務(wù)器的安裝與使用方法(Pureftpd和Proftpd)
FTP是網(wǎng)站文件維護(hù)中使用比較多的,目前LNMP一鍵安裝包中有Pureftpd和Proftpd服務(wù)器安裝腳本2013-06-06
系統(tǒng)講解Apache Kafka消息管理與異常處理的最佳實(shí)踐
Apache Kafka 作為分布式流處理平臺(tái)的核心組件,廣泛應(yīng)用于實(shí)時(shí)數(shù)據(jù)管道、日志聚合和事件驅(qū)動(dòng)架構(gòu),下面我們就來(lái)系統(tǒng)講解 Kafka 消息管理與異常處理的最佳實(shí)踐吧2025-04-04
Linux系統(tǒng)中卸載與安裝JDK的詳細(xì)教程
本文詳細(xì)介紹了如何在Linux系統(tǒng)中通過(guò)Xshell和Xftp工具連接與傳輸文件,然后進(jìn)行JDK的安裝與卸載,安裝步驟包括連接Linux、傳輸JDK安裝包、解壓并配置環(huán)境變量,卸載過(guò)程則涉及查找JDK路徑、刪除文件及更新環(huán)境變量,需要的朋友可以參考下2025-04-04
apache提示Failed loading ZendLoader.dll解決方法
這篇文章主要介紹了apache提示Failed loading ZendLoader.dll解決方法,需要的朋友可以參考下2015-04-04
Linux 字符設(shè)備驅(qū)動(dòng)框架詳細(xì)介紹
這篇文章主要介紹了Linux 字符設(shè)備驅(qū)動(dòng)框架詳細(xì)介紹的相關(guān)資料,字符設(shè)備就是字節(jié)流形式通訊的I/O設(shè)備,絕大部分設(shè)備都是字符設(shè)備,這里提供簡(jiǎn)單的實(shí)例,需要的朋友可以參考下2016-12-12
Linux修改主機(jī)名(hostname)的兩種方法(親測(cè)可用)
要想在虛擬機(jī)的 Linux 系統(tǒng)內(nèi)部改變主機(jī)名(hostname),需要通過(guò)系統(tǒng)的配置來(lái)修改,在大多數(shù)基于 Red Hat 的 Linux 發(fā)行版(比如 CentOS、Fedora)中,本文小編給大家介紹了;兩種修改主機(jī)名的方法,感興趣的小伙伴跟著小編一起來(lái)看看吧2023-11-11

