www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當(dāng)前位置:首頁 > > 小林coding
[導(dǎo)讀]什么是文件?程序員使用I/O最終都逃不過文件這個概念

什么是文件

程序員使用I/O最終都逃不過文件這個概念。

在Linux世界中文件是一個很簡單的概念,作為程序員我們只需要將其理解為一個N byte的序列就可以了:

b1, b2, b3, b4, ....... bN

實際上所有的I/O設(shè)備都被抽象為了文件這個概念,一切皆文件,Everything is File,磁盤、網(wǎng)絡(luò)數(shù)據(jù)、終端,甚至進(jìn)程間通信工具管道pipe等都被當(dāng)做文件對待。

所有的I/O操作也都可以通過文件讀寫來實現(xiàn),這一非常優(yōu)雅的抽象可以讓程序員使用一套接口就能對所有外設(shè)I/O操作

常用的I/O操作接口一般有以下幾類:

  • 打開文件,open

  • 改變讀寫位置,seek

  • 文件讀寫,read、write

  • 關(guān)閉文件,close

程序員通過這幾個接口幾乎可以實現(xiàn)所有I/O操作,這就是文件這個概念的強大之處。


文件描述符

要想進(jìn)行I/O讀操作,像磁盤數(shù)據(jù),我們需要指定一個buff用來裝入數(shù)據(jù),一般都是這樣寫的:

read(buff);
但是這里我們忽略了一個關(guān)鍵問題,那就是雖然我們指定了往哪里寫數(shù)據(jù),但是我們該從哪里讀數(shù)據(jù)呢?
從上一節(jié)中我們知道,通過文件這個概念我們能實現(xiàn)幾乎所有I/O操作, 因此這里少的一個主角就是文件。

那么我們一般都怎樣使用文件呢?

如果周末你去比較火的餐廳吃飯應(yīng)該會有體會,一般周末人氣高的餐廳都會排隊,然后服務(wù)員會給你一個排隊序號,通過這個序號服務(wù)員就能找到你,這里的好處就是服務(wù)員無需記住你是誰、你的名字是什么、來自哪里、喜好是什么、是不是保護環(huán)境愛護小動物等等,這里的關(guān)鍵點就是服務(wù)員對你一無所知,但依然可以通過一個號碼就能找到你。

同樣的,在Linux世界要想使用文件,我們也需要借助一個號碼,根據(jù)“弄不懂原則”,這個號碼就被稱為了文件描述符,file descriptors,在Linux世界中鼎鼎大名,其道理和上面那個排隊號碼一樣。

因此,文件描述僅僅就是一個數(shù)字而已,但是通過這個數(shù)字我們可以操作一個打開的文件,這一點要記住。

有了文件描述符,進(jìn)程可以對文件一無所知,比如文件在磁盤的什么位置、加載到內(nèi)存中又是怎樣管理的等等,這些信息統(tǒng)統(tǒng)交由操作系統(tǒng)打理,進(jìn)程無需關(guān)心,操作系統(tǒng)只需要給進(jìn)程一個文件描述符就足夠了。

因此我們來完善上述程序:

int?fd?=?open(file_name);?//?獲取文件描述符read(fd,?buff);

怎么樣,是不是非常簡單。


文件描述符太多了怎么辦

經(jīng)過了這么多的鋪墊,終于要到高性能、高并發(fā)這一主題了。

從前幾節(jié)我們知道,所有I/O操作都可以通過文件樣的概念來進(jìn)行,這當(dāng)然包括網(wǎng)絡(luò)通信。

如果你有一個web服務(wù)器,當(dāng)三次握手成功以后,我們會調(diào)用accept來獲取一個鏈接,調(diào)用該函數(shù)我們同樣會得到一個文件描述符,通過這個文件描述符就可以處理客戶端發(fā)送的請求并且把處理結(jié)果發(fā)送回去。也就是說通過這個描述符我們就可以和客戶端進(jìn)行通信了。

