💓博主CSDN主頁:杭電碼農-NEO💓
?
?專欄分類:Linux從入門到精通?
?
🚚代碼倉庫:NEO的學習日記🚚
?
🌹關注我🫵帶你學更多操作系統知識
? 🔝🔝
Linux高級IO
- 1. 前言
- 2. 重談對IO的理解
- 3. 阻塞IO講解
- 4. 非阻塞IO講解
- 5. 信號驅動IO
- 6. IO多路轉接
- 7. 異步IO
- 8. 理解異步和同步
- 9. 總結以及拓展
1. 前言
本篇文章開始, 將與大家分享高級IO相關的內容
本章重點:
本篇文章會帶大家初識五種常見的IO模型, 分別是: 阻塞IO, 非阻塞IO, 信號驅動IO, IO多路轉接, 異步IO. 其中, 多路轉接將會是本系列文章后續的重點
2. 重談對IO的理解
IO: input or output --> 訪問外設 效率低
IO一定是非常低效的, 以讀取為例:
當我們read/recv時, 如果底層緩沖區
沒有數據
, read/recv函數會阻塞
當我們read/recv時, 如果底層緩沖區有數據
, read/recv函數會拷貝
所以說, IO = 等待 + 拷貝 !!!
記住這句話, 會一直貫穿整個IO系列文章
探討低效IO和高效IO:
很明顯, IO = 等待 + 拷貝.
所以可以得出下面的結論:
- 低效IO: 單位時間內, 大部分時間, IO類接口都在等待
- 高效IO: 讓等待的比重降低
接下來的五種IO模型, 就是圍繞著是否高效進行的
3. 阻塞IO講解
正如其名, 阻塞IO 在內核將數據準備好之前, 系統調用會一直等待, 并且所有的套接字默認都是阻塞IO. 阻塞IO是最常見的IO模型, 它比較好理解, 下面是關于阻塞IO的圖像講解
通俗來講, 阻塞IO就是, 你去河邊釣魚, 只拿一根魚竿等于上鉤, 并且時刻盯著水面
4. 非阻塞IO講解
顧名思義, 非阻塞IO就是說, 當底層數據沒有準備就緒時, 不會傻傻的等待, 而是直接返回. 但是調用recv時, 出現錯誤也會直接返回, 應該怎樣區分這兩種情況呢? 答案是阻塞式IO的正常返回時, 會將errno全局遍歷設置為宏: EWOULDBLOCK. 這下就能將它們區分開了
非阻塞IO往往需要程序員循環的方式反復嘗試讀寫文件描述符, 這個過程稱為輪詢. 這對CPU來說是較大的浪費, 一般只有特定場景下才使用. 下面是非阻塞IO的簡單示例:
#include <fcntl.h>
#include <unistd.h>
#include<errno.h>
#include <cstdlib>
#include <iostream>
using namespace std;//對指定的fd設置非阻塞
void SetNonBlock(int fd) {int fl = fcntl(fd, F_GETFL);if (fl < 0) {cerr << "fcntl error" << endl;exit(1);}fcntl(fd, F_SETFL, fl | O_NONBLOCK);
}int main() {SetNonBlock(0);while (1) {char buffer[1024];ssize_t s = read(0, buffer, sizeof(buffer) - 1);if (s > 0) {buffer[s] = 0;cout << buffer << endl;} else if (s == 0) {cout << "讀到文件結尾了" << endl;break;}else{//1. 數據沒用準備好 2. 真的出錯了. 都以-1的返回值返回// 數據沒有準備好,不算出錯. 需要區分這兩種情況if(errno == EWOULDBLOCK || errno == EAGAIN){cout<<"os底層數據還沒就緒"<<endl;cout<<errno<<endl;}//被信號中斷, 也不算read出錯else if(errno == EINTR){cout<<"IO interrupted by signal"<<endl;}else{cout<<"read error"<<endl;break;}}sleep(1);}
}
調用fcntl函數將fd設置為非阻塞
通俗來講, 非阻塞IO就是, 你去河邊釣魚, 也只用一根魚竿, 但是你過一分鐘才去看看有沒有魚上鉤, 其他時間你可能在刷抖音
5. 信號驅動IO
信號驅動IO: 內核將數據準備好的時候, 使用SIGIO信號通知應用程序進行IO操作. 也就是說信號驅動的方式你不用像非阻塞IO一樣, 每過一段時間去檢查是否有數據就緒, 一旦有數據就緒, 會有信號通知你, 這也就可以更多時間刷抖音了(不是)
通俗來講, 信號驅動IO就是, 你去河邊釣魚, 也只拿一個魚竿, 只不過魚竿上有壓力傳感器, 一旦有魚上鉤就會發出聲音提醒你. 其余時間我們當然可以愉快的刷抖音
6. IO多路轉接
前面幾個釣魚的人是不是有點寒酸了?一次只拿一個魚竿, 效率太低了吧! 多路轉接直接把桌子掀了, 它拿了100個魚竿去釣魚: IO多路轉接能夠同時等待多個文件描述符的就緒狀態
通俗來說就是你拿一百個魚竿去釣魚, 同時等待一百種可能, 一旦有魚上鉤了, 會同時把所有上鉤的魚都拉上來, 這效率簡直是指數級增長, 所以這也是在實際生活中使用的最多的IO方案
7. 異步IO
前面所有的IO方式, 都是同步IO, IO=等待+拷貝, 同步IO就是要么參與了等待過程, 要么參與了拷貝過程, 要么都參與了. 而異步IO則是等待和拷貝都不參與: 由內核在數據拷貝完成時, 通知應用程序(而信號驅動是告訴應用程序何時可以開始拷貝數據).
還是拿釣魚的例子來說, 前面的釣魚者, 不管你一次性帶多少魚竿(多路轉接), 不管你在魚竿上安裝什么高科技(信號驅動), 但是你總得去河邊, 自己拿著魚竿釣魚. 而異步IO是怎么做的呢? 他直接雇傭了一個人幫它去釣魚, 什么時候魚上鉤, 你等待了多久我都不在乎, 我只需要你在晚上九點的時候將釣的魚全部帶給我即可.
8. 理解異步和同步
同步和異步關注的是消息通信機制.
同步和異步在實際場景中怎樣運用?
雖然說大部分IO類型都是同步IO, 但是實際生活中運用異步IO的概率也不小. 舉個例子, 你是王者榮耀的后端, 一個英雄放了一個技能打在對面身上, 此時我們后端要將這個操作做成同步的還是異步的? 很明顯是同步, 因為我想要實時的看見對面英雄的血條在減少. 再舉個例子, 現在你是QQ的后端, 你現在要查詢一千萬個QQ號中, 有哪些QQ號超過1個月沒有上線了. 你把此功能做成同步還是異步? 很明顯考慮到成本問題一定是做成異步, 一千萬個QQ號如果用一臺機器可能會查詢幾個小時, 你可能會說, 那我可以用多臺機器做負載均衡, 是的沒錯, 但是機器數量多了, 成本就上去了. 所以做成異步的IO比較好
綜上所述, 實際場景中要根據自己的情況和需求來覺得使用同步還是異步, 不要覺得在學習時都用同步, 以后工作了也就無腦的用同步
9. 總結以及拓展
本篇文章只是簡單的介紹了IO模型的幾個分類, 其中, 最重要的模型是多路轉接, 后面的文章會著重講解它. 多路轉接為什么重要? 因為它是業內最常用的用來提高并發性的模型, 后續大家都接觸都reactor模型, 而reactor模型可以有多種實現方式, 而效率最高的reactor模型則是用多路轉接實現的!!!