自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Linux-C 編程 | 3 分鐘快速了解信號驅(qū)動式 IO

系統(tǒng)
系統(tǒng)調(diào)用可能因為無法立即完成而被操作系統(tǒng)掛起,直到等待的事件發(fā)生為止。系統(tǒng)調(diào)用則總是立即返回,而不管事件是否已經(jīng)發(fā)生。

[[384784]]

本文轉(zhuǎn)載自微信公眾號「嵌入式Hacker」,作者嵌入式Hacker。轉(zhuǎn)載本文請聯(lián)系嵌入式Hacker公眾號。

一、Linux 的 5 種 IO 模型

二、如何使用信號驅(qū)動式 I/O?

三、內(nèi)核何時會發(fā)送 "IO 就緒" 信號?

四、最簡單的示例

五、擴展知識

一、Linux 的 5 種 IO 模型

阻塞式 I/O:

系統(tǒng)調(diào)用可能因為無法立即完成而被操作系統(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)核注冊一組事件,內(nèi)核通過 I/O 復(fù)用函數(shù)把其中就緒的事件通知給應(yīng)用程序。

信號驅(qū)動式 I/O (SIGIO):

為一個目標(biāo)文件描述符指定宿主進程,當(dāng)文件描述符上有事件發(fā)生時,SIGIO 的信號處理函數(shù)將被觸發(fā),然后便可對目標(biāo)文件描述符執(zhí)行 I/O 操作。

異步 I/O (POSIX 的 aio_ 系列函數(shù)):

異步 I/O 的讀寫操作總是立即返回,而不論 I/O 是否是阻塞的,真正的讀寫操作由內(nèi)核接管。

思考一下,什么時候應(yīng)該選擇何種 I/O 模型?為何要這么選擇?

下面重點關(guān)注信號驅(qū)動式 I/O 這一模型,其他模型可查閱文末參考書籍。

二、如何使用信號驅(qū)動式 I/O?

一般通過如下 6 個步驟來使用信號驅(qū)動式 I/O 模型。

1> 為通知信號安裝處理函數(shù)。

通過 sigaction() 來完成:

  1. int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); 

默認情況下,這個通知信號為 SIGIO。

2> 為文件描述符的設(shè)置屬主。

通過 fcntl() 的 F_SETOWN 操作來完成:

  1. fcntl(fd, F_SETOWN, pid) 

屬主是當(dāng)文件描述符上可執(zhí)行 I/O 時,會接收到通知信號的進程或進程組。

pid 為正整數(shù)時,代表了進程 ID 號。

pid 為負整數(shù)時,它的絕對值就代表了進程組 ID 號。

3> 使能非阻塞 I/O。

通過 fcntl() 的 F_SETFL 操作來完成:

  1. flags = fcntl(fd, F_GETFL); 
  2. fcntl(fd, F_SETFL, flags | O_NONBLOCK); 

4> 使能信號驅(qū)動 I/O。

通過 fcntl() 的 F_SETFL 操作來完成:

  1. flags = fcntl(fd, F_GETFL); 
  2. fcntl(fd, F_SETFL, flags | O_ASYNC); 

5> 進程等待 "IO 就緒" 信號的到來。

當(dāng) I/O 操作就緒時,內(nèi)核會給進程發(fā)送一個信號,然后調(diào)用在第 1 步中安裝好的信號處理函數(shù)。

6> 進程盡可能多地執(zhí)行 I/O 操作。

循環(huán)執(zhí)行 I/O 系統(tǒng)調(diào)用直到失敗為止,此時錯誤碼為 EAGAIN 或 EWOULDBLOCK。

原因:

信號驅(qū)動 I/O 提供的是邊緣觸發(fā)通知,即只有當(dāng) I/O 事件發(fā)生時我們才會收到通知,

且當(dāng)文件描述符收到 I/O 事件通知時,并不知道要處理多少 I/O 數(shù)據(jù)。

三、內(nèi)核何時會發(fā)送 "IO 就緒" 信號?

對于不同類型的文件描述符,情況不一樣。

1> 終端

對于終端,當(dāng)有新的輸入時會會產(chǎn)生信號。

2> 管道和 FIFO

對于讀端,下列情況會產(chǎn)生信號:

  • 數(shù)據(jù)寫入到管道中;
  • 管道的寫端關(guān)閉;

對于寫端,下列情況會產(chǎn)生信號:

  • 對管道的讀操作增加了管道中的空余空間大小。
  • 管道的讀端關(guān)閉;

3> 套接字

對于 UDP 套接字,下列情況會產(chǎn)生信號:

  • 數(shù)據(jù)報到達套接字;
  • 套接字上發(fā)生異步錯誤;

對于 TCP 套接字,信號驅(qū)動式 I/O 近乎無用。

太多情況都會產(chǎn)生信號,而我們又無法得知事件類型,因此這里就不再列舉其產(chǎn)生信號的情況。

四、最簡單的示例

信號處理函數(shù):

  1. static volatile sig_atomic_t gotSigio = 0; 
  2.  
  3. static void handler(int sig) 
  4.     gotSigio = 1; 