// 通過accept獲取客戶端的文件描述符int conn_fd = accept(...);

server的處理邏輯通常是讀取客戶端請求數(shù)據(jù),然后執(zhí)行某些特定邏輯:

if(read(conn_fd, request_buff) > 0) { do_something(request_buff);}

是不是非常簡單,然而世界終歸是復(fù)雜的,當(dāng)然也不是這么簡單的。

接下來就是比較復(fù)雜的了。

既然我們的主題是高并發(fā),那么server就不可能只和一個客戶端通信,而是可能會同時和成千上萬個客戶端進(jìn)行通信。這時你需要處理不再是一個描述符這么簡單,而是有可能要處理成千上萬個描述符。

為了不讓問題一上來就過于復(fù)雜,我們先簡單化,假設(shè)只同時處理兩個客戶端的請求。

有的同學(xué)可能會說,這還不簡單,這樣寫不就行了:

if(read(socket_fd1, buff) > 0) { // 處理第一個 do_something();}if(read(socket_fd2, buff) > 0) { // 處理第二個 do_something();

在上一篇《讀取文件時,程序都經(jīng)歷了什么》中我們討論過這是非常典型的阻塞式I/O,如果此時沒有數(shù)據(jù)可讀那么進(jìn)程會被阻塞而暫停運行,這時我們就無法處理第二個請求了,即使第二個請求的數(shù)據(jù)已經(jīng)就位,這也就意味著處理某一個客戶端時由于進(jìn)程被阻塞導(dǎo)致剩下的所有其它客戶端必須等待,在同時處理幾萬客戶端的server上,這顯然是不能容忍的。

聰明的你一定會想到使用多線程,為每個客戶端請求開啟一個線程,這樣一個客戶端被阻塞就不會影響到處理其它客戶端的線程了,注意,既然是高并發(fā),那么我們要為成千上萬個請求開啟成千上萬個線程嗎,大量創(chuàng)建銷毀線程會嚴(yán)重影響系統(tǒng)性能。

那么這個問題該怎么解決呢?

這里的關(guān)鍵點在于,我們事先并不知道一個文件描述對應(yīng)的I/O設(shè)備是否是可讀的、是否是可寫的,在外設(shè)的不可讀或不可寫的狀態(tài)下進(jìn)行I/O只會導(dǎo)致進(jìn)程阻塞被暫停運行。

因此要優(yōu)雅的解決這個問題,就要從其它角度來思考這個問題了。


不要打電話給我,有需要我會打給你

大家生活中肯定會接到過推銷電話,而且不止一個,一天下來接上十個八個推銷電話你的身體會被掏空的。

這個場景的關(guān)鍵點在于打電話的人并不知道你是不是要買東西,只能來一遍遍問你,因此一種更好的策略是不要讓他們打電話給你,記下他們的電話,有需要的話打給他們,這樣推銷員就不會一遍一遍的來煩你了(雖然現(xiàn)實生活中這并不可能)。

在這個例子中,你,就好比內(nèi)核,推銷者就好比應(yīng)用程序,電話號碼就好比文件描述符,和你用電話溝通就好比I/O。

現(xiàn)在你應(yīng)該明白了吧,處理多個文件描述符的更好方法其實就存在于推銷電話中。

因此相比上一節(jié)中我們通過I/O接口主動問內(nèi)核這些文件描述符對應(yīng)的外設(shè)是不是已經(jīng)就緒了,一種更好的方法是,我們把這些感興趣的文件描述符一股腦扔給內(nèi)核,并霸氣的告訴內(nèi)核:“我這里有1萬個文件描述符,你替我監(jiān)視著它們,有可以讀寫的文件描述符時你就告訴我,我好處理”。而不是弱弱的問內(nèi)核:“第一個文件描述可以讀寫了嗎?第二個文件描述符可以讀寫嗎?第三個文件描述符可以讀寫了嗎?。。?!?/p>

這樣應(yīng)用程序就從“繁忙”的主動變?yōu)榱饲彘e的被動反正文件描述可讀可寫了內(nèi)核會通知我,能偷懶我才不要那么勤奮。

