鴻蒙內核源碼分析(中斷管理篇) | 江湖從此不再怕中斷

關于中斷部分系列篇將用三篇詳細說明整個過程.

  • 中斷概念篇 中斷概念很多,比如中斷控制器,中斷源,中斷向量,中斷共享,中斷處理程序等等.本篇做一次整理.先了解透概念才好理解中斷過程.用海公公打比方說明白中斷各個概念.可前往查看.

  • 中斷管理篇(本篇) 從中斷初始化HalIrqInit開始,到注冊中斷的LOS_HwiCreate函數,到消費中斷函數的 HalIrqHandler,剖析鴻蒙內核實現中斷的過程,很像設計模式中的觀察者模式.

  • 中斷切換篇 用自下而上的方式,從中斷源頭純匯編代碼往上跟蹤代碼細節(jié).說清楚保存和恢復中斷現場TaskIrqContext過程.

編譯開關

//....
#define LOSCFG_ARCH_ARM_VER "armv7-a"
#define LOSCFG_ARCH_CPU "cortex-a7"
#define LOSCFG_PLATFORM "hi3516dv300"
#define LOSCFG_PLATFORM_BSP_GIC_V2 1
#define LOSCFG_PLATFORM_ROOTFS 1
#define LOSCFG_KERNEL_CPPSUPPORT 1
#define LOSCFG_HW_RANDOM_ENABLE 1
#define LOSCFG_ARCH_CORTEX_A7 1
#define LOSCFG_DRIVERS_HDF_PLATFORM_RTC 1
#define LOSCFG_DRIVERS_HDF_PLATFORM_UART 1

中斷初始化

hi3516dv300 中斷控制器選擇了 LOSCFG_PLATFORM_BSP_GIC_V2 ,對應代碼為 gic_v2.c
GIC(Generic Interrupt Controller)是ARM公司提供的一個通用的中斷控制器.
看這種代碼因為涉及硬件部分,需要對照 ARM中斷控制器 gic_v2.pdf 文檔看.可前往地址下載查看.

//硬件中斷初始化
VOID HalIrqInit(VOID)
{
    UINT32 i;

    /* set externel interrupts to be level triggered, active low. */    //將外部中斷設置為電平觸發(fā),低電平激活
    for (i = 32; i < OS_HWI_MAX_NUM; i += 16) {
        GIC_REG_32(GICD_ICFGR(i / 16)) = 0;
    }

    /* set externel interrupts to CPU 0 */  //將外部中斷設置為CPU 0
    for (i = 32; i < OS_HWI_MAX_NUM; i += 4) {
        GIC_REG_32(GICD_ITARGETSR(i / 4)) = 0x01010101;
    }

    /* set priority on all interrupts */    //設置所有中斷的優(yōu)先級
    for (i = 0; i < OS_HWI_MAX_NUM; i += 4) {
        GIC_REG_32(GICD_IPRIORITYR(i / 4)) = GICD_INT_DEF_PRI_X4;
    }

    /* disable all interrupts. */           //禁用所有中斷。
    for (i = 0; i < OS_HWI_MAX_NUM; i += 32) {
        GIC_REG_32(GICD_ICENABLER(i / 32)) = ~0;
    }

    HalIrqInitPercpu();//初始化當前CPU中斷信息

    /* enable gic distributor control */
    GIC_REG_32(GICD_CTLR) = 1; //使能分發(fā)中斷寄存器,該寄存器作用是允許給CPU發(fā)送中斷信號

#if (LOSCFG_KERNEL_SMP == YES)
    /* register inter-processor interrupt *///注冊核間中斷,啥意思?就是CPU各核直接可以發(fā)送中斷信號
    //處理器間中斷允許一個CPU向系統(tǒng)其他的CPU發(fā)送中斷信號,處理器間中斷(IPI)不是通過IRQ線傳輸的,而是作為信號直接放在連接所有CPU本地APIC的總線上。
    LOS_HwiCreate(LOS_MP_IPI_WAKEUP, 0xa0, 0, OsMpWakeHandler, 0);//注冊喚醒CPU的中斷處理函數
    LOS_HwiCreate(LOS_MP_IPI_SCHEDULE, 0xa0, 0, OsMpScheduleHandler, 0);//注冊調度CPU的中斷處理函數
    LOS_HwiCreate(LOS_MP_IPI_HALT, 0xa0, 0, OsMpScheduleHandler, 0);//注冊停止CPU的中斷處理函數
#endif
}
//給每個CPU core初始化硬件中斷
VOID HalIrqInitPercpu(VOID)
{
    /* unmask interrupts */ //取消中斷屏蔽
    GIC_REG_32(GICC_PMR) = 0xFF;

    /* enable gic cpu interface */  //啟用gic cpu接口
    GIC_REG_32(GICC_CTLR) = 1;
}

