linux系統(tǒng)IO模型簡(jiǎn)析

相關(guān)概念:

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

現(xiàn)在操作系統(tǒng)都是采用虛擬存儲(chǔ)器,操作系統(tǒng)的核心是內(nèi)核,獨(dú)立于普通的應(yīng)用程序,可以訪問(wèn)受保護(hù)的內(nèi)存空間养渴,也有訪問(wèn)底層硬件設(shè)備的所有權(quán)限。為了保證用戶進(jìn)程不能直接操作內(nèi)核(kernel)臂拓,保證內(nèi)核的安全厚脉,操作系統(tǒng)將虛擬空間劃分為兩部分习寸,一部分為內(nèi)核空間胶惰,一部分為用戶空間。針對(duì)linux操作系統(tǒng)而言霞溪,將最高的1G字節(jié)孵滞,供內(nèi)核使用,稱為內(nèi)核空間鸯匹;而將較低的3G字節(jié)坊饶,供各個(gè)進(jìn)程使用,稱為用戶空間殴蓬。

進(jìn)程切換

為了控制進(jìn)程的執(zhí)行匿级,內(nèi)核必須有能力掛起正在CPU上運(yùn)行的進(jìn)程,并恢復(fù)以前掛起的某個(gè)進(jìn)程的執(zhí)行染厅。
這種行為被稱為進(jìn)程切換痘绎。因此可以說(shuō),任何進(jìn)程都是在操作系統(tǒng)內(nèi)核的支持下運(yùn)行的肖粮,是與內(nèi)核緊密相關(guān)的孤页。

從一個(gè)進(jìn)程的運(yùn)行轉(zhuǎn)到另一個(gè)進(jìn)程上運(yùn)行,這個(gè)過(guò)程中經(jīng)過(guò)下面這些變化:

1涩馆、保存處理機(jī)上下文行施,包括程序計(jì)數(shù)器和其他寄存器。
2魂那、更新PCB信息蛾号。
3、把進(jìn)程的PCB移入相應(yīng)的隊(duì)列涯雅,如就緒鲜结、在某事件阻塞等隊(duì)列。
4、選擇另一個(gè)進(jìn)程執(zhí)行轻腺,并更新其PCB乐疆。
5、更新內(nèi)存管理的數(shù)據(jù)結(jié)構(gòu)贬养。
6挤土、恢復(fù)處理機(jī)上下文。

進(jìn)程的阻塞

正在執(zhí)行的進(jìn)程误算,由于期待的某些事件未發(fā)生仰美,如請(qǐng)求系統(tǒng)資源失敗、等待某種操作的完成儿礼、新數(shù)據(jù)尚未到達(dá)或無(wú)新工作做等咖杂,則由系統(tǒng)自動(dòng)執(zhí)行阻塞原語(yǔ)(Block),使自己由運(yùn)行狀態(tài)變?yōu)樽枞麪顟B(tài)蚊夫∷咦郑可見(jiàn),進(jìn)程的阻塞是進(jìn)程自身的一種主動(dòng)行為知纷,也因此只有處于運(yùn)行態(tài)的進(jìn)程(獲得CPU)壤圃,才可能將其轉(zhuǎn)為阻塞狀態(tài)。當(dāng)進(jìn)程進(jìn)入阻塞狀態(tài)琅轧,是不占用CPU資源的伍绳。

文件描述符fd

文件描述符(File descriptor)是計(jì)算機(jī)科學(xué)中的一個(gè)術(shù)語(yǔ),是一個(gè)用于表述指向文件的引用的抽象化概念乍桂。

文件描述符在形式上是一個(gè)非負(fù)整數(shù)冲杀。實(shí)際上,它是一個(gè)索引值睹酌,指向內(nèi)核為每一個(gè)進(jìn)程所維護(hù)的該進(jìn)程打開(kāi)文件的記錄表权谁。當(dāng)程序打開(kāi)一個(gè)現(xiàn)有文件或者創(chuàng)建一個(gè)新文件時(shí),內(nèi)核向進(jìn)程返回一個(gè)文件描述符忍疾。在程序設(shè)計(jì)中闯传,一些涉及底層的程序編寫(xiě)往往會(huì)圍繞著文件描述符展開(kāi)。但是文件描述符這一概念往往只適用于UNIX卤妒、Linux這樣的操作系統(tǒng)甥绿。

緩存 IO 和 直接IO

緩存IO:數(shù)據(jù)從磁盤(pán)先通過(guò)DMA copy到內(nèi)核空間,再?gòu)膬?nèi)核空間通過(guò)cpu copy到用戶空間则披。
直接IO:數(shù)據(jù)從磁盤(pán)通過(guò)DMA copy到用戶空間共缕。

緩存IO

緩存 IO 又被稱作標(biāo)準(zhǔn) IO,大多數(shù)文件系統(tǒng)的默認(rèn) IO 操作都是緩存 IO士复。在 Linux 的緩存 IO 機(jī)制中图谷,操作系統(tǒng)會(huì)將 IO 的數(shù)據(jù)緩存在文件系統(tǒng)的頁(yè)緩存( page cache )中翩活,也就是說(shuō),數(shù)據(jù)會(huì)先從磁盤(pán)被拷貝到操作系統(tǒng)內(nèi)核的緩沖區(qū)中便贵,然后才會(huì)從操作系統(tǒng)內(nèi)核的緩沖區(qū)拷貝到應(yīng)用程序的地址空間菠镇。

讀操作:

操作系統(tǒng)檢查內(nèi)核的緩沖區(qū)有沒(méi)有需要的數(shù)據(jù),如果已經(jīng)緩存了承璃,那么就直接從緩存中返回利耍;否則從磁盤(pán)中讀
取,然后緩存在操作系統(tǒng)的緩存中盔粹。

寫(xiě)操作:

將數(shù)據(jù)從用戶空間復(fù)制到內(nèi)核空間的緩存中隘梨。這時(shí)對(duì)用戶程序來(lái)說(shuō)寫(xiě)操作就已經(jīng)完成,至于什么時(shí)候再寫(xiě)到磁
盤(pán)中由操作系統(tǒng)決定舷嗡,除非顯示地調(diào)用了sync同步命令轴猎。

緩存I/O的優(yōu)點(diǎn):

在一定程度上分離了內(nèi)核空間和用戶空間,保護(hù)系統(tǒng)本身的運(yùn)行安全进萄;
可以減少讀盤(pán)的次數(shù)捻脖,從而提高性能。

緩存I/O的缺點(diǎn):

