細(xì)說:程序運(yùn)行的環(huán)境和運(yùn)行過程场晶,再看不懂請自行面壁

本文已收錄GitHub混埠,更有互聯(lián)網(wǎng)大廠面試真題,面試攻略诗轻,高效學(xué)習(xí)資料等

編譯器的任務(wù)钳宪,是要生成能夠在計(jì)算機(jī)上運(yùn)行的代碼,但要生成代碼扳炬,我們必須對程序的運(yùn)行環(huán)境和運(yùn)行機(jī)制有比較透徹的了解吏颖。

你要知道,大型的恨樟、復(fù)雜一點(diǎn)兒的系統(tǒng)半醉,比如像淘寶一樣的電商系統(tǒng)、搜索引擎系統(tǒng)等等劝术,都存在一些技術(shù)任務(wù)缩多,是需要你深入了解底層機(jī)制才能解決的。比如淘寶的基礎(chǔ)技術(shù)團(tuán)隊(duì)就曾經(jīng)貢獻(xiàn)過养晋,Java 虛擬機(jī)即時(shí)編譯功能中的一個(gè)補(bǔ)丁衬吆。

這反映出掌握底層技術(shù)能力的重要性,所以匙握,如果你想進(jìn)階成為這個(gè)層次的工程師咆槽,不能只學(xué)學(xué)上層的語法,而是要把計(jì)算機(jī)語言從上層的語法到底層的運(yùn)行機(jī)制都了解透徹圈纺。

文本我會(huì)對計(jì)算機(jī)程序如何運(yùn)行秦忿,做一個(gè)解密,話題分成兩個(gè)部分:

  1. 了解程序運(yùn)行的環(huán)境蛾娶,包括 CPU灯谣、內(nèi)存和操作系統(tǒng),探知它們跟程序到底有什么關(guān)系蛔琅。
  2. 了解程序運(yùn)行的過程胎许。比如,一個(gè)程序是怎么跑起來的,代碼是怎樣執(zhí)行和跳轉(zhuǎn)的辜窑,又是如何管理內(nèi)存的钩述。

首先,我們先來了解一下程序運(yùn)行的環(huán)境穆碎。

程序運(yùn)行的環(huán)境

程序運(yùn)行的過程中牙勘,主要是跟兩個(gè)硬件(CPU 和內(nèi)存)以及一個(gè)軟件(操作系統(tǒng))打交道。

本質(zhì)上所禀,我們的程序只關(guān)心 CPU 和內(nèi)存這兩個(gè)硬件方面。你可能說:“不對啊,計(jì)算機(jī)還有其他硬件色徘,比如顯示器和硬盤啊恭金。”但對我們的程序來說褂策,操作這些硬件横腿,也只是執(zhí)行某些特定的驅(qū)動(dòng)代碼,跟執(zhí)行其他代碼并沒有什么差異辙培。

1. 關(guān)注CPU和內(nèi)存

CPU 的內(nèi)部有很多組成部分蔑水,對于本文來說,我們重點(diǎn)關(guān)注的是寄存器以及高速緩存扬蕊,它們跟程序的執(zhí)行機(jī)制和優(yōu)化密切相關(guān)搀别。

寄存器是 CPU 指令在進(jìn)行計(jì)算的時(shí)候,臨時(shí)數(shù)據(jù)存儲(chǔ)的地方尾抑。CPU 指令一般都會(huì)用到寄存器歇父,比如,典型的一個(gè)加法計(jì)算(c=a+b)的過程是這樣的:

  • 指令 1(mov):從內(nèi)取 a 的值放到寄存器中再愈;
  • 指令 2(add):再把內(nèi)存中 b 的值取出來與這個(gè)寄存器中的值相加榜苫,仍然保存在寄存器中;
  • 指令 3(mov):最后再把寄存器中的數(shù)據(jù)寫回內(nèi)存中 c 的地址翎冲。

寄存器的速度也很快垂睬,所以能用寄存器就別用內(nèi)存。盡量充分利用寄存器抗悍,是編譯器做優(yōu)化的內(nèi)容之一驹饺。