解讀

  • 上來四個循環(huán),是對中斷控制器寄存器組的初始化,也就是驅動程序,驅動程序是配置硬件寄存器的過程.寄存器分通用和專用寄存器.下圖為 gic_v2 的寄存器功能 ,這里對照代碼和datasheet重點說下中斷配置寄存器(GICD_ICFGRn)
  • 以下是GICD_ICFGRn的介紹

    The GICD_ICFGRs provide a 2-bit Int_config field for each interrupt supported by the GIC.
    This field identifies whether the corresponding interrupt is edge-triggered or level-sensitive

    GICD_ICFGRs為GIC支持的每個中斷提供一個2位配置字段。此字段標識相應的中斷是邊緣觸發(fā)的還是電平觸發(fā)的

    0xC00 - 0xCFC GICD_ICFGRn RW IMPLEMENTATION DEFINED Interrupt Configuration Registers
    #define GICD_ICFGR(n)                   (GICD_OFFSET + 0xc00 + (n) * 4) /* Interrupt Configuration Registers */     //中斷配置寄存器

如此一個32位寄存器可以記錄16個中斷的信息,這也是代碼中出現 GIC_REG_32(GICD_ICFGR(i / 16))的原因.

  • GIC-v2支持三種類型的中斷

    • PPI:私有外設中斷(Private Peripheral Interrupt),是每個CPU私有的中斷。最多支持16個PPI中斷,硬件中斷號從ID16~ID31。PPI通常會送達到指定的CPU上,應用場景有CPU本地時鐘。
    • SPI:公用外設中斷(Shared Peripheral Interrupt),最多可以支持988個外設中斷,硬件中斷號從ID32~ID1019。
    • SGI:軟件觸發(fā)中斷(Software Generated Interrupt)通常用于多核間通訊,最多支持16個SGI中斷,硬件中斷號從ID0~ID15。SGI通常在內核中被用作 IPI 中斷(inter-processor interrupts),并會送達到系統(tǒng)指定的CPU上,函數的最后就注冊了三個核間中斷的函數.
        typedef enum {//核間中斷
            LOS_MP_IPI_WAKEUP,  //喚醒CPU
            LOS_MP_IPI_SCHEDULE,//調度CPU
            LOS_MP_IPI_HALT,    //停止CPU
        } MP_IPI_TYPE;

中斷相關的結構體

size_t g_intCount[LOSCFG_KERNEL_CORE_NUM] = {0};//記錄每個CPUcore的中斷數量 
HwiHandleForm g_hwiForm[OS_HWI_MAX_NUM];//中斷注冊表 @note_why 用 form 來表示?有種寫 HTML的感覺 :P
STATIC CHAR *g_hwiFormName[OS_HWI_MAX_NUM] = {0};//記錄每個硬中斷的名稱 
STATIC UINT32 g_hwiFormCnt[OS_HWI_MAX_NUM] = {0};//記錄每個硬中斷的總數量
STATIC UINT32 g_curIrqNum = 0; //記錄當前中斷號
typedef VOID (*HWI_PROC_FUNC)(VOID); //中斷函數指針
typedef struct tagHwiHandleForm {   
    HWI_PROC_FUNC pfnHook;  //中斷處理函數
    HWI_ARG_T uwParam;      //中斷處理函數參數
    struct tagHwiHandleForm *pstNext;   //節(jié)點,指向下一個中斷,用于共享中斷的情況
} HwiHandleForm;

typedef struct tagIrqParam {    //中斷參數
    int swIrq;      //  軟件中斷
    VOID *pDevId;   //  設備ID
    const CHAR *pName;  //名稱
} HwiIrqParam;

注冊硬中斷