在緩存 I/O 機(jī)制中垮斯,DMA 方式可以將數(shù)據(jù)直接從磁盤(pán)讀到頁(yè)緩存中郎仆,或者將數(shù)據(jù)從頁(yè)緩存直接寫(xiě)回到磁盤(pán)上,而不能直接在應(yīng)用程序地址空間和磁盤(pán)之間進(jìn)行數(shù)據(jù)傳輸兜蠕,這樣,數(shù)據(jù)在傳輸過(guò)程中需要在應(yīng)用程序地址空間(用戶空間)和緩存(內(nèi)核空間)之間進(jìn)行多次數(shù)據(jù)拷貝操作抛寝,這些數(shù)據(jù)拷貝操作所帶來(lái)的CPU以及內(nèi)存開(kāi)銷是非常大的熊杨。

直接IO

直接IO就是應(yīng)用程序直接訪問(wèn)磁盤(pán)數(shù)據(jù),而不經(jīng)過(guò)內(nèi)核緩沖區(qū)盗舰,也就是繞過(guò)內(nèi)核緩沖區(qū)晶府,自己管理I/O緩存區(qū),這樣做的目的是減少一次從內(nèi)核緩沖區(qū)到用戶程序緩存的數(shù)據(jù)復(fù)制钻趋。

引入內(nèi)核緩沖區(qū)的目的在于提高磁盤(pán)文件的訪問(wèn)性能川陆,因?yàn)楫?dāng)進(jìn)程需要讀取磁盤(pán)文件時(shí),如果文件內(nèi)容已經(jīng)在內(nèi)核緩沖區(qū)中蛮位,那么就不需要再次訪問(wèn)磁盤(pán)较沪;而當(dāng)進(jìn)程需要向文件中寫(xiě)入數(shù)據(jù)時(shí),實(shí)際上只是寫(xiě)到了內(nèi)核緩沖區(qū)便告訴進(jìn)程已經(jīng)寫(xiě)成功失仁,而真正寫(xiě)入磁盤(pán)是通過(guò)一定的策略進(jìn)行延遲的尸曼。

然而,對(duì)于一些較復(fù)雜的應(yīng)用萄焦,比如數(shù)據(jù)庫(kù)服務(wù)器控轿,它們?yōu)榱顺浞痔岣咝阅埽M@過(guò)內(nèi)核緩沖區(qū),由自己在用戶態(tài)空間實(shí)現(xiàn)并管理I/O緩沖區(qū)茬射,包括緩存機(jī)制和寫(xiě)延遲機(jī)制等鹦蠕,以支持獨(dú)特的查詢機(jī)制,比如數(shù)據(jù)庫(kù)可以根據(jù)更加合理的策略來(lái)提高查詢緩存命中率在抛。另一方面片部,繞過(guò)內(nèi)核緩沖區(qū)也可以減少系統(tǒng)內(nèi)存的開(kāi)銷,因?yàn)閮?nèi)核緩沖區(qū)本身就在使用系統(tǒng)內(nèi)存霜定。

應(yīng)用程序直接訪問(wèn)磁盤(pán)數(shù)據(jù)档悠,不經(jīng)過(guò)操作系統(tǒng)內(nèi)核數(shù)據(jù)緩沖區(qū),這樣做的目的是減少一次從內(nèi)核緩沖區(qū)到用戶程序緩存的數(shù)據(jù)復(fù)制望浩。這種方式通常是在對(duì)數(shù)據(jù)的緩存管理由應(yīng)用程序?qū)崿F(xiàn)的數(shù)據(jù)庫(kù)管理系統(tǒng)中辖所。

直接I/O的缺點(diǎn):

如果訪問(wèn)的數(shù)據(jù)不在應(yīng)用程序緩存中,那么每次數(shù)據(jù)都會(huì)直接從磁盤(pán)進(jìn)行加載磨德,這種直接加載會(huì)非常緩慢缘回。通常直接I/O跟異步I/O結(jié)合使用會(huì)得到較好的性能。

813155-20200627200040986-198855887.png

訪問(wèn)步驟:

image.png

Linux提供了對(duì)這種需求的支持典挑,即在open()系統(tǒng)調(diào)用中增加參數(shù)選項(xiàng)O_DIRECT酥宴,用它打開(kāi)的文件便可以繞過(guò)內(nèi)核緩沖區(qū)的直接訪問(wèn),這樣便有效避免了CPU和內(nèi)存的多余時(shí)間開(kāi)銷您觉。

順便提一下拙寡,與O_DIRECT類似的一個(gè)選項(xiàng)是O_SYNC,后者只對(duì)寫(xiě)數(shù)據(jù)有效琳水,它將寫(xiě)入內(nèi)核緩沖區(qū)的數(shù)據(jù)立即寫(xiě)入磁盤(pán)肆糕,將機(jī)器故障時(shí)數(shù)據(jù)的丟失減少到最小,但是它仍然要經(jīng)過(guò)內(nèi)核緩沖區(qū)在孝。

Linux IO模型

網(wǎng)絡(luò)IO的本質(zhì)是socket的讀取诚啃,socket在linux系統(tǒng)被抽象為流,IO可以理解為對(duì)流的操作私沮。剛才說(shuō)了始赎,對(duì)于一次IO訪問(wèn)(以read舉例),數(shù)據(jù)會(huì)先被拷貝到操作系統(tǒng)內(nèi)核的緩沖區(qū)中仔燕,然后才會(huì)從操作系統(tǒng)內(nèi)核的緩沖區(qū)拷貝到應(yīng)用程序的地址空間造垛。所以說(shuō),當(dāng)一個(gè)read操作發(fā)生時(shí)涨享,它會(huì)經(jīng)歷兩個(gè)階段:

第一階段:等待數(shù)據(jù)準(zhǔn)備 (Waiting for the data to be ready)筋搏。
第二階段:將數(shù)據(jù)從內(nèi)核拷貝到進(jìn)程中 (Copying the data from the kernel to the process)。

對(duì)于socket流而言厕隧,

第一步:通常涉及等待網(wǎng)絡(luò)上的數(shù)據(jù)分組到達(dá)奔脐,然后被復(fù)制到內(nèi)核的某個(gè)緩沖區(qū)俄周。
第二步:把數(shù)據(jù)從內(nèi)核緩沖區(qū)復(fù)制到應(yīng)用進(jìn)程緩沖區(qū)。

網(wǎng)絡(luò)應(yīng)用需要處理的無(wú)非就是兩大類問(wèn)題髓迎,網(wǎng)絡(luò)IO峦朗,數(shù)據(jù)計(jì)算。相對(duì)于后者排龄,網(wǎng)絡(luò)IO的延遲波势,給應(yīng)用帶來(lái)的性能瓶頸大于后者。