而高速緩存可以彌補(bǔ) CPU 的處理速度和內(nèi)存訪問速度之間的差距。所以缴渊,我們的指令在內(nèi)存讀一個(gè)數(shù)據(jù)的時(shí)候赏壹,它不是老老實(shí)實(shí)地只讀進(jìn)當(dāng)前指令所需要的數(shù)據(jù),而是把跟這個(gè)數(shù)據(jù)相鄰的一組數(shù)據(jù)都讀進(jìn)高速緩存了衔沼。這就相當(dāng)于外賣小哥送餐的時(shí)候蝌借,不會(huì)為每一單來回跑一趟昔瞧,而是一次取一批,如果這一批外賣恰好都是同一個(gè)寫字樓里的菩佑,那小哥的送餐效率就會(huì)很高自晰。

內(nèi)存和高速緩存的速度差異差不多是兩個(gè)數(shù)量級(jí),也就是一百倍擎鸠。比如缀磕,高速緩存的讀取時(shí)間可能是 0.5ns缘圈,而內(nèi)存的訪問時(shí)間可能是 50ns劣光。不同硬件的參數(shù)可能有差異,但總體來說是幾十倍到上百倍的差異糟把。

你寫程序時(shí)绢涡,盡量把某個(gè)操作所需的數(shù)據(jù)都放在內(nèi)存中的連續(xù)區(qū)域中,不要零零散散地到處放遣疯,這樣有利于充分利用高速緩存雄可。這種優(yōu)化思路,叫做數(shù)據(jù)的局部性缠犀。

這里提一句数苫,在寫系統(tǒng)級(jí)的程序時(shí),你要對各種 IO 的時(shí)間有基本的概念辨液,比如高速緩存虐急、內(nèi)存、磁盤滔迈、網(wǎng)絡(luò)的 IO 大致都是什么數(shù)量級(jí)的止吁。因?yàn)檫@都影響到系統(tǒng)的整體性能,也影響到你如何做程序優(yōu)化燎悍。如果你需要對程序做更多的優(yōu)化敬惦,還需要了解更多的 CPU 運(yùn)行機(jī)制,包括流水線機(jī)制谈山、并行機(jī)制等等俄删,這里就不展開了。

講完 CPU 之后奏路,還有內(nèi)存這個(gè)硬件畴椰。

程序在運(yùn)行時(shí),操作系統(tǒng)會(huì)給它分配一塊虛擬的內(nèi)存空間思劳,讓它在運(yùn)行期可以使用迅矛。我們目前使用的都是 64 位的機(jī)器,你可以用一個(gè) 64 位的長整型來表示內(nèi)存地址潜叛,它能夠表示的所有地址秽褒,我們叫做尋址空間壶硅。

64 位機(jī)器的尋址空間就有 2 的 64 次方那么大,也就是有很多很多個(gè) T(Terabyte)销斟,大到你的程序根本用不完庐椒。不過,操作系統(tǒng)一般會(huì)給予一定的限制蚂踊,不會(huì)給你這么大的尋址空間约谈,比如給到 100 來個(gè) G,這對一般的程序犁钟,也足夠用了棱诱。

在存在操作系統(tǒng)的情況下,程序邏輯上可使用的內(nèi)存一般大于實(shí)際的物理內(nèi)存涝动。程序在使用內(nèi)存的時(shí)候迈勋,操作系統(tǒng)會(huì)把程序使用的邏輯地址映射到真實(shí)的物理內(nèi)存地址。有的物理內(nèi)存區(qū)域會(huì)映射進(jìn)多個(gè)進(jìn)程的地址空間醋粟。

對于不太常用的內(nèi)存數(shù)據(jù)靡菇,操作系統(tǒng)會(huì)寫到磁盤上,以便騰出更多可用的物理內(nèi)存米愿。

當(dāng)然厦凤,也存在沒有操作系統(tǒng)的情況,這個(gè)時(shí)候你的程序所使用的內(nèi)存就是物理內(nèi)存育苟,我們必須自己做好內(nèi)存的管理较鼓。

對于這個(gè)內(nèi)存,該怎么用呢宙搬?

本質(zhì)上來說笨腥,你想怎么用就怎么用,并沒有什么特別的限制勇垛。一個(gè)編譯器的作者脖母,可以決定在哪兒放代碼,在哪兒放數(shù)據(jù)闲孤,當(dāng)然了谆级,別的作者也可能采用其他的策略。實(shí)際上讼积,C 語言和 Java 虛擬機(jī)對內(nèi)存的管理和使用策略就是不同的肥照。

