哈嘍,我是老吳,俺又來(lái)分享文章啦!
渾渾噩噩到了 30 歲,距離開滴滴還有 5 年的時(shí)間。
還有機(jī)會(huì)全身而退嗎?
哈哈!
30 而立,今年會(huì)是值得拼搏的一年,干它!
以下是正文:
一、Linux 的 5 種 IO 模型
二、如何使用信號(hào)驅(qū)動(dòng)式 I/O?
三、內(nèi)核何時(shí)會(huì)發(fā)送 "IO 就緒" 信號(hào)?
四、最簡(jiǎn)單的示例
五、擴(kuò)展知識(shí)
一、Linux 的 5 種 IO 模型
阻塞式 I/O:
系統(tǒng)調(diào)用可能因?yàn)闊o(wú)法立即完成而被操作系統(tǒng)掛起,直到等待的事件發(fā)生為止。
非阻塞式 I/O (O_NONBLOCK):
系統(tǒng)調(diào)用則總是立即返回,而不管事件是否已經(jīng)發(fā)生。
I/O 復(fù)用 (select、poll、epoll):
通過 I/O 復(fù)用函數(shù)向內(nèi)核注冊(cè)一組事件,內(nèi)核通過 I/O 復(fù)用函數(shù)把其中就緒的事件通知給應(yīng)用程序。
信號(hào)驅(qū)動(dòng)式 I/O (SIGIO):
為一個(gè)目標(biāo)文件描述符指定宿主進(jìn)程,當(dāng)文件描述符上有事件發(fā)生時(shí),SIGIO 的信號(hào)處理函數(shù)將被觸發(fā),然后便可對(duì)目標(biāo)文件描述符執(zhí)行 I/O 操作。
異步 I/O (POSIX 的 aio_ 系列函數(shù)):
異步 I/O 的讀寫操作總是立即返回,而不論 I/O 是否是阻塞的,真正的讀寫操作由內(nèi)核接管。
思考一下,什么時(shí)候應(yīng)該選擇何種 I/O 模型?為何要這么選擇?
下面重點(diǎn)關(guān)注信號(hào)驅(qū)動(dòng)式 I/O 這一模型,其他模型可查閱文末參考書籍。
二、如何使用信號(hào)驅(qū)動(dòng)式 I/O?
一般通過如下 6 個(gè)步驟來(lái)使用信號(hào)驅(qū)動(dòng)式 I/O 模型。
1> 為通知信號(hào)安裝處理函數(shù)。
intsigaction(intsignum,conststructsigaction*act,structsigaction*oldact);
默認(rèn)情況下,這個(gè)通知信號(hào)為 SIGIO。
2> 為文件描述符的設(shè)置屬主。
通過 fcntl() 的 F_SETOWN 操作來(lái)完成:
fcntl(fd,F_SETOWN,pid)
屬主是當(dāng)文件描述符上可執(zhí)行 I/O 時(shí),會(huì)接收到通知信號(hào)的進(jìn)程或進(jìn)程組。
pid 為正整數(shù)時(shí),代表了進(jìn)程 ID 號(hào)。
pid 為負(fù)整數(shù)時(shí),它的絕對(duì)值就代表了進(jìn)程組 ID 號(hào)。
3> 使能非阻塞 I/O。
通過 fcntl() 的 F_SETFL 操作來(lái)完成:
flags=fcntl(fd,F_GETFL);
fcntl(fd,F_SETFL,flags|O_NONBLOCK);
4> 使能信號(hào)驅(qū)動(dòng) I/O。
通過 fcntl() 的 F_SETFL 操作來(lái)完成:
flags=fcntl(fd,F_GETFL);
fcntl(fd,F_SETFL,flags|O_ASYNC);
5> 進(jìn)程等待 "IO 就緒" 信號(hào)的到來(lái)。
當(dāng) I/O 操作就緒時(shí),內(nèi)核會(huì)給進(jìn)程發(fā)送一個(gè)信號(hào),然后調(diào)用在第 1 步中安裝好的信號(hào)處理函數(shù)。
6> 進(jìn)程盡可能多地執(zhí)行 I/O 操作。
循環(huán)執(zhí)行 I/O 系統(tǒng)調(diào)用直到失敗為止,此時(shí)錯(cuò)誤碼為 EAGAIN 或 EWOULDBLOCK。
原因:
信號(hào)驅(qū)動(dòng) I/O 提供的是邊緣觸發(fā)通知,即只有當(dāng) I/O 事件發(fā)生時(shí)我們才會(huì)收到通知,
且當(dāng)文件描述符收到 I/O 事件通知時(shí),并不知道要處理多少 I/O 數(shù)據(jù)。
三、內(nèi)核何時(shí)會(huì)發(fā)送 "IO 就緒" 信號(hào)?
對(duì)于不同類型的文件描述符,情況不一樣。
1> 終端
- 對(duì)于終端,當(dāng)有新的輸入時(shí)會(huì)會(huì)產(chǎn)生信號(hào)。
2> 管道和 FIFO
對(duì)于讀端,下列情況會(huì)產(chǎn)生信號(hào):
- 數(shù)據(jù)寫入到管道中;
- 管道的寫端關(guān)閉;
對(duì)于寫端,下列情況會(huì)產(chǎn)生信號(hào):
- 對(duì)管道的讀操作增加了管道中的空余空間大小。
- 管道的讀端關(guān)閉;
3> 套接字
對(duì)于 UDP 套接字,下列情況會(huì)產(chǎn)生信號(hào):
- 數(shù)據(jù)報(bào)到達(dá)套接字;
- 套接字上發(fā)生異步錯(cuò)誤;
對(duì)于 TCP 套接字,信號(hào)驅(qū)動(dòng)式 I/O 近乎無(wú)用。
- 太多情況都會(huì)產(chǎn)生信號(hào),而我們又無(wú)法得知事件類型,因此這里就不再列舉其產(chǎn)生信號(hào)的情況。
四、最簡(jiǎn)單的示例
信號(hào)處理函數(shù):
staticvolatilesig_atomic_tgotSigio=0;
staticvoidhandler(intsig)
{
gotSigio=1;
}
主程序:
intmain(intargc,char*argv[])
{
intflags,j,cnt;
structtermiosorigTermios;
charch;
structsigactionsa;
intdone;
/*Establishhandler*/
sigemptyset(&sa.sa_mask);
sa.sa_flags=SA_RESTART;
sa.sa_handler=handler;
if(sigaction(SIGIO,&sa,NULL)==-1){
perror("sigaction()
");
exit(1);
}
/*Setownerprocess*/
if(fcntl(STDIN_FILENO,F_SETOWN,getpid())==-1){
perror("fcntl()/F_SETOWN
");
exit(1);
}
/*Enable"I/Opossible"signalingandmakeI/Ononblocking*/
flags=fcntl(STDIN_FILENO,F_GETFL);
if(fcntl(STDIN_FILENO,F_SETFL,flags|O_ASYNC|O_NONBLOCK)==-1){
perror("fcntl()/F_SETFL
");
exit(1);
}
for(done=0,cnt=0;!done;cnt++){
sleep(1);
if(gotSigio){
gotSigio=0;
/*Readallavailableinputuntilerror(probablyEAGAIN)
orEOF*/
while(read(STDIN_FILENO,&ch,1)>0&&!done){
printf("cnt=%d;read%c
",cnt,ch);
done=ch=='#';
}
}
}
exit(0);
}
運(yùn)行效果:
./build/sigio
a
cnt=0;reada
cnt=0;read
abc
cnt=4;reada
cnt=4;readb
cnt=4;readc
cnt=4;read
#
cnt=7;read#
該程序會(huì)先使能信號(hào)驅(qū)動(dòng) IO,然后循環(huán)執(zhí)行計(jì)數(shù)操作。
當(dāng)有 IO 就緒信號(hào)到來(lái)時(shí),會(huì)去終端讀取數(shù)據(jù)并打印出來(lái),然后繼續(xù)執(zhí)行計(jì)數(shù)操作。
五、擴(kuò)展知識(shí)
I/O 多路復(fù)用 、信號(hào)驅(qū)動(dòng) I/O 以及 epoll 機(jī)制可用于監(jiān)視多個(gè)文件描述符。
它們并不實(shí)際執(zhí)行 I/O 操作,當(dāng)某個(gè)文件描述符處于就緒態(tài),仍需采用傳統(tǒng)的 I/O 系統(tǒng)調(diào)用來(lái)完成 I/O 操作。
相比 I/O 多路復(fù)用,當(dāng)監(jiān)視大量的文件描述符時(shí)信號(hào)驅(qū)動(dòng) I/O 有著顯著的性能優(yōu)勢(shì),原因是內(nèi)核能夠幫進(jìn)程記錄了正在監(jiān)視的文件描述符列表。
信號(hào)驅(qū)動(dòng) I/O 的缺點(diǎn):
-
信號(hào)的處理流程較為復(fù)雜;
-
無(wú)法指定需要監(jiān)控的事件類型。
Linux 特有的 epoll 是一個(gè)更好的選擇。
六、相關(guān)參考
UNIX 網(wǎng)絡(luò)編程卷1
- 6.2 I/O模型
- 25 信號(hào)驅(qū)動(dòng)式I/O
Linux-UNIX 系統(tǒng)編程手冊(cè)
- 63 其他備選的I/O模型
Linux 高性能服務(wù)器編程
- 8.3 I/O 模型
Linux 多線程服務(wù)端編程_使用muduo C++網(wǎng)絡(luò)庫(kù)
- 7.4.1 muduo的IO模型
審核編輯 :李倩
-
Linux
+關(guān)注
關(guān)注
87文章
11353瀏覽量
210601 -
信號(hào)處理
+關(guān)注
關(guān)注
48文章
1043瀏覽量
103434 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4349瀏覽量
63033
原文標(biāo)題:思考技術(shù),也思考人生
文章出處:【微信號(hào):zhuyandz,微信公眾號(hào):FPGA之家】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
OpenAI即將推出GPT-5模型
請(qǐng)問有沒有不在linux上對(duì).pt模型向.kmodel轉(zhuǎn)換的教程呢?
λ-IO:存儲(chǔ)計(jì)算下的IO棧設(shè)計(jì)
![λ-<b class='flag-5'>IO</b>:存儲(chǔ)計(jì)算下的<b class='flag-5'>IO</b>棧設(shè)計(jì)](https://file1.elecfans.com/web3/M00/00/AF/wKgZO2dNHbaAIkp-AAAM3550zYU863.png)
Linux--IO多路復(fù)用(select,poll,epoll)
華納云監(jiān)視Linux磁盤IO性能命令:iotop,iostat,vmstat,atop,dstat,ioping
本地IO與遠(yuǎn)程IO:揭秘工業(yè)自動(dòng)化中的兩大關(guān)鍵角色
請(qǐng)問如何將HSPICE和 IBIS兩種模型怎么轉(zhuǎn)換成TINA軟件中用?
linux驅(qū)動(dòng)程序如何加載進(jìn)內(nèi)核
Linux磁盤IO詳細(xì)解析
![<b class='flag-5'>Linux</b>磁盤<b class='flag-5'>IO</b>詳細(xì)解析](https://file1.elecfans.com/web2/M00/01/89/wKgaomawhMqAf6B7AAAMGWWLN8I803.png)
一體式IO與分布式IO:工業(yè)控制系統(tǒng)的兩種架構(gòu)
![一體式<b class='flag-5'>IO</b>與分布式<b class='flag-5'>IO</b>:工業(yè)控制系統(tǒng)的兩<b class='flag-5'>種</b>架構(gòu)](https://file1.elecfans.com/web2/M00/FD/ED/wKgaomaXfAKAQqW9AAz1XZeTcnQ637.png)
評(píng)論