網(wǎng)絡(luò)IO的模型大致有如下幾種:

同步模型(synchronous IO)
阻塞IO(bloking IO)
非阻塞IO(non-blocking IO)
多路復(fù)用IO(multiplexing IO)
信號(hào)驅(qū)動(dòng)式IO(signal-driven IO):不常用
異步IO(asynchronous IO)

常見(jiàn)的IO模型有阻塞橄维、非阻塞尺铣、IO多路復(fù)用,異步争舞。
以一個(gè)生動(dòng)形象的例子來(lái)說(shuō)明這四個(gè)概念凛忿。周末我和女友去逛街,中午餓了竞川,我們準(zhǔn)備去吃飯店溢。周末人多,吃飯需要排隊(duì)委乌,我和女友有以下幾種方案床牧。

同步阻塞 IO(blocking IO)

場(chǎng)景描述

我和女友點(diǎn)完餐后,不知道什么時(shí)候能做好遭贸,只好坐在餐廳里面等戈咳,直到做好,然后吃完才離開(kāi)革砸。女友本想還和我一起逛街的除秀,但是不知道飯能什么時(shí)候做好,只好和我一起在餐廳等算利,而不能去逛街,直到吃完飯才能去逛街泳姐,中間等待做飯的時(shí)間浪費(fèi)掉了效拭。--------------------這就是典型的阻塞。

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

同步阻塞 IO 模型是最常用的一個(gè)模型胖秒,也是最簡(jiǎn)單的模型缎患。在linux中,默認(rèn)情況下所有的socket都是blocking阎肝。它符合人們最常見(jiàn)的思考邏輯挤渔。阻塞就是進(jìn)程 "被" 休息, CPU處理其它進(jìn)程去了。

在這個(gè)IO模型中风题,用戶空間的應(yīng)用程序執(zhí)行一個(gè)系統(tǒng)調(diào)用(recvform)判导,這會(huì)導(dǎo)致應(yīng)用程序阻塞嫉父,什么也不干,直到數(shù)據(jù)準(zhǔn)備好眼刃,并且將數(shù)據(jù)從內(nèi)核復(fù)制到用戶進(jìn)程绕辖,最后進(jìn)程再處理數(shù)據(jù),在等待數(shù)據(jù)到處理數(shù)據(jù)的兩個(gè)階段擂红,整個(gè)進(jìn)程都被阻塞仪际。不能處理別的網(wǎng)絡(luò)IO。調(diào)用應(yīng)用程序處于一種不再消費(fèi) CPU 而只是簡(jiǎn)單等待響應(yīng)的狀態(tài)昵骤,因此從處理的角度來(lái)看树碱,這是非常有效的。在調(diào)用recv()/recvfrom()函數(shù)時(shí)变秦,發(fā)生在內(nèi)核中等待數(shù)據(jù)和復(fù)制數(shù)據(jù)的過(guò)程成榜,大致如下圖:


image.png

流程描述

當(dāng)用戶進(jìn)程調(diào)用了recv()/recvfrom()這個(gè)系統(tǒng)調(diào)用,kernel就開(kāi)始了IO的第一個(gè)階段:準(zhǔn)備數(shù)據(jù)(對(duì)于網(wǎng)絡(luò)IO來(lái)說(shuō)伴栓,很多時(shí)候數(shù)據(jù)在一開(kāi)始還沒(méi)有到達(dá)伦连。比如,還沒(méi)有收到一個(gè)完整的UDP包钳垮。這個(gè)時(shí)候kernel就要等待足夠的數(shù)據(jù)到來(lái))惑淳。這個(gè)過(guò)程需要等待,也就是說(shuō)數(shù)據(jù)被拷貝到操作系統(tǒng)內(nèi)核的緩沖區(qū)中是需要一個(gè)過(guò)程的饺窿。而在用戶進(jìn)程這邊歧焦,整個(gè)進(jìn)程會(huì)被阻塞(當(dāng)然,是進(jìn)程自己選擇的阻塞)肚医。第二個(gè)階段:當(dāng)kernel一直等到數(shù)據(jù)準(zhǔn)備好了绢馍,它就會(huì)將數(shù)據(jù)從kernel中拷貝到用戶內(nèi)存,然后kernel返回結(jié)果肠套,用戶進(jìn)程才解除block的狀態(tài)舰涌,重新運(yùn)行起來(lái)。

所以你稚,blocking IO的特點(diǎn)就是在IO執(zhí)行的兩個(gè)階段都被block了瓷耙。

優(yōu)點(diǎn):
能夠及時(shí)返回?cái)?shù)據(jù),無(wú)延遲刁赖;
對(duì)內(nèi)核開(kāi)發(fā)者來(lái)說(shuō)這是省事了搁痛;
缺點(diǎn):
對(duì)用戶來(lái)說(shuō)處于等待就要付出性能的代價(jià)了;

同步非阻塞 IO(nonblocking IO)

場(chǎng)景描述

我女友不甘心白白在這等宇弛,又想去逛商場(chǎng)鸡典,又擔(dān)心飯好了。所以我們逛一會(huì)枪芒,回來(lái)詢問(wèn)服務(wù)員飯好了沒(méi)有彻况,來(lái)來(lái)回回好多次谁尸,飯都還沒(méi)吃都快累死了啦。

這就是非阻塞疗垛。需要不斷的詢問(wèn)症汹,是否準(zhǔn)備好了。

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

同步非阻塞就是 “每隔一會(huì)兒瞄一眼進(jìn)度條” 的輪詢(polling)方式贷腕。在這種模型中背镇,設(shè)備是以非阻塞的形式打開(kāi)的。這意味著 IO 操作不會(huì)立即完成泽裳,read 操作可能會(huì)返回一個(gè)錯(cuò)誤代碼瞒斩,說(shuō)明這個(gè)命令不能立即滿足(EAGAIN 或 EWOULDBLOCK)。

在網(wǎng)絡(luò)IO時(shí)候涮总,非阻塞IO也會(huì)進(jìn)行recvform系統(tǒng)調(diào)用胸囱,檢查數(shù)據(jù)是否準(zhǔn)備好,與阻塞IO不一樣瀑梗,"非阻塞將大的整片時(shí)間的阻塞分成N多的小的阻塞, 所以進(jìn)程不斷地有機(jī)會(huì) '被' CPU光顧"烹笔。

