Select_poll_epoll詳解

參考鏈接

  1. epoll簡介及觸發(fā)模式(accept、read、send)
  2. epoll內(nèi)核源碼詳解+自己總結(jié)的流程
  3. linux man page

epoll函數(shù)

注意: epoll不屬于任何namespace。

#include <sys/epoll.h>

int epoll_create(int size);  // return epollfd, 失敗return -1

/*
op:
EPOLL_CTL_ADD
EPOLL_CTL_MOD
EPOLL_CTL_DEL 如果是delete的話, epoll_ctl的最后一個參數(shù)event可以是NULL
*/
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);  // 成功return0淤堵, 失敗return -1
int epoll_wait(int epfd, struct epoll_event *events,
                int maxevents, int timeout);  // 成功return nready. 失敗return -1

//epoll_event
/*
其實這個epoll_data只是給用戶自行使用的,epoll不關(guān)心里面的內(nèi)容顷扩。 這個dta回隨著epoll_data 返回的epoll_event一并返回
*/
typedef union epoll_data {
    void        *ptr;
    int          fd;
    uint32_t     u32;
    uint64_t     u64;
} epoll_data_t;

struct epoll_event {
    uint32_t     events;      /* Epoll events */
    epoll_data_t data;        /* User data variable */
};

close

其實在外面關(guān)閉一個fd之后拐邪,就可以不用再在epoll list里面刪除了,但是為了安全起見隘截,還是用EPOLL_CTL_DEL刪掉吧扎阶。詳情可以看 epoll(7) man page FAQ。

epoll event

  1. EPOLLIN :表示對應(yīng)的文件描述符可以讀(包括對端SOCKET正常關(guān)閉)婶芭;
  2. EPOLLOUT:表示對應(yīng)的文件描述符可以寫东臀;
  3. EPOLLPRI:表示對應(yīng)的文件描述符有緊急的數(shù)據(jù)可讀(這里應(yīng)該表示有帶外數(shù)據(jù)到來);
  4. EPOLLERR:表示對應(yīng)的文件描述符發(fā)生錯誤犀农;
  5. EPOLLHUP:表示對應(yīng)的文件描述符被掛斷惰赋;
  6. EPOLLET: 將EPOLL設(shè)為邊緣觸發(fā)(Edge Triggered)模式,這是相對于水平觸發(fā)(Level Triggered)來說的呵哨。
  7. EPOLLONESHOT:只監(jiān)聽一次事件赁濒,當(dāng)監(jiān)聽完這次事件之后贵扰,如果還需要繼續(xù)監(jiān)聽這個socket的話,需要再次把這個socket加入到EPOLL隊列里

EL/LT

有關(guān)ET/LT 阻塞/非阻塞的操作流部,網(wǎng)絡(luò)上基本都是錯的戚绕,只要你安排的好,既可以用阻塞枝冀,也可以用非阻塞舞丛。(linux man page上也讓你用阻塞)

