門戶網(wǎng)站建設(shè)方案文檔如何自己建立一個網(wǎng)站
????????在TCP/IP網(wǎng)絡(luò)分層模型里,整個協(xié)議棧被分成了物理層、鏈路層、網(wǎng)絡(luò)層,傳輸層和應(yīng)用層。物理層對應(yīng)的是網(wǎng)卡和網(wǎng)線,應(yīng)用層對應(yīng)的是我們常見的Nginx,FTP等等各種應(yīng)用。Linux實現(xiàn)的是鏈路層、網(wǎng)絡(luò)層和傳輸層這三層。
????????在Linux內(nèi)核實現(xiàn)中,鏈路層協(xié)議靠網(wǎng)卡驅(qū)動來實現(xiàn),內(nèi)核協(xié)議棧來實現(xiàn)網(wǎng)絡(luò)層和傳輸層。內(nèi)核對更上層的應(yīng)用層提供socket接口來供用戶進(jìn)程訪問。我們用Linux的視角來看到的TCP/IP網(wǎng)絡(luò)分層模型應(yīng)該是下面這個樣子的。
????????在了解網(wǎng)絡(luò)收包過程之前,先了解一下網(wǎng)絡(luò)收包過程的一些概念:
????????1、硬中斷+軟中斷:當(dāng)設(shè)備上有數(shù)據(jù)到達(dá)的時候,會給CPU的相關(guān)引腳上觸發(fā)一個電壓變化(就是硬中斷引腳),以通知CPU來處理數(shù)據(jù)。對于網(wǎng)絡(luò)模塊來說,由于處理過程比較復(fù)雜和耗時,如果在中斷函數(shù)中完成所有的處理,將會導(dǎo)致中斷處理函數(shù)(優(yōu)先級過高)將過度占據(jù)CPU,將導(dǎo)致CPU無法響應(yīng)其它設(shè)備,例如鼠標(biāo)和鍵盤的消息。因此Linux中斷處理函數(shù)是分上半部和下半部的。上半部是只進(jìn)行最簡單的工作,快速處理然后釋放CPU,接著CPU就可以允許其它中斷進(jìn)來。剩下將絕大部分的工作都放到下半部中,可以慢慢從容處理。2.4以后的內(nèi)核版本采用的下半部實現(xiàn)方式是軟中斷,由ksoftirqd內(nèi)核線程全權(quán)處理。和硬中斷不同的是,硬中斷是通過給CPU物理引腳施加電壓變化,而軟中斷是通過給內(nèi)存中的一個變量的二進(jìn)制值以通知軟中斷處理程序。
????????2、ring buffer:ring buffer稱作環(huán)形緩沖區(qū),也稱作環(huán)形隊列(circular queue),是一種用于表示一個固定尺寸、頭尾相連的緩沖區(qū)的數(shù)據(jù)結(jié)構(gòu),適合緩存數(shù)據(jù)流。如下為環(huán)形緩沖區(qū)(ring buffer) 的概念示意圖。
????????任務(wù)間的通信是ring buffer的典型應(yīng)用場景。如進(jìn)程A發(fā)數(shù)據(jù),進(jìn)程B取數(shù)據(jù)去處理,兩個進(jìn)程通常不可能無縫銜接,即進(jìn)程B取數(shù)據(jù)的時機和進(jìn)程A發(fā)數(shù)據(jù)的時機不能完全銜接上,所以需要一個緩存來做緩沖。具體應(yīng)用有串口數(shù)據(jù)收發(fā)、log緩存、網(wǎng)卡處理網(wǎng)絡(luò)數(shù)據(jù)包、音頻/視頻流處理等。在網(wǎng)絡(luò)數(shù)據(jù)收發(fā)處理中,ring buffer位于網(wǎng)卡和內(nèi)核協(xié)議棧之間,在物理上就是主機內(nèi)存里的一塊區(qū)域,另外ring buffer雖然名字叫buffer,但其本身不存儲數(shù)據(jù),實際上是個隊列,隊列里存放的是描述符,描述符描述的是存放數(shù)據(jù)包的內(nèi)存地址,這個指定的地址就是socket buffer,下面講。
????????ring buffer有兩個主要作用:
? ? ? ? a、可以平滑生產(chǎn)者(數(shù)據(jù)來源)和消費者(處理數(shù)據(jù))的速度。
????????b、通過 NAPI 的機制(就是硬中斷加軟中斷,當(dāng)網(wǎng)卡數(shù)據(jù)DMA到ring buffer的指定位置后,網(wǎng)卡會向CPU發(fā)出硬中斷,這個硬中斷處理函數(shù)沒干別的,就只發(fā)出軟中斷請求,然后在軟中斷處理函數(shù)中調(diào)用poll函數(shù)將ring buffer指定的數(shù)據(jù)取到內(nèi)核協(xié)議棧里,在此過程中硬中斷是關(guān)閉的,數(shù)據(jù)取完了再打開硬中斷),合并以減少 IRQ 次數(shù)。
??? ring buffer,一篇文章講透它? - 知乎 (zhihu.com),此文有ringbuffer的詳細(xì)描述,這里只說個基本概念。
????????3、socket buffer:Ring Buffer 隊列內(nèi)存放的是一個個 Packet 描述符,其有兩種狀態(tài):ready和used。初始時描述符是空的,指向一個空的socket buffer,處在ready狀態(tài)。當(dāng)有數(shù)據(jù)時,DMA負(fù)責(zé)從 NIC 取數(shù)據(jù),并在Ring Buffer 上按順序找到下一個ready的描述符,將數(shù)據(jù)存入該 描述符指向的socket buffer中,并標(biāo)記槽為 used。在此過程中,根據(jù)數(shù)據(jù)類型的不同,數(shù)據(jù)會被加上各種包頭信息,封裝成socket buffer指定的數(shù)據(jù)結(jié)構(gòu)。當(dāng)應(yīng)用程序調(diào)用 read 系統(tǒng)調(diào)用時,程序會切換到內(nèi)核區(qū),并且會把 socket 接收緩沖區(qū)中的數(shù)據(jù)拷貝到用戶區(qū),拷貝后的數(shù)據(jù)會從 socket 緩沖區(qū)中移除。socket buffer可以看做是用戶空間和內(nèi)核空間的接口,同時也是網(wǎng)卡和內(nèi)核之間的接口。Socket Buffer的設(shè)計優(yōu)點是避免了重復(fù)拷貝數(shù)據(jù),在發(fā)送和接收的分別都只有兩次,分別是應(yīng)用層和內(nèi)核空間之間的拷貝、網(wǎng)卡的硬件緩沖區(qū)和內(nèi)核空間之間的拷貝。
????????有了以上的基本概念后,下面給出一個收包的基本流程:
????????1.當(dāng)收到報文時,網(wǎng)卡把數(shù)據(jù)包寫入它自身的內(nèi)存。
????????2. 網(wǎng)卡通過CRC校驗檢查數(shù)據(jù)包是否有效,之后調(diào)用DMA把數(shù)據(jù)包發(fā)送到主機的內(nèi)存緩沖區(qū),這是驅(qū)動程序提前向內(nèi)核申請好的一塊內(nèi)存區(qū)域,就是ring buffer指向的socket buffer空間。
????????3.數(shù)據(jù)包的實際大小、checksum和其他信息會保存在獨立的Ring Buffer(Rx.ring)中,Ring Buffer接收之后,NIC 會向主機發(fā)出中斷,告知內(nèi)核有新的數(shù)據(jù)到達(dá)。收到中斷,驅(qū)動會把數(shù)據(jù)包包裝成指定的數(shù)據(jù)結(jié)構(gòu)(sk_buff)并發(fā)送到上一層。
????????4.鏈路層會檢查數(shù)據(jù)包是否有效并且解析出上層的協(xié)議(網(wǎng)絡(luò)協(xié)議)。
????????5.IP 層同樣會檢查數(shù)據(jù)包是否有效。檢查IP checksum。
????????6.TCP層檢查數(shù)據(jù)包是否有效。檢查 TCP checksum。
????????7.如果是TCP報文,內(nèi)核會根據(jù)TCP控制塊中的端口號信息,找到對應(yīng)的 socket,數(shù)據(jù)會被增加到socke的接收緩沖區(qū),socket接收緩沖區(qū)的大小就是 TCP 接收窗口。Udp報文同理,不同的是TCP的發(fā)送和接收都有socket buffer,udp只有接收端有。
????????8.當(dāng)應(yīng)用程序調(diào)用 read 系統(tǒng)調(diào)用時,程序會切換到內(nèi)核區(qū),并且會把 socket 接收緩沖區(qū)中的數(shù)據(jù)拷貝到用戶區(qū),拷貝后的數(shù)據(jù)會從 socket 緩沖區(qū)中移除。
????????如下兩圖:
?
?
??? 以上參考自:簡述 Linux I/O 原理及零拷貝(下) — 網(wǎng)絡(luò) I/O_Linux_Qunar技術(shù)沙龍_InfoQ寫作社區(qū)
??? 發(fā)包過程基本就是相反流程,具體可以查看以上鏈接。
?