這是一種更加高效的I/O處理機制,現(xiàn)在我們可以一次處理多路I/O了,為這種機制起一個名字吧,再次祭出“弄不懂原則”,就叫I/O多路復(fù)用吧,這就是 I/O multiplexing。


I/O多路復(fù)用,I/O multiplexing

multiplexing一詞其實多用于通信領(lǐng)域,為了充分利用通信線路,希望在一個信道中傳輸多路信號,要想在一個信道中傳輸多路信號就需要把這多路信號結(jié)合為一路,將多路信號組合成一個信號的設(shè)備被稱為multiplexer,顯然接收方接收到這一路組合后的信號后要恢復(fù)原先的多路信號,這個設(shè)備被稱為demultiplexer,如圖所示:

回到我們的主題。

所謂I/O多路復(fù)用指的是這樣一個過程:

1. 我們拿到了一堆文件描述符(不管是網(wǎng)絡(luò)相關(guān)的、還是磁盤文件相關(guān)等等,任何文件描述符都可以)

2. 通過調(diào)用某個函數(shù)告訴內(nèi)核:“這個函數(shù)你先不要返回,你替我監(jiān)視著這些描述符,當(dāng)這堆文件描述符中有可以進(jìn)行I/O讀寫操作的時候你再返回

3. 當(dāng)調(diào)用的這個函數(shù)返回后我們就能知道哪些文件描述符可以進(jìn)行I/O操作了。

也就是說通過I/O多路復(fù)用我們可以同時處理多路I/O。那么有哪些函數(shù)可以用來進(jìn)行I/O多路復(fù)用呢?

在Linux世界中有這樣三種機制可以用來進(jìn)行I/O多路復(fù)用:

  • select

  • poll

  • epoll

接下來我們就來介紹一下牛掰的I/O多路復(fù)用三劍客。


I/O多路復(fù)用三劍客

本質(zhì)上select、poll、epoll都是阻塞式I/O,也就是我們常說的同步I/O,原因在于調(diào)用這些I/O多路復(fù)用函數(shù)時如果任何一個需要監(jiān)視的文件描述符都不可讀或者可寫那么進(jìn)程會被阻塞暫停執(zhí)行,直到有文件描述符可讀或者可寫才繼續(xù)運行。

1,select:初出茅廬

在select這種I/O多路復(fù)用機制下,我們需要把想監(jiān)控的文件描述集合通過函數(shù)參數(shù)的形式告訴select,然后select會將這些文件描述符集合拷貝到內(nèi)核中,我們知道數(shù)據(jù)拷貝是有性能損耗的,因此為了減少這種數(shù)據(jù)拷貝帶來的性能損耗,Linux內(nèi)核對集合的大小做了限制,并規(guī)定用戶監(jiān)控的文件描述集合不能超過1024個,同時當(dāng)select返回后我們僅僅能知道有些文件描述符可以讀寫了,但是我們不知道是哪一個,因此程序員必須再遍歷一邊找到具體是哪個文件描述符可以讀寫了。

因此,總結(jié)下來select有這樣幾個特點:

  • 我能照看的文件描述符數(shù)量有限,不能超過1024個

  • 用戶給我的文件描述符需要拷貝的內(nèi)核中

  • 我只能告訴你有文件描述符滿足要求了,但是我不知道是哪個,你自己一個一個去找吧(遍歷)

因此我們可以看到,select機制的這些特性在高并發(fā)網(wǎng)絡(luò)服務(wù)器動輒幾萬幾十萬并發(fā)鏈接的場景下無疑是低效的。


2,poll:小有所成

