ARM體系架構(gòu)——MMU

一、前言

嵌入式Linux 開發(fā)中,往往會聽到 MMU 這個詞,但大多數(shù)情況下并不會去了解它,因為操作系統(tǒng)已經(jīng)做好了關(guān)于 MMU 的一切操作,我們只需要在操作系統(tǒng)的框架下直接使用即可。但了解 MMU 有助于幫助我們理解操作系統(tǒng),理解進程等,讓我們對 嵌入式Linux 的理解上升一個層次。本文將簡單地講述一下關(guān)于 MMU 的基本信息。

注意:本文將按照ARMv7的二級頁表映射進行講述

二、MMU

2.1 MMU基本信息

MMU 全稱為 Memory Management Unit,即 內(nèi)存管理單元。在 帶有MMU的嵌入式Linux 中,CPU 訪問的地址都是 虛擬地址,而 MMU 負責將程序中 代碼或數(shù)據(jù)虛擬地址 翻譯為 物理地址,以便程序訪問內(nèi)存。

在執(zhí)行操作時,MMU 會自動轉(zhuǎn)換 CPU發(fā)出的虛擬地址,無法人工進行操作,只需要配置好 MMU 相關(guān)屬性即可。

虛擬地址 是在 編譯和鏈接 時定義的,可以簡單地理解為 由鏈接器和鏈接器腳本 指定虛擬地址。
除了 翻譯虛擬地址MMU 還可以配置 內(nèi)存區(qū)域 的各項配置,如內(nèi)存區(qū)域的訪問權(quán)限內(nèi)存區(qū)域是否使能cache等功能。
總結(jié) MMU 的功能,如下:

  • 翻譯虛擬地址
  • 配置內(nèi)存區(qū)域的相關(guān)屬性

2.2 MMU基本概念

看到 MMU 的相關(guān)文章時,總會提及幾個概念如 頁框(頁幀)頁表頁表項TLB等等,下面我們逐個拆分來講述。

2.2.1 頁

MMU 管理 虛擬地址空間 時,是按照 為單位來進行管理。在 ARMv7MMU頁大小 一共有 16M(Super Section)1M(Section)64K(Large Page) 4K(Page)頁大小 可以通過 協(xié)處理器CP15 進行配置,越小的頁意味著內(nèi)存的顆粒度越小,內(nèi)存使用時的浪費會越小,但也意味著使用的TLB行越多。越大的也內(nèi)存的顆粒度月大,內(nèi)存的使用浪費也可能月大,但使用的TLB行越少。比如只需要申請 7K 大小的 物理內(nèi)存,如果使用 7K大小 的內(nèi)存,我們可以分配 24K頁,如果分配 64K的大頁,則浪費的空間就比較大。

2.2.2 頁框

因為 虛擬地址空間 需要有所對應的 物理地址,這樣才能在 虛擬地址 中存儲數(shù)據(jù)。所以 MMU 管理 物理地址空間 時,按照 頁幀 為單位進行管理。其大小分為 64K4K。一段 虛擬地址空間 有可能存在著多個 ,這些 對應著多個 頁幀

按照筆者理解,頁幀不同地址空間下的關(guān)于內(nèi)存空間大小的概念

2.2.3 頁表及頁表項

MMU 在進行 地址轉(zhuǎn)換 時,需要一些信息,存放這些信息的就是 頁表。每個 頁表 的最小單位就是 頁表項
頁表 存儲在 物理地址空間 中,且一個 頁表項 對應著一個
切換頁表 時,通過將 頁表的物理首地址 設(shè)置到 協(xié)處理器CP15 中的 TTBR寄存器(Translation Table Base Register)。此后 MMU 會通過該地址自動去 物理地址空間 中找到對應的 頁表,從而完成 虛擬地址到物理地址的映射

在不考慮 TLB多級頁表 的情況下,可以簡單地如下圖所示:

頁表及頁表項

2.2.4 TLB