也就是說(shuō)非阻塞的recvform系統(tǒng)調(diào)用之后,進(jìn)程并沒(méi)有被阻塞抛丽,內(nèi)核馬上返回給進(jìn)程谤职,如果數(shù)據(jù)還沒(méi)準(zhǔn)備好,此時(shí)會(huì)返回一個(gè)error亿鲜。進(jìn)程在返回之后搀暑,可以干點(diǎn)別的事情撤奸,然后再發(fā)起recvform系統(tǒng)調(diào)用哑子。重復(fù)上面的過(guò)程岳枷,循環(huán)往復(fù)的進(jìn)行recvform系統(tǒng)調(diào)用。這個(gè)過(guò)程通常被稱之為輪詢垒探。輪詢檢查內(nèi)核數(shù)據(jù)妓蛮,直到數(shù)據(jù)準(zhǔn)備好,再拷貝數(shù)據(jù)到進(jìn)程圾叼,進(jìn)行數(shù)據(jù)處理仔引。需要注意,拷貝數(shù)據(jù)整個(gè)過(guò)程褐奥,進(jìn)程仍然是屬于阻塞的狀態(tài)。

在linux下翘簇,可以通過(guò)設(shè)置socket使其變?yōu)閚on-blocking撬码。當(dāng)對(duì)一個(gè)non-blocking socket執(zhí)行讀操作時(shí),流程如圖所示:


image.png

流程描述

當(dāng)用戶進(jìn)程發(fā)出read操作時(shí)版保,如果kernel中的數(shù)據(jù)還沒(méi)有準(zhǔn)備好呜笑,那么它并不會(huì)block用戶進(jìn)程夫否,而是立刻返回一個(gè)error。從用戶進(jìn)程角度講叫胁,它發(fā)起一個(gè)read操作后凰慈,并不需要等待,而是馬上就得到了一個(gè)結(jié)果驼鹅。用戶進(jìn)程判斷結(jié)果是一個(gè)error時(shí)微谓,它就知道數(shù)據(jù)還沒(méi)有準(zhǔn)備好,于是它可以再次發(fā)送read操作输钩。一旦kernel中的數(shù)據(jù)準(zhǔn)備好了豺型,并且又再次收到了用戶進(jìn)程的系統(tǒng)調(diào)用,那么它馬上就將數(shù)據(jù)拷貝到了用戶內(nèi)存买乃,然后返回姻氨。

所以,nonblocking IO的特點(diǎn)是用戶進(jìn)程需要不斷的主動(dòng)詢問(wèn)kernel數(shù)據(jù)好了沒(méi)有剪验。

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

優(yōu)點(diǎn):
能夠在等待任務(wù)完成的時(shí)間里干其他活了(包括提交其他任務(wù)肴焊,也就是 “后臺(tái)” 可以有多個(gè)任務(wù)在同時(shí)執(zhí)行)。

缺點(diǎn):
任務(wù)完成的響應(yīng)延遲增大了功戚,因?yàn)槊窟^(guò)一段時(shí)間才去輪詢一次read操作娶眷,而任務(wù)可能在兩次輪詢之間的任意時(shí)間完成。這會(huì)導(dǎo)致整體數(shù)據(jù)吞吐量的降低疫铜。

IO 多路復(fù)用( IO multiplexing)

場(chǎng)景描述

與第二個(gè)方案差不多茂浮,餐廳安裝了電子屏幕用來(lái)顯示點(diǎn)餐的狀態(tài),這樣我和女友逛街一會(huì)壳咕,回來(lái)就不用去詢問(wèn)服務(wù)員了席揽,直接看電子屏幕就可以了。這樣每個(gè)人的餐是否好了谓厘,都直接看電子屏幕就可以了幌羞。

這就是典型的IO多路復(fù)用。

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

由于同步非阻塞方式需要不斷主動(dòng)輪詢竟稳,輪詢占據(jù)了很大一部分過(guò)程属桦,輪詢會(huì)消耗大量的CPU時(shí)間,而 “后臺(tái)” 可能有多個(gè)任務(wù)在同時(shí)進(jìn)行他爸,人們就想到了循環(huán)查詢多個(gè)任務(wù)的完成狀態(tài)聂宾,只要有任何一個(gè)任務(wù)完成,就去處理它诊笤。如果輪詢不是進(jìn)程的用戶態(tài)系谐,而是有人幫忙就好了。那么這就是所謂的 “IO 多路復(fù)用”。UNIX/Linux 下的 select纪他、poll鄙煤、epoll 就是干這個(gè)的(epoll 比 poll、select 效率高茶袒,做的事情是一樣的)梯刚。

IO多路復(fù)用有兩個(gè)特別的系統(tǒng)調(diào)用select、poll薪寓、epoll函數(shù)亡资。

select調(diào)用是內(nèi)核級(jí)別的,select輪詢相對(duì)非阻塞的輪詢的區(qū)別:

前者可以等待多個(gè)socket预愤,能實(shí)現(xiàn)同時(shí)對(duì)多個(gè)IO端口進(jìn)行監(jiān)聽(tīng)沟于,當(dāng)其中任何一個(gè)socket的數(shù)據(jù)準(zhǔn)好了,就能返回進(jìn)行可讀植康,然后進(jìn)程再進(jìn)行recvform系統(tǒng)調(diào)用旷太,將數(shù)據(jù)由內(nèi)核拷貝到用戶進(jìn)程,當(dāng)然這個(gè)過(guò)程是阻塞的销睁。

select或poll調(diào)用之后供璧,會(huì)阻塞進(jìn)程,與blocking IO阻塞不同在于:

此時(shí)的select不是等到socket數(shù)據(jù)全部到達(dá)再處理, 而是有了一部分?jǐn)?shù)據(jù)就會(huì)調(diào)用用戶進(jìn)程來(lái)處理冻记。如何知道有一部分?jǐn)?shù)據(jù)到達(dá)了呢睡毒?監(jiān)視的事情交給了內(nèi)核,內(nèi)核負(fù)責(zé)數(shù)據(jù)到達(dá)的處理冗栗。也可以理解為"非阻塞"吧演顾。

I/O復(fù)用模型會(huì)用到select、poll隅居、epoll函數(shù)钠至,這幾個(gè)函數(shù)也會(huì)使進(jìn)程阻塞,但是和阻塞I/O所不同的胎源,這兩個(gè)函數(shù)可以同時(shí)阻塞多個(gè)I/O操作棉钧。而且可以同時(shí)對(duì)多個(gè)讀操作,多個(gè)寫(xiě)操作的I/O函數(shù)進(jìn)行檢測(cè)涕蚤,直到有數(shù)據(jù)可讀或可寫(xiě)時(shí)(注意不是全部數(shù)據(jù)可讀或可寫(xiě))宪卿,才真正調(diào)用I/O操作函數(shù)。

