NVMain和Gem5環(huán)境搭建

前沿

這個是華科研究生課程計算機(jī)系統(tǒng)設(shè)計一個實驗,做完整個實驗大約花了兩天多的時間吧,其中也是遇到了很多問題,想在博客中記錄下來,日后若是能夠幫助師弟師妹們或者其他的博友,便不負(fù)這篇博客的作用.

這是我整個結(jié)構(gòu)的目錄樹,如果你在接下來閱讀過程中,存在任何路徑上的問題,請仔細(xì)閱讀這四個目錄的位置,這四個目錄是同一級目錄.

+-- nvmain
|
+-- gem5
|
+-- fs-image
|
+-- benchmark

NVMain環(huán)境搭建

下載NVMain

下載nvmain,并解壓,將解壓后目錄的名字更改為nvmain

wget https://bitbucket.org/mrp5060/nvmain/get/9c0e87b164bc.zip -O nvmain.zip
unzip nvmain.zip
mv mrp5060-nvmain-9c0e87b164bc nvmain 

NVMain編譯

  • 安裝編譯工具scons, scons是一個開放源碼挠阁、以Python語言編碼的自動化構(gòu)建工具靴寂,可用來替代make編寫復(fù)雜的makefile。并且scons是跨平臺的,只要scons腳本寫的好,可以在Linux和Windows下隨意編譯。
sudo apt-get install scons
  • 編譯NVMain,其中編譯的類型可以選擇fast|debug|prof三種的任意一種,這里我們選用fast
scons --build-type=fast

如果在編譯的過程中,報了如下的錯誤,無須緊張,這只是個小問題,是因為我們暫時還沒有用到gem5,所以只需將這個報錯的那一行代碼注釋即可.

scons: Reading SConscript files ...
ImportError: No module named gem5_scons:
  File "/home/greek/master/experiment/nvmain/SConstruct", line 253:
    SConscript(joinpath(root, 'SConscript'), variant_dir=build_dir)
  File "/usr/lib/scons/SCons/Script/SConscript.py", line 614:
    return method(*args, **kw)
  File "/usr/lib/scons/SCons/Script/SConscript.py", line 551:
    return _SConscript(self.fs, *files, **subst_kw)
  File "/usr/lib/scons/SCons/Script/SConscript.py", line 256:
    call_stack[-1].globals)
  File "/home/greek/master/experiment/nvmain/build/SConscript", line 36:
    from gem5_scons import Transform

具體的解決辦法是將nvmain/build/SConscript中的第36行注釋

# from gem5_scons import Transform # 這行注釋
  • 解決錯誤之后,再重新編譯
NVMain編譯成功標(biāo)識

NVMain測試

  • 指定配置文件,指定測試文件,周期, 命令的格式為: ./nvmain CONFIG_FILE TRACE_FILE [Cycles [PARAM=value]]

這里我們使用的測試命令如下

./nvmain.fast Config/PCM_ISSCC_2012_4GB.config Tests/Traces/hello_world.nvt 1000000
NVMain測試結(jié)果

Gem5環(huán)境搭建

下載Gem5

  • 使用常用工具git, 但是倉庫在google中,如果不能翻墻,可能無法下載
git clone https://gem5.googlesource.com/public/gem5
  • [推薦使用] 使用工具hg. Mercurial是一款非常優(yōu)秀的分布式版本控制系統(tǒng)(DCVS)褂删,具有高效率、跨平臺冲茸、可擴(kuò)展屯阀、使用簡便且開源等優(yōu)點(diǎn),是目前最為流行的版本控制工具之一.
sudo apt-get install mercurial
hg clone http://repo.gem5.org/gem5

編譯Gem5

  • 安裝依賴庫protobuf-compiler
sudo apt-get install protobuf-compiler
  • 編譯Gem5,這里可以選擇不同的架構(gòu),比如X86|ARM|ALPHA, 這里我們選擇使用X86(我的電腦是X86結(jié)構(gòu)的,一開始是怕出錯,就編譯了這個,后來感覺應(yīng)該沒啥影響))
scons build/X86/gem5.opt
Gem5編譯成功

Gem5的SE測試

SE測試

  • 用來運(yùn)行單個應(yīng)用,一系列指令在MP/SMT上
  • 建模用戶可見的ISA和常見的系統(tǒng)調(diào)用
  • 模擬系統(tǒng)調(diào)用,通過調(diào)用主機(jī)操作系統(tǒng)
  • 簡單的地址翻譯,沒有調(diào)度