ET Edge Trigger 邊沿觸發(fā)工作模式

  1. 必須使用非阻塞 工作模式,因為在循環(huán)調(diào)用epoll_wait的時候果漾,有可能某個句柄已知會ready, 如果用阻塞操作球切,會導(dǎo)致一個文件句柄的阻塞操作把多個文件描述符餓死。
    1. 基于非阻塞文件句柄
    2. 只有當(dāng)read(2)或者write(2)返回EAGAIN時才需要掛起绒障,等待(退出read/write返回epoll_wait)吨凑。但這并不是說每次read()時都需要循環(huán)讀,直到讀到產(chǎn)生一個EAGAIN才認(rèn)為此次事件處理完成户辱,當(dāng)read()返回的讀到的數(shù)據(jù)長度小于請求的數(shù)據(jù)長度時(即小于sizeof(buf))鸵钝,就可以確定此時緩沖中已沒有數(shù)據(jù)了,也就可以認(rèn)為此事讀事件已處理完成庐镐。
    3. 阻塞IO的事件處理原則:
      1. recv() > 0:(并且小于請求的數(shù)據(jù)長度sizeof(buf))恩商, 表示接收數(shù)據(jù)完畢,返回值即是接收到的字節(jié)數(shù)必逆。
      2. recv() == 0: 表示鏈接已經(jīng)正常斷開怠堪,這個時候就可以把fd關(guān)掉,從epoll里面移除了
      3. recv() < 0 && errno == EAGAIN: 表示recv操作還未完成
      4. recv() < 0 && errno != EAGAIN: 表示操作遇到系統(tǒng)errno
  2. 邊緣觸發(fā)但是這種模式下在讀數(shù)據(jù)的時候一定要注意名眉,因為如果一次可寫事件我們沒有把數(shù)據(jù)讀完粟矿,如果沒有讀完,在socket沒有新的數(shù)據(jù)可讀時epoll就不回返回了损拢,只有在新的數(shù)據(jù)到來時陌粹,我們才能讀取到上次沒有讀完的數(shù)據(jù)。最差的情況是client在發(fā)送的n個byte之后已經(jīng)關(guān)閉了探橱,但是epoll由于接收緩沖區(qū)沒有清空申屹,這個fd在服務(wù)端并不會關(guān)掉绘证。
  3. 使用ET模式隧膏,就算接收緩沖區(qū)里的數(shù)據(jù)沒有讀完,如果再接收到新的數(shù)據(jù)嚷那, epoll_wait 還是會觸發(fā)可讀事件的胞枕。
  4. 設(shè)置為EPOLLET之后仍然會對同一事件多次觸發(fā)的原因:
    1. 接收緩沖區(qū)過小,無法容納所有發(fā)送過來的數(shù)據(jù)(這個我自己沒有復(fù)現(xiàn)出來)
    2. 用EPOLL_CTL_MOD更改了epollevent魏宽,會重置之前的觸發(fā)

LT Level Trigger 水平觸發(fā)工作模式

  1. poll(), select() 都是水平觸發(fā)
  2. 如果我們用水平觸發(fā)不用擔(dān)心數(shù)據(jù)有沒有讀完因為下次epoll返回時腐泻,沒有讀完的socket依然會被返回
  3. 但是要注意這種模式下的寫事件决乎,因為是水平觸發(fā),每次socket可寫時epoll都會返回派桩,當(dāng)我們寫的數(shù)據(jù)包過大時构诚,一次寫不完,要多次才能寫完或者每次socket寫都寫一個很小的數(shù)據(jù)包時铆惑,每次寫都會被epoll檢測到范嘱,因此長期關(guān)注socket寫事件會無故cpu消耗過大甚至導(dǎo)致cpu跑滿,所以在水平觸發(fā)模式下我們一般不關(guān)注socket可寫事件而是通過調(diào)用socket write或者send api函數(shù)來寫socket
  4. 但是如果使用LT模式员魏,每次讀事件只要調(diào)用一次recv()就可以了丑蛤。不用像ET一樣反復(fù)調(diào)用recv()直到返回EAGAIN,對于追求低延遲的系統(tǒng)調(diào)用來說撕阎,這么做是搞笑的受裹,并且也不用擔(dān)心因為某個連接上數(shù)據(jù)量過大導(dǎo)致影響其他連接處理消息。
  5. 我們可以看到這種模式在效率上是沒有邊緣觸發(fā)高的虏束,因為每個socket讀或者寫可能被返回兩次甚至多次

epoll 源碼解析

https://blog.csdn.net/wangyin159/article/details/48895287

epoll_wait

  1. 檢查MAXEXENT參數(shù)
  2. 用access_ok() 檢查event指針是否可寫碧聪,如果這個指針是空指針或者指向內(nèi)核態(tài)的指針,那么會設(shè)置errno EFAULT今野。
    1. Just because a pointer was supplied by userspace doesn't mean that it's definitely a userspace pointer - in many cases "kernel pointer" simply means that it's pointing within a particular region of the virtual address space.https://stackoverflow.com/questions/12357752/what-is-the-point-of-using-the-linux-macro-access-ok
  3. 獲取epfd對應(yīng)的eventpoll文件實例燃逻,如果取不到,errno:EBADF
  4. 檢查eventpoll文件是不是真的是一個epoll文件坑律, 如果不是說值errno EINVAL
  5. 其實epoll_wait 中如果出錯了岩梳,那么基本上應(yīng)該是程序本身的問題,比如陷入死循環(huán)之類
  6. 調(diào)用ep_epoll函數(shù)晃择,這個函數(shù)在做一些配置之后就會主動讓出處理器冀值,進(jìn)入睡眠狀態(tài),等待文件就緒(回調(diào)函數(shù)喚醒本進(jìn)程)或者超時或者信號中斷
  • 缺省的工作模式