對(duì)于多路復(fù)用万栅,也就是輪詢多個(gè)socket佑钾。多路復(fù)用既然可以處理多個(gè)IO,也就帶來(lái)了新的問(wèn)題烦粒,多個(gè)IO之間的順序變得不確定了次绘,當(dāng)然也可以針對(duì)不同的編號(hào)。具體流程,如下圖所示:


image.png

流程描述

IO multiplexing就是我們說(shuō)的select邮偎,poll,epoll义黎,有些地方也稱這種IO方式為event driven IO禾进。select/epoll的好處就在于單個(gè)process就可以同時(shí)處理多個(gè)網(wǎng)絡(luò)連接的IO。它的基本原理就是select廉涕,poll泻云,epoll這個(gè)function會(huì)不斷的輪詢所負(fù)責(zé)的所有socket,當(dāng)某個(gè)socket有數(shù)據(jù)到達(dá)了狐蜕,就通知用戶進(jìn)程宠纯。

當(dāng)用戶進(jìn)程調(diào)用了select,那么整個(gè)進(jìn)程會(huì)被block层释,而同時(shí)婆瓜,kernel會(huì)“監(jiān)視”所有select負(fù)責(zé)的socket,當(dāng)任何一個(gè)socket中的數(shù)據(jù)準(zhǔn)備好了贡羔,select就會(huì)返回廉白。這個(gè)時(shí)候用戶進(jìn)程再調(diào)用read操作,將數(shù)據(jù)從kernel拷貝到用戶進(jìn)程乖寒。

多路復(fù)用的特點(diǎn)是通過(guò)一種機(jī)制一個(gè)進(jìn)程能同時(shí)等待IO文件描述符猴蹂,內(nèi)核監(jiān)視這些文件描述符(套接字描述符),其中的任意一個(gè)進(jìn)入讀就緒狀態(tài)楣嘁,select磅轻, poll,epoll函數(shù)就可以返回逐虚。對(duì)于監(jiān)視的方式聋溜,又可以分為 select, poll痊班, epoll三種方式勤婚。

上面的圖和blocking IO的圖其實(shí)并沒(méi)有太大的不同,事實(shí)上涤伐,還更差一些馒胆。因?yàn)檫@里需要使用兩個(gè)system call (select 和 recvfrom),而blocking IO只調(diào)用了一個(gè)system call (recvfrom)凝果。但是祝迂,用select的優(yōu)勢(shì)在于它可以同時(shí)處理多個(gè)connection。

所以器净,如果處理的連接數(shù)不是很高的話型雳,使用select/epoll的web server不一定比使用multi-threading + blocking IO的web server性能更好,可能延遲還更大。(select/epoll的優(yōu)勢(shì)并不是對(duì)于單個(gè)連接能處理得更快纠俭,而是在于能處理更多的連接沿量。)

在IO multiplexing Model中,實(shí)際中冤荆,對(duì)于每一個(gè)socket朴则,一般都設(shè)置成為non-blocking,但是钓简,如上圖所示乌妒,整個(gè)用戶的process其實(shí)是一直被block的。只不過(guò)process是被select這個(gè)函數(shù)block外邓,而不是被socket IO給block撤蚊。所以IO多路復(fù)用是阻塞在select,epoll這樣的系統(tǒng)調(diào)用之上损话,而沒(méi)有阻塞在真正的I/O系統(tǒng)調(diào)用如recvfrom之上侦啸。

在I/O編程過(guò)程中,當(dāng)需要同時(shí)處理多個(gè)客戶端接入請(qǐng)求時(shí)席镀,可以利用多線程或者I/O多路復(fù)用技術(shù)進(jìn)行處理匹中。I/O多路復(fù)用技術(shù)通過(guò)把多個(gè)I/O的阻塞復(fù)用到同一個(gè)select的阻塞上,從而使得系統(tǒng)在單線程的情況下可以同時(shí)處理多個(gè)客戶端請(qǐng)求豪诲。與傳統(tǒng)的多線程/多進(jìn)程模型比顶捷,I/O多路復(fù)用的最大優(yōu)勢(shì)是系統(tǒng)開(kāi)銷小,系統(tǒng)不需要?jiǎng)?chuàng)建新的額外進(jìn)程或者線程屎篱,也不需要維護(hù)這些進(jìn)程和線程的運(yùn)行服赎,降底了系統(tǒng)的維護(hù)工作量,節(jié)省了系統(tǒng)資源交播,I/O多路復(fù)用的主要應(yīng)用場(chǎng)景如下:

服務(wù)器需要同時(shí)處理多個(gè)處于監(jiān)聽(tīng)狀態(tài)或者多個(gè)連接狀態(tài)的套接字重虑。

服務(wù)器需要同時(shí)處理多種網(wǎng)絡(luò)協(xié)議的套接字。

了解了前面三種IO模式秦士,在用戶進(jìn)程進(jìn)行系統(tǒng)調(diào)用的時(shí)候缺厉,他們?cè)诘却龜?shù)據(jù)到來(lái)的時(shí)候,處理的方式不一樣隧土,直接等待提针,輪詢,select或poll輪詢曹傀,兩個(gè)階段過(guò)程:

第一階段:等待數(shù)據(jù)準(zhǔn)備 (Waiting for the data to be ready)辐脖。

第二階段:將數(shù)據(jù)從內(nèi)核拷貝到進(jìn)程中 (Copying the data from the kernel to the process)。

第一個(gè)階段有的阻塞皆愉,有的不阻塞嗜价,有的可以阻塞又可以不阻塞艇抠。

第二個(gè)階段都是阻塞的。

從整個(gè)IO過(guò)程來(lái)看久锥,他們都是順序執(zhí)行的家淤,因此可以歸為同步模型(synchronous)。都是進(jìn)程主動(dòng)等待且向內(nèi)核檢查狀態(tài)奴拦∶焦模【此句很重要!4硌!】

高并發(fā)的程序一般使用同步非阻塞方式 而非 多線程 + 同步阻塞方式疚沐。