盡管如此,大多數(shù)語言還是會(huì)采用一些通用的內(nèi)存管理模式勤众。以 C 語言為例舆绎,會(huì)把內(nèi)存劃分為代碼區(qū)、靜態(tài)數(shù)據(jù)區(qū)们颜、棧和堆吕朵。

一般來講猎醇,代碼區(qū)是在最低的地址區(qū)域,然后是靜態(tài)數(shù)據(jù)區(qū)努溃,然后是堆硫嘶。而棧傳統(tǒng)上是從高地址向低地址延伸,棧的最頂部有一塊區(qū)域梧税,用來保存環(huán)境變量沦疾。

代碼區(qū)(也叫文本段)存放編譯完成以后的機(jī)器碼。這個(gè)內(nèi)存區(qū)域是只讀的第队,不會(huì)再修改哮塞,但也不絕對。現(xiàn)代語言的運(yùn)行時(shí)已經(jīng)越來越動(dòng)態(tài)化斥铺,除了保存機(jī)器碼彻桃,還可以存放中間代碼,并且還可以在運(yùn)行時(shí)把中間代碼編譯成機(jī)器碼晾蜘,寫入代碼區(qū)。

靜態(tài)數(shù)據(jù)區(qū)保存程序中全局的變量和常量眠屎。它的地址在編譯期就是確定的剔交,在生成的代碼里直接使用這個(gè)地址就可以訪問它們,它們的生存期是從程序啟動(dòng)一直到程序結(jié)束改衩。它又可以細(xì)分為 Data 和 BSS 兩個(gè)段岖常。Data 段中的變量是在編譯期就初始化好的,直接從程序裝在進(jìn)內(nèi)存葫督。BSS 段中是那些沒有聲明初始化值的變量竭鞍,都會(huì)被初始化成 0。

堆適合管理生存期較長的一些數(shù)據(jù)橄镜,這些數(shù)據(jù)在退出作用域以后也不會(huì)消失偎快。比如,我們在某個(gè)方法里創(chuàng)建了一個(gè)對象并返回洽胶,并希望代表這個(gè)對象的數(shù)據(jù)在退出函數(shù)后仍然可以訪問晒夹。

而棧適合保存生存期比較短的數(shù)據(jù),比如函數(shù)和方法里的本地變量姊氓。它們在進(jìn)入某個(gè)作用域的時(shí)候申請內(nèi)存丐怯,退出這個(gè)作用域的時(shí)候就可以釋放掉。

講完了 CPU 和內(nèi)存之后翔横,我們再來看看跟程序打交道的操作系統(tǒng)读跷。

2. 程序和操作系統(tǒng)的關(guān)系

程序跟操作系統(tǒng)的關(guān)系比較微妙:

一方面我們的程序可以編譯成不需要操作系統(tǒng)也能運(yùn)行,就像一些物聯(lián)網(wǎng)應(yīng)用那樣禾唁,完全跑在裸設(shè)備上效览。
另一方面些膨,有了操作系統(tǒng)的幫助,可以為程序提供便利钦铺,比如可以使用超過物理內(nèi)存的存儲(chǔ)空間订雾,操作系統(tǒng)負(fù)責(zé)進(jìn)行虛擬內(nèi)存的管理。

在存在操作系統(tǒng)的情況下矛洞,因?yàn)楹芏噙M(jìn)程共享計(jì)算機(jī)資源洼哎,所以就要遵循一些約定。這就仿佛辦公室是所有同事共享的沼本,那么大家就都要遵守一些約定噩峦,如果一個(gè)人大聲喧嘩,就會(huì)影響到其他人抽兆。

程序需要遵守的約定包括:程序文件的二進(jìn)制格式約定识补,這樣操作系統(tǒng)才能程序正確地加載進(jìn)來,并為同一個(gè)程序的多個(gè)進(jìn)程共享代碼區(qū)辫红。在使用寄存器和棧的時(shí)候也要遵守一些約定凭涂,便于操作系統(tǒng)在不同的進(jìn)程之間切換的時(shí)候、在做系統(tǒng)調(diào)用的時(shí)候贴妻,做好上下文的保護(hù)切油。

所以,我們編譯程序的時(shí)候名惩,要知道需要遵守哪些約定澎胡。因?yàn)榫退闶鞘褂猛瑯拥?CPU,針對不同的操作系統(tǒng)娩鹉,編譯的結(jié)果也是非常不同的攻谁。