一道騰訊后臺開發(fā)面試題

Q:使用Linux epoll模型宫屠,水平(LT)觸發(fā)模式列疗,當(dāng)socket可寫時,會不停的觸發(fā)socket可寫的事件浪蹂,如何處理抵栈?

  1. 第一種最普遍的方式:
    • 需要向socket寫數(shù)據(jù)的時候才把socket加入epoll,等待可寫事件坤次。接受到可寫事件后古劲,調(diào)用write或者send發(fā)送數(shù)據(jù)。當(dāng)所有數(shù)據(jù)都寫完后缰猴,把socket移出epoll(用EPOLLONESHOT也行)产艾。
    • 這種方式的缺點是,即使發(fā)送很少的數(shù)據(jù),也要把socket加入epoll闷堡,寫完后在移出epoll隘膘,有一定操作代價。
  2. 一種改進(jìn)的方式:
    • 開始不把socket加入epoll杠览,需要向socket寫數(shù)據(jù)的時候弯菊,直接調(diào)用write或者send發(fā)送數(shù)據(jù)。如果返回EAGAIN(緩沖區(qū)滿了踱阿,后面還需要繼續(xù)發(fā))误续,把socket加入epoll,在epoll的驅(qū)動下寫數(shù)據(jù)扫茅,全部數(shù)據(jù)發(fā)送完畢后蹋嵌,再移出epoll。
    • 這種方式的優(yōu)點是:數(shù)據(jù)不多的時候可以避免epoll的事件處理葫隙,提高效率栽烂。

ET/LT 比較

  1. 因為ET要基于非阻塞IO, LT在讀寫的時候不必等待EAGAIN的出現(xiàn)恋脚,可以節(jié)省系統(tǒng)調(diào)用次數(shù)腺办,降低延遲
  2. ET可以保證每次只觸發(fā)一個

epoll 優(yōu)點

  1. 對應(yīng)select()的缺點, epoll都有解決的方法

    1. 每次調(diào)用select糟描,都需要把fd集合從用戶態(tài)拷貝到內(nèi)核態(tài)怀喉,這個開銷在fd很多時會很大
      • 使用epoll_ctl()函數(shù),只有在注冊船响、修改躬拢、刪除的時候才會對內(nèi)核進(jìn)行操作。
    2. 同時每次調(diào)用select都需要在內(nèi)核遍歷傳遞進(jìn)來的所有fd见间,這個開銷在fd很多時也很大
      • epoll的解決方案不像select或poll一樣每次都把current輪流加入fd對應(yīng)的設(shè)備等待隊列中聊闯,而只在epoll_ctl時把current掛一遍(這一遍必不可少)并為每個fd指定一個回調(diào)函數(shù),當(dāng)設(shè)備就緒米诉,喚醒等待隊列上的等待者時菱蔬,就會調(diào)用這個回調(diào)函數(shù),而這個回調(diào)函數(shù)會把就緒的fd加入一個就緒鏈表)史侣。epoll_wait的工作實際上就是在這個就緒鏈表中查看有沒有就緒的fd(利用schedule_timeout()實現(xiàn)睡一會拴泌,判斷一會的效果,和select實現(xiàn)中的第7步是類似的)
    3. select支持的文件描述符數(shù)量太小了惊橱,默認(rèn)是1024
      • epoll沒有這個限制蚪腐,它所支持的FD上限是最大可以打開文件的數(shù)目,這個數(shù)字一般遠(yuǎn)大于2048,舉個例子,我的1GB內(nèi)存阿里云ECS是999999李皇,具體數(shù)目可以cat /proc/sys/fs/file-max察看,一般來說這個數(shù)目和系統(tǒng)內(nèi)存關(guān)系很大削茁。
  2. poll每次返回整個文件描述符數(shù)組宙枷, 用戶需要遍歷數(shù)組已找到哪些文件描述符上有IO事件掉房。 而epoll_wait(2)返回的是活動fd的列表茧跋,需要遍歷的數(shù)組通常會小很多,在并發(fā)連接數(shù)較大而活動連接比例不高時卓囚,epoll(4)比epoll(2)更高效瘾杭。