./build/X86/gem5.opt configs/example/se.py -c tests/test-progs/hello/bin/x86/linux/hello 
SE測試結(jié)果

Gem5的FS測試

FS測試

  • 能夠啟動完整的操作系統(tǒng)
  • 建模硬件設(shè)備
  • 中斷,異常,故障處理函數(shù)

參考下載的網(wǎng)址: www.m5sim.org/Download

  1. 下載X86全系統(tǒng)對應(yīng)的文件,一定要下載自己編譯架構(gòu)的對應(yīng)文件
wget http://www.m5sim.org/dist/current/x86/x86-system.tar.bz2
  1. 解壓文件,同時將解壓后的文件夾放入同一目錄fs-image
tar vxfj x86-system.tar.bz2
mkdir fs-image
mv binaries fs-image
mv disks fs-image
  1. 下載alpha對應(yīng)的全系統(tǒng)文件,里面包含linux-bigswap2.img,這個是運(yùn)行的一個必須文件.下載之后,解壓文件,同時將文件拷貝到fs-image/disks目錄下,可以理解為專門存放鏡像文件的目錄.
wget http://www.m5sim.org/dist/current/m5_system_2.0b3.tar.bz2
tar vxfj m5_system_2.0b3.tar.bz2
cp m5_system_2.0b3/disks/linux-bigswap2.img fs-image/disks
  1. 運(yùn)行FS模擬
 ./build/X86/gem5.opt configs/example/fs.py 

報了如下的錯誤,這里因為腳本代碼中默認(rèn)了鏡像為x86root.img

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "build/X86/python/m5/main.py", line 453, in main
    exec(filecode, scope)
  File "configs/example/fs.py", line 340, in <module>
    test_sys = build_test_system(np)
  File "configs/example/fs.py", line 93, in build_test_system
    cmdline=cmdline)
  File "/home/greek/master/experiment/gem5/configs/common/FSConfig.py", line 614, in makeLinuxX86System
    makeX86System(mem_mode, numCPUs, mdesc, self, Ruby)
  File "/home/greek/master/experiment/gem5/configs/common/FSConfig.py", line 539, in makeX86System
    disk0.childImage(mdesc.disk())
  File "/home/greek/master/experiment/gem5/configs/common/Benchmarks.py", line 63, in disk
    return env.get('LINUX_IMAGE', disk('x86root.img'))
  File "/home/greek/master/experiment/gem5/configs/common/SysPaths.py", line 71, in __call__
    raise IOError("Can't find file '%s' on path." % filename)
IOError: Can't find file 'x86root.img' on path.

解決辦法

這里有兩種解決辦法,推薦使用第二種:

  • 修改代碼gem5/configs/common/Benchmarks.py中,第63行,修改為如下,其中linux-86.imgfs-image/disks目錄下的鏡像文件
return env.get('LINUX_IMAGE', disk('linux-x86.img'))
  • [推薦使用] 直接使用參數(shù)指定鏡像文件,使用參數(shù) --disk-image以及--kernel

命令使用如下格式:

./build/X86/gem5.opt configs/example/fs.py --disk-image linux-x86.img --kernel x86_64-vmlinux-2.6.22.9

指定M5_PATH路徑,系統(tǒng)會自動從M5_PATH路徑中disksbinaries兩個目錄尋找指定的鏡像文件和對應(yīng)的內(nèi)核文件.

其中指定M5_PATH我提供了兩種方式,推薦使用第二種(雖然我用的第一種)):

  • 修改.basrhc文件,在文件的末尾加入M5_PATH的路徑
export M5_PATH=$M5_PATH:/home/hsc/fs-image
  • 直接在運(yùn)行命令中動態(tài)添加環(huán)境變量
M5_PATH=/home/hsc/fs-image ./build/X86/gem5.opt configs/example/fs.py --disk-image linux-x86.img --kernel x86_64-vmlinux-2.6.22.9

linux-x86.imgfs-image/disks/目錄下文件, x86_64-vmlinux-2.6.22.9fs-image/binaries/目錄下文件,我看網(wǎng)上很多教程將x86_64-vmlinux-2.6.22.9改名為vmlinux,這步的話其實意義不是特別大,不過改了也方便,我這里暫不做修改.

  1. 連接模擬系統(tǒng)

當(dāng)運(yùn)行了FS測試之后,系統(tǒng)就啟動了,可以通過遠(yuǎn)程連接的方式,連接上我們的系統(tǒng).這里我們介紹兩種方式進(jìn)行連接:

  • 利用gem5系統(tǒng)自帶的一個連接程序,在gem5/util/term/目錄中,先進(jìn)行make,生成可執(zhí)行文件
