聊聊Linux 五種IO模型

一篇《聊聊同步、異步、阻塞與非阻塞》已經通俗的講解了,要理解同步、異步、阻塞與非阻塞重要的兩個概念點了,沒有看過的,建議先看這篇博文理解這兩個概念點。在認知上,建立統一的模型。這樣,大家在繼續看本篇時,才不會理解有偏差。

那么,在正式開始講Linux IO模型前,比如:同步IO和異步IO,阻塞IO和非阻塞IO分別是什么,到底有什么區別?不同的人在不同的上下文下給出的答案是不同的。所以先限定一下本文的上下文。

1 概念說明#

在進行解釋之前,首先要說明幾個概念:

用戶空間和內核空間

進程切換

進程的阻塞

文件描述符

緩存 IO

1.1 用戶空間與內核空間##

現在操作系統都是采用虛擬存儲器,那么對32位操作系統而言,它的尋址空間(虛擬存儲空間)為4G(2的32次方)。操作系統的核心是內核,獨立于普通的應用程序,可以訪問受保護的內存空間,也有訪問底層硬件設備的所有權限。為了保證用戶進程不能直接操作內核(kernel),保證內核的安全,操作系統將虛擬空間劃分為兩部分,一部分為內核空間,一部分為用戶空間。針對linux操作系統而言,將最高的1G字節(從虛擬地址0xC0000000到0xFFFFFFFF),供內核使用,稱為內核空間,而將較低的3G字節(從虛擬地址0x00000000到0xBFFFFFFF),供各個進程使用,稱為用戶空間。

1.2 進程切換##

為了控制進程的執行,內核必須有能力掛起正在CPU上運行的進程,并恢復以前掛起的某個進程的執行。這種行為被稱為進程切換。因此可以說,任何進程都是在操作系統內核的支持下運行的,是與內核緊密相關的。

從一個進程的運行轉到另一個進程上運行,這個過程中經過下面這些變化:

  1. 保存處理機上下文,包括程序計數器和其他寄存器。

  2. 更新PCB信息。

  3. 把進程的PCB移入相應的隊列,如就緒、在某事件阻塞等隊列。

  4. 選擇另一個進程執行,并更新其PCB。

  5. 更新內存管理的數據結構。

  6. 恢復處理機上下文。

注:總而言之就是很耗資源,具體的可以參考這篇文章:進程切換。

1.3 進程的阻塞##

正在執行的進程,由于期待的某些事件未發生,如請求系統資源失敗、等待某種操作的完成、新數據尚未到達或無新工作做等,則由系統自動執行阻塞原語(Block),使自己由運行狀態變為阻塞狀態。可見,進程的阻塞是進程自身的一種主動行為,也因此只有處于運行態的進程(獲得CPU),才可能將其轉為阻塞狀態。當進程進入阻塞狀態,是不占用CPU資源的

1.4 文件描述符fd##

文件描述符(File descriptor)是計算機科學中的一個術語,是一個用于表述指向文件的引用的抽象化概念

文件描述符在形式上是一個非負整數。實際上,它是一個索引值,指向內核為每一個進程所維護的該進程打開文件的記錄表。當程序打開一個現有文件或者創建一個新文件時,內核向進程返回一個文件描述符。在程序設計中,一些涉及底層的程序編寫往往會圍繞著文件描述符展開。但是文件描述符這一概念往往只適用于UNIX、Linux這樣的操作系統。

1.5 緩存 IO##

緩存 IO 又被稱作標準 IO,大多數文件系統的默認 IO 操作都是緩存 IO。在 Linux 的緩存 IO 機制中,操作系統會將 IO 的數據緩存在文件系統的頁緩存( page cache )中,也就是說,數據會先被拷貝到操作系統內核的緩沖區中,然后才會從操作系統內核的緩沖區拷貝到應用程序的地址空間

緩存 IO 的缺點:

數據在傳輸過程中需要在應用程序地址空間和內核進行多次數據拷貝操作,這些數據拷貝操作所帶來的 CPU 以及內存開銷是非常大的。

2 Linux IO模型#