好了,我們了解了程序運(yùn)行時(shí)的硬件和操作系統(tǒng)環(huán)境弯予。接下來戚宦,我們看看程序運(yùn)行時(shí),是怎么跟它們互動(dòng)的熙涤。

程序運(yùn)行的過程

你天天運(yùn)行程序阁苞,可對于程序運(yùn)行的細(xì)節(jié),真的清楚嗎祠挫?

1. 程序運(yùn)行的細(xì)節(jié)

首先那槽,可運(yùn)行的程序一般是由操作系統(tǒng)加載到內(nèi)存的,并且定位到代碼區(qū)里程序的入口開始執(zhí)行等舔。比如骚灸,C 語言的 main 函數(shù)的第一行代碼。

每次加載一條代碼慌植,程序都會(huì)順序執(zhí)行甚牲,碰到跳轉(zhuǎn)語句义郑,才會(huì)跳到另一個(gè)地址執(zhí)行。CPU里有一個(gè)指令寄存器丈钙,里面保存了下一條指令的地址非驮。

假設(shè)我們運(yùn)行這樣一段代碼編譯后形成的程序:

int main(){
    int a = 1;
    foo(3);
    bar();
}
int foo(int c){
    int b = 2;
    return b+c;
}
int bar(){
    return foo(4) + 1;
}

我們首先激活(Activate)main() 函數(shù),main() 函數(shù)又激活 foo() 函數(shù)雏赦,然后又激活 bar()函數(shù)劫笙,bar() 函數(shù)還會(huì)激活 foo() 函數(shù),其中 foo() 函數(shù)被兩次以不同的路徑激活星岗。

我們把每次調(diào)用一個(gè)函數(shù)的過程填大,叫做一次活動(dòng)(Activation)。每個(gè)活動(dòng)都對應(yīng)一個(gè)活動(dòng)記錄(Activation Record)俏橘,這個(gè)活動(dòng)記錄里有這個(gè)函數(shù)運(yùn)行所需要的信息允华,比如參數(shù)、返回值寥掐、本地變量等靴寂。

目前我們用棧來管理內(nèi)存,所以可以把活動(dòng)記錄等價(jià)于棧楨曹仗。棧楨是活動(dòng)記錄的實(shí)現(xiàn)方式榨汤,我們可以自由設(shè)計(jì)活動(dòng)記錄或棧楨的結(jié)構(gòu),下圖是一個(gè)常見的設(shè)計(jì):

  • 返回值:一般放在最頂上怎茫,這樣它的地址是固定的。foo() 函數(shù)返回以后妓灌,它的調(diào)用者可以到這里來取到返回值轨蛤。在實(shí)際情況中,我們會(huì)優(yōu)先通過寄存器來傳遞返回值虫埂,比通過內(nèi)存?zhèn)鬟f性能更高祥山。
  • 參數(shù):在調(diào)用 foo 函數(shù)時(shí),把參數(shù)寫到這個(gè)地址里掉伏。同樣缝呕,我們也可以通過寄存器來傳遞,而不是內(nèi)存斧散。
  • 控制鏈接:就是上一級(jí)棧楨的地址供常。如果用到了上一級(jí)作用域中的變量,就可以順著這個(gè)鏈接找到上一級(jí)棧楨鸡捐,并找到變量的值栈暇。
  • 返回地址:foo 函數(shù)執(zhí)行完畢以后,繼續(xù)執(zhí)行哪條指令箍镜。同樣源祈,我們可以用寄存器來保存這個(gè)信息煎源。
  • 本地變量:foo 函數(shù)的本地變量 b 的存儲(chǔ)空間。
  • 寄存器信息:我們還經(jīng)常在棧楨里保存寄存器的數(shù)據(jù)香缺。如果在 foo 函數(shù)里要使用某個(gè)寄存器手销,可能需要先把它的值保存下來,防止破壞了別的代碼保存在這里的數(shù)據(jù)图张。這種約定叫做被調(diào)用者責(zé)任锋拖,也就是使用寄存器的人要保護(hù)好寄存器里原有的信息。某個(gè)函數(shù)如果使用了某個(gè)寄存器埂淮,但它又要調(diào)用別的函數(shù)姑隅,為了防止別的函數(shù)把自己放在寄存器中的數(shù)據(jù)覆蓋掉,要自己保存在棧楨中倔撞。這種約定叫做調(diào)用者責(zé)任讲仰。