cd util/term/
make

./m5term localhost 3456
  • [推薦使用] 直接利用telnet連接系統(tǒng)
telnet localhost 3456
FS系統(tǒng)運(yùn)行狀態(tài)
連接之后進(jìn)入命令行狀態(tài)
  1. 退出

這里我依舊使用兩種方法,推薦使用第一種方法

  • 在連接的命令行中輸入 m5 exit
  • 在連接的命令行中輸入 ~.

NVMain和Gem5的集成環(huán)境

Gem5補(bǔ)丁

首先為了消除NVMainGem5版本之間的差異,所以首先需要給Gem5打上補(bǔ)丁.(別聽到補(bǔ)丁就覺得畏懼,一開始我有這種感覺,其實后面發(fā)現(xiàn)真的沒啥).

這里我提供兩種方式,兩種方式都很OK,如果第一種失敗建議換成第二種.如果你的gem5不是用hg下載,請換第二種方式.

  1. 補(bǔ)丁文件已經(jīng)在nvmain/patches/gem5目錄中給出,在gem5目錄中執(zhí)行下列命令
hg qinit
hg qimport -f ../nvmain/patches/gem5/nvmain2-gem5-11688+
hg qpush

nvmain2-gem5-11688+這個是補(bǔ)丁文件的名字,在nvmain/patches/gem5中,以后可能是其他的版本,換成自己的版本補(bǔ)丁名字即可.

  1. 手動補(bǔ)丁,直接查看補(bǔ)丁的內(nèi)容,手動修改文件,如下所示為補(bǔ)丁的內(nèi)容
diff --git a/configs/common/Options.py b/configs/common/Options.py
--- a/configs/common/Options.py
+++ b/configs/common/Options.py
@@ -63,6 +63,12 @@
 # being used, and consequently no CPUs, but rather various types of
 # testers and traffic generators.
 def addNoISAOptions(parser):