網絡IO的本質是socket的讀取,socket在linux系統被抽象為流,IO可以理解為對流的操作。剛才說了,對于一次IO訪問(以read舉例),數據會先被拷貝到操作系統內核的緩沖區中,然后才會從操作系統內核的緩沖區拷貝到應用程序的地址空間。所以說,當一個read操作發生時,它會經歷兩個階段:

  1. 第一階段:等待數據準備 (Waiting for the data to be ready)。

  2. 第二階段:將數據從內核拷貝到進程中 (Copying the data from the kernel to the process)。

對于socket流而言,

  1. 第一步:通常涉及等待網絡上的數據分組到達,然后被復制到內核的某個緩沖區。

  2. 第二步:把數據從內核緩沖區復制到應用進程緩沖區。

網絡應用需要處理的無非就是兩大類問題,網絡IO,數據計算。相對于后者,網絡IO的延遲,給應用帶來的性能瓶頸大于后者。網絡IO的模型大致有如下幾種:

  • 同步模型(synchronous IO)

  • 阻塞IO(bloking IO)

  • 非阻塞IO(non-blocking IO)

  • 多路復用IO(multiplexing IO)

  • 信號驅動式IO(signal-driven IO)

  • 異步IO(asynchronous IO)

注:由于signal driven IO在實際中并不常用,所以我這只提及剩下的四種IO Model。

在深入介紹Linux IO各種模型之前,讓我們先來探索一下基本 Linux IO 模型的簡單矩陣。如下圖所示:

輸入圖片說明

輸入圖片說明

每個 IO 模型都有自己的使用模式,它們對于特定的應用程序都有自己的優點。本節將簡要對其一一進行介紹。常見的IO模型有阻塞、非阻塞、IO多路復用,異步。以一個生動形象的例子來說明這四個概念。周末我和女友去逛街,中午餓了,我們準備去吃飯。周末人多,吃飯需要排隊,我和女友有以下幾種方案。

2.1 同步阻塞 IO(blocking IO)##

2.1.1 場景描述###

我和女友點完餐后,不知道什么時候能做好,只好坐在餐廳里面等,直到做好,然后吃完才離開。女友本想還和我一起逛街的,但是不知道飯能什么時候做好,只好和我一起在餐廳等,而不能去逛街,直到吃完飯才能去逛街,中間等待做飯的時間浪費掉了。這就是典型的阻塞

2.1.2 網絡模型###

同步阻塞 IO 模型是最常用的一個模型,也是最簡單的模型。在linux中,默認情況下所有的socket都是blocking。它符合人們最常見的思考邏輯。阻塞就是進程 "被" 休息, CPU處理其它進程去了

在這個IO模型中,用戶空間的應用程序執行一個系統調用(recvform),這會導致應用程序阻塞,什么也不干,直到數據準備好,并且將數據從內核復制到用戶進程,最后進程再處理數據,在等待數據到處理數據的兩個階段,整個進程都被阻塞。不能處理別的網絡IO。調用應用程序處于一種不再消費 CPU 而只是簡單等待響應的狀態,因此從處理的角度來看,這是非常有效的。在調用recv()/recvfrom()函數時,發生在內核中等待數據和復制數據的過程,大致如下圖:

輸入圖片說明

輸入圖片說明

2.1.3 流程描述###

當用戶進程調用了recv()/recvfrom()這個系統調用,kernel就開始了IO的第一個階段:準備數據(對于網絡IO來說,很多時候數據在一開始還沒有到達。比如,還沒有收到一個完整的UDP包。這個時候kernel就要等待足夠的數據到來)。這個過程需要等待,也就是說數據被拷貝到操作系統內核的緩沖區中是需要一個過程的。而在用戶進程這邊,整個進程會被阻塞(當然,是進程自己選擇的阻塞)。第二個階段:當kernel一直等到數據準備好了,它就會將數據從kernel中拷貝到用戶內存,然后kernel返回結果,用戶進程才解除block的狀態,重新運行起來。

所以,blocking IO的特點就是在IO執行的兩個階段都被block了。