你可以看到,每個(gè)棧楨的長度是不一樣的痪蝇。

用到的參數(shù)和本地變量多鄙陡,棧楨就要長一點(diǎn)。但是躏啰,棧楨的長度和結(jié)構(gòu)是在編譯期就能完全確定的趁矾。這樣就便于我們計(jì)算地址的偏移量,獲取棧楨里某個(gè)數(shù)據(jù)给僵。

總的來說毫捣,棧楨的設(shè)計(jì)很自由。但是帝际,你要考慮不同語言編譯形成的模塊要能夠鏈接在一起蔓同,所以還是要遵守一些公共的約定的,否則蹲诀,你寫的函數(shù)斑粱,別人就沒辦法調(diào)用了。

在之前的文章中我提到過棧楨脯爪,這次我們用了更加貼近具體實(shí)現(xiàn)的描述:棧楨就是一塊確定的內(nèi)存则北,變量就是這塊內(nèi)存里的地址。在下一講痕慢,我會(huì)帶你動(dòng)手實(shí)現(xiàn)我們的棧楨尚揣。

2.從全局角度看整個(gè)運(yùn)行過程

了解了棧楨的實(shí)現(xiàn)之后,我們再來看一個(gè)更大的場景守屉,從全局的角度看看整個(gè)運(yùn)行過程中都發(fā)生了什么惑艇。

代碼區(qū)里存儲(chǔ)了一些代碼,main 函數(shù)、bar 函數(shù)和 foo 函數(shù)各自有一段連續(xù)的區(qū)域來存儲(chǔ)代碼滨巴,我用了一些匯編指令來表示這些代碼(實(shí)際運(yùn)行時(shí)這里其實(shí)是機(jī)器碼)思灌。

假設(shè)我們執(zhí)行到 foo 函數(shù)中的一段指令,來計(jì)算“b+c”的值恭取,并返回泰偿。這里用到了mov、add蜈垮、jmp 這三個(gè)指令耗跛。mov 是把某個(gè)值從一個(gè)地方拷貝到另一個(gè)地方芒率,add 是往

某個(gè)地方加一個(gè)值裹虫,jmp 是改變代碼執(zhí)行的順序,跳轉(zhuǎn)到另一個(gè)地方去執(zhí)行(匯編命令的細(xì)節(jié)头镊,我們下節(jié)再講惠猿,你現(xiàn)在簡單了解一下就行了)羔砾。

mov b的地址寄存器1
add c的地址寄存器1
mov寄存器1 foo的返回值地址
jmp返回地址//或ret指令

執(zhí)行完這幾個(gè)指令以后,foo 的返回值位置就寫入了 6偶妖,并跳轉(zhuǎn)到 bar 函數(shù)中執(zhí)行 foo 之后的代碼姜凄。

這時(shí),foo 的棧楨就沒用了趾访,新的棧頂是 bar 的棧楨的頂部态秧。理論上講,操作系統(tǒng)這時(shí)可以把 foo 的棧楨所占的內(nèi)存收回了扼鞋。比如申鱼,可以映射到另一個(gè)程序的尋址空間,讓另一個(gè)程序使用云头。但是在這個(gè)例子中你會(huì)看到润讥,即使返回了 bar 函數(shù),我們?nèi)砸L問棧頂之外的一個(gè)內(nèi)存地址盘寡,也就是返回值的地址。

所以撮慨,目前的調(diào)用約定都規(guī)定竿痰,程序的棧頂之外,仍然會(huì)有一小塊內(nèi)存(比如 128K)是可以由程序訪問的砌溺,比如我們可以拿來存儲(chǔ)返回值影涉。這一小段內(nèi)存操作系統(tǒng)并不會(huì)回收。

我們目前只講了棧规伐,堆的使用也類似蟹倾,只不過是要手工進(jìn)行申請和釋放,比棧要多一些維護(hù)工作。

總結(jié)