/******************************************************************************
 創(chuàng)建一個硬中斷
 中斷創(chuàng)建,注冊中斷號、中斷觸發(fā)模式、中斷優(yōu)先級、中斷處理程序。中斷被觸發(fā)時,
 handleIrq會調用該中斷處理程序
******************************************************************************/
LITE_OS_SEC_TEXT_INIT UINT32 LOS_HwiCreate(HWI_HANDLE_T hwiNum, //硬中斷句柄編號 默認范圍[0-127]
                                           HWI_PRIOR_T hwiPrio,     //硬中斷優(yōu)先級    
                                           HWI_MODE_T hwiMode,      //硬中斷模式 共享和非共享
                                           HWI_PROC_FUNC hwiHandler,//硬中斷處理函數
                                           HwiIrqParam *irqParam)   //硬中斷處理函數參數
{
    UINT32 ret;

    (VOID)hwiPrio;
    if (hwiHandler == NULL) {//中斷處理函數不能為NULL
        return OS_ERRNO_HWI_PROC_FUNC_NULL;
    }
    if ((hwiNum > OS_USER_HWI_MAX) || ((INT32)hwiNum < OS_USER_HWI_MIN)) {//中斷數區(qū)間限制 [32,96]
        return OS_ERRNO_HWI_NUM_INVALID;
    }

#ifdef LOSCFG_NO_SHARED_IRQ //不支持共享中斷
    ret = OsHwiCreateNoShared(hwiNum, hwiMode, hwiHandler, irqParam);
#else
    ret = OsHwiCreateShared(hwiNum, hwiMode, hwiHandler, irqParam);
#endif
    return ret;
}
//創(chuàng)建一個共享硬件中斷,共享中斷就是一個中斷能觸發(fā)多個響應函數
STATIC UINT32 OsHwiCreateShared(HWI_HANDLE_T hwiNum, HWI_MODE_T hwiMode,
                                HWI_PROC_FUNC hwiHandler, const HwiIrqParam *irqParam)
{
    UINT32 intSave;
    HwiHandleForm *hwiFormNode = NULL;
    HwiHandleForm *hwiForm = NULL;
    HwiIrqParam *hwiParam = NULL;
    HWI_MODE_T modeResult = hwiMode & IRQF_SHARED;

    if (modeResult && ((irqParam == NULL) || (irqParam->pDevId == NULL))) {
        return OS_ERRNO_HWI_SHARED_ERROR;
    }

    HWI_LOCK(intSave);//中斷自旋鎖

    hwiForm = &g_hwiForm[hwiNum];//獲取中斷處理項
    if ((hwiForm->pstNext != NULL) && ((modeResult == 0) || (!(hwiForm->uwParam & IRQF_SHARED)))) {
        HWI_UNLOCK(intSave);
        return OS_ERRNO_HWI_SHARED_ERROR;
    }

    while (hwiForm->pstNext != NULL) {//pstNext指向 共享中斷的各處理函數節(jié)點,此處一直擼到最后一個
        hwiForm = hwiForm->pstNext;//找下一個中斷
        hwiParam = (HwiIrqParam *)(hwiForm->uwParam);//獲取中斷參數,用于檢測該設備ID是否已經有中斷處理函數
        if (hwiParam->pDevId == irqParam->pDevId) {//設備ID一致時,說明設備對應的中斷處理函數已經存在了.
            HWI_UNLOCK(intSave);
            return OS_ERRNO_HWI_ALREADY_CREATED;
        }
    }

    hwiFormNode = (HwiHandleForm *)LOS_MemAlloc(m_aucSysMem0, sizeof(HwiHandleForm));//創(chuàng)建一個中斷處理節(jié)點
    if (hwiFormNode == NULL) {
        HWI_UNLOCK(intSave);
        return OS_ERRNO_HWI_NO_MEMORY;
    }

    hwiFormNode->uwParam = OsHwiCpIrqParam(irqParam);//獲取中斷處理函數的參數
    if (hwiFormNode->uwParam == LOS_NOK) {
        HWI_UNLOCK(intSave);
        (VOID)LOS_MemFree(m_aucSysMem0, hwiFormNode);
        return OS_ERRNO_HWI_NO_MEMORY;
    }

    hwiFormNode->pfnHook = hwiHandler;//綁定中斷處理函數
    hwiFormNode->pstNext = (struct tagHwiHandleForm *)NULL;//指定下一個中斷為NULL,用于后續(xù)遍歷找到最后一個中斷項(見于以上 while (hwiForm->pstNext != NULL)處)
    hwiForm->pstNext = hwiFormNode;//共享中斷

    if ((irqParam != NULL) && (irqParam->pName != NULL)) {
        g_hwiFormName[hwiNum] = (CHAR *)irqParam->pName;
    }

    g_hwiForm[hwiNum].uwParam = modeResult;

    HWI_UNLOCK(intSave);
    return LOS_OK;
}

