偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

I/O多路復(fù)用,從來(lái)沒(méi)遇到過(guò)這么明白的文章

系統(tǒng) Linux
很多對(duì)技術(shù)有追求的讀者朋友,做到一定階段后都希望技術(shù)有所精進(jìn)。有些讀者朋友可能會(huì)研究一些中間件的技術(shù)架構(gòu)和實(shí)現(xiàn)原理。

[[404839]]

本文轉(zhuǎn)載自微信公眾號(hào)「 二馬讀書(shū)」,作者濤哥 。轉(zhuǎn)載本文請(qǐng)聯(lián)系 二馬讀書(shū)公眾號(hào)。

很多對(duì)技術(shù)有追求的讀者朋友,做到一定階段后都希望技術(shù)有所精進(jìn)。有些讀者朋友可能會(huì)研究一些中間件的技術(shù)架構(gòu)和實(shí)現(xiàn)原理。比如,Nginx為什么能同時(shí)支撐數(shù)萬(wàn)乃至數(shù)十萬(wàn)的連接?為什么單工作線程的Redis性能比多線程的Memcached還要強(qiáng)?Dubbo的底層實(shí)現(xiàn)是怎樣的,為什么他的通信效率非常高?

實(shí)際上,上面的一些問(wèn)題都和網(wǎng)絡(luò)模型相關(guān)。本文從基礎(chǔ)的概念和網(wǎng)絡(luò)編程開(kāi)始,循序漸進(jìn)講解Linux五大網(wǎng)絡(luò)模型,包括耳熟能詳?shù)亩嗦窂?fù)用IO模型。相信讀完本文,大家會(huì)對(duì)網(wǎng)絡(luò)編程和網(wǎng)絡(luò)模型有一個(gè)較清晰的理解。

基本概念

我們先來(lái)了解幾個(gè)基本概念。

什么是I/O?

所謂的I/O(Input/Output)操作實(shí)際上就是輸入輸出的數(shù)據(jù)傳輸行為。程序員最關(guān)注的主要是磁盤(pán)IO和網(wǎng)絡(luò)IO,因?yàn)檫@兩個(gè)IO操作和應(yīng)用程序的關(guān)系最直接最緊密。

磁盤(pán)IO:磁盤(pán)的輸入輸出,比如磁盤(pán)和內(nèi)存之間的數(shù)據(jù)傳輸。

網(wǎng)絡(luò)IO:不同系統(tǒng)間跨網(wǎng)絡(luò)的數(shù)據(jù)傳輸,比如兩個(gè)系統(tǒng)間的遠(yuǎn)程接口調(diào)用。

下面這張圖展示了應(yīng)用程序中發(fā)生IO的具體場(chǎng)景:

通過(guò)上圖,我們可以了解到IO操作發(fā)生的具體場(chǎng)景。一個(gè)請(qǐng)求過(guò)程可能會(huì)發(fā)生很多次的IO操作:

1,頁(yè)面請(qǐng)求到服務(wù)器會(huì)發(fā)生網(wǎng)絡(luò)IO

2,服務(wù)之間遠(yuǎn)程調(diào)用會(huì)發(fā)生網(wǎng)絡(luò)IO

3,應(yīng)用程序訪問(wèn)數(shù)據(jù)庫(kù)會(huì)發(fā)生網(wǎng)絡(luò)IO

4,數(shù)據(jù)庫(kù)查詢或者寫(xiě)入數(shù)據(jù)會(huì)發(fā)生磁盤(pán)IO

阻塞與非阻塞

所謂阻塞,就是發(fā)出一個(gè)請(qǐng)求不能立刻返回響應(yīng),要等所有的邏輯全處理完才能返回響應(yīng)。

非阻塞反之,發(fā)出一個(gè)請(qǐng)求立刻返回應(yīng)答,不用等處理完所有邏輯。

內(nèi)核空間與用戶空間

在Linux中,應(yīng)用程序穩(wěn)定性遠(yuǎn)遠(yuǎn)比不上操作系統(tǒng)程序,為了保證操作系統(tǒng)的穩(wěn)定性,Linux區(qū)分了內(nèi)核空間和用戶空間??梢赃@樣理解,內(nèi)核空間運(yùn)行操作系統(tǒng)程序和驅(qū)動(dòng)程序,用戶空間運(yùn)行應(yīng)用程序。Linux以這種方式隔離了操作系統(tǒng)程序和應(yīng)用程序,避免了應(yīng)用程序影響到操作系統(tǒng)自身的穩(wěn)定性。這也是Linux系統(tǒng)超級(jí)穩(wěn)定的主要原因。