主程序:

  1. int main(int argc, char *argv[]) 
  2.     int flags, j, cnt; 
  3.     struct termios origTermios; 
  4.     char ch; 
  5.     struct sigaction sa; 
  6.     int done; 
  7.  
  8.     /* Establish handler */ 
  9.     sigemptyset(&sa.sa_mask); 
  10.     sa.sa_flags = SA_RESTART; 
  11.     sa.sa_handler = handler; 
  12.     if (sigaction(SIGIO, &sa, NULL) == -1) { 
  13.         perror("sigaction()\n"); 
  14.         exit(1); 
  15.     } 
  16.  
  17.     /* Set owner process */ 
  18.     if (fcntl(STDIN_FILENO, F_SETOWN, getpid()) == -1) { 
  19.         perror("fcntl() / F_SETOWN\n"); 
  20.         exit(1); 
  21.     } 
  22.  
  23.     /* Enable "I/O possible" signaling and make I/O nonblocking */ 
  24.     flags = fcntl(STDIN_FILENO, F_GETFL); 
  25.     if (fcntl(STDIN_FILENO, F_SETFL, flags | O_ASYNC | O_NONBLOCK) == -1) { 
  26.         perror("fcntl() / F_SETFL\n"); 
  27.         exit(1); 
  28.     } 
  29.  
  30.     for (done = 0, cnt = 0; !done ; cnt++) { 
  31.         sleep(1); 
  32.  
  33.         if (gotSigio) { 
  34.             gotSigio = 0; 
  35.  
  36.             /* Read all available input until error (probably EAGAIN) 
  37.                or EOF */ 
  38.             while (read(STDIN_FILENO, &ch, 1) > 0 && !done) { 
  39.                 printf("cnt=%d; read %c\n", cnt, ch); 
  40.                 done = ch == '#'
  41.             } 
  42.         } 
  43.     } 
  44.     exit(0); 

運行效果:

  1. ./build/sigio  
  2. cnt=0; read a 
  3. cnt=0; read  
  4.  
  5. abc 
  6. cnt=4; read a 
  7. cnt=4; read b 
  8. cnt=4; read c 
  9. cnt=4; read  
  10.  
  11. cnt=7; read # 

該程序會先使能信號驅(qū)動 IO,然后循環(huán)執(zhí)行計數(shù)操作。

當(dāng)有 IO 就緒信號到來時,會去終端讀取數(shù)據(jù)并打印出來,然后繼續(xù)執(zhí)行計數(shù)操作。

五、擴展知識

I/O 多路復(fù)用 、信號驅(qū)動 I/O 以及 epoll 機制可用于監(jiān)視多個文件描述符。

它們并不實際執(zhí)行 I/O 操作,當(dāng)某個文件描述符處于就緒態(tài),仍需采用傳統(tǒng)的 I/O 系統(tǒng)調(diào)用來完成 I/O 操作。

相比 I/O 多路復(fù)用,當(dāng)監(jiān)視大量的文件描述符時信號驅(qū)動 I/O 有著顯著的性能優(yōu)勢,原因是內(nèi)核能夠幫進程記錄了正在監(jiān)視的文件描述符列表。

信號驅(qū)動 I/O 的缺點:

  • 信號的處理流程較為復(fù)雜;
  • 無法指定需要監(jiān)控的事件類型。

Linux 特有的 epoll 是一個更好的選擇。

六、相關(guān)參考

UNIX 網(wǎng)絡(luò)編程卷1

  • 6.2 I/O模型
  • 25 信號驅(qū)動式I/O

Linux-UNIX 系統(tǒng)編程手冊

  • 63 其他備選的I/O模型

Linux 高性能服務(wù)器編程

  • 8.3 I/O 模型

Linux 多線程服務(wù)端編程_使用muduo C++網(wǎng)絡(luò)庫

  • 7.4.1 muduo的IO模型

 

責(zé)任編輯:武曉燕 來源: 嵌入式Hacker
相關(guān)推薦

2021-03-31 09:10:25

UI設(shè)計經(jīng)驗

2016-12-30 13:43:35

異步編程RxJava

2023-11-28 09:17:05

Linux編程

2016-08-11 10:11:07

JavaScript函數(shù)編程

2016-08-11 10:34:37

Javascript函數(shù)編程

2009-11-12 10:05:09

Visual C++

2024-01-09 18:09:43

模型方式DMA

2020-10-18 07:13:44

Linux系統(tǒng)編程信號捕捉

2018-05-06 16:26:03

關(guān)聯(lián)規(guī)則數(shù)據(jù)分析關(guān)聯(lián)規(guī)則推薦

2020-07-17 07:44:25

云計算邊緣計算IT

2021-01-27 18:15:01

Docker底層宿主機

2020-03-23 15:18:26

知識圖譜Web

2020-09-25 07:34:40

Linux系統(tǒng)編程信號量

2018-07-02 15:18:39

Linux文件系統(tǒng)

2017-10-26 08:53:38

前端JavaScript函數(shù)式編程

2022-09-08 11:12:09

ES6JavaScript

2024-11-07 16:09:53

2017-03-01 13:06:39

Linux驅(qū)動技術(shù)DMA編程

2021-01-06 05:23:15

ServiceMesh網(wǎng)絡(luò)阿帕網(wǎng)

2023-02-13 09:01:29

Linux驅(qū)動實例
點贊
收藏

51CTO技術(shù)棧公眾號