要理解這一點(diǎn)暂氯,首先要扯到并發(fā)和并行的區(qū)別。比如去某部門(mén)辦事需要依次去幾個(gè)窗口亮蛔,辦事大廳里的人數(shù)就是并發(fā)數(shù)痴施,而窗口個(gè)數(shù)就是并行度。也就是說(shuō)并發(fā)數(shù)是指同時(shí)進(jìn)行的任務(wù)數(shù)(如同時(shí)服務(wù)的 HTTP 請(qǐng)求)究流,而并行數(shù)是可以同時(shí)工作的物理資源數(shù)量(如 CPU 核數(shù))辣吃。通過(guò)合理調(diào)度任務(wù)的不同階段,并發(fā)數(shù)可以遠(yuǎn)遠(yuǎn)大于并行度芬探,這就是區(qū)區(qū)幾個(gè) CPU 可以支持上萬(wàn)個(gè)用戶并發(fā)請(qǐng)求的奧秘神得。在這種高并發(fā)的情況下,為每個(gè)任務(wù)(用戶請(qǐng)求)創(chuàng)建一個(gè)進(jìn)程或線程的開(kāi)銷非常大偷仿。而同步非阻塞方式可以把多個(gè) IO 請(qǐng)求丟到后臺(tái)去哩簿,這就可以在一個(gè)進(jìn)程里服務(wù)大量的并發(fā) IO 請(qǐng)求。

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

同步與異步的根本性區(qū)別节榜,同步是需要主動(dòng)等待消息通知,而異步則是被動(dòng)接收消息通知别智,通過(guò)回調(diào)宗苍、通知、狀態(tài)等方式來(lái)被動(dòng)獲取消息薄榛。

IO多路復(fù)用在阻塞到select階段時(shí)讳窟,用戶進(jìn)程是主動(dòng)等待并調(diào)用select函數(shù)獲取數(shù)據(jù)就緒狀態(tài)消息,并且其進(jìn)程狀態(tài)為阻塞蛇数。

所以挪钓,把IO多路復(fù)用歸為同步阻塞模式。
參考:https://github.com/CyC2018/CS-Notes/issues/194

多路復(fù)用是同步的耳舅,阻塞/非阻塞取決于調(diào)用時(shí)的參數(shù)設(shè)置碌上。

所有I/O多路復(fù)用操作都是同步的倚评,涵蓋select/poll。
阻塞/非阻塞是相對(duì)于同步I/O來(lái)說(shuō)的馏予,與異步I/O無(wú)關(guān)天梧。
select/poll/epoll本身是同步的,可以阻塞也可以不阻塞霞丧。

關(guān)于Select是否阻塞:
在使用int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)函數(shù)時(shí)呢岗,可以設(shè)置timeval決定該系統(tǒng)調(diào)用是否阻塞。

關(guān)于Poll是否阻塞:
在使用int poll(struct pollfd *fds, nfds_t nfds, int timeout)函數(shù)獲取信息時(shí)蛹尝,可以通過(guò)指定timeout的值來(lái)決定是否阻塞(當(dāng)timeout<0時(shí)后豫,會(huì)無(wú)限期阻塞;當(dāng)timeout=0時(shí)突那,會(huì)立即返回)挫酿。

關(guān)于Epoll是否阻塞:
在使用epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)函數(shù)來(lái)獲取是否有發(fā)生變化/事件的文件描述符時(shí),可以通過(guò)指定timeout來(lái)指定該調(diào)用是否阻塞(當(dāng)timeout=-1時(shí)愕难,會(huì)無(wú)限期阻塞早龟;當(dāng)timeout=0時(shí),會(huì)立即返回)猫缭。

信號(hào)驅(qū)動(dòng)式IO(signal-driven IO)

信號(hào)驅(qū)動(dòng)式I/O:首先我們?cè)试SSocket進(jìn)行信號(hào)驅(qū)動(dòng)IO,并安裝一個(gè)信號(hào)處理函數(shù)葱弟,進(jìn)程繼續(xù)運(yùn)行并不阻塞。當(dāng)數(shù)據(jù)準(zhǔn)備好時(shí)猜丹,進(jìn)程會(huì)收到一個(gè)SIGIO信號(hào)芝加,可以在信號(hào)處理函數(shù)中調(diào)用I/O操作函數(shù)處理數(shù)據(jù)。過(guò)程如下圖所示:


image.png

異步非阻塞 IO(asynchronous IO)

場(chǎng)景描述

女友不想逛街居触,又餐廳太吵了妖混,回家好好休息一下。于是我們叫外賣轮洋,打個(gè)電話點(diǎn)餐制市,然后我和女友可以在家好好休息一下,飯好了送貨員送到家里來(lái)弊予。

這就是典型的異步祥楣,只需要打個(gè)電話說(shuō)一下,然后可以做自己的事情汉柒,飯好了就送來(lái)了误褪。

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

相對(duì)于同步IO,異步IO不是順序執(zhí)行碾褂。用戶進(jìn)程進(jìn)行aio_read系統(tǒng)調(diào)用之后兽间,無(wú)論內(nèi)核數(shù)據(jù)是否準(zhǔn)備好,都會(huì)直接返回給用戶進(jìn)程正塌,然后用戶態(tài)進(jìn)程可以去做別的事情嘀略。等到socket數(shù)據(jù)準(zhǔn)備好了恤溶,內(nèi)核直接復(fù)制數(shù)據(jù)給進(jìn)程,然后從內(nèi)核向進(jìn)程發(fā)送通知帜羊。IO兩個(gè)階段咒程,進(jìn)程都是非阻塞的。

Linux提供了AIO庫(kù)函數(shù)實(shí)現(xiàn)異步讼育,但是用的很少帐姻。目前有很多開(kāi)源的異步IO庫(kù),例如libevent奶段、libev饥瓷、libuv。異步過(guò)程如下圖所示:

image.png

流程描述

用戶進(jìn)程發(fā)起aio_read操作之后痹籍,立刻就可以開(kāi)始去做其它的事扛伍。而另一方面,從kernel的角度词裤,當(dāng)它受到一個(gè)asynchronous read之后,首先它會(huì)立刻返回鳖宾,所以不會(huì)對(duì)用戶進(jìn)程產(chǎn)生任何block吼砂。然后,kernel會(huì)等待數(shù)據(jù)準(zhǔn)備完成鼎文,然后將數(shù)據(jù)拷貝到用戶內(nèi)存渔肩,當(dāng)這一切都完成之后,kernel會(huì)給用戶進(jìn)程發(fā)送一個(gè)signal或執(zhí)行一個(gè)基于線程的回調(diào)函數(shù)來(lái)完成這次 IO 處理過(guò)程拇惋,告訴它read操作完成了周偎。

在 Linux 中,通知的方式是 “信號(hào)”:

如果這個(gè)進(jìn)程正在用戶態(tài)忙著做別的事(例如在計(jì)算兩個(gè)矩陣的乘積)撑帖,那就強(qiáng)行打斷之蓉坎,調(diào)用事先注冊(cè)的信號(hào)處理函數(shù),這個(gè)函數(shù)可以決定何時(shí)以及如何處理這個(gè)異步任務(wù)胡嘿。由于信號(hào)處理函數(shù)是突然闖進(jìn)來(lái)的蛉艾,因此跟中斷處理程序一樣,有很多事情是不能做的衷敌,因此保險(xiǎn)起見(jiàn)勿侯,一般是把事件 “登記” 一下放進(jìn)隊(duì)列,然后返回該進(jìn)程原來(lái)在做的事缴罗。

如果這個(gè)進(jìn)程正在內(nèi)核態(tài)忙著做別的事助琐,例如以同步阻塞方式讀寫(xiě)磁盤(pán),那就只好把這個(gè)通知掛起來(lái)了面氓,等到內(nèi)核態(tài)的事情忙完了兵钮,快要回到用戶態(tài)的時(shí)候蛆橡,再觸發(fā)信號(hào)通知。

如果這個(gè)進(jìn)程現(xiàn)在被掛起了矢空,例如無(wú)事可做 sleep 了航罗,那就把這個(gè)進(jìn)程喚醒,下次有 CPU 空閑的時(shí)候屁药,就會(huì)調(diào)度到這個(gè)進(jìn)程粥血,觸發(fā)信號(hào)通知。

異步 API 說(shuō)來(lái)輕巧酿箭,做來(lái)難复亏,這主要是對(duì) API 的實(shí)現(xiàn)者而言的。Linux 的異步 IO(AIO)支持是 2.6.22 才引入的缭嫡,還有很多系統(tǒng)調(diào)用不支持異步 IO缔御。Linux 的異步 IO 最初是為數(shù)據(jù)庫(kù)設(shè)計(jì)的,因此通過(guò)異步 IO 的讀寫(xiě)操作不會(huì)被緩存或緩沖妇蛀,這就無(wú)法利用操作系統(tǒng)的緩存與緩沖機(jī)制耕突。

很多人把 Linux 的 O_NONBLOCK 認(rèn)為是異步方式,但事實(shí)上這是前面講的同步非阻塞方式评架。需要指出的是眷茁,雖然 Linux 上的 IO API 略顯粗糙,但每種編程框架都有封裝好的異步 IO 實(shí)現(xiàn)纵诞。操作系統(tǒng)少做事上祈,把更多的自由留給用戶,正是 UNIX 的設(shè)計(jì)哲學(xué)浙芙,也是 Linux 上編程框架百花齊放的一個(gè)原因登刺。

從前面 IO 模型的分類中,我們可以看出 AIO異步非阻塞 的動(dòng)機(jī):

同步阻塞模型需要在 IO 操作開(kāi)始時(shí)阻塞應(yīng)用程序嗡呼。這意味著不可能同時(shí)重疊進(jìn)行處理和 IO 操作纸俭。

同步非阻塞模型允許處理和 IO 操作重疊進(jìn)行,但是這需要應(yīng)用程序根據(jù)重現(xiàn)的規(guī)則來(lái)檢查 IO 操作的狀態(tài)晤锥。

這樣就剩下異步非阻塞 IO 了掉蔬,它允許處理和 IO 操作重疊進(jìn)行,包括 IO 操作完成的通知矾瘾。

IO多路復(fù)用除了需要阻塞之外女轿,select 函數(shù)所提供的功能(異步阻塞 IO)與 AIO 類似。不過(guò)壕翩,它是對(duì)通知事件進(jìn)行阻塞蛉迹,而不是對(duì) IO 調(diào)用進(jìn)行阻塞。

五種IO模型總結(jié)

blocking和non-blocking區(qū)別

調(diào)用blocking IO會(huì)一直block住對(duì)應(yīng)的進(jìn)程直到操作完成放妈,而non-blocking IO在kernel還準(zhǔn)備數(shù)據(jù)的情況下會(huì)立刻返回北救。

synchronous IO和asynchronous IO區(qū)別

在說(shuō)明synchronous IO和asynchronous IO的區(qū)別之前荐操,需要先給出兩者的定義。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;

兩者的區(qū)別就在于synchronous IO做 ”IO operation” 的時(shí)候會(huì)將process阻塞珍策。按照這個(gè)定義托启,之前所述的blocking IO,non-blocking IO攘宙,IO multiplexing都屬于synchronous IO屯耸。

有人會(huì)說(shuō),non-blocking IO并沒(méi)有被block啊蹭劈。這里有個(gè)非沉菩澹“狡猾”的地方,定義中所指的”IO operation”是指真實(shí)的IO操作铺韧,就是例子中的recvfrom這個(gè)system call多矮。non-blocking IO在執(zhí)行recvfrom這個(gè)system call的時(shí)候,如果kernel的數(shù)據(jù)沒(méi)有準(zhǔn)備好哈打,這時(shí)候不會(huì)block進(jìn)程塔逃。但是,當(dāng)kernel中數(shù)據(jù)準(zhǔn)備好的時(shí)候料仗,recvfrom會(huì)將數(shù)據(jù)從kernel拷貝到用戶內(nèi)存中患雏,這個(gè)時(shí)候進(jìn)程是被block了,在這段時(shí)間內(nèi)罢维,進(jìn)程是被block的。

而asynchronous IO則不一樣丙挽,當(dāng)進(jìn)程發(fā)起IO 操作之后肺孵,就直接返回再也不理睬了,直到kernel發(fā)送一個(gè)信號(hào)颜阐,告訴進(jìn)程說(shuō)IO完成平窘。在這整個(gè)過(guò)程中,進(jìn)程完全沒(méi)有被block凳怨。

各個(gè)IO Model的比較如圖所示:

image.png

通過(guò)上面的圖片瑰艘,可以發(fā)現(xiàn)non-blocking IO和asynchronous IO的區(qū)別還是很明顯的。在non-blocking IO中肤舞,雖然進(jìn)程大部分時(shí)間都不會(huì)被block紫新,但是它仍然要求進(jìn)程去主動(dòng)的check,并且當(dāng)數(shù)據(jù)準(zhǔn)備完成以后李剖,也需要進(jìn)程主動(dòng)的再次調(diào)用recvfrom來(lái)將數(shù)據(jù)拷貝到用戶內(nèi)存芒率。而asynchronous IO則完全不同。它就像是用戶進(jìn)程將整個(gè)IO操作交給了他人(kernel)完成篙顺,然后他人做完后發(fā)信號(hào)通知偶芍。在此期間充择,用戶進(jìn)程不需要去檢查IO操作的狀態(tài),也不需要主動(dòng)的去拷貝數(shù)據(jù)匪蟀。