TLB 全程為 Translation Lookaside Buffer,即 旁路轉(zhuǎn)換緩沖。它是 MMU 的專屬 全相聯(lián)cache,用于臨時存放 虛擬地址到物理地址映射 所需要的信息。
下面按照步驟說明 TLB 的作用:

  1. CPU 訪問 虛擬地址MMU
  2. MMU 根據(jù)規(guī)則(規(guī)則在下文講述)查看 虛擬地址 是否在 TLB 中。
  3. 如果在 TLB 中,則稱為 TLB命中。從 TLB 中直接獲取 物理地址 對內(nèi)存進行訪問
  4. 如果不在 TLB 中,則稱為 TLB失效。此時 MMU 將進行 translation table walking,即通過 訪問頁表來獲取 物理地址。并將該 虛擬地址 的信息存入 TLB,以便下次使用。

值得注意的是:ARM架構(gòu)的TLB只存儲有效的頁表項,對于無效的頁表項TLB并不會存儲

TLB 由許多 TLB行 組成,如下圖所示:

TLB

TLB行3個 部分組成,分別為 標簽ASID描述符

  • 標簽:該部分由 虛擬地址的一部分bit 組成,MMU 通過將 虛擬地址的一部分bitTLB 的所有標簽對比進行搜索。
  • ASID:全稱為 Address Space ID,一般用于 多進程系統(tǒng),下文會詳細講述。
  • 描述符:由 2個 部分組成,分別為 物理地址(一部分bit)內(nèi)存區(qū)域?qū)傩?/strong> 組成。可以理解為 cache 中的數(shù)據(jù)。

一般情況下,切換 進程 時會切換 頁表,因為隨著進程的切換, 虛擬地址物理地址 的映射已經(jīng)改變。此時需要 清理TLB(即無效化TLB中的數(shù)據(jù)) 來保持 TLB一致性清理TLB 一般通過 協(xié)處理器CP15 來完成,在 Linux內(nèi)核 中,有 flush_tlb_all()flush_tlb_range() 函數(shù)來完成該工作。

2.3 MMU組成

如下圖所示:

MMU組成

MMU 的工作流程可以總結(jié)為下面 2 種情況:

  • 訪問 虛擬地址 時,MMU 通過查找 TLB 來找出對應的 頁幀,從而訪問 物理地址,如圖中的 頁1頁2頁3
  • 如果 MMUTLB 中沒找到對應的 TLB行 時,將進行 traslation table working。即從 物理地址空間頁表中 找出對應的 頁表項,并根據(jù) 頁表項 找到對應的 物理地址。并將該 頁表項 更新到 TLB 中,以備下次使用。

2.4 MMU工作過程

ARMv7 下的 MMU 具有 2級頁表,分為 1級頁表2級頁表

2.4.1 1級頁表

1級頁表 也稱 主頁表段頁表,下面簡稱 L1頁表。它將 4GB 的地址空間劃分為 40961MB 大小的 ,每個段的地址為 32bit。所以 1級頁表 擁有 409632bit頁表項

2.4.1.1 一級頁表項

L1頁表 使用了 短描述符頁表(Short-descriptor translation table),其 頁表項 具有以下特征:

  • 32bit 的頁描述符
  • 具有 2級 以上的 頁表
  • 支持 32bit物理地址
  • 支持 4種 內(nèi)存大小:
    • 16MB/1M,稱為
    • 64KB/4KB,稱為

在前面說了 TTBR寄存器 是存放 頁表物理地址 的寄存器,需要注意的是:存放在TTBR寄存器的地址需要16KB對齊

一級頁表項 一共有 4種 格式,如下圖所示:

一級頁表項格式

每種格式都由 物理地址部分+屬性部分 組成,可以直接在圖中看出 物理地址部分 的示意,這里不多贅述。各種格式的含義如下:

  1. 1MB段轉(zhuǎn)換頁表項(Section) ,映射到 1MB 的物理地址范圍。其 物理地址部分 即為所需要映射的 物理基地址
  2. 物理地址部分 指向 2級頁表物理基地址
  3. 16MB段(SuperSection) 轉(zhuǎn)換頁表項,是一種特殊的 1MB段轉(zhuǎn)換頁表項。其 物理地址部分 即為所需要映射的 物理基地址
  4. 無效頁表項,當訪問該頁表項時,將觸發(fā) 指令取指異常取數(shù)據(jù)異常