epoll 源碼解讀

當(dāng)某一進(jìn)程調(diào)用epoll_create方法時,Linux內(nèi)核會創(chuàng)建一個eventpoll結(jié)構(gòu)體哪亿,這個結(jié)構(gòu)體中有兩個成員與epoll的使用方式密切相關(guān)粥烁。eventpoll結(jié)構(gòu)體如下所示:

struct eventpoll{
    ....
    /*紅黑樹的根節(jié)點,這顆樹中存儲著所有添加到epoll中的需要監(jiān)控的事件*/
    struct rb_root  rbr;
    /*雙鏈表中則存放著將要通過epoll_wait返回給用戶的滿足條件的事件*/
    struct list_head rdlist;
    ....
    };

每一個epoll對象都有一個獨立的eventpoll結(jié)構(gòu)體蝇棉,用于存放通過epoll_ctl方法向epoll對象中添加進(jìn)來的事件讨阻。這些事件都會掛載在紅黑樹中,如此篡殷,重復(fù)添加的事件就可以通過紅黑樹而高效的識別出來(紅黑樹的插入時間效率是lgn钝吮,其中n為樹的高度)。

而所有添加到epoll中的事件都會與設(shè)備(網(wǎng)卡)驅(qū)動程序建立回調(diào)關(guān)系板辽,也就是說奇瘦,當(dāng)相應(yīng)的事件發(fā)生時會調(diào)用這個回調(diào)方法。這個回調(diào)方法在內(nèi)核中叫ep_poll_callback,它會將發(fā)生的事件添加到rdlist雙鏈表中劲弦。

struct epitem{
    struct rb_node  rbn;//紅黑樹節(jié)點
    struct list_head    rdllink;//雙向鏈表節(jié)點
    struct epoll_filefd  ffd;  //事件句柄信息
    struct eventpoll *ep;    //指向其所屬的eventpoll對象
    struct epoll_event event; //期待發(fā)生的事件類型
    }

當(dāng)調(diào)用epoll_wait檢查是否有事件發(fā)生時耳标,只需要檢查eventpoll對象中的rdlist雙鏈表中是否有epitem元素即可。如果rdlist不為空邑跪,則把發(fā)生的事件復(fù)制到用戶態(tài)次坡,同時將事件數(shù)量返回給用戶。

epoll數(shù)據(jù)結(jié)構(gòu)

select()

select()簡介

  1. select()函數(shù)是阻塞的画畅, 只有某些端口狀態(tài)轉(zhuǎn)換了或者達(dá)到timeout才會返回
  2. 該函數(shù)可以允許進(jìn)程指示等待多個事件中任何一個的發(fā)生
  3. select(), poll() 都是水平觸發(fā)

為什么需要select()?

  1. 多路復(fù)用io mutiplexing
    1. 如果不采用多路復(fù)用贸毕,要么使用阻塞IO(會使線程長時間處于阻塞狀態(tài),無法執(zhí)行任何計算或者響應(yīng)任何網(wǎng)絡(luò)請求),要么使用非阻塞IO:(要用while循環(huán)調(diào)用recv函數(shù)夜赵,大幅占用CPU資源)明棍, 復(fù)用的優(yōu)勢在于可以同時處理多個連接

select()函數(shù)

#include <sys/select.h>
#include <sys/time.h>

int select(int maxfdp1,fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timeval *timeout);

返回: 若有描述符就緒,則返回就緒描述符的數(shù)量寇僧,若超時則為0摊腋, 若出錯則為1

1. timeout-->timeval

struct timeval {
  long tv_sec;  // seconds
  log tv_usec;  // microseconds
}
  • 用于指定timeout的秒數(shù)和微秒數(shù)
  • 如果輸入為0,那么select函數(shù)會一直等下去一直到某個描述符準(zhǔn)備好
  • 如果輸入這個參數(shù)嘁傀,那么最長等待時間就確定了
  • 如果輸入這個結(jié)構(gòu)兴蒸,但是其中的兩個值為0,那么就不等待-->輪詢機(jī)制
