找回密碼
 注冊帳號

掃一掃,訪問微社區

碧俐千仞 如果這篇文章說不清epoll的本質,那就過來掐死我吧! (1)

9
回復
1309
查看
打印 上一主題 下一主題
[ 復制鏈接 ]
排名
19948
昨日變化

41

主題

222

帖子

877

積分

Rank: 9Rank: 9Rank: 9

UID
53741
好友
39
蠻牛幣
2648
威望
0
注冊時間
2014-11-6
在線時間
208 小時
最后登錄
2019-8-9

專欄作家

馬上注冊,結交更多好友,享用更多功能,讓你輕松玩轉社區。

您需要 登錄 才可以下載或查看,沒有帳號?注冊帳號

x
本帖最后由 tyxxxx 于 2019-5-16 22:14 編輯




從事服務端開發,少不了要接觸網絡編程。epoll作為linux下高性能網絡服務器的必備技術至關重要,nginx、redis、skynet和大部分游戲服務器都使用到這一多路復用技術。

文/羅培羽
因為epoll的重要性,不少游戲公司(如某某九九)在招聘服務端同學時,可能會問及epoll相關的問題。比如epoll和select的區別是什么?epoll高效率的原因是什么?如果只靠背誦,顯然不能算上深刻的理解。
網上雖然也有不少講解epoll的文章,但要不是過于淺顯,就是陷入源碼解析,很少能有通俗易懂的。于是決定編寫此文,讓缺乏專業背景知識的讀者也能夠明白epoll的原理。文章核心思想是:

要讓讀者清晰明白EPOLL為什么性能好。

本文會從網卡接收數據的流程講起,串聯起CPU中斷、操作系統進程調度等知識;再一步步分析阻塞接收數據、select到epoll的進化過程;最后探究epoll的實現細節。目錄:

一、從網卡接收數據說起
二、如何知道接收了數據?
三、進程阻塞為什么不占用cpu資源?
四、內核接收網絡數據全過程
五、同時監視多個socket的簡單方法
六、epoll的設計思路
七、epoll的原理和流程
八、epoll的實現細節
九、結論


一、從網卡接收數據說起
下圖是一個典型的計算機結構圖,計算機由CPU、存儲器(內存)、網絡接口等部件組成。了解epoll本質的第一步,要從硬件的角度看計算機怎樣接收網絡數據
計算機結構圖(圖片來源:linux內核完全注釋之微型計算機組成結構)

下圖展示了網卡接收數據的過程。在①階段,網卡收到網線傳來的數據;經過②階段的硬件電路的傳輸;最終將數據寫入到內存中的某個地址上(③階段)。這個過程涉及到DMA傳輸、IO通路選擇等硬件有關的知識,但我們只需知道:網卡會把接收到的數據寫入內存。


網卡接收數據的過程

通過硬件傳輸,網卡接收的數據存放到內存中。操作系統就可以去讀取它們。

二、如何知道接收了數據?

了解epoll本質的第二步,要從CPU的角度來看數據接收。要理解這個問題,要先了解一個概念——中斷。
計算機執行程序時,會有優先級的需求。比如,當計算機收到斷電信號時(電容可以保存少許電量,供CPU運行很短的一小段時間),它應立即去保存數據,保存數據的程序具有較高的優先級。

一般而言,由硬件產生的信號需要cpu立馬做出回應(不然數據可能就丟失),所以它的優先級很高。cpu理應中斷掉正在執行的程序,去做出響應;當cpu完成對硬件的響應后,再重新執行用戶程序。中斷的過程如下圖,和函數調用差不多。只不過函數調用是事先定好位置,而中斷的位置由“信號”決定。

中斷程序調用

以鍵盤為例,當用戶按下鍵盤某個按鍵時,鍵盤會給cpu的中斷引腳發出一個高電平。cpu能夠捕獲這個信號,然后執行鍵盤中斷程序。下圖展示了各種硬件通過中斷與cpu交互。