poll和select是非常相似的,poll相對于select的優(yōu)化僅僅在于解決了文件描述符不能超過1024個的限制,select和poll都會隨著監(jiān)控的文件描述數(shù)量增加而性能下降,因此不適合高并發(fā)場景。

3,epoll:獨步天下

在select面臨的三個問題中,文件描述數(shù)量限制已經(jīng)在poll中解決了,剩下的兩個問題呢?

針對拷貝問題,epoll使用的策略是各個擊破共享內(nèi)存

實際上文件描述符集合的變化頻率比較低,select和poll頻繁的拷貝整個集合,內(nèi)核都快被煩死了,epoll通過引入epoll_ctl很體貼的做到了只操作那些有變化的文件描述符,同時epoll和內(nèi)核還成為了好朋友,共享了同一塊內(nèi)存,這塊內(nèi)存中保存的就是那些已經(jīng)可讀或者可寫的的文件描述符集合,這樣就減少了內(nèi)核和程序的拷貝開銷。

針對需要遍歷文件描述符才能知道哪個可讀可寫這一問題,epoll使用的策略是“當(dāng)小弟”。

在select和poll機制下,進(jìn)程要親自下場去各個文件描述符上等待,任何一個文件描述可讀或者可寫就喚醒進(jìn)程,但是進(jìn)程被喚醒后也是一臉懵逼并不知道到底是哪個文件描述符可讀或可寫,還要再從頭到尾檢查一遍。

但epoll就懂事多了,主動找到進(jìn)程要當(dāng)小弟替大哥出頭。

在這種機制下,進(jìn)程不需要親自下場了,進(jìn)程只要等待在epoll上,epoll代替進(jìn)程去各個文件描述符上等待,當(dāng)哪個文件描述符可讀或者可寫的時候就告訴epoll,epoll用小本本認(rèn)真記錄下來然后喚醒大哥:“進(jìn)程大哥,快醒醒,你要處理的文件描述符我都記下來了”,這樣進(jìn)程被喚醒后就無需自己從頭到尾檢查一遍,因為epoll小弟都已經(jīng)記下來了。

因此我們可以看到,在epoll這種機制下,實際上利用的就是“不要打電話給我,有需要我會打給你”這種策略,進(jìn)程不需要一遍一遍麻煩的問各個文件描述符,而是翻身做主人了,“你們這些文件描述符有哪個可讀或者可寫了主動報上來”,這種機制實際上就是大名鼎鼎的事件驅(qū)動,Event-driven,這也是我們下一篇的主題。

實際上在Linux平臺,epoll基本上就是高并發(fā)的代名詞


總結(jié)

基于一切皆文件的設(shè)計哲學(xué),I/O也可以通過文件的形式實現(xiàn),高并發(fā)場景下要與多個文件交互,這就離不開高效的I/O多路復(fù)用技術(shù),本文我們詳細(xì)講解了什么是I/O多路復(fù)用以及使用方法,這其中以epoll為代表的I/O多路復(fù)用(基于事件驅(qū)動)技術(shù)使用非常廣泛,實際上你會發(fā)現(xiàn)但凡涉及到高并發(fā)、高性能的場景基本上都能見到事件驅(qū)動的編程方法,當(dāng)然這也是下一篇我們要重點講解的主題,敬請期待。



哈嘍,我是小林,就愛圖解計算機基礎(chǔ)。如果覺得文章對你有幫助,歡迎分享給你的朋友,也給小林點個「在看」,這對小林非常重要。


謝謝你們,給各位小姐姐小哥哥們抱拳了,我們下次見!


推薦閱讀
讀者問:小林你能分享做公眾號的經(jīng)驗嗎?
CPU 執(zhí)行程序的秘密,藏在了這 15 張圖里
知道硬盤很慢,但沒想到比 CPU Cache 慢 10000000 倍
如何寫出讓 CPU 跑得更快的代碼?
10 張圖打開 CPU 緩存一致性的大門