下面簡單說下各個字段的含義:

  • Ignored:忽略
  • Level 2 Descriptor Base Address二級頁表物理基地址
  • Section Base Address1MB段基地址
  • Supersection Base Address16MB段基地址
  • SBZ:全稱 should be zero,無效屬性字段
  • AP:全稱 Access Permissions,內(nèi)存區(qū)域訪問權(quán)限
  • Domain:用于權(quán)限控制,下文講述。
  • TEX:全稱 Type extension,設(shè)置內(nèi)存區(qū)域類型
  • B:全稱 Bufferable,是否設(shè)置 寫緩沖
  • C:全稱 Cacheable,是否設(shè)置 cache
  • nG:全稱 non-Global。如果 頁表項的nGbit 被設(shè)置,那么該 頁表項 對應的 內(nèi)存區(qū)域 將只能被 特定的進程 使用。當MMU 使用該 頁表項 進行映射時,也需要使用到 ASID
  • S:全稱 Shareable,共享設(shè)置項。
  • bit[18]:該 bit 決定 段頁表項1MB頁表項 還是 16MB頁表項
  • bit[1:0]:這 2個bit 決定頁表項的類型,如下:
    • 00無效頁表項
    • 01轉(zhuǎn)換表頁表項
    • 10段頁表項

2.4.1.2 一級頁轉(zhuǎn)換

1MB段 舉例,假設(shè) L1頁表 的物理地址為 0x12300000,現(xiàn)在有一個虛擬地址 0x00100000。其轉(zhuǎn)換過程如圖所示:

查表過程

轉(zhuǎn)換過程

  1. 查表過程:將 虛擬地址高12bit,即0x001 乘以 4 得到 0x0040x004 即為 該虛擬地址所在段的頁表項在頁表中的偏移,所以 該虛擬地址對應的頁表項的物理地址為0x12300000+0x004=0x12300004
  2. 根據(jù)查到的 頁表項,將 頁表項高12bit虛擬地址低30bit 結(jié)合,即為 該虛擬地址在該1MB段內(nèi)的物理地址

值得注意的是:例子中,高12位一共是4096個頁表項,那么4096x4一共是16384字節(jié)的大小,因為每個頁表項是32位。所以4096個頁表項需要16K大小的內(nèi)存來存儲頁表。也是因為如此,每個虛擬地址的高12bit都需要乘以4.

下圖為例子的完整轉(zhuǎn)換過程,其余類型的 頁表項 轉(zhuǎn)換過程類似、

L1轉(zhuǎn)換完整過程

2.4.2 二級頁表

2級頁表 一共有 2564字節(jié)大小頁表項,總共占據(jù) 1KB大小 的內(nèi)存空間。L2頁表 的大部分內(nèi)容與 L1頁表 類似,相同部分下文將不再贅述

2.4.2 二級頁表項

二級頁表項 一共有 3種 格式,如下圖所示:

二級頁表項

每種格式與 L1頁表項 一樣由 物理地址部分+屬性部分 組成,可以直接在圖中看出 物理地址部分 的示意,格式如下:

2級頁表項 具有以下特征:

  1. 粗頁表項: 其 物理地址部分指向 64KB大小物理基地址
  2. 細頁表項: 其 物理地址部分指向 4KB大小物理基地址
  3. 無效頁表項,當訪問該頁表項時,將觸發(fā) 指令取指異常取數(shù)據(jù)異常

屬性字段 的含義請參考 1級頁表 章節(jié)。

2.4.2 二級頁表轉(zhuǎn)換

L2頁表 的轉(zhuǎn)換過程與 L1頁表 的轉(zhuǎn)換過程一脈相承。以 4KB 為例子,如下圖所示:

image.png

由上圖可以看出其轉(zhuǎn)換步驟如下:

  1. 通過 虛擬地址 找出 L1頁表項 并轉(zhuǎn)換為 L2頁表基地址
  2. 根據(jù) L2頁表基地址 并集合 虛擬地址的[19:12]bit 找出 虛擬地址 對應的 L2頁表項
  3. 虛擬地址[11:0]bitL2頁表項物理地址部分 結(jié)合得出具體的 物理地址

結(jié)合 L1頁表 的完整轉(zhuǎn)化過程如下圖所示:

image.png

2.5 MMU內(nèi)存屬性

2.5.1 內(nèi)存區(qū)域權(quán)限