解讀

  • 內核將硬中斷進行編號,如:
      #define NUM_HAL_INTERRUPT_TIMER0        33
      #define NUM_HAL_INTERRUPT_TIMER1        33
      #define NUM_HAL_INTERRUPT_TIMER2        34
      #define NUM_HAL_INTERRUPT_TIMER3        34
      #define NUM_HAL_INTERRUPT_TIMER4        35
      #define NUM_HAL_INTERRUPT_TIMER5        35
      #define NUM_HAL_INTERRUPT_TIMER6        36
      #define NUM_HAL_INTERRUPT_TIMER7        36
      #define NUM_HAL_INTERRUPT_DMAC          60
      #define NUM_HAL_INTERRUPT_UART0         38
      #define NUM_HAL_INTERRUPT_UART1         39
      #define NUM_HAL_INTERRUPT_UART2         40
      #define NUM_HAL_INTERRUPT_UART3         41
      #define NUM_HAL_INTERRUPT_UART4         42
      #define NUM_HAL_INTERRUPT_TIMER         NUM_HAL_INTERRUPT_TIMER4

例如:時鐘節(jié)拍處理函數 OsTickHandler 就是在 HalClockInit中注冊的

    //硬時鐘初始化
    VOID HalClockInit(VOID)
    {
      // ...
      (void)LOS_HwiCreate(NUM_HAL_INTERRUPT_TIMER, 0xa0, 0, OsTickHandler, 0);//注冊OsTickHandler到中斷向量表 
    }
    //節(jié)拍中斷處理函數 ,鴻蒙默認10ms觸發(fā)一次
      LITE_OS_SEC_TEXT VOID OsTickHandler(VOID)
      {
          UINT32 intSave;
          TICK_LOCK(intSave);//tick自旋鎖
          g_tickCount[ArchCurrCpuid()]++;// 累加當前CPU核tick數
          TICK_UNLOCK(intSave);
          OsTimesliceCheck();//時間片檢查
          OsTaskScan(); /* task timeout scan *///掃描超時任務 例如:delay(300)
          #if (LOSCFG_BASE_CORE_SWTMR == YES)
              OsSwtmrScan();//掃描定時器,查看是否有超時定時器,加入隊列
          #endif
      } 
  • 鴻蒙是支持中斷共享的,在OsHwiCreateShared中,將函數注冊到g_hwiForm中.中斷向量完成注冊后,就是如何觸發(fā)和回調的問題.觸發(fā)在中斷切換篇中已經講清楚,觸發(fā)是從底層匯編向上調用,調用的C函數就是HalIrqHandler

中斷怎么觸發(fā)的?

分兩種情況:

  • 通過硬件觸發(fā),比如按鍵,USB的插拔這些中斷源向中斷控制器發(fā)送電信號(高低電平觸發(fā)或是上升/下降沿觸發(fā)),中斷控制器經過過濾后將信號發(fā)給對應的CPU處理,通過硬件改變PC和CPSR寄存值,直接跳轉到中斷向量(固定地址)執(zhí)行.
      b   reset_vector            @開機代碼
      b   _osExceptUndefInstrHdl    @異常處理之CPU碰到不認識的指令
      b   _osExceptSwiHdl           @異常處理之:軟中斷
      b   _osExceptPrefetchAbortHdl @異常處理之:取指異常
      b   _osExceptDataAbortHdl     @異常處理之:數據異常
      b   _osExceptAddrAbortHdl     @異常處理之:地址異常
      b   OsIrqHandler              @異常處理之:硬中斷
      b   _osExceptFiqHdl               @異常處理之:快中斷
  • 通過軟件觸發(fā),常見于核間中斷的情況, 核間中斷指的是幾個CPU之間相互通訊的過程.以下為某一個CPU向其他CPU(可以是多個)發(fā)起讓這些CPU重新調度LOS_MpSchedule的中斷請求信號.最終是寫了中斷控制器的GICD_SGIR寄存器,這是一個由軟件觸發(fā)中斷的寄存器.中斷控制器會將請求分發(fā)給對應的CPU處理中斷,即觸發(fā)了OsIrqHandler.
    //給參數CPU發(fā)送調度信號
      VOID LOS_MpSchedule(UINT32 target)//target每位對應CPU core 
      {
          UINT32 cpuid = ArchCurrCpuid();
          target &= ~(1U << cpuid);//獲取除了自身之外的其他CPU
          HalIrqSendIpi(target, LOS_MP_IPI_SCHEDULE);//向目標CPU發(fā)送調度信號,核間中斷(Inter-Processor Interrupts),IPI
      }
      //SGI軟件觸發(fā)中斷(Software Generated Interrupt)通常用于多核間通訊
      STATIC VOID GicWriteSgi(UINT32 vector, UINT32 cpuMask, UINT32 filter)
      {
          UINT32 val = ((filter & 0x3) << 24) | ((cpuMask & 0xFF) << 16) |
                      (vector & 0xF);

          GIC_REG_32(GICD_SGIR) = val;//寫SGI寄存器
      }
      //向指定核發(fā)送核間中斷
      VOID HalIrqSendIpi(UINT32 target, UINT32 ipi)
      {
          GicWriteSgi(ipi, target, 0);
      }