cpu中斷(圖片來源:net.pku.edu.cn)

現在可以回答本節提出的問題了:當網卡把數據寫入到內存后,網卡向cpu發出一個中斷信號,操作系統便能得知有新數據到來,再通過網卡中斷程序去處理數據。

三、進程阻塞為什么不占用cpu資源?

了解epoll本質的第三步,要從操作系統進程調度的角度來看數據接收。阻塞是進程調度的關鍵一環,指的是進程在等待某事件(如接收到網絡數據)發生之前的等待狀態,recv、select和epoll都是阻塞方法。了解“進程阻塞為什么不占用cpu資源?”,也就能夠了解這一步。
為簡單起見,我們從普通的recv接收開始分析,先看看下面代碼:

[C] 純文本查看 復制代碼
//創建
socketint s = socket(AF_INET, SOCK_STREAM, 0);   
//綁定
bind(s, ...)
//監聽
listen(s, ...)
//接受客戶端連接
int c = accept(s, ...)
//接收客戶端數據
recv(c, ...);
//將數據打印出來
printf(...)

這是一段最基礎的網絡編程代碼,先新建socket對象,依次調用bind、listen、accept,最后調用recv接收數據。recv是個阻塞方法,當程序運行到recv時,它會一直等待,直到接收到數據才往下執行。

插入:如果您還不太熟悉網絡編程,歡迎閱讀我編寫的《網絡游戲實戰(第2版)》,會有詳細的介紹。
那么阻塞的原理是什么?

工作隊列

操作系統為了支持多任務,實現了進程調度的功能,會把進程分為“運行”和“等待”等幾種狀態。運行狀態是進程獲得cpu使用權,正在執行代碼的狀態;等待狀態是阻塞狀態,比如上述程序運行到recv時,程序會從運行狀態變為等待狀態,接收到數據后又變回運行狀態。操作系統會分時執行各個運行狀態的進程,由于速度很快,看上去就像是同時執行多個任務。
下圖中的計算機中運行著A、B、C三個進程,其中進程A執行著上述基礎網絡程序,一開始,這3個進程都被操作系統的工作隊列所引用,處于運行狀態,會分時執行。

工作隊列中有A、B和C三個進程

等待隊列

當進程A執行到創建socket的語句時,操作系統會創建一個由文件系統管理的socket對象(如下圖)。這個socket對象包含了發送緩沖區、接收緩沖區、等待隊列等成員。等待隊列是個非常重要的結構,它指向所有需要等待該socket事件的進程。

創建socket

當程序執行到recv時,操作系統會將進程A從工作隊列移動到該socket的等待隊列中(如下圖)。由于工作隊列只剩下了進程B和C,依據進程調度,cpu會輪流執行這兩個進程的程序,不會執行進程A的程序。所以進程A被阻塞,不會往下執行代碼,也不會占用cpu資源


socket的等待隊列

ps:操作系統添加等待隊列只是添加了對這個“等待中”進程的引用,以便在接收到數據時獲取進程對象、將其喚醒,而非直接將進程管理納入自己之下。上圖為了方便說明,直接將進程掛到等待隊列之下。

喚醒進程

當socket接收到數據后,操作系統將該socket等待隊列上的進程重新放回到工作隊列,該進程變成運行狀態,繼續執行代碼。也由于socket的接收緩沖區已經有了數據,recv可以返回接收到的數據。

以下內容待續
四、內核接收網絡數據全過程
五、同時監視多個socket的簡單方法
六、epoll的設計思路
七、epoll的原理和流程
八、epoll的實現細節
九、結論

既然說到網絡編程,筆者的《Unity3D網絡游戲實戰(第2版)》是一本專門介紹如何開發多人網絡游戲的書籍,用實例介紹開發游戲的全過程,非常實用。書中對網絡編程有詳細的講解,全書用一個大例子貫穿,真正的“實戰”教程。
致謝:本文力圖詳細說明epoll的原理,特別感謝