每個 內(nèi)存區(qū)域 都有自己的權(quán)限,不符合訪問權(quán)限的 內(nèi)存訪問 都會引發(fā) 異常。如果是 數(shù)據(jù)訪問 則引發(fā) 數(shù)據(jù)異常。如果是 指令訪問,且該指令在執(zhí)行前沒有被 flush,將引發(fā) 預取指異常
引發(fā)的 異常原因 將會被設(shè)置在 CP15the fault address and fault status registers

內(nèi)存區(qū)域權(quán)限APAPXDomain(域) 共同控制,如下:

  • AP/APX:字段 APAPX 的不同組合將形成不同的 訪問權(quán)限,如圖所示。按照筆者理解,
    Privileged 指的是 CPU 處于 svc 等狀態(tài),而 Unprivileged 則為 CPU 處于 user 狀態(tài)。
    訪問權(quán)限組合表
  • Domain:這是一種 ARM架構(gòu) 不常用的 內(nèi)存全權(quán)限控制 方式。MMU 可以將所有 內(nèi)存區(qū)域 分配到 16個域 中,每一個 都有自己的 訪問權(quán)限。被分配到 中的 內(nèi)存區(qū)域 必須遵循該 的訪問權(quán)限。可以通過設(shè)置 頁表項 中的 Domain字段 來實現(xiàn) 的分配。
    CP15協(xié)處理器 中有一個 DACR寄存器(Domain Access Control Register),該寄存器為 32bit,每個 訪問權(quán)限2個bit 設(shè)置,一共設(shè)置 16個域 的權(quán)限設(shè)置如下:
    • 不可訪問(no-access):對該 內(nèi)存區(qū)域 進行訪問將引發(fā) 異常
    • 管理者(Manager mode):訪問不受任何控制,不產(chǎn)生 異常
    • 用戶(Client mode):使用 頁表項 中的 AP/APX字段 進行控制

需要注意的是:內(nèi)存區(qū)域控制以域控制為主,頁表項的AP/APX字段為次。ARMv7不建議使用域進行控制,所以建議把DACR寄存器設(shè)置為用戶模式

2.5.2 內(nèi)存類型

ARM架構(gòu) 實現(xiàn)了 3種內(nèi)存類型,每種類型都是 互斥的,如下:

  • Strongly-ordered
  • Device
  • Normal

每種 類型 的細節(jié)如下圖所示:

image.png

需要注意的是,Device類型的Shareable內(nèi)存區(qū)域現(xiàn)在已經(jīng)被棄用

內(nèi)存區(qū)域類型 可以通過 TEX字段C字段B字段 來進行設(shè)置,如下圖所示

image.png

值得注意的是:按照筆者理解,inner cache是L1 cache,而outer cache是指在L1cache下面的cache,比如L2cache

操作系統(tǒng)如何使用頁表

2.6 進程與MMU

操作系統(tǒng) 會為 每個進程 分配一個 頁表,該 頁表 使用 物理地址 存儲。當 進程 使用類似 malloc 等需要 映射代碼或數(shù)據(jù) 的操作時,操作系統(tǒng) 會在隨后馬上 修改頁表 以加入新的 物理內(nèi)存。當進程完成退出時,內(nèi)核會將相關(guān)的頁表項刪除掉,以便分配給新的進程。

2.6.1 Address Space ID

在操作系統(tǒng)中, 多進程 是一種常態(tài)。那么多進程 的情況下,每次 切換進程 都需要進行 TLB清理。這樣會導致切換的效率變低。
為了解決問題,TLB 引入了 ASID(Address Space ID)ASID 的范圍是 0-255
ASID 由操作系統(tǒng)分配,當前進程的ASID值 被寫在 ASID寄存器(使用CP15 c3訪問)TLB 在更新 頁表項 時也會將 ASID 寫入 TLB
如果設(shè)置了如果 當前進程的ASID,那么 MMU 在查找 TLB 時, 只會查找 TLB 中具有 相同ASID值TLB行。且在切換進程是,TLB 中被設(shè)置了 ASIDTLB行 不會被清理掉,當下次切換回來的時候還在。所以ASID 的出現(xiàn)使得切換進程時不需要清理 TLB 中的所有數(shù)據(jù),可以大大減少 切換開銷

具體可以看參考鏈接《多核MMU和ASID管理邏輯》

2.6.2 TTBR0和TTBR1