+    # Check for extra nvmain configuration override options
+    for arg in sys.argv:
+        if arg[:9] == "--nvmain-":
+            parser.add_option(arg, type="string", default="NULL",
+                       help="Set NVMain configuration value for a parameter")
+
     parser.add_option("-n", "--num-cpus", type="int", default=1)
     parser.add_option("--sys-voltage", action="store", type="string",
                       default='1.0V',

只需在gem5/configs/common/Options.py的第82行之后,加入如下三行

for arg in sys.argv:
    if arg[:9] == "--nvmain-":
        parser.add_option(arg, type="string", default="NULL", help="Set NVMain configuration value for a parameter")
補(bǔ)丁之后

NVMain和Gem5混合編譯

  • 編譯命令
scons EXTRAS=../nvmain build/X86/gem5.opt
  • 測試,其中混合編譯成功的一個重要標(biāo)志是可以使用--mem-type=NVMainMemory參數(shù)來指定主存為NVM
M5_PATH=/home/hsc/fs-image ./build/X86/gem5.opt configs/example/se.py -c tests/test-progs/hello/bin/x86/linux/hello --caches --l2cache --mem-type=NVMainMemory --nvmain-config=../nvmain/Config/PCM_ISSCC_2012_4GB.config
混合測試結(jié)果

PARSEC 測試

我參考的資料是: pfzuo.github.io/2016/06/06/Configure-and-run-parsec-2.1-benchmark-in-GEM5/

這一層我的目錄結(jié)構(gòu)為

+-- fs-image/
|      +-- disks/
|      |     +-- linux-bigswap2.img
|      |     +-- x86root-parsec.img
|      +-- binaries/
|      |     +-- x86_64-vmlinux-2.6.28.4-smp
|      |     +-- tsb_osfpal
+-- benchmark
|      +--  TR-09-32-parsec-2.1-alpha-files  
  1. 下載kernel文件,復(fù)制到fs-image/binaries目錄下
wget http://www.cs.utexas.edu/~parsec_m5/x86_64-vmlinux-2.6.28.4-smp
  1. 下載PARSEC對應(yīng)的PAL code文件, 復(fù)制到fs-image/binaries目錄下
wget http://www.cs.utexas.edu/~parsec_m5/tsb_osfpal
  1. 下載PARSEC-2.1 Disk Image并解壓到fs-image/disks目錄中
wget http://www.cs.utexas.edu/~parsec_m5/x86root-parsec.img.bz2

bzip2 -d x86root-parsec.img.bz2
  1. 下載PARSEC script生成包轴术,并解壓到benchmark目錄下
wget http://www.cs.utexas.edu/~parsec_m5/TR-09-32-parsec-2.1-alpha-files.tar.gz
tar zxvf TR-09-32-parsec-2.1-alpha-files.tar.gz
  1. 生成script
 ./writescripts.pl <benchmark> <nthreads>

其中benchmark包含如下

 blackscholes
 bodytrack
 canneal
 dedup
 facesim
 ferret
 fluidanimate
 freqmine
 streamcluster
 swaptions
 vips
 x264
 rtview

我生成的腳本是blackscholes,利用gem5進(jìn)行全系統(tǒng)測試

./writescripts.pl blackscholes 4
  1. 運(yùn)行生成的腳本
M5_PATH=/home/hsc/fs-image ./build/X86/gem5.opt ./configs/example/fs.py -n 2 --script=../benchmark/TR-09-32-parsec-2.1-alpha-files/blackscholes_4c_test.rcS --disk-image x86root-parsec.img --kernel x86_64-vmlinux-2.6.28.4-smp
  1. 連接測試
telnet localhost 3456
PARSEC測試結(jié)果

FPC壓縮方案

在修改NVMain源代碼實現(xiàn)FPC壓縮方案時,我走了不少的彎路.一開始看了實驗推薦的一篇關(guān)于FPC的論文,但是確實看的特別懵,感覺不是很清楚作者壓縮的思路.究其原因,作者沒有在論文中給出一個具體的實例,導(dǎo)致理解起來有些困難.在此,我還得特別感謝坐我旁邊的魏博士,是他推薦了一篇更新的關(guān)于FPC的論文,并且論文中給出了詳細(xì)的實例,便于理解.并給我梳理NVMain的一個整體框架,使我能夠快速找到修改的位置.

參考論文,推薦重點(diǎn)看第二篇

修改位置

打了箭頭的位置,即為修改的位置,其中FPCompress為自己創(chuàng)建的文件夾,并新建的類,及其實現(xiàn).

+-- nvmain/
|     +-- DataEncoders/
|     |        +-- FlipNWrite/
|     |        |       +--  FlipNWrite.h
|     |        |       +--  FlipNWrite.cpp
|     |        |       +--  SConscript
|     |        +-- FPCompress/                  <-  
|     |        |       +--  FPCompress.h        <-
|     |        |       +--  FPCompress.cpp      <-
|     |        |       +--  SConsript           <-
|     |        +-- DataEncoderFactory.cpp       <- 

其實整體的思路可以理解為,要寫入的數(shù)據(jù),其中默認(rèn)有一種編碼方式,即不進(jìn)行任何操作,而FlipNWrite為另一種編碼方式,我們只需要模仿FlipNWrite實現(xiàn)自己的編碼方案,在編碼方式中實現(xiàn)自己的FPC壓縮,就可以相當(dāng)于攔截寫入的數(shù)據(jù),并替換成自己的壓縮后的數(shù)據(jù),成功寫入.

/* FPCompress.h
 * Author: hsc
 * date: 2019-10-9
*/

#ifndef __NVMAIN_FPCOMPRESS_H__
#define __NVMAIN_FPCOMPRESS_H__

#include "src/DataEncoder.h"

namespace NVM {

class FPCompress : public DataEncoder {
  public:
    FPCompress();
    ~FPCompress();

    void SetConfig( Config *config, bool createChildren = true );

    ncycle_t Read( NVMainRequest *request );
    ncycle_t Write( NVMainRequest *request );

    void RegisterStats( );
  
  private:
    uint64_t p0; /* pattern zero */
    uint64_t p1;
    uint64_t p2;
    uint64_t p3;
    uint64_t p4;
    uint64_t p5;
    uint64_t p6;
    uint64_t p7; /* pattern seven */

    void set_data( uint8_t *address, uint64_t index, uint64_t data);
};

/* zero run */
static inline bool pattern_zero(int64_t x){
  return x == 0;
}

/* 8bit sign extended */
static inline bool pattern_one(int64_t x){
  return (x >> 7) == -1 || (x >> 7) == 0;
}

/* 16 bit sign extended */
static inline bool pattern_two(int64_t x){
  return (x >> 15) == -1 || (x >> 15) == 0;
}

/* halfword sign extended */
static inline bool pattern_three(int64_t x){
  return (x >> 31) == -1 || (x >> 31) == 0;
}

/* halfword padded with a zero halfword */
static inline bool pattern_four(int64_t x){
  return (x << 32) == 0;
}

/* two half words, each a byte sign-extended */
static inline bool pattern_five(int64_t x){
  int32_t high = static_cast<int32_t>(x >> 32);
  int32_t low  = static_cast<int32_t>(x & 0xffffffff);

  bool high_valid = (high >> 15) == -1 || (high >> 15) == 0;
  bool low_valid  = (low >> 15) == -1 || (low >> 15) == 0;

  return high_valid && low_valid;
}

/* word consisting of repeated bytes */
static inline bool pattern_six(int64_t x){
  int64_t total = 0;
  
  
  
int64_t pattern_byte = x & 0xffff;
  
  total |= pattern_byte;
  total <<= 16;

  total |= pattern_byte;
  total <<= 16;

  total |= pattern_byte;
  total <<= 16;

  total |= pattern_byte;
  total <<= 16;

  return total == x;
}

};

#endif

其中,我覺得比較重要的是實現(xiàn)Write, RegisterStats這兩個函數(shù),一個用來實現(xiàn)壓縮方案,一個用來打印各種數(shù)據(jù)模式的統(tǒng)計量.在各種模式的判別中,一開始我是讀的uint64_t無符號類型的,發(fā)現(xiàn)這樣判斷實現(xiàn)的不是很優(yōu)雅,后來換成了帶符號數(shù)據(jù)int64_t.

/* FPCompress.cpp
 * Author : hsc
 * date: 2019-10-9
*/

#include "DataEncoders/FPCompress/FPCompress.h"

using namespace NVM;

FPCompress::FPCompress()
{
    /* clear statistics */
    p0 = 0;
    p1 = 0;
    p2 = 0;
    p3 = 0;
    p4 = 0;
    p5 = 0;
    p6 = 0;
    p7 = 0;
}

FPCompress::~FPCompress()
{
    /*
     * nothing to do here
     */
}

void FPCompress::SetConfig( Config *config, bool /*createChildren*/ )
{
    Params *params = new Params( );
    params->SetParams( config );
    SetParams( params );
}

void FPCompress::RegisterStats()
{
    AddStat(p0);
    AddStat(p1);
    AddStat(p2);
    AddStat(p3);
    AddStat(p4);
    AddStat(p5);
    AddStat(p6);
    AddStat(p7);
}

ncycle_t FPCompress::Read( NVMainRequest *request )
{
   ncycle_t rv = 0;

    // TODO: Add some energy here

    return rv; 
}

ncycle_t FPCompress::Write( NVMainRequest *request ){
    NVMDataBlock& newData = request->data;
    NVMDataBlock& oldData = request->oldData;

    int64_t *ptr = (int64_t*)(newData.rawData);
    
    uint64_t offset = 0, size =  0, encode_data = 0;

    while(size < newData.GetSize()){
        int64_t dword = *ptr;

        if(pattern_zero(dword)){
            encode_data = 0;
            set_data(oldData.rawData, offset,  encode_data);
            p0++; /* zero statistics */
            offset += 3;
        }else if(pattern_one(dword)){
            encode_data = dword & 0xff;
            encode_data |= (0x1 << 8);
            set_data(oldData.rawData, offset,  encode_data);
            p1++;
            offset += 11;
        }else if(pattern_two(dword)){
            encode_data = dword & 0xffff;
            encode_data |= (0x2 << 16);
            set_data(oldData.rawData, offset,  encode_data);
            p2++;
            offset += 19;
        }else if(pattern_three(dword)){
            encode_data = dword & 0xffffffff;
            encode_data |= ((uint64_t)0x3 << 32);
            set_data(oldData.rawData, offset,  encode_data);
            p3++;
            offset += 35;
        }else if(pattern_four(dword)){
            encode_data = dword >> 32;
            encode_data |= ((uint64_t)0x4 << 32);
            set_data(oldData.rawData, offset,  encode_data);
            p4++;
            offset += 35;
        }else if(pattern_five(dword)){
            encode_data = (dword & 0xffff);
            encode_data |= ((dword >> 32) & 0xffff) << 16;
            encode_data |= ((uint64_t)0x5 << 32);
            set_data(oldData.rawData, offset,  encode_data);
            p5++;
            offset += 35;
        }else if(pattern_six(dword)){
            encode_data = dword && 0xffff;
            encode_data |= (0x6 << 16);
            set_data(oldData.rawData, offset,  encode_data);
            p6++;
            offset += 19;
        }else {
            encode_data = dword;
            set_data(oldData.rawData, offset, (encode_data >> 32) & 0xffffffff);
            set_data(oldData.rawData, offset + 32, (encode_data & 0xffffffff));
            p7++;
            offset += 64;
        }

        /* pointer to next dword */
        ptr++;

        size += 8;
    }
}

void FPCompress::set_data( uint8_t *address, uint64_t offset, uint64_t data)
{
    uint64_t *ptr = (uint64_t*)(address + (offset / 8));
    uint64_t new_data = *ptr;
    uint64_t len = offset - (offset / 8) * 8;
    new_data = (new_data << (64 - len)) >> (64 - len);
    new_data |= (data << len);
    *ptr = new_data;
}

思路

  • request中包含的是uint8_t類型的指針地址,將其強(qiáng)制轉(zhuǎn)換為int64_t類型的指針,即可讀取64bit數(shù)據(jù)
  • 讀出數(shù)據(jù),判斷是8種數(shù)據(jù)模式中的哪一種數(shù)據(jù)模式,然后構(gòu)造壓縮后的數(shù)據(jù)
  • 因為壓縮的數(shù)據(jù),特別不規(guī)整,有3bit, 11bit, 19bit, 35bit不等.比如我當(dāng)前要寫3bit,這3bit可能還跨了不同的字節(jié),因此導(dǎo)致情況特別復(fù)雜.
  • 我的方法是計算寫入位置最近的以字節(jié)為單位對齊的偏移地址, 從該偏移地址中讀取64bit數(shù)據(jù),這樣的話,這64bit中最多包含7位之前寫入的數(shù)據(jù).我們通過將之前寫入的數(shù)據(jù),和壓縮后的數(shù)據(jù)進(jìn)行合并成一個新的64bit數(shù)據(jù),然后重新寫入.
數(shù)據(jù)寫入

DataEncoderFactory.cpp中加入自己的編碼方式

// DataEncoderFactory.cpp
DataEncoder *DataEncoderFactory::CreateDataEncoder( std::string encoderName )
{
    DataEncoder *encoder = NULL;

    if( encoderName == "default" ) encoder = new DataEncoder( );
    else if( encoderName == "FlipNWrite" ) encoder = new FlipNWrite( );
    else if( encoderName == "FPCompress") encoder = new FPCompress( );

    return encoder;
}

為了使用我們的編碼方式,我們需要在配置文件中指定編碼方式,這里我選用的配置文件為nvmain/Config/PCM_MLC_example.config,在文件的末尾,加入如下內(nèi)容:

DataEncoder FPCompress

編譯

scons --build-type=fast

測試

利用我們剛剛修改的配置文件nvmain/Config/PCM_MLC_example.config進(jìn)行測試

./nvmain.fast Config/PCM_MLC_example.config Tests/Traces/hello_world.nvt 1000000

數(shù)據(jù)寫入統(tǒng)計

我是若木,倘若大家有更好的實現(xiàn)方式,記得與我聯(lián)系,也可參觀一下我的博客.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末难衰,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子逗栽,更是在濱河造成了極大的恐慌盖袭,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件彼宠,死亡現(xiàn)場離奇詭異鳄虱,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)凭峡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進(jìn)店門拙已,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人想罕,你說我怎么就攤上這事悠栓。” “怎么了按价?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵惭适,是天一觀的道長。 經(jīng)常有香客問我楼镐,道長癞志,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任框产,我火速辦了婚禮凄杯,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘秉宿。我一直安慰自己戒突,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布描睦。 她就那樣靜靜地躺著膊存,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上隔崎,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天今艺,我揣著相機(jī)與錄音,去河邊找鬼爵卒。 笑死虚缎,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的钓株。 我是一名探鬼主播实牡,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼轴合!你這毒婦竟也來了铲掐?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤值桩,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后豪椿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體奔坟,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年搭盾,在試婚紗的時候發(fā)現(xiàn)自己被綠了咳秉。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡鸯隅,死狀恐怖澜建,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蝌以,我是刑警寧澤炕舵,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站跟畅,受9級特大地震影響咽筋,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜徊件,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一奸攻、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧虱痕,春花似錦睹耐、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春捎迫,著一層夾襖步出監(jiān)牢的瞬間晃酒,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工窄绒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留贝次,地道東北人。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓彰导,卻偏偏與公主長得像蛔翅,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子位谋,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評論 2 355

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