IO模型舉例理解

例1:

阻塞IO, 給女神發(fā)一條短信, 說(shuō)我來(lái)找你了, 然后就默默的一直等著女神下樓, 這個(gè)期間除了等待你不會(huì)做其他事情, 屬于備胎做法.

非阻塞IO, 給女神發(fā)短信, 如果不回, 接著再發(fā), 一直發(fā)到女神下樓, 這個(gè)期間你除了發(fā)短信等待不會(huì)做其他事情, 屬于專一做法.

IO多路復(fù)用, 是找一個(gè)宿管大媽來(lái)幫你監(jiān)視下樓的女生, 這個(gè)期間你可以些其他的事情. 例如可以順便看看其他妹子,玩玩王者榮耀, 上個(gè)廁所等等. IO復(fù)用又包括 select, poll, epoll 模式. 那么它們的區(qū)別是什么?

select大媽每一個(gè)女生下樓, select大媽都不知道這個(gè)是不是你的女神, 她需要一個(gè)一個(gè)詢問(wèn), 并且select大媽能力還有限, 最多一次幫你監(jiān)視1024個(gè)妹子椎麦。

poll大媽不限制盯著女生的數(shù)量, 只要是經(jīng)過(guò)宿舍樓門(mén)口的女生, 都會(huì)幫你去問(wèn)是不是你女神。

epoll大媽不限制盯著女生的數(shù)量, 并且也不需要一個(gè)一個(gè)去問(wèn). 那么如何做呢?

epoll大媽會(huì)為每個(gè)進(jìn)宿舍樓的女生臉上貼上一個(gè)大字條,上面寫(xiě)上女生自己的名字, 只要女生下樓了, epoll大媽就知道這個(gè)是不是你女神了, 然后大媽再通知你.

上面這些同步IO有一個(gè)共同點(diǎn)就是, 當(dāng)女神走出宿舍門(mén)口的時(shí)候, 你已經(jīng)站在宿舍門(mén)口等著女神的, 此時(shí)你屬于同步等待狀態(tài)材彪。

異步IO 你告訴女神我來(lái)了, 然后你就去王者榮耀了, 一直到女神下樓了, 發(fā)現(xiàn)找不見(jiàn)你了,女神再給你打電話通知你, 說(shuō)我下樓了, 你在哪呢? 這時(shí)候你才來(lái)到宿舍門(mén)口观挎。

例2:
阻塞I/O模型 老李去火車站買票,排隊(duì)三天買到一張退票查刻。 耗費(fèi):在車站吃喝拉撒睡 3天键兜,其他事一件沒(méi)干。

非阻塞I/O模型 老李去火車站買票穗泵,隔12小時(shí)去火車站問(wèn)有沒(méi)有退票普气,三天后買到一張票。耗費(fèi):往返車站6次佃延,路上6小時(shí)现诀,其他時(shí)間做了好多事。

I/O復(fù)用模型

1.select/poll 老李去火車站買票履肃,委托黃牛仔沿,然后每隔6小時(shí)電話黃牛詢問(wèn),黃牛三天內(nèi)買到票尺棋,然后老李去火車站交錢(qián)領(lǐng)票封锉。

耗費(fèi):往返車站2次,路上2小時(shí)膘螟,黃牛手續(xù)費(fèi)100元成福,打電話17次

2.epoll 老李去火車站買票,委托黃牛荆残,黃牛買到后即通知老李去領(lǐng)奴艾,然后老李去火車站交錢(qián)領(lǐng)票。

耗費(fèi):往返車站2次内斯,路上2小時(shí)蕴潦,黃牛手續(xù)費(fèi)100元,無(wú)需打電話

信號(hào)驅(qū)動(dòng)I/O模型 老李去火車站買票俘闯,給售票員留下電話潭苞,有票后,售票員電話通知老李真朗,然后老李去火車站交錢(qián)領(lǐng)票萄传。 耗費(fèi):往返車站2次,路上2小時(shí),免黃牛費(fèi)100元秀菱,無(wú)需打電話

異步I/O模型 老李去火車站買票振诬,給售票員留下電話,有票后衍菱,售票員電話通知老李并快遞送票上門(mén)赶么。 耗費(fèi):往返車站1次,路上1小時(shí)脊串,免黃牛費(fèi)100元辫呻,無(wú)需打電話

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市琼锋,隨后出現(xiàn)的幾起案子放闺,更是在濱河造成了極大的恐慌,老刑警劉巖缕坎,帶你破解...
    沈念sama閱讀 219,110評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件怖侦,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡谜叹,警方通過(guò)查閱死者的電腦和手機(jī)匾寝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)荷腊,“玉大人艳悔,你說(shuō)我怎么就攤上這事∨觯” “怎么了猜年?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,474評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)疾忍。 經(jīng)常有香客問(wèn)我码倦,道長(zhǎng),這世上最難降的妖魔是什么锭碳? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,881評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮勿璃,結(jié)果婚禮上擒抛,老公的妹妹穿的比我還像新娘。我一直安慰自己补疑,他們只是感情好歧沪,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,902評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著莲组,像睡著了一般诊胞。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,698評(píng)論 1 305
  • 那天撵孤,我揣著相機(jī)與錄音迈着,去河邊找鬼。 笑死邪码,一個(gè)胖子當(dāng)著我的面吹牛裕菠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播闭专,決...
    沈念sama閱讀 40,418評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼奴潘,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了影钉?” 一聲冷哼從身側(cè)響起画髓,我...
    開(kāi)封第一講書(shū)人閱讀 39,332評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎平委,沒(méi)想到半個(gè)月后奈虾,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,796評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡肆汹,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,968評(píng)論 3 337
  • 正文 我和宋清朗相戀三年愚墓,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片昂勉。...
    茶點(diǎn)故事閱讀 40,110評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡浪册,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出岗照,到底是詐尸還是另有隱情村象,我是刑警寧澤,帶...
    沈念sama閱讀 35,792評(píng)論 5 346
  • 正文 年R本政府宣布攒至,位于F島的核電站厚者,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏迫吐。R本人自食惡果不足惜库菲,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,455評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望志膀。 院中可真熱鬧熙宇,春花似錦、人聲如沸溉浙。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,003評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)戳稽。三九已至馆蠕,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背互躬。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,130評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工播赁, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人吨铸。 一個(gè)月前我還...
    沈念sama閱讀 48,348評(píng)論 3 373
  • 正文 我出身青樓行拢,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親诞吱。 傳聞我的和親對(duì)象是個(gè)殘疾皇子舟奠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,047評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容