前面講了 TTBR寄存器 是用于存放 頁表基地址,在 ARmv7 中一共有 2個 這樣的寄存器,分別是 TTBR0TTBR1
那么這里提出一個問題:在進行 Translation Table walking 的時候,選擇哪個TTBR寄存器,又如何選擇?
ARMv7 中,有一個寄存器為 TTBCR(TTB Control Register),即TTB控制寄存器TTBCR寄存器 可以被設(shè)置為 0-7 這幾個值。
在進行 地址映射 時, MMU 會根據(jù) TTBCR寄存器 中的值查看 虛擬地址 是高位地址,根據(jù) 高位地址 選擇對應的 TTBR寄存器
舉個例子,假設(shè) TTBCR寄存器 被設(shè)置為 4,則 MMU 會檢查 虛擬地址高4bit,如果 高4bit 都為 0,則此時選擇 TTBR0

需要注意的是:TTBCR被設(shè)置為 0 時,默認選擇 TTBR0。

下面我們看看使用和不使用 TTBR1 帶來的影響。

  • 不使用:在 ARM32架構(gòu)的操作系統(tǒng)中,不使用 TTBR1寄存器。此時,用戶空間內(nèi)核空間 共用一個 頁表。也就是說 用戶空間內(nèi)核空間 都使用 TTBR0 來記錄 頁表地址,這樣可以避免一個問題,就是進行 用戶空間和內(nèi)核空間的切換時,可以避免切換頁表帶來的性能損耗。但與此同時也帶來一個問題,用戶空間每個進程 都擁有 內(nèi)核頁表副本,當 內(nèi)核空間頁表 修改時,所有 進程 都需要同步修改其 內(nèi)核頁表副本。造成一定的性能損失
  • 使用ARM64架構(gòu)的操作系統(tǒng)中虛擬地址空間 非常大。用戶空間內(nèi)核空間 都是 256T用戶空間地址高位為0,內(nèi)核空間地址高位為1。這樣的特性滿足 TTBR1寄存器 的使用條件。根據(jù) 用戶空間地址內(nèi)核空間地址 的不同,選擇對應的 TTBR寄存器。這樣就不需要為每個 進程 維護一份 內(nèi)核頁表副本

2.6.3 代碼實例

本小節(jié)簡單地講述一下 Linux 進行 MMU切換 時的代碼片段。以 ARMv7單核CPU 為例子。
根據(jù)筆者的理解,其調(diào)用圖譜如下:

->switch_mm
  ->check_and_switch_context
    ->cpu_switch_mm(processor.switch_mm)
      ->cpu_v7_switch_mm

筆者會將簡單的說明注釋在代碼中,不進行另外的說明。

/* arch/arm/include/asm/mmu_context.h */
static inline void
switch_mm(struct mm_struct *prev, struct mm_struct *next,
      struct task_struct *tsk)
{
#ifdef CONFIG_MMU
    unsigned int cpu = smp_processor_id();

    /*
     * __sync_icache_dcache doesn't broadcast the I-cache invalidation,
     * so check for possible thread migration and invalidate the I-cache
     * if we're new to this CPU.
     */
    /* 這里應該是說進程如果調(diào)度到新的CPU,則需要將該CPU的cache給清理掉 */
    if (cache_ops_need_broadcast() &&
        !cpumask_empty(mm_cpumask(next)) &&
        !cpumask_test_cpu(cpu, mm_cpumask(next)))
        __flush_icache_all();

    if (!cpumask_test_and_set_cpu(cpu, mm_cpumask(next)) || prev != next) {
        /* 如果調(diào)度的進程不是本進程,則執(zhí)行check_and_switch_context */
        check_and_switch_context(next, tsk);
        if (cache_is_vivt())
            cpumask_clear_cpu(cpu, mm_cpumask(prev));
    }
#endif
}

static inline void check_and_switch_context(struct mm_struct *mm,
                        struct task_struct *tsk)
{
    if (unlikely(mm->context.vmalloc_seq != init_mm.context.vmalloc_seq))
        __check_vmalloc_seq(mm);