延伸 gettimeofday()
  • 用gettimeofday() 可以獲得微秒(us)級別的時間细办。
  • 會把目前的時間tv所指的結(jié)構(gòu)返回橙凳,當(dāng)?shù)貢r區(qū)的信息則放到tz所指的結(jié)構(gòu)中蕾殴。
  • 1970年1月1日到現(xiàn)在的時間
  • 調(diào)用兩次gettimeofday(), 前后做減法,從而達(dá)到計算時間的目的岛啸。
#include <sys/time.h>
int gettimeofday(struct timeval *tv,struct timezone *tz);

2. readset, writeset, exceptset

#include <sys/select.h>

struct fd_set myset;
//四個相關(guān)的宏函數(shù)

void FD_ZERO(fd_set *fdset);  // clean all bits at fdset
void FD_SET(int fd, fd_set *fdset);  // turn on the bit for fd in fdset
void FD_CLR(int fd, fd_set *fdset);  // turn on the bit for fd in fdset
void FD_ISSET(int fd, fd_set *fdset);  //is the bit for fd on in fdset? 如果set了钓觉,返回1
  1. fd_set 每一位表示一個fd, set其中的某一位就表示要監(jiān)視某個fd.
  2. 指針輸入, 輸入的時候把我們所關(guān)心的fd置為1. 返回時坚踩,他將指示哪些描述符已經(jīng)就緒了荡灾。因此,每次重新調(diào)用select時瞬铸,我們都需要再次把所有我們關(guān)心的描述符置為1批幌。

3. maxfdp1

  1. maxfdp1 = 最大描述符+1
  2. 最大描述符系統(tǒng)內(nèi)是有定義的 FD_SETSIZE

例子

select\strcliselect01.c

void
str_cli(FILE *fp, int sockfd)
{
  int maxfdp1;
  fd_set  rset;
  char  sendline[MAXLINE], recvline[MAXLINE];

  FD_ZERO(&rset);
  for ( ; ; ) {
    FD_SET(fileno(fp), &rset);
    FD_SET(sockfd, &rset);
    maxfdp1 = max(fileno(fp), sockfd) + 1;
    Select(maxfdp1, &rset, NULL, NULL, NULL);

    if (FD_ISSET(sockfd, &rset)) {  /* socket is readable */
      if (Readline(sockfd, recvline, MAXLINE) == 0)
        err_quit("str_cli: server terminated prematurely");
      Fputs(recvline, stdout);
    }

    if (FD_ISSET(fileno(fp), &rset)) {  /* input is readable */
      if (Fgets(sendline, MAXLINE, fp) == NULL)
        return;     /* all done */
      Writen(sockfd, sendline, strlen(sendline));
    }
  }
}

select 缺點

  1. 每次調(diào)用select,都需要把fd集合從用戶態(tài)拷貝到內(nèi)核態(tài)嗓节,這個開銷在fd很多時會很大
  2. 同時每次調(diào)用select都需要在內(nèi)核遍歷傳遞進(jìn)來的所有fd荧缘,這個開銷在fd很多時也很大
  3. select支持的文件描述符數(shù)量太小了,默認(rèn)是1024

select() 文件描述符上限

這個問題的關(guān)鍵其實要先理解select關(guān)于文件描述符上限的原因

  1. linux系統(tǒng)本身就有文件描述符上限拦宣,文件描述符的建立會連帶建立很多其它表項截粗,具體可以搜索文件描述符的詳解,也就是說文件描述符一定會占用資源恢着,那在有限的硬件條件下桐愉,文件描述符必定會有上限,我在ubuntu14.04的ECS里通過
cat /proc/sys/fs/file-max //結(jié)果99999
  1. 進(jìn)程文件描述符上限user limit中nofile的soft limit掰派,實際上這是單個用戶的文件描述符上限从诲,通過
ulimit -n //結(jié)果65535

soft limit可以修改,但是不能超過hard limit

ulimit -Hn //結(jié)果65535
  1. select函數(shù)本身限制靡羡,主要是頭文件中FD_SETSIZE的大小系洛,一般來說是1024,這就限定了select函數(shù)中的文件描述符上限略步,當(dāng)然可以做修改描扯,但是需要重新編譯內(nèi)核,而且效果由于select的實現(xiàn)機(jī)制趟薄,會比較差

poll()

#include <poll.h>
#include <limits.h>  /* for OPEN_MAX */ // 描述了poll的最大數(shù)量

int poll(struct pollfd *fdarray, unsigned long nfds, int timeout);