優點:

  1. 能夠及時返回數據,無延遲;

  2. 對內核開發者來說這是省事了;

缺點:

  1. 對用戶來說處于等待就要付出性能的代價了;

2.2 同步非阻塞 IO(nonblocking IO)##

2.2.1 場景描述###

我女友不甘心白白在這等,又想去逛商場,又擔心飯好了。所以我們逛一會,回來詢問服務員飯好了沒有,來來回回好多次,飯都還沒吃都快累死了啦。這就是非阻塞。需要不斷的詢問,是否準備好了。

2.2.2 網絡模型###

同步非阻塞就是 “每隔一會兒瞄一眼進度條” 的輪詢(polling)方式。在這種模型中,設備是以非阻塞的形式打開的。這意味著 IO 操作不會立即完成,read 操作可能會返回一個錯誤代碼,說明這個命令不能立即滿足(EAGAIN 或 EWOULDBLOCK)。

在網絡IO時候,非阻塞IO也會進行recvform系統調用,檢查數據是否準備好,與阻塞IO不一樣,"非阻塞將大的整片時間的阻塞分成N多的小的阻塞, 所以進程不斷地有機會 '被' CPU光顧"。

也就是說非阻塞的recvform系統調用調用之后,進程并沒有被阻塞,內核馬上返回給進程,如果數據還沒準備好,此時會返回一個error。進程在返回之后,可以干點別的事情,然后再發起recvform系統調用。重復上面的過程,循環往復的進行recvform系統調用。這個過程通常被稱之為輪詢。輪詢檢查內核數據,直到數據準備好,再拷貝數據到進程,進行數據處理。需要注意,拷貝數據整個過程,進程仍然是屬于阻塞的狀態

在linux下,可以通過設置socket使其變為non-blocking。當對一個non-blocking socket執行讀操作時,流程如圖所示:

輸入圖片說明

輸入圖片說明

2.2.3 流程描述###

當用戶進程發出read操作時,如果kernel中的數據還沒有準備好,那么它并不會block用戶進程,而是立刻返回一個error。從用戶進程角度講,它發起一個read操作后,并不需要等待,而是馬上就得到了一個結果。用戶進程判斷結果是一個error時,它就知道數據還沒有準備好,于是它可以再次發送read操作。一旦kernel中的數據準備好了,并且又再次收到了用戶進程的system call,那么它馬上就將數據拷貝到了用戶內存,然后返回。

所以,nonblocking IO的特點是用戶進程需要不斷的主動詢問kernel數據好了沒有。

同步非阻塞方式相比同步阻塞方式:

優點:能夠在等待任務完成的時間里干其他活了(包括提交其他任務,也就是 “后臺” 可以有多個任務在同時執行)。

缺點:任務完成的響應延遲增大了,因為每過一段時間才去輪詢一次read操作,而任務可能在兩次輪詢之間的任意時間完成。這會導致整體數據吞吐量的降低。

2.3 IO 多路復用( IO multiplexing)##

2.3.1 場景描述###

與第二個方案差不多,餐廳安裝了電子屏幕用來顯示點餐的狀態,這樣我和女友逛街一會,回來就不用去詢問服務員了,直接看電子屏幕就可以了。這樣每個人的餐是否好了,都直接看電子屏幕就可以了,這就是典型的IO多路復用

2.3.2 網絡模型###

由于同步非阻塞方式需要不斷主動輪詢,輪詢占據了很大一部分過程,輪詢會消耗大量的CPU時間,而 “后臺” 可能有多個任務在同時進行,人們就想到了循環查詢多個任務的完成狀態,只要有任何一個任務完成,就去處理它。如果輪詢不是進程的用戶態,而是有人幫忙就好了。那么這就是所謂的 “IO 多路復用”。UNIX/Linux 下的 select、poll、epoll 就是干這個的(epoll 比 poll、select 效率高,做的事情是一樣的)。