    if (irqs_disabled())
        /*
         * cpu_switch_mm() needs to flush the VIVT caches. To avoid
         * high interrupt latencies, defer the call and continue
         * running with the old mm. Since we only support UP systems
         * on non-ASID CPUs, the old mm will remain valid until the
         * finish_arch_post_lock_switch() call.
         */
        mm->context.switch_pending = 1;
    else
        /* 使用該函數(shù)進行MMU切換頁表 */
        cpu_switch_mm(mm->pgd, mm);
}

/* arch/arm/include/asm/proc-fns.h */
/* 根據(jù)筆者找的代碼,cpu_switch_mm 應該直接調(diào)用了processor.switch_mm */
#define cpu_do_switch_mm        processor.switch_mm
#define cpu_switch_mm(pgd,mm) cpu_do_switch_mm(virt_to_phys(pgd),mm)

processor.switch_mm 是一個 回調(diào)函數(shù),根據(jù)筆者找到的資料,應該是指向 ** arch/arm/mm** 目錄下的一些列 MMU 操作代碼。這里以 proc-v7-2level.S(即ARMv7 2級頁表) 進行說明

/* arch/arm/mm/proc-v7-2level.S */
/* 根據(jù)APCS,傳入的參數(shù)是存放在寄存器 r0和r1 */
ENTRY(cpu_v7_switch_mm)
#ifdef CONFIG_MMU
        mmid    r1, r1                          @ get mm->context.id
        ALT_SMP(orr     r0, r0, #TTB_FLAGS_SMP)
        ALT_UP(orr      r0, r0, #TTB_FLAGS_UP)
#ifdef CONFIG_PID_IN_CONTEXTIDR
        mrc     p15, 0, r2, c13, c0, 1          @ read current context ID
        lsr     r2, r2, #8                      @ extract the PID
        bfi     r1, r2, #8, #24                 @ insert into new context ID
#endif
#ifdef CONFIG_ARM_ERRATA_754322
        dsb
#endif
        mcr     p15, 0, r1, c13, c0, 1          @ set context ID
        isb
        /* 在這里,將r0所指向的頁表基地址設(shè)置到TTBR0中,完成頁表的切換 */
        mcr     p15, 0, r0, c2, c0, 0           @ set TTB 0
        isb
#endif
        bx      lr
ENDPROC(cpu_v7_switch_mm)

三、參考鏈接

《ARM Cortex-A Series Programmer’s Guide》
《Cortex-A7 MPCore Technical Reference Manual》
《多核MMU和ASID管理邏輯》
TLB的作用及工作過程
MMU和cache詳解(TLB機制)
inux-kernel – Linux內(nèi)核ARM轉(zhuǎn)換表庫(TTB0和TTB1)
ARM TTBR0,TTBR1寄存器與ARM32頁表復制
選擇使用TTBR0或TTBR1做為translation table base地址寄存器
TLB中ASID和nG bit的關(guān)系
ASID
Linux arm 進程切換
ARM-LINUX的進程切換

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,563評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,694評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,672評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,965評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,690評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,019評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,013評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,188評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,718評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,438評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,667評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,149評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,845評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,252評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,590評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,384評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,635評論 2 380

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

  • 本文轉(zhuǎn)載自 https://juejin.im/post/59f8691b51882534af254317 參考:...
    xingdong閱讀 2,739評論 0 3
  • ## 一些小話 前段時間因為一些雜七雜八的事情 被逼無奈 趕各種作業(yè) 再加上某個姓張的賤人 欺騙我說計網(wǎng)...
    小師弟_831a閱讀 2,326評論 0 5
  • 前言 虛擬內(nèi)存算是操作系統(tǒng)中比較重要的一部分了,內(nèi)容也很多,早在看操作系統(tǒng)相關(guān)書籍的時候就有想要總結(jié)這一部分,但是...
    HusterYP閱讀 1,361評論 0 5
  • 維基百科——虛擬內(nèi)存定義 虛擬內(nèi)存是計算機內(nèi)存管理的一種技術(shù)。它使得應用程序認為它擁有連續(xù)可用的內(nèi)存(一個連續(xù)完整...
    小胖學編程閱讀 858評論 0 8
  • 近日,一名自稱是中鐵十二局的員工在網(wǎng)絡(luò)發(fā)帖稱,由中鐵十二局成貴鐵路11標項目經(jīng)理部修建的成貴高鐵鎮(zhèn)雄段存在安全質(zhì)量...
    KisenJ閱讀 295評論 0 1