所有的系統(tǒng)資源操作都在內(nèi)核空間進(jìn)行,比如讀寫(xiě)磁盤(pán)文件,內(nèi)存分配和回收,網(wǎng)絡(luò)接口調(diào)用等。所以在一次網(wǎng)絡(luò)IO讀取過(guò)程中,數(shù)據(jù)并不是直接從網(wǎng)卡讀取到用戶空間中的應(yīng)用程序緩沖區(qū),而是先從網(wǎng)卡拷貝到內(nèi)核空間緩沖區(qū),然后再?gòu)膬?nèi)核拷貝到用戶空間中的應(yīng)用程序緩沖區(qū)。對(duì)于網(wǎng)絡(luò)IO寫(xiě)入過(guò)程,過(guò)程則相反,先將數(shù)據(jù)從用戶空間中的應(yīng)用程序緩沖區(qū)拷貝到內(nèi)核緩沖區(qū),再?gòu)膬?nèi)核緩沖區(qū)把數(shù)據(jù)通過(guò)網(wǎng)卡發(fā)送出去。

Socket(套接字)

Socket可以理解成,在兩個(gè)應(yīng)用程序進(jìn)行網(wǎng)絡(luò)通信時(shí),一個(gè)應(yīng)用程序?qū)?shù)據(jù)寫(xiě)入Socket,然后通過(guò)網(wǎng)卡把數(shù)據(jù)發(fā)送到另外一個(gè)應(yīng)用程序的Socket中。

所有的網(wǎng)絡(luò)協(xié)議都是基于Socket進(jìn)行通信的,不管是TCP還是UDP協(xié)議,應(yīng)用層的HTTP協(xié)議也不例外。這些協(xié)議都需要基于Socket實(shí)現(xiàn)網(wǎng)絡(luò)通信。5種網(wǎng)絡(luò)IO模型也都要基于Socket實(shí)現(xiàn)網(wǎng)絡(luò)通信。實(shí)際上,HTTP協(xié)議是建立在TCP協(xié)議之上的應(yīng)用層協(xié)議。HTTP協(xié)議負(fù)責(zé)如何包裝數(shù)據(jù),而TCP協(xié)議負(fù)責(zé)如何傳輸數(shù)據(jù)。

絕大部分編程語(yǔ)言,都支持Socket編程,例如Java,Php,Python等等。而這些語(yǔ)言的Socket SDK都是基于操作系統(tǒng)提供的 socket() 函數(shù)來(lái)實(shí)現(xiàn)的。不管是Linux還是windows,都提供了相應(yīng)的 socket() 函數(shù)。

Socket 編程過(guò)程

我們來(lái)看看Socket 編程過(guò)程是怎樣的。

不管Java、Python還是Php,很多編程語(yǔ)言都支持Socket編程。Linux,Windows等操作系統(tǒng)都開(kāi)放了網(wǎng)絡(luò)編程接口。只不過(guò),各種編程語(yǔ)言對(duì)底層操作系統(tǒng)提供的網(wǎng)絡(luò)編程接口做了封裝而已。

從服務(wù)端開(kāi)始,服務(wù)端首先調(diào)用 socket() 函數(shù),按指定的網(wǎng)絡(luò)協(xié)議和傳輸協(xié)議創(chuàng)建 Socket ,例如創(chuàng)建一個(gè)網(wǎng)絡(luò)協(xié)議為 IPv4,傳輸協(xié)議為 TCP 的Socket。接著調(diào)用 bind() 函數(shù),給這個(gè) Socket 綁定一個(gè) IP 地址和端口,綁定這兩個(gè)的目的是什么?

  • 綁定端口的目的:當(dāng)內(nèi)核收到 TCP 報(bào)文,通過(guò) TCP 頭里的端口號(hào),來(lái)找到我們的應(yīng)用程序,然后把數(shù)據(jù)傳遞給我們
  • 綁定 IP 地址的目的:一臺(tái)機(jī)器可能有多個(gè)網(wǎng)卡,每個(gè)網(wǎng)卡都對(duì)應(yīng)一個(gè) IP 地址,只有綁定一個(gè)網(wǎng)卡對(duì)應(yīng)的IP時(shí),內(nèi)核在收到該網(wǎng)卡上的包,才會(huì)發(fā)給我們的應(yīng)用程序