IO多路復用有兩個特別的系統調用select、poll、epoll函數。select調用是內核級別的,select輪詢相對非阻塞的輪詢的區別在于---前者可以等待多個socket,能實現同時對多個IO端口進行監聽,當其中任何一個socket的數據準好了,就能返回進行可讀然后進程再進行recvform系統調用,將數據由內核拷貝到用戶進程,當然這個過程是阻塞的。select或poll調用之后,會阻塞進程,與blocking IO阻塞不同在于,此時的select不是等到socket數據全部到達再處理, 而是有了一部分數據就會調用用戶進程來處理。如何知道有一部分數據到達了呢?監視的事情交給了內核,內核負責數據到達的處理。也可以理解為"非阻塞"吧

I/O復用模型會用到select、poll、epoll函數,這幾個函數也會使進程阻塞,但是和阻塞I/O所不同的的,這兩個函數可以同時阻塞多個I/O操作。而且可以同時對多個讀操作,多個寫操作的I/O函數進行檢測,直到有數據可讀或可寫時(注意不是全部數據可讀或可寫),才真正調用I/O操作函數。

對于多路復用,也就是輪詢多個socket。多路復用既然可以處理多個IO,也就帶來了新的問題,多個IO之間的順序變得不確定了,當然也可以針對不同的編號。具體流程,如下圖所示:

輸入圖片說明

輸入圖片說明

2.3.3 流程描述###

IO multiplexing就是我們說的select,poll,epoll,有些地方也稱這種IO方式為event driven IO。select/epoll的好處就在于單個process就可以同時處理多個網絡連接的IO。它的基本原理就是select,poll,epoll這個function會不斷的輪詢所負責的所有socket,當某個socket有數據到達了,就通知用戶進程。

當用戶進程調用了select,那么整個進程會被block,而同時,kernel會“監視”所有select負責的socket,當任何一個socket中的數據準備好了,select就會返回。這個時候用戶進程再調用read操作,將數據從kernel拷貝到用戶進程。

多路復用的特點是通過一種機制一個進程能同時等待IO文件描述符,內核監視這些文件描述符(套接字描述符),其中的任意一個進入讀就緒狀態,select, poll,epoll函數就可以返回。對于監視的方式,又可以分為 select, poll, epoll三種方式。

上面的圖和blocking IO的圖其實并沒有太大的不同,事實上,還更差一些。因為這里需要使用兩個system call (select 和 recvfrom),而blocking IO只調用了一個system call (recvfrom)。但是,用select的優勢在于它可以同時處理多個connection

所以,如果處理的連接數不是很高的話,使用select/epoll的web server不一定比使用multi-threading + blocking IO的web server性能更好,可能延遲還更大。(select/epoll的優勢并不是對于單個連接能處理得更快,而是在于能處理更多的連接。)

在IO multiplexing Model中,實際中,對于每一個socket,一般都設置成為non-blocking,但是,如上圖所示,整個用戶的process其實是一直被block的。只不過process是被select這個函數block,而不是被socket IO給block。所以IO多路復用是阻塞在select,epoll這樣的系統調用之上,而沒有阻塞在真正的I/O系統調用如recvfrom之上。

在I/O編程過程中,當需要同時處理多個客戶端接入請求時,可以利用多線程或者I/O多路復用技術進行處理。I/O多路復用技術通過把多個I/O的阻塞復用到同一個select的阻塞上,從而使得系統在單線程的情況下可以同時處理多個客戶端請求。與傳統的多線程/多進程模型比,I/O多路復用的最大優勢是系統開銷小,系統不需要創建新的額外進程或者線程,也不需要維護這些進程和線程的運行,降底了系統的維護工作量,節省了系統資源,I/O多路復用的主要應用場景如下:

服務器需要同時處理多個處于監聽狀態或者多個連接狀態的套接字。

服務器需要同時處理多種網絡協議的套接字。

了解了前面三種IO模式,在用戶進程進行系統調用的時候,他們在等待數據到來的時候,處理的方式不一樣,直接等待,輪詢,select或poll輪詢,兩個階段過程:

第一個階段有的阻塞,有的不阻塞,有的可以阻塞又可以不阻塞。

第二個階段都是阻塞的。

從整個IO過程來看,他們都是順序執行的,因此可以歸為同步模型(synchronous)。都是進程主動等待且向內核檢查狀態。【此句很重要!!!】

