操作系统和Web服务器那点事儿
操作系統(tǒng)老大
又一個(gè)進(jìn)程啟動(dòng)了,操作系統(tǒng)老大嘆了一口氣,畢竟自己的肩頭又多了一份責(zé)任。
讓人煩惱的是,新來(lái)的家伙們很無(wú)知,幾乎就是一張白紙。有些老實(shí)本分的會(huì)按照自己的規(guī)矩來(lái)做事,有些刺頭兒喜歡問(wèn)這問(wèn)那,時(shí)不時(shí)還想搞點(diǎn)非法的訪問(wèn),想訪問(wèn)別的進(jìn)程的地址空間,甚至想訪問(wèn)內(nèi)核的代碼和數(shù)據(jù)! 這時(shí)候,我只有把他kill掉祭天,留下一個(gè)core dump的尸體讓碼農(nóng)們?nèi)シ治觥?/p>
規(guī)矩很重要!
想到此處,老大又看了一眼自己的內(nèi)核空間,這個(gè)機(jī)器只有可憐巴巴的4G內(nèi)存,0-3G給各個(gè)進(jìn)程共享使用,自己獨(dú)占了從3G-4G的內(nèi)存空間。
新啟動(dòng)的進(jìn)程是一個(gè)Web服務(wù)器,自稱(chēng)小W,這是個(gè)喜歡問(wèn)問(wèn)題的家伙,他第一個(gè)問(wèn)題就是: ”老大,你為啥不和群眾打成一片,反而自己要獨(dú)占空間呢?“
“這是為你們好?”
“為我們好? ”
“計(jì)算機(jī)的硬件資源是有限的,硬盤(pán)、內(nèi)存、網(wǎng)卡,鍵盤(pán),鼠標(biāo),時(shí)鐘...... 如果任由你們這些進(jìn)程隨意訪問(wèn),大家你爭(zhēng)我搶,豈不亂套?”?
“再說(shuō)了,那些底層的硬件、驅(qū)動(dòng)操作是極其麻煩的,讓你們每個(gè)進(jìn)程都去寫(xiě)那些‘惡心’的代碼,你們受得了嗎? “
”還有,如果某個(gè)惡意的家伙故意搗亂,那還了得?”
老大的三連問(wèn)簡(jiǎn)直是振聾發(fā)聵, 小W立刻覺(jué)得氣短了三分。
“所以你就不讓我們直接訪問(wèn)了?”
“對(duì)啊,我就做了一個(gè)抽象層,你們必須通過(guò)這個(gè)抽象層來(lái)訪問(wèn)硬件資源。這個(gè)抽象層之下就是我的內(nèi)核,是我的代碼和數(shù)據(jù),所以我必須得單獨(dú)居住,不能和你們混在一起?!?/p>
?
系統(tǒng)調(diào)用
“那我想訪問(wèn)一個(gè)硬盤(pán)上的文件,到底該怎么辦?” 小W問(wèn)道。
“非常簡(jiǎn)單,我的抽象層中有對(duì)外提供的接口,叫做系統(tǒng)調(diào)用,例如read、open、close等。 你可以open 一個(gè)文件,read它的內(nèi)容,讀完了close。”
“聽(tīng)起來(lái)好像是函數(shù)調(diào)用啊!”
“對(duì),就是函數(shù)調(diào)用,但是和你內(nèi)部的函數(shù)調(diào)用有本質(zhì)的不同,這種系統(tǒng)調(diào)用會(huì)讓你從用戶(hù)態(tài)切換到內(nèi)核態(tài),?也就是到我的內(nèi)核代碼中來(lái)執(zhí)行!”
小W懵懂地點(diǎn)點(diǎn)頭,似乎明白了。
他應(yīng)該沒(méi)有明白,他也明白不了, 操作系統(tǒng)老大心里想,系統(tǒng)調(diào)用之復(fù)雜遠(yuǎn)遠(yuǎn)超過(guò)他的想象。
首先所有Linux的系統(tǒng)調(diào)用的參數(shù)都是通過(guò)寄存器而不是棧來(lái)傳遞的,按照慣例寄存器EAX保存了系統(tǒng)調(diào)用的編號(hào)(例如1表示exit這個(gè)系統(tǒng)調(diào)用,2表示fork,3表示read......),寄存器EBX,ECX,EDX,ESI,EDI可以包含最多6個(gè)任意的參數(shù)。
比如:write(1,"hello",5);
這就是個(gè)系統(tǒng)調(diào)用, 就是向stdout(控制臺(tái))輸出一個(gè)字符串,在運(yùn)行時(shí),必須把寄存器給設(shè)置好:
EAX = 4 (4表示系統(tǒng)調(diào)用的編號(hào))
EBX = 1 ?(1 表示stdout)
ECX = 那個(gè)字符串的地址
EDX = 字符串的長(zhǎng)度
然后調(diào)用int 0x80 系統(tǒng)中斷,這就進(jìn)入了內(nèi)核, 我會(huì)取出EAX, 從一個(gè)內(nèi)核的表格中查到第4號(hào)對(duì)應(yīng)的系統(tǒng)調(diào)用處理程序來(lái)執(zhí)行。
對(duì)了,我還需要把CPU的特權(quán)等級(jí)從3置為0,表示內(nèi)核態(tài)。
看看,我容易嗎我! ?操作系統(tǒng)心里略微有點(diǎn)傷感。
?
read 和 write
這時(shí)候小W探出頭來(lái),興奮地說(shuō):“hi ,老大,有客戶(hù)要訪問(wèn)咱們硬盤(pán)的文件,我得讀取一下,然后通過(guò)socket發(fā)出去。是不是需要系統(tǒng)調(diào)用了?”
“那是肯定的,訪問(wèn)文件系統(tǒng)必須得通過(guò)我,訪問(wèn)socket也得通過(guò)我,不用系統(tǒng)調(diào)用怎么可能? 除去open ,close, 你需要兩個(gè)關(guān)鍵的系統(tǒng)調(diào)用:”
// 從文件(用fd表示)中讀取len長(zhǎng)度的內(nèi)容,放到buffer中
read(fd, buffer, len);??
// 把buffer中長(zhǎng)度為len 的內(nèi)容寫(xiě)入到socket中(用sockfd表示)
write(sockfd, buffer, len);
(注: read和write 應(yīng)該是sys_read和sys_write的“包裹”函數(shù),我們這里簡(jiǎn)化,認(rèn)為就是直接的函數(shù)調(diào)用。)
“好滴!” 小W做了一些準(zhǔn)備工作,然后便開(kāi)始read, 然后滿(mǎn)心歡喜地等待數(shù)據(jù)的到來(lái)。
操作系統(tǒng)收到read調(diào)用,陷入內(nèi)核,正式進(jìn)入了內(nèi)核態(tài),然后毫不客氣地暫停了小W的執(zhí)行,讓他進(jìn)入了阻塞隊(duì)列(假設(shè)小W只有一個(gè)線程)。
小W表示不滿(mǎn):“怎么不讓我運(yùn)行了?”
“讀取文件太慢,你先歇會(huì)兒,數(shù)據(jù)來(lái)了會(huì)通知你的?!?/p>
老大使用DMA(Direct Memory Access)的方式把文件的數(shù)據(jù)從硬盤(pán)復(fù)制到了內(nèi)核的緩沖區(qū), 然后又復(fù)制到了用戶(hù)的緩沖區(qū),read調(diào)用完成,返回用戶(hù)態(tài) ,小W可以繼續(xù)執(zhí)行了。
小W要通過(guò)socket發(fā)送數(shù)據(jù),于是又發(fā)出了write調(diào)用,再次陷入內(nèi)核,進(jìn)入內(nèi)核態(tài)。
老大把數(shù)據(jù)又從用戶(hù)緩沖區(qū)復(fù)制到socket緩沖區(qū), write調(diào)用返回,返回用戶(hù)態(tài)。
小W問(wèn)道:“這次這么快就返回了?數(shù)據(jù)發(fā)出去沒(méi)有啊?”
老大說(shuō):“這就不用你操心了,網(wǎng)卡驅(qū)動(dòng)會(huì)在合適的時(shí)候發(fā)送的,這是個(gè)異步的操作?!?/p>
小W畫(huà)了一張圖,試圖理解整個(gè)過(guò)程,等他把圖畫(huà)完,不由得咂舌:“嘖嘖,這么兩個(gè)簡(jiǎn)單的系統(tǒng)調(diào)用,代價(jià)竟然如此之高啊。”
?
(1) 需要進(jìn)入內(nèi)核態(tài)兩次,返回兩次。
(2) 數(shù)據(jù)居然發(fā)生了三次復(fù)制,硬盤(pán)-->內(nèi)核緩沖區(qū)-->用戶(hù)緩沖區(qū)-->socket緩沖區(qū)
如果說(shuō)第一次從硬盤(pán)到內(nèi)核緩沖區(qū)必不可少,后面的兩次就太浪費(fèi)了。
老大說(shuō):“你看到了吧,系統(tǒng)調(diào)用的開(kāi)銷(xiāo)很大啊,以后要少點(diǎn)調(diào)用啊。”
小W說(shuō):“我覺(jué)得你這個(gè)內(nèi)核雖然保護(hù)了硬件,但是導(dǎo)致效率很低啊,能不能優(yōu)化一下,省去用戶(hù)態(tài)<-->核心態(tài)之間的數(shù)據(jù)復(fù)制? 這太浪費(fèi)了!”
?
sendfile
老大哈哈一笑, 說(shuō)道: “我早就料到了這一層,我這里還有個(gè)系統(tǒng)調(diào)用,叫做sendfile,你可以試試啊,通過(guò)這個(gè)系統(tǒng)調(diào)用,可以直接把文件內(nèi)容發(fā)給socket。 ”
sendfile(socket, file, len);
小W一看,不錯(cuò)啊,自己只需要調(diào)用sendfile,進(jìn)入內(nèi)核態(tài)一次就可以了,老大可以把數(shù)據(jù)從硬盤(pán)復(fù)制到內(nèi)核緩沖區(qū),然后直接復(fù)制到socket緩沖區(qū), 完全不用自己介入,就用它了!
可是轉(zhuǎn)念一想,這從內(nèi)核緩沖區(qū)到socket緩沖區(qū)的復(fù)制有必要嗎? 那個(gè)網(wǎng)卡驅(qū)動(dòng)不能直接從內(nèi)核緩沖區(qū)讀數(shù)據(jù)嗎?
老大似乎看穿了小W的心思,說(shuō)道:“我知道你在想啥,放心吧, 我早就做了優(yōu)化了,不會(huì)把數(shù)據(jù)從內(nèi)核緩沖區(qū)復(fù)制到socket緩沖區(qū),相反,我只會(huì)把一些位置和數(shù)據(jù)長(zhǎng)度等信息復(fù)制過(guò)去,很省事的。網(wǎng)卡驅(qū)動(dòng)可以直接從內(nèi)核緩沖區(qū)讀去數(shù)據(jù)。”
小W放心了,開(kāi)始使用這種sendfile的方式,果然,性能大為提升!
?
?這其實(shí)就是所謂的zero copy技術(shù), 從內(nèi)核角度看,除了把文件從硬盤(pán)讀出來(lái)之外,沒(méi)有任何的額外copy。
zero copy技術(shù)減少了上下文的切換,避免了數(shù)據(jù)不斷地在用戶(hù)態(tài)和核心態(tài)搬運(yùn),不需要CPU參與數(shù)據(jù)的復(fù)制,提高了系統(tǒng)性能,在ngnix, apache等web 服務(wù)器中都引入了zero copy技術(shù)。
《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專(zhuān)家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的操作系统和Web服务器那点事儿的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 到底是Java好还是Python好?
- 下一篇: 深入浅出Python元编程