綁定完 IP 地址和端口后,就可以調(diào)用 listen() 函數(shù)進(jìn)行監(jiān)聽(tīng)。如果我們要判定服務(wù)器上某個(gè)網(wǎng)絡(luò)程序有沒(méi)有啟動(dòng),可以通過(guò) netstat 命令查看對(duì)應(yīng)的端口號(hào)是否被監(jiān)聽(tīng)。

服務(wù)端進(jìn)入了監(jiān)聽(tīng)狀態(tài)后,通過(guò)調(diào)用 accept() 函數(shù),來(lái)從內(nèi)核獲取客戶端的連接,如果沒(méi)有客戶端連接,則會(huì)阻塞等待客戶端連接的到來(lái)。

那客戶端是怎么發(fā)起連接的呢?客戶端在創(chuàng)建好 Socket 后,調(diào)用 connect()函數(shù)發(fā)起連接,該函數(shù)的參數(shù)要指明服務(wù)端的 IP 地址和端口號(hào),然后眾所周知的 TCP 三次握手就開(kāi)始了。

連接建立后,客戶端和服務(wù)端就開(kāi)始相互傳輸數(shù)據(jù)了,雙方可以通過(guò) read()和 write() 函數(shù)來(lái)讀寫(xiě)數(shù)據(jù)。

基于TCP 協(xié)議的 Socket 編程過(guò)程就結(jié)束了,整個(gè)過(guò)程如下圖所示:

網(wǎng)絡(luò)IO模型

5種Linux網(wǎng)絡(luò)IO模型包括:同步阻塞IO、同步非阻塞IO、多路復(fù)用IO、信號(hào)驅(qū)動(dòng)IO和異步IO。

同步阻塞IO

我們先看一下傳統(tǒng)阻塞IO。在Linux中,默認(rèn)情況下所有socket都是阻塞模式的。當(dāng)用戶線程調(diào)用系統(tǒng)函數(shù)read(),內(nèi)核開(kāi)始準(zhǔn)備數(shù)據(jù)(從網(wǎng)絡(luò)接收數(shù)據(jù)),內(nèi)核準(zhǔn)備數(shù)據(jù)完成后,數(shù)據(jù)從內(nèi)核拷貝到用戶空間的應(yīng)用程序緩沖區(qū),數(shù)據(jù)拷貝完成后,請(qǐng)求才返回。從發(fā)起read請(qǐng)求到最終完成內(nèi)核到應(yīng)用程序的拷貝,整個(gè)過(guò)程都是阻塞的。為了提高性能,可以為每個(gè)連接都分配一個(gè)線程。因此,在大量連接的場(chǎng)景下就需要大量的線程,會(huì)造成巨大的性能損耗,這也是傳統(tǒng)阻塞IO的最大缺陷。

同步非阻塞IO

用戶線程在發(fā)起Read請(qǐng)求后立即返回,不用等待內(nèi)核準(zhǔn)備數(shù)據(jù)的過(guò)程。如果Read請(qǐng)求沒(méi)讀取到數(shù)據(jù),用戶線程會(huì)不斷輪詢發(fā)起Read請(qǐng)求,直到數(shù)據(jù)到達(dá)(內(nèi)核準(zhǔn)備好數(shù)據(jù))后才停止輪詢。非阻塞IO模型雖然避免了由于線程阻塞問(wèn)題帶來(lái)的大量線程消耗,但是頻繁的重復(fù)輪詢大大增加了請(qǐng)求次數(shù),對(duì)CPU消耗也比較明顯。這種模型在實(shí)際應(yīng)用中很少使用。

多路復(fù)用IO模型

多路復(fù)用IO模型,建立在多路事件分離函數(shù)select,poll,epoll之上。在發(fā)起read請(qǐng)求前,先更新select的socket監(jiān)控列表,然后等待select函數(shù)返回(此過(guò)程是阻塞的,所以說(shuō)多路復(fù)用IO并非完全非阻塞)。當(dāng)某個(gè)socket有數(shù)據(jù)到達(dá)時(shí),select函數(shù)返回。此時(shí)用戶線程才正式發(fā)起read請(qǐng)求,讀取并處理數(shù)據(jù)。這種模式用一個(gè)專(zhuān)門(mén)的監(jiān)視線程去檢查多個(gè)socket,如果某個(gè)socket有數(shù)據(jù)到達(dá)就交給工作線程處理。由于等待Socket數(shù)據(jù)到達(dá)過(guò)程非常耗時(shí),所以這種方式解決了阻塞IO模型一個(gè)Socket連接就需要一個(gè)線程的問(wèn)題,也不存在非阻塞IO模型忙輪詢帶來(lái)的CPU性能損耗的問(wèn)題。多路復(fù)用IO模型的實(shí)際應(yīng)用場(chǎng)景很多,比如大家耳熟能詳?shù)腏ava NIO,Redis,Nginx以及Dubbo采用的通信框架Netty都采用了這種模型。