免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務(wù)。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

LED驅(qū)動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關(guān)鍵字: 驅(qū)動電源

在工業(yè)自動化蓬勃發(fā)展的當(dāng)下,工業(yè)電機作為核心動力設(shè)備,其驅(qū)動電源的性能直接關(guān)系到整個系統(tǒng)的穩(wěn)定性和可靠性。其中,反電動勢抑制與過流保護是驅(qū)動電源設(shè)計中至關(guān)重要的兩個環(huán)節(jié),集成化方案的設(shè)計成為提升電機驅(qū)動性能的關(guān)鍵。

關(guān)鍵字: 工業(yè)電機 驅(qū)動電源

LED 驅(qū)動電源作為 LED 照明系統(tǒng)的 “心臟”,其穩(wěn)定性直接決定了整個照明設(shè)備的使用壽命。然而,在實際應(yīng)用中,LED 驅(qū)動電源易損壞的問題卻十分常見,不僅增加了維護成本,還影響了用戶體驗。要解決這一問題,需從設(shè)計、生...

關(guān)鍵字: 驅(qū)動電源 照明系統(tǒng) 散熱

根據(jù)LED驅(qū)動電源的公式,電感內(nèi)電流波動大小和電感值成反比,輸出紋波和輸出電容值成反比。所以加大電感值和輸出電容值可以減小紋波。

關(guān)鍵字: LED 設(shè)計 驅(qū)動電源

電動汽車(EV)作為新能源汽車的重要代表,正逐漸成為全球汽車產(chǎn)業(yè)的重要發(fā)展方向。電動汽車的核心技術(shù)之一是電機驅(qū)動控制系統(tǒng),而絕緣柵雙極型晶體管(IGBT)作為電機驅(qū)動系統(tǒng)中的關(guān)鍵元件,其性能直接影響到電動汽車的動力性能和...

關(guān)鍵字: 電動汽車 新能源 驅(qū)動電源

在現(xiàn)代城市建設(shè)中,街道及停車場照明作為基礎(chǔ)設(shè)施的重要組成部分,其質(zhì)量和效率直接關(guān)系到城市的公共安全、居民生活質(zhì)量和能源利用效率。隨著科技的進(jìn)步,高亮度白光發(fā)光二極管(LED)因其獨特的優(yōu)勢逐漸取代傳統(tǒng)光源,成為大功率區(qū)域...

關(guān)鍵字: 發(fā)光二極管 驅(qū)動電源 LED

LED通用照明設(shè)計工程師會遇到許多挑戰(zhàn),如功率密度、功率因數(shù)校正(PFC)、空間受限和可靠性等。

關(guān)鍵字: LED 驅(qū)動電源 功率因數(shù)校正

在LED照明技術(shù)日益普及的今天,LED驅(qū)動電源的電磁干擾(EMI)問題成為了一個不可忽視的挑戰(zhàn)。電磁干擾不僅會影響LED燈具的正常工作,還可能對周圍電子設(shè)備造成不利影響,甚至引發(fā)系統(tǒng)故障。因此,采取有效的硬件措施來解決L...

關(guān)鍵字: LED照明技術(shù) 電磁干擾 驅(qū)動電源

開關(guān)電源具有效率高的特性,而且開關(guān)電源的變壓器體積比串聯(lián)穩(wěn)壓型電源的要小得多,電源電路比較整潔,整機重量也有所下降,所以,現(xiàn)在的LED驅(qū)動電源

關(guān)鍵字: LED 驅(qū)動電源 開關(guān)電源

LED驅(qū)動電源是把電源供應(yīng)轉(zhuǎn)換為特定的電壓電流以驅(qū)動LED發(fā)光的電壓轉(zhuǎn)換器,通常情況下:LED驅(qū)動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關(guān)鍵字: LED 隧道燈 驅(qū)動電源
關(guān)閉