本文帶你了解了程序運(yùn)行的環(huán)境和過程鲜棠,我們的程序主要跟 CPU肌厨、內(nèi)存,以及操作系統(tǒng)打交道豁陆。你需要了解的重點(diǎn)如下:

  • CPU 上運(yùn)行程序的指令柑爸,運(yùn)行過程中要用到寄存器、高速緩存來提高指令和數(shù)據(jù)的存取效率盒音。
  • 內(nèi)存可以劃分成不同的區(qū)域保存代碼表鳍、靜態(tài)數(shù)據(jù),并用棧和堆來存放運(yùn)行時(shí)產(chǎn)生的動(dòng)態(tài)數(shù)據(jù)祥诽。
  • 操作系統(tǒng)會(huì)把物理的內(nèi)存映射成進(jìn)程的尋址空間譬圣,同一份代碼會(huì)被映射進(jìn)多個(gè)進(jìn)程的內(nèi)存空間,操作系統(tǒng)的公共庫也會(huì)被映射進(jìn)進(jìn)程的內(nèi)存空間雄坪,操作系統(tǒng)還會(huì)自動(dòng)維護(hù)棧厘熟。
  • 程序在運(yùn)行時(shí)順序執(zhí)行代碼,可以根據(jù)跳轉(zhuǎn)指令來跳轉(zhuǎn)诸衔;棧被劃分成棧楨盯漂,棧楨的設(shè)計(jì)有一定的自由度,但通常也要遵守一些約定笨农;棧楨的大小和結(jié)構(gòu)在編譯時(shí)就能決定就缆;在運(yùn)行時(shí),棧楨作為活動(dòng)記錄谒亦,不停地被動(dòng)態(tài)創(chuàng)建和釋放竭宰。

以上這些內(nèi)容就是一個(gè)程序運(yùn)行時(shí)的秘密。你再面對代碼時(shí)份招,腦海里就會(huì)想象出它是怎樣跟CPU切揭、內(nèi)存和操作系統(tǒng)打交道的了。而且有了這些背景知識(shí)锁摔,你也可以讓編譯器生成代碼廓旬,按照本文所說的模式運(yùn)行了!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末谐腰,一起剝皮案震驚了整個(gè)濱河市孕豹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌十气,老刑警劉巖励背,帶你破解...
    沈念sama閱讀 217,907評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異砸西,居然都是意外死亡叶眉,警方通過查閱死者的電腦和手機(jī)址儒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來衅疙,“玉大人莲趣,你說我怎么就攤上這事×陡颍” “怎么了妖爷?”我有些...
    開封第一講書人閱讀 164,298評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長理朋。 經(jīng)常有香客問我絮识,道長,這世上最難降的妖魔是什么嗽上? 我笑而不...
    開封第一講書人閱讀 58,586評(píng)論 1 293
  • 正文 為了忘掉前任次舌,我火速辦了婚禮,結(jié)果婚禮上兽愤,老公的妹妹穿的比我還像新娘彼念。我一直安慰自己,他們只是感情好浅萧,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,633評(píng)論 6 392
  • 文/花漫 我一把揭開白布逐沙。 她就那樣靜靜地躺著,像睡著了一般洼畅。 火紅的嫁衣襯著肌膚如雪吩案。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,488評(píng)論 1 302
  • 那天帝簇,我揣著相機(jī)與錄音徘郭,去河邊找鬼。 笑死丧肴,一個(gè)胖子當(dāng)著我的面吹牛残揉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播芋浮,決...
    沈念sama閱讀 40,275評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼抱环,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了纸巷?” 一聲冷哼從身側(cè)響起江醇,我...
    開封第一講書人閱讀 39,176評(píng)論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎何暇,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體凛驮,經(jīng)...
    沈念sama閱讀 45,619評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡裆站,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,819評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宏胯。...
    茶點(diǎn)故事閱讀 39,932評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡羽嫡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出肩袍,到底是詐尸還是另有隱情杭棵,我是刑警寧澤,帶...
    沈念sama閱讀 35,655評(píng)論 5 346
  • 正文 年R本政府宣布氛赐,位于F島的核電站魂爪,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏艰管。R本人自食惡果不足惜滓侍,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,265評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望牲芋。 院中可真熱鬧撩笆,春花似錦、人聲如沸缸浦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽裂逐。三九已至歹鱼,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間絮姆,已是汗流浹背醉冤。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留篙悯,地道東北人蚁阳。 一個(gè)月前我還...
    沈念sama閱讀 48,095評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像鸽照,于是被迫代替她去往敵國和親螺捐。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,884評(píng)論 2 354