高并發的程序一般使用同步非阻塞方式而非多線程 + 同步阻塞方式。要理解這一點,首先要扯到并發和并行的區別。比如去某部門辦事需要依次去幾個窗口,辦事大廳里的人數就是并發數,而窗口個數就是并行度。也就是說并發數是指同時進行的任務數(如同時服務的 HTTP 請求),而并行數是可以同時工作的物理資源數量(如 CPU 核數)。通過合理調度任務的不同階段,并發數可以遠遠大于并行度,這就是區區幾個 CPU 可以支持上萬個用戶并發請求的奧秘。在這種高并發的情況下,為每個任務(用戶請求)創建一個進程或線程的開銷非常大。而同步非阻塞方式可以把多個 IO 請求丟到后臺去,這就可以在一個進程里服務大量的并發 IO 請求

注意:IO多路復用是同步阻塞模型還是異步阻塞模型,在此給大家分析下:

此處仍然不太清楚的,強烈建議大家在細究《聊聊同步、異步、阻塞與非阻塞》中講同步與異步的根本性區別,同步是需要主動等待消息通知,而異步則是被動接收消息通知,通過回調、通知、狀態等方式來被動獲取消息IO多路復用在阻塞到select階段時,用戶進程是主動等待并調用select函數獲取數據就緒狀態消息,并且其進程狀態為阻塞。所以,把IO多路復用歸為同步阻塞模式

2.4 信號驅動式IO(signal-driven IO)##

信號驅動式I/O:首先我們允許Socket進行信號驅動IO,并安裝一個信號處理函數,進程繼續運行并不阻塞。當數據準備好時,進程會收到一個SIGIO信號,可以在信號處理函數中調用I/O操作函數處理數據。過程如下圖所示:

輸入圖片說明

輸入圖片說明

2.5 異步非阻塞 IO(asynchronous IO)##

2.5.1 場景描述###

女友不想逛街,又餐廳太吵了,回家好好休息一下。于是我們叫外賣,打個電話點餐,然后我和女友可以在家好好休息一下,飯好了送貨員送到家里來。這就是典型的異步,只需要打個電話說一下,然后可以做自己的事情,飯好了就送來了。

2.5.2 網絡模型###

相對于同步IO,異步IO不是順序執行。用戶進程進行aio_read系統調用之后,無論內核數據是否準備好,都會直接返回給用戶進程,然后用戶態進程可以去做別的事情。等到socket數據準備好了,內核直接復制數據給進程,然后從內核向進程發送通知IO兩個階段,進程都是非阻塞的

Linux提供了AIO庫函數實現異步,但是用的很少。目前有很多開源的異步IO庫,例如libevent、libev、libuv。異步過程如下圖所示:

輸入圖片說明

輸入圖片說明

2.5.3 流程描述###

用戶進程發起aio_read操作之后,立刻就可以開始去做其它的事。而另一方面,從kernel的角度,當它受到一個asynchronous read之后,首先它會立刻返回,所以不會對用戶進程產生任何block。然后,kernel會等待數據準備完成,然后將數據拷貝到用戶內存,當這一切都完成之后,kernel會給用戶進程發送一個signal或執行一個基于線程的回調函數來完成這次 IO 處理過程,告訴它read操作完成了。

在 Linux 中,通知的方式是 “信號”:

如果這個進程正在用戶態忙著做別的事(例如在計算兩個矩陣的乘積),那就強行打斷之,調用事先注冊的信號處理函數,這個函數可以決定何時以及如何處理這個異步任務。由于信號處理函數是突然闖進來的,因此跟中斷處理程序一樣,有很多事情是不能做的,因此保險起見,一般是把事件 “登記” 一下放進隊列,然后返回該進程原來在做的事

如果這個進程正在內核態忙著做別的事,例如以同步阻塞方式讀寫磁盤,那就只好把這個通知掛起來了,等到內核態的事情忙完了,快要回到用戶態的時候,再觸發信號通知