中斷統(tǒng)一處理入口函數 HalIrqHandler

//硬中斷統(tǒng)一處理函數,這里由硬件觸發(fā),調用見于 ..\arch\arm\arm\src\los_dispatch.S
VOID HalIrqHandler(VOID)
{
    UINT32 iar = GIC_REG_32(GICC_IAR);//從中斷確認寄存器獲取中斷ID號
    UINT32 vector = iar & 0x3FFU;//計算中斷向量號
    /*
     * invalid irq number, mainly the spurious interrupts 0x3ff,
     * gicv2 valid irq ranges from 0~1019, we use OS_HWI_MAX_NUM
     * to do the checking.
     */
    if (vector >= OS_HWI_MAX_NUM) {
        return;
    }
    g_curIrqNum = vector;//記錄當前中斷ID號
    OsInterrupt(vector);//調用上層中斷處理函數
    /* use orignal iar to do the EOI */
    GIC_REG_32(GICC_EOIR) = iar;//更新中斷結束寄存器
}
VOID OsInterrupt(UINT32 intNum)//中斷實際處理函數
{
    HwiHandleForm *hwiForm = NULL;
    UINT32 *intCnt = NULL;

    intCnt = &g_intCount[ArchCurrCpuid()];//當前CPU的中斷總數量 ++
    *intCnt = *intCnt + 1;//@note_why 這里沒看明白為什么要 +1

#ifdef LOSCFG_CPUP_INCLUDE_IRQ //開啟查詢系統(tǒng)CPU的占用率的中斷
    OsCpupIrqStart();//記錄本次中斷處理開始時間
#endif

#ifdef LOSCFG_KERNEL_TICKLESS
    OsTicklessUpdate(intNum);
#endif
    hwiForm = (&g_hwiForm[intNum]);//獲取對應中斷的實體
#ifndef LOSCFG_NO_SHARED_IRQ    //如果沒有定義不共享中斷 ,意思就是如果是共享中斷
    while (hwiForm->pstNext != NULL) { //一直擼到最后
        hwiForm = hwiForm->pstNext;//下一個繼續(xù)擼
#endif
        if (hwiForm->uwParam) {//有參數的情況
            HWI_PROC_FUNC2 func = (HWI_PROC_FUNC2)hwiForm->pfnHook;//獲取回調函數
            if (func != NULL) {
                UINTPTR *param = (UINTPTR *)(hwiForm->uwParam);
                func((INT32)(*param), (VOID *)(*(param + 1)));//運行帶參數的回調函數
            }
        } else {//木有參數的情況
            HWI_PROC_FUNC0 func = (HWI_PROC_FUNC0)hwiForm->pfnHook;//獲取回調函數
            if (func != NULL) {
                func();//運行回調函數
            }
        }
#ifndef LOSCFG_NO_SHARED_IRQ
    }
#endif
    ++g_hwiFormCnt[intNum];//中斷號計數器總數累加

    *intCnt = *intCnt - 1;  //@note_why 這里沒看明白為什么要 -1 
#ifdef LOSCFG_CPUP_INCLUDE_IRQ  //開啟查詢系統(tǒng)CPU的占用率的中斷
    OsCpupIrqEnd(intNum);//記錄中斷處理時間完成時間
#endif
}

解讀
統(tǒng)一中斷處理函數是一個通過一個中斷號去找到注冊函數的過程,分四步走:

  • 第一步:取號,這號是由中斷控制器的 GICC_IAR寄存器提供的,這是一個專門保存當前中斷號的寄存器.
  • 第二步:從注冊表g_hwiForm中查詢注冊函數,同時取出參數.
  • 第三步:執(zhí)行函數,也就是回調注冊函數,分有參和無參兩種情況 func(...),在中斷共享的情況,注冊函數會指向 next 注冊函數pstNext,依次執(zhí)行回調函數,這是中斷共享的實現細節(jié).
    typedef struct tagHwiHandleForm {   
          HWI_PROC_FUNC pfnHook;    //中斷處理函數
          HWI_ARG_T uwParam;        //中斷處理函數參數
          struct tagHwiHandleForm *pstNext; //節(jié)點,指向next中斷,用于共享中斷的情況
    } HwiHandleForm;
  • 第四步:銷號,本次中斷完成了就需要消除記錄,中斷控制器也有專門的銷號寄存器GICC_EOIR
  • 另外的是一些統(tǒng)一數據,每次中斷號處理內核都會記錄次數,和耗時,以便定位/跟蹤/診斷問題.

寫在最后

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

推薦閱讀更多精彩內容