返回: 若有描述符就緒绽诚,則返回就緒描述符的數(shù)量,若超時則為0杭煎, 若出錯則為1

fdarray

指向一個數(shù)組結(jié)構(gòu)第一個元素的指針恩够,沒一個元素都是一個pollfd結(jié)構(gòu),使用這個結(jié)構(gòu)羡铲,避免了select中使用一個參數(shù)既表示我們關(guān)心的值蜂桶,又表示結(jié)果。

struct pollfd {
  int fd;  // 描述符
  short events;  // 我們關(guān)心的狀態(tài)
  short revents;  // 返回的結(jié)果
}

nfds

第一個參數(shù)中的數(shù)組元素的個數(shù)

timeout

timeout 說明
INFTIM (直接填-1) 永遠(yuǎn)等待
0 立即返回也切,不阻塞進(jìn)程扑媚,改成0會打滿一個核
> 0 等待指定的毫秒數(shù)

poll() 文件描述符上限

poll雖然不像select一樣受到select() 中FD_SETSIZE 的限制腰湾,但是仍然受到ulimit中設(shè)定的一個進(jìn)程所能打開的最大文件描述符的限制

ulimit -n //結(jié)果65535

poll()/select()的區(qū)別

  1. poll() 解決了select文件描述符最大只有1024的限制
  2. select和poll都需要自己不斷輪詢所有fd集合,直到設(shè)備就緒疆股,(首先把所有的fd掛到對應(yīng)的等待隊列上费坊,然后睡眠,在設(shè)備收到一條消息或者填寫完文件數(shù)據(jù)之后押桃,會喚醒設(shè)備等待隊列上的進(jìn)程葵萎,進(jìn)程會再次掃描整個注冊文件描述符的集合导犹,并返回就緒文件描述符的數(shù)目給用戶)期間可能要睡眠和喚醒多次交替(存疑),雖然epoll也需要喚醒唱凯,但是喚醒之后只需要檢測就緒鏈表是否為空就行了。

select()/poll()對多線程的支持

如果一個線程正在select()/poll()谎痢,這時另一個線程向fd組里增加fd磕昼,那么這個新增的fd是無法被poll到的,只有等當(dāng)前的polltimeout了节猿,下一次poll才能poll到票从。但是epoll沒有這個限制,只要加進(jìn)去了就能poll到滨嘱。

  1. Bug 43072 (epoll_threads) - epoll & threads
  2. Executing epoll_ctl in one thread while the other thread is in the middle of epoll_wait
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末峰鄙,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子太雨,更是在濱河造成了極大的恐慌吟榴,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件囊扳,死亡現(xiàn)場離奇詭異吩翻,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)锥咸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進(jìn)店門狭瞎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人搏予,你說我怎么就攤上這事熊锭。” “怎么了雪侥?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵碗殷,是天一觀的道長。 經(jīng)常有香客問我校镐,道長亿扁,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任鸟廓,我火速辦了婚禮从祝,結(jié)果婚禮上襟己,老公的妹妹穿的比我還像新娘。我一直安慰自己牍陌,他們只是感情好擎浴,可當(dāng)我...
    茶點故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著毒涧,像睡著了一般贮预。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上契讲,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天仿吞,我揣著相機(jī)與錄音,去河邊找鬼捡偏。 笑死唤冈,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的银伟。 我是一名探鬼主播你虹,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼彤避!你這毒婦竟也來了傅物?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤琉预,失蹤者是張志新(化名)和其女友劉穎董饰,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體模孩,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡尖阔,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了榨咐。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片介却。...
    茶點故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖块茁,靈堂內(nèi)的尸體忽然破棺而出齿坷,到底是詐尸還是另有隱情,我是刑警寧澤数焊,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布永淌,位于F島的核電站,受9級特大地震影響佩耳,放射性物質(zhì)發(fā)生泄漏遂蛀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一干厚、第九天 我趴在偏房一處隱蔽的房頂上張望李滴。 院中可真熱鬧螃宙,春花似錦、人聲如沸所坯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽芹助。三九已至堂湖,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間状土,已是汗流浹背无蜂。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留声诸,地道東北人酱讶。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓退盯,卻偏偏與公主長得像彼乌,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子渊迁,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,675評論 2 359

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