下圖是基于select函數(shù)Socket編程的詳細(xì)流程。

用一句話解釋多路復(fù)用模型。多路:可以理解成多個(gè)網(wǎng)絡(luò)連接(TCP連接)。復(fù)用:服務(wù)端反復(fù)使用同一個(gè)線程去監(jiān)聽(tīng)所有網(wǎng)絡(luò)連接中是否有IO事件(如果有IO事件就交給工作線程從對(duì)應(yīng)的連接中讀取并處理數(shù)據(jù))。

信號(hào)驅(qū)動(dòng)IO模型

信號(hào)驅(qū)動(dòng)IO模型,應(yīng)用進(jìn)程使用sigaction函數(shù),內(nèi)核會(huì)立即返回,也就是說(shuō)內(nèi)核準(zhǔn)備數(shù)據(jù)的階段應(yīng)用進(jìn)程是非阻塞的。內(nèi)核準(zhǔn)備好數(shù)據(jù)后向應(yīng)用進(jìn)程發(fā)送SIGIO信號(hào),接到信號(hào)后數(shù)據(jù)被復(fù)制到應(yīng)用程序進(jìn)程。

采用這種方式,CPU的利用率很高。不過(guò)這種模式下,在大量IO操作的情況下可能造成信號(hào)隊(duì)列溢出導(dǎo)致信號(hào)丟失,造成災(zāi)難性后果。

異步IO模型

異步IO模型的基本機(jī)制是,應(yīng)用進(jìn)程告訴內(nèi)核啟動(dòng)某個(gè)操作,內(nèi)核操作完成后再通知應(yīng)用進(jìn)程。在多路復(fù)用IO模型中,socket狀態(tài)事件到達(dá),得到通知后,應(yīng)用進(jìn)程才開(kāi)始自行讀取并處理數(shù)據(jù)。在異步IO模型中,應(yīng)用進(jìn)程得到通知時(shí),內(nèi)核已經(jīng)讀取完數(shù)據(jù)并把數(shù)據(jù)放到了應(yīng)用進(jìn)程的緩沖區(qū)中,此時(shí)應(yīng)用進(jìn)程

直接使用數(shù)據(jù)即可。

很明顯,異步IO模型性能很高。不過(guò)到目前為止,異步IO和信號(hào)驅(qū)動(dòng)IO模型應(yīng)用并不多見(jiàn),傳統(tǒng)阻塞IO和多路復(fù)用IO模型還是目前應(yīng)用的主流。Linux2.6版本后才引入異步IO模型,目前很多系統(tǒng)對(duì)異步IO模型支持尚不成熟。很多應(yīng)用場(chǎng)景采用多路復(fù)用IO替代異步IO模型。

 

責(zé)任編輯:武曉燕 來(lái)源: 二馬讀書(shū)
相關(guān)推薦

2023-05-08 00:06:45

Go語(yǔ)言機(jī)制

2021-03-17 16:53:51

IO多路

2019-12-23 14:53:26

IO復(fù)用

2021-02-10 08:09:48

Netty網(wǎng)絡(luò)多路復(fù)用

2021-03-24 08:03:38

NettyJava NIO網(wǎng)絡(luò)技術(shù)

2020-10-13 07:51:03

五種IO模型

2025-05-08 08:01:05

2020-04-26 14:40:19

戴爾

2024-12-30 00:00:05

2023-11-08 09:22:14

I/ORedis阻塞

2022-04-13 07:59:23

IOBIONIO

2011-12-08 10:51:25

JavaNIO

2022-10-20 18:00:59

OCP模型參數(shù)

2023-01-09 10:04:47

IO多路復(fù)用模型

2023-08-07 08:52:03

Java多路復(fù)用機(jī)制

2023-12-06 07:16:31

Go語(yǔ)言語(yǔ)句

2017-07-14 09:29:45

AndroidWebview

2020-11-08 14:38:35

JavaScript代碼開(kāi)發(fā)

2021-12-30 09:32:04

緩存數(shù)據(jù)庫(kù)數(shù)據(jù)

2020-10-14 09:11:44

IO 多路復(fù)用實(shí)現(xiàn)機(jī)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)