如果這個進程現在被掛起了,例如無事可做 sleep 了,那就把這個進程喚醒,下次有 CPU 空閑的時候,就會調度到這個進程,觸發信號通知。

異步 API 說來輕巧,做來難,這主要是對 API 的實現者而言的。Linux 的異步 IO(AIO)支持是 2.6.22 才引入的,還有很多系統調用不支持異步 IO。Linux 的異步 IO 最初是為數據庫設計的,因此通過異步 IO 的讀寫操作不會被緩存或緩沖,這就無法利用操作系統的緩存與緩沖機制

很多人把 Linux 的 O_NONBLOCK 認為是異步方式,但事實上這是前面講的同步非阻塞方式。需要指出的是,雖然 Linux 上的 IO API 略顯粗糙,但每種編程框架都有封裝好的異步 IO 實現。操作系統少做事,把更多的自由留給用戶,正是 UNIX 的設計哲學,也是 Linux 上編程框架百花齊放的一個原因。

從前面 IO 模型的分類中,我們可以看出 AIO 的動機:

同步阻塞模型需要在 IO 操作開始時阻塞應用程序。這意味著不可能同時重疊進行處理和 IO 操作。

同步非阻塞模型允許處理和 IO 操作重疊進行,但是這需要應用程序根據重現的規則來檢查 IO 操作的狀態。

這樣就剩下異步非阻塞 IO 了,它允許處理和 IO 操作重疊進行,包括 IO 操作完成的通知。

IO多路復用除了需要阻塞之外,select 函數所提供的功能(異步阻塞 IO)與 AIO 類似。不過,它是對通知事件進行阻塞,而不是對 IO 調用進行阻塞

2.6 關于異步阻塞##

有時我們的 API 只提供異步通知方式,例如在 node.js 里,但業務邏輯需要的是做完一件事后做另一件事,例如數據庫連接初始化后才能開始接受用戶的 HTTP 請求。這樣的業務邏輯就需要調用者是以阻塞方式來工作

為了在異步環境里模擬 “順序執行” 的效果,就需要把同步代碼轉換成異步形式,這稱為 CPS(Continuation Passing Style)變換。BYVoid 大神的 continuation.js 庫就是一個 CPS 變換的工具。用戶只需用比較符合人類常理的同步方式書寫代碼,CPS 變換器會把它轉換成層層嵌套的異步回調形式

輸入圖片說明

輸入圖片說明

輸入圖片說明

輸入圖片說明

另外一種使用阻塞方式的理由是降低響應延遲。如果采用非阻塞方式,一個任務 A 被提交到后臺,就開始做另一件事 B,但 B 還沒做完,A 就完成了,這時要想讓 A 的完成事件被盡快處理(比如 A 是個緊急事務),要么丟棄做到一半的 B,要么保存 B 的中間狀態并切換回 A,任務的切換是需要時間的(不管是從磁盤載入到內存,還是從內存載入到高速緩存),這勢必降低 A 的響應速度。因此,對實時系統或者延遲敏感的事務,有時采用阻塞方式比非阻塞方式更好

3 五種IO模型總結#

3.1 blocking和non-blocking區別##

調用blocking IO會一直block住對應的進程直到操作完成,而non-blocking IO在kernel還準備數據的情況下會立刻返回。

3.2 synchronous IO和asynchronous IO區別##

在說明synchronous IO和asynchronous IO的區別之前,需要先給出兩者的定義。POSIX的定義是這樣子的:

A synchronous I/O operation causes the requesting process to be blocked until that I/O operation completes;

An asynchronous I/O operation does not cause the requesting process to be blocked;

兩者的區別就在于synchronous IO做”IO operation”的時候會將process阻塞。按照這個定義,之前所述的blocking IO,non-blocking IO,IO multiplexing都屬于synchronous IO。

有人會說,non-blocking IO并沒有被block啊。這里有個非常“狡猾”的地方,定義中所指的”IO operation”是指真實的IO操作,就是例子中的recvfrom這個system call。non-blocking IO在執行recvfrom這個system call的時候,如果kernel的數據沒有準備好,這時候不會block進程。但是,當kernel中數據準備好的時候,recvfrom會將數據從kernel拷貝到用戶內存中,這個時候進程是被block了,在這段時間內,進程是被block的。