@陸俊壕

@AllenKong12

雄爺、堂叔 等同事審閱了文章并給予修改意見

回復

使用道具 舉報

7日久生情
2062/5000
排名
4092
昨日變化

0

主題

1345

帖子

2062

積分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
254705
好友
1
蠻牛幣
1877
威望
0
注冊時間
2017-11-16
在線時間
355 小時
最后登錄
2019-8-9
沙發
2019-5-17 08:10:27 只看該作者
666666666666666666666666666666
回復 支持 反對

使用道具 舉報

3偶爾光臨
225/300
排名
17382
昨日變化

0

主題

58

帖子

225

積分

Rank: 3Rank: 3Rank: 3

UID
177972
好友
0
蠻牛幣
140
威望
0
注冊時間
2016-10-24
在線時間
129 小時
最后登錄
2019-8-9
板凳
2019-5-17 08:50:55 只看該作者
謝謝分享
回復

使用道具 舉報

5熟悉之中
707/1000
排名
10706
昨日變化

0

主題

458

帖子

707

積分

Rank: 5Rank: 5

UID
301976
好友
1
蠻牛幣
1059
威望
0
注冊時間
2018-10-31
在線時間
151 小時
最后登錄
2019-8-9
地板
2019-5-17 10:05:40 只看該作者
大佬大佬 膜拜大佬!!!
回復 支持 反對

使用道具 舉報

5熟悉之中
506/1000
排名
10960
昨日變化

1

主題

87

帖子

506

積分

Rank: 5Rank: 5

UID
130631
好友
0
蠻牛幣
621
威望
0
注冊時間
2015-12-2
在線時間
324 小時
最后登錄
2019-8-9
5#
2019-5-23 17:04:43 只看該作者
你在哪 我去掐你
回復

使用道具 舉報

3偶爾光臨
195/300
排名
14744
昨日變化

0

主題

73

帖子

195

積分

Rank: 3Rank: 3Rank: 3

UID
236535
好友
0
蠻牛幣
252
威望
0
注冊時間
2017-8-8
在線時間
68 小時
最后登錄
2019-8-9
6#
2019-5-30 11:41:59 只看該作者
66666666666666666
回復 支持 反對

使用道具 舉報

5熟悉之中
960/1000
排名
3264
昨日變化

1

主題

70

帖子

960

積分

Rank: 5Rank: 5

UID
149521
好友
2
蠻牛幣
586
威望
0
注冊時間
2016-5-23
在線時間
433 小時
最后登錄
2019-7-24
7#
2019-5-30 16:07:09 只看該作者
你可能要死了。
回復

使用道具 舉報

5熟悉之中
707/1000
排名
10706
昨日變化

0

主題

458

帖子

707

積分

Rank: 5Rank: 5

UID
301976
好友
1
蠻牛幣
1059
威望
0
注冊時間
2018-10-31
在線時間
151 小時
最后登錄
2019-8-9
8#
2019-5-31 10:14:03 只看該作者
不錯不錯...
回復

使用道具 舉報

1

主題

15

帖子

63

積分

Rank: 2Rank: 2

UID
322252
好友
0
蠻牛幣
221
威望
0
注冊時間
2019-5-16
在線時間
47 小時
最后登錄
2019-8-8
9#
2019-6-1 10:35:39 只看該作者
贊贊贊666666666
回復 支持 反對

使用道具 舉報

6蠻牛粉絲
1047/1500
排名
10818
昨日變化

3

主題

780

帖子

1047

積分

Rank: 6Rank: 6Rank: 6

UID
310426
好友
0
蠻牛幣
1376
威望
0
注冊時間
2019-1-2
在線時間
168 小時
最后登錄
2019-8-9
10#
2019-6-2 17:41:15 只看該作者
epoll的本質
回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 注冊帳號

本版積分規則

女校游泳队彩金