而asynchronous IO則不一樣,當進程發起IO 操作之后,就直接返回再也不理睬了,直到kernel發送一個信號,告訴進程說IO完成。在這整個過程中,進程完全沒有被block。

各個IO Model的比較如圖所示:

輸入圖片說明

輸入圖片說明

通過上面的圖片,可以發現non-blocking IO和asynchronous IO的區別還是很明顯的。在non-blocking IO中,雖然進程大部分時間都不會被block,但是它仍然要求進程去主動的check,并且當數據準備完成以后,也需要進程主動的再次調用recvfrom來將數據拷貝到用戶內存。而asynchronous IO則完全不同。它就像是用戶進程將整個IO操作交給了他人(kernel)完成,然后他人做完后發信號通知。在此期間,用戶進程不需要去檢查IO操作的狀態,也不需要主動的去拷貝數據



作者:猿碼道
鏈接:https://www.jianshu.com/p/486b0965c296
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯系作者獲得授權并注明出處。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/383660.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/383660.shtml
英文地址,請注明出處:http://en.pswp.cn/news/383660.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

操作系統【四】分頁存儲管理

連續分配方式的缺點: 固定分區分配:缺乏靈活性,產生大量的內部碎片,內存的利用率較低 動態分區分配:會產生許多外部碎片,雖然可以用緊湊技術處理,但是緊湊技術的時間代價較高 基本分頁存儲管理…

聊聊同步、異步、阻塞與非阻塞

近來遇到了一些常見的概念,尤其是網絡編程方面的概念,如:阻塞、非阻塞、異步I/O等等,對于這些概念自己也沒有太清晰的認識,只是很模糊的概念,說了解吧也了解,但是要讓自己準確的描述概念方面的具…

操作系統【五】分段內存管理+段頁式內存管理

基本分段存儲管理 與分頁最大的區別:離散分配時所分配地址空間的基本單位不同 進程的地址空間:按照程序自身的邏輯關系劃分為若干個段,每個段都有一個段名,每段從0開始編址 內存分配規則:以段位單位進行分配&#xff…

計算機網絡【六】網絡層協議

網絡層負責在不同網絡之間盡力轉發數據包(基于數據包的IP地址轉發)。不負責丟失重傳,也不負責順序(每一個數據包都是單獨選擇路徑)。 可靠傳輸是由傳輸層實現。 網絡設備和OSI參考模型 通過分層,屏蔽了…

epoll 水平觸發與邊緣觸發

https://blog.csdn.net/lihao21/article/details/67631516?refmyread epoll也是實現I/O多路復用的一種方法,為了深入了解epoll的原理,我們先來看下epoll水平觸發(level trigger,LT,LT為epoll的默認工作模式&#xff…

計算機網絡【3】網絡層

主要任務時把分組從源端發送到目的端,為分組交換網上的不同主機提供服務。網絡層傳輸單位是數據報 功能: 路由選擇與分組轉發(最佳路徑 )異構網絡互聯擁塞控制 數據交換方式 電路交換:通信時延小、有序傳輸、沒有沖…

C++空類的大小

https://blog.csdn.net/lihao21/article/details/47973609 本文中所說是C的空類是指這個類不帶任何數據,即類中沒有非靜態(non-static)數據成員變量,沒有虛函數(virtual function),也沒有虛基類(virtual base class)。 直觀地看&#xff0c…

Linux探秘之用戶態與內核態

https://www.cnblogs.com/bakari/p/5520860.html 一、 Unix/Linux的體系架構 如上圖所示,從宏觀上來看,Linux操作系統的體系架構分為用戶態和內核態(或者用戶空間和內核)。內核從本質上看是一種軟件——控制計算機的硬件資源&…

哈夫曼算法證明+哈夫曼編碼譯碼程序實現

哈夫曼算法證明 哈夫曼算法是一種貪心算法,我們考慮證明其最優子結構和貪心選擇性質: 最優子結構:假設一個樹是哈夫曼樹,則以其任意節點為根節點的最大子樹也是哈夫曼樹。 證明:子樹的根節點的值是其所有葉子節點出現…

Python3小知識

對于迭代器對象,Python默認賦值是將引用賦值,即指向同一片內存空間。為了實現對內存空間的賦值,我們可以使用分片進行深復制。例如: 當定義元組的時候,我們一般使用小括號將元素包圍起來,也可以不使用括號…

匯編:實現日歷星期數查詢工具

編制一個簡單日歷查詢工具,輸入年、月、日,能夠判斷當日的星期數,并進行輸出,數據的輸入和結果的輸出要有必要的提示,且提示獨占一行。 查閱資料 ? 經過查閱資料,發現有兩個相關的算法可以解決這個問題&…

一個通用純C隊列的實現

https://blog.csdn.net/kxcfzyk/article/details/31728179 隊列并不是很復雜的數據結構,但是非常實用,這里實現一個隊列是因為在我的另一篇博客非常精簡的Linux線程池實現中要用到。 隊列API定義如下: //queue.h #ifndef QUEUE_H_INCLUDED…

Dijkstra算法介紹+正確性證明+性能分析

算法介紹 源點s,數組d[u]表示s到u的最短距離,空集S,點集Q初始化:將源點s從點集中去掉,加入S,d[s]0,?v∈Q,d[v]w[s][v]\forall v\in Q ,d[v]w[s][v]?v∈Q,d[v]w[s][v]將Q中d[v]最小的點去掉加入S,并對u∈…

Linux C 實現一個簡單的線程池

https://www.cnblogs.com/GyForever1004/p/9185240.html 線程池的定義 線程池是一種多線程處理形式,處理過程中將任務添加到隊列,然后在創建線程后自動啟動這些任務。線程池線程都是后臺線程。每個線程都使用默認的堆棧大小,以默認的優先級…

斐波那契數列求解+尾遞歸

1.普通遞歸 這里觀察f[4]的遞歸樹代替f[10]的遞歸樹(后者比較大,畫不下)。 使用遞歸求解的時候復雜度為T(n)T(n?1)T(n?2)T(n)T(n-1)T(n-2)T(n)T(n?1)T(n?2),觀察遞歸樹,發現降速最快的是最右邊每次減2&#xff0c…

循環服務器,并發服務器模型以及I/O多路轉接模型

https://blog.csdn.net/xinianbuxiu/article/details/53455784 一、基于TCP/IP協議的基本循環服務器 tcp_server.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #incl…

c++繼承父類的子類,如何調用父類的同名函數?

https://blog.csdn.net/qq_26399665/article/details/52080215 子類調用父類的同名函數&#xff1a; 子類和父類返回值參數相同&#xff0c;函數名相同&#xff0c;有virtual關鍵字&#xff0c;則由對象的類型決定調用哪個函數。 子類和父類只要函數名相同&#xff0c;沒有vi…

LCS最長公共子串

問題介紹 LCS問題(longest common subsequence problem)指的是求解兩個字符串最長公共子序列問題。這里的子序列是可以不連續的。LCS問題廣泛地出現在計算生物學中&#xff08;DNA序列、系統生成樹等等&#xff09;。這里介紹如何解決LCS問題&#xff0c;以及算法的正確性證明…

將字符串中的空格用%20替換

如果不需要原地操作&#xff0c;則一遍遍歷&#xff0c;將非空串復制&#xff0c;遇到空格加上%20&#xff0c;如果需要原地操作&#xff0c;首先進行遍歷出空格的個數x,然后擴容2x,從后往前遍歷實現。如果非空格字符串比空格字符串多的多的時候而且字符串非常長的時候使用原地…

12步輕松搞定python裝飾器

http://python.jobbole.com/81683/ 呵呵&#xff01;作為一名教python的老師&#xff0c;我發現學生們基本上一開始很難搞定python的裝飾器&#xff0c;也許因為裝飾器確實很難懂。搞定裝飾器需要你了解一些函數式編程的概念&#xff0c;當然還有理解在python中定義和調用函數…