Android 系統(tǒng)啟動 - init 進程

前言

我們之前在 Android 系統(tǒng)啟動流程簡析 中提到:Android 系統(tǒng)內(nèi)核啟動之后讲婚,除了初始化各種軟硬件環(huán)境忍些,加載驅(qū)動程序撬统,掛載根文件系統(tǒng)等操作之外缝呕,最重要的一個操作就是在系統(tǒng)中找到可執(zhí)行程序 init ,啟動 init 進程遮怜。

下面我們對 init 進程啟動源碼進行簡析佳魔。但在簡析之前,需要我們預(yù)先了解一些前置知識偿枕。

前置知識

  • 本文 Android 源碼版本android-6.0.1_r81

  • 關(guān)鍵路徑:如下表所示:

File Path
init 進程 /system/core/init/init.cpp
init.rc 腳本 /system/core/rootdir/init.rc
readme.txt(Android Init Language) /system/core/init/readme.txt
  • init.rc 文件格式:init 進程在啟動過程中璧瞬,會啟動許多關(guān)鍵的系統(tǒng)服務(wù),如果在源碼中一項項啟動會顯得很繁瑣渐夸,而且不易擴展嗤锉,因此 Android 引入了 init.rcinit.rc 是一個配置腳本墓塌,其內(nèi)部使用一種名為 Android Init Language 的語言編寫而成瘟忱,詳細(xì)內(nèi)容可在 /system/core/init/readme.txt 中查看。

?這里我們就簡單介紹下 init.rc 的一些主要配置語法:

?? init.rc 內(nèi)部主要包含五種類型語句:Action苫幢、Service酷誓、CommandOptionsImport态坦。其中盐数,主要的配置選項為:ActionService,Trigger 和 Command 是對 Action 的補充伞梯,Options 是對 Service 的補充玫氢。Import 用于導(dǎo)入其他 .rc 文件并進行解析。下面簡單介紹下 ActionService 的配置語法:

?Action:以 on 開頭谜诫,trigger 是判斷條件漾峡,command 是具體執(zhí)行一些操作,當(dāng)滿足 trigger 條件時喻旷,執(zhí)行這些 command生逸。

on <trigger> [&& <trigger>]* //設(shè)置觸發(fā)器  
       <command> //觸發(fā)器觸發(fā)之后要完成的操作
       <command>
       <command>

?Service:服務(wù)是 init 進程啟動的程序、當(dāng)服務(wù)退出時 init 進程會視情況重啟服務(wù)且预。其語法以 service 開頭槽袄,name 是指定這個服務(wù)的名稱,pathname 表示這個服務(wù)的執(zhí)行文件路徑锋谐,argument 表示傳遞給執(zhí)行文件的參數(shù)遍尺,option 表示這個服務(wù)的一些配置。

service <name> <pathname> [ <argument> ]*   
       <option>
       <option>
       ...

?? .rc 文件是由一個個 Section 組成涮拗。action 加上 trigger 以及一些 command 組成一個Section乾戏;service 加上一些 option,也組成一個Section 三热;

:在 Android 7.0 以前鼓择,init 進程只解析根目錄下的 init.rc 文件,但是隨著版本的迭代就漾,init.rc 越來越臃腫呐能,所以在 7.0 以后,init.rc 一些業(yè)務(wù)被分拆到 /system/etc/init从藤,/vendor/etc/init催跪,/odm/etc/init 三個目錄下。

init 進程啟動流程 簡析

init 進程的入口函數(shù)為:main夷野,其位于:/system/core/init/init.cpp懊蒸,具體內(nèi)容如下:


int main(int argc, char** argv) {
    // 如果不是 ueventd,則進入
    if (!strcmp(basename(argv[0]), "ueventd")) {
        /* 該函數(shù)定義在在system/core/init/ueventd.cpp
         * init 進程會創(chuàng)建子進程 ueventd悯搔,uevented 主要負(fù)責(zé)設(shè)備節(jié)點文件的創(chuàng)建骑丸,
         * 創(chuàng)建方式有兩種:冷插拔(Cold Plug) 和 熱插拔(Hot Plug)
         */
        return ueventd_main(argc, argv);
    }

    if (!strcmp(basename(argv[0]), "watchdogd")) {
        /* 該函數(shù)定義在/system/core/init/watchdogd.cpp,主要用于啟動“看門狗”
         *
         * “看門狗”本身是一個定時器電路妒貌,內(nèi)部會不斷的進行計時(或計數(shù))操作,
         * 計算機系統(tǒng)和”看門狗”有兩個引腳相連接通危,正常運行時每隔一段時間就會
         * 通過其中一個引腳向”看門狗”發(fā)送信號,”看門狗”接收到信號后會將計時器
         * 清零并重新開始計時,而一旦系統(tǒng)出現(xiàn)問題灌曙,進入死循環(huán)或任何阻塞狀態(tài)菊碟,
         * 不能及時發(fā)送信號讓”看門狗”的計時器清零,當(dāng)計時結(jié)束時在刺,
         * ”看門狗”就會通過另一個引腳向系統(tǒng)發(fā)送“復(fù)位信號”逆害,讓系統(tǒng)重啟。 
         */
        return watchdogd_main(argc, argv);
    }

    // Clear the umask.
    umask(0);

    // 注冊環(huán)境變量PATH
    // _PATH_DEFPATH 定義在 bionic/libc/include/paths.h 中
    add_environment("PATH", _PATH_DEFPATH);

    bool is_first_stage = (argc == 1) || (strcmp(argv[1], "--second-stage") != 0);

    // Get the basic filesystem setup we need put together in the initramdisk
    // on / and then we'll let the rc file figure out the rest.
    if (is_first_stage) {
        // 掛載 tmpfs 到 /dev 目錄
        mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
        // 創(chuàng)建文件夾 /dev/pts
        mkdir("/dev/pts", 0755);
        mkdir("/dev/socket", 0755);
        mount("devpts", "/dev/pts", "devpts", 0, NULL);
        mount("proc", "/proc", "proc", 0, NULL);
        mount("sysfs", "/sys", "sysfs", 0, NULL);
    }

    // We must have some place other than / to create the device nodes for
    // kmsg and null, otherwise we won't be able to remount / read-only
    // later on. Now that tmpfs is mounted on /dev, we can actually talk
    // to the outside world.
    open_devnull_stdio();
    // 初始化內(nèi)核log蚣驼,位于節(jié)點/dev/kmsg
    klog_init();
    // 設(shè)置 log 級別
    klog_set_level(KLOG_NOTICE_LEVEL);

    NOTICE("init%s started!\n", is_first_stage ? "" : " second stage");

    if (!is_first_stage) {
        // Indicate that booting is in progress to background fw loaders, etc.
        close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));

        // 定義在 /system/core/init/property_service.cpp
        // 初始化屬性服務(wù)
        property_init();

        // If arguments are passed both on the command line and in DT,
        // properties set in DT always have priority over the command-line ones.
        process_kernel_dt(); // 處理DT屬性
        process_kernel_cmdline(); // 處理命令行屬性

        // Propogate the kernel variables to internal variables
        // used by init as well as the current required properties.
        export_kernel_boot_props();// 處理其他的一些屬性
    }

    // Set up SELinux, including loading the SELinux policy if we're in the kernel domain.
    // 初始化 selinux 安全策略
    selinux_initialize(is_first_stage);

    // If we're in the kernel domain, re-exec init to transition to the init domain now
    // that the SELinux policy has been loaded.
    if (is_first_stage) {
        if (restorecon("/init") == -1) {
            ERROR("restorecon failed: %s\n", strerror(errno));
            security_failure();
        }
        char* path = argv[0];
        char* args[] = { path, const_cast<char*>("--second-stage"), nullptr };
        // 調(diào)用 execv 重新執(zhí)行 main 函數(shù)魄幕,進入 second-stage 階段
        if (execv(path, args) == -1) {
            ERROR("execv(\"%s\") failed: %s\n", path, strerror(errno));
            security_failure();
        }
    }

    // These directories were necessarily created before initial policy load
    // and therefore need their security context restored to the proper value.
    // This must happen before /dev is populated by ueventd.
    INFO("Running restorecon...\n");
    restorecon("/dev");
    restorecon("/dev/socket");
    restorecon("/dev/__properties__");
    restorecon_recursive("/sys");

    epoll_fd = epoll_create1(EPOLL_CLOEXEC);
    if (epoll_fd == -1) {
        ERROR("epoll_create1 failed: %s\n", strerror(errno));
        exit(1);
    }

    signal_handler_init();

    // 加載default.prop文件
    property_load_boot_defaults();
    // 啟動屬性服務(wù),開啟一個socket監(jiān)聽系統(tǒng)屬性的設(shè)置
    start_property_service();

    // 解析 init.rc
    init_parse_config_file("/init.rc");

    // 執(zhí)行rc文件中觸發(fā)器為 on early-init 的語句
    action_for_each_trigger("early-init", action_add_queue_tail);

    // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
    // 等待冷插拔設(shè)備初始化完成
    queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
    // ... so that we can start queuing up actions that require stuff from /dev.
    queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
    // 設(shè)備組合件的初始化操作
    queue_builtin_action(keychord_init_action, "keychord_init");
    // 屏幕顯示 Android 靜態(tài) Logo
    queue_builtin_action(console_init_action, "console_init");

    // Trigger all the boot actions to get us started.
    // 觸發(fā)rc文件中觸發(fā)器為on init的語句
    action_for_each_trigger("init", action_add_queue_tail);

    // Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
    // wasn't ready immediately after wait_for_coldboot_done
    queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");

    // Don't mount filesystems or start core system services in charger mode.
    char bootmode[PROP_VALUE_MAX];
    // 當(dāng)處于充電模式時颖杏,將 charger 加入執(zhí)行隊列纯陨;否則將 late-init 加入隊列
    if (property_get("ro.bootmode", bootmode) > 0 && strcmp(bootmode, "charger") == 0) {
        action_for_each_trigger("charger", action_add_queue_tail);
    } else {
        action_for_each_trigger("late-init", action_add_queue_tail);
    }

    // Run all property triggers based on current state of the properties.
    queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");

    while (true) {
        if (!waiting_for_exec) {
            execute_one_command();
            restart_processes();
        }

        int timeout = -1;
        if (process_needs_restart) {
            timeout = (process_needs_restart - gettime()) * 1000;
            if (timeout < 0)
                timeout = 0;
        }

        if (!action_queue_empty() || cur_action) {
            timeout = 0;
        }

        bootchart_sample(&timeout);

        epoll_event ev;
        // 循環(huán)等待事件發(fā)生
        int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));
        if (nr == -1) {
            ERROR("epoll_wait failed: %s\n", strerror(errno));
        } else if (nr == 1) {
            ((void (*)()) ev.data.ptr)();
        }
    }

    return 0;
}

從源碼中看到,init 進程分為兩個階段:first-stagesecond-stage留储。
第一個階段 first-stage 主要做的事為:掛載一些必要的文件系統(tǒng):tmpfs翼抠,devpts,proc获讳,sysfs等机久,然后初始化 selinux 安全策略,在 selinux 初始化完成后就立即重新執(zhí)行main函數(shù)赔嚎,進入第二階段 second-stage膘盖,也就是將 init 進程從內(nèi)核態(tài)轉(zhuǎn)化為用戶態(tài)執(zhí)行。

first-stagesecond-stage 都會做一些同樣的工作尤误,比如設(shè)置環(huán)境變量侠畔,設(shè)置日志,設(shè)置不同階段的 selinux 策略损晤。對于 second-stage 來說软棺,其主要做的就是:初始化和啟動屬性服務(wù),解析 init.rc 文件尤勋,并按順序先后執(zhí)行觸發(fā)器: on early-init -> init -> late-init喘落。

屬性服務(wù) 的作用是讓 init 進程可以對其他程序進行權(quán)限控制茵宪。在 Android 中,對屬性的設(shè)置都統(tǒng)一交由 init 進程管理瘦棋,其他進程不能直接修改屬性稀火,而必須通過屬性服務(wù)申請。其實屬性服務(wù)的是一個跨進程通訊赌朋,使用的通訊機制是 socket凰狞。這里我們就不深入代碼講解了,感興趣的請自行查看沛慢。

對于 init.rc 文件的具體解析過程赡若,我們這里也不做講解。但簡單提一下团甲,init_parse_config_file("/init.rc"); 該函數(shù)源碼位于:/system/core/init/init_parser.cpp 逾冬,主要做的就是對 init.rc 的文件內(nèi)容進行解析,將解析出的 services 和 actions 等添加到運行隊列中躺苦,等待 trigger 觸發(fā)器的觸發(fā)運行粉渠。

我們重點講下 Zygote 的啟動點。

啟動 Zygote 進程

我們知道圾另,Zygote 的啟動是在 init 進程解析 init.rc 后啟動的霸株,那我們先來看下 init.rc 文件的內(nèi)容:

import /init.environ.rc
import /init.usb.rc
import /init.${ro.hardware}.rc
import /init.usb.configfs.rc
import /init.${ro.zygote}.rc
import /init.trace.rc

on early-init
    # Set init and its forked children's oom_adj.
    write /proc/1/oom_score_adj -1000
···

這里看到 Zygote 的啟動腳本被 import 了進來:import /init.${ro.zygote}.rc, ${ro.zygote} 會被替換成 ro.zyogte 的屬性值集乔,這個是由不同的硬件廠商自己定制的去件, 有四個值,zygote32扰路、zygote64尤溜、zygote32_64、zygote64_32 汗唱,也就是說可能有四種 .rc 文件宫莱。

這里我們選用 64 位處理器的版本來分析:/system/core/rootdir/init.zygote64.rc

service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
    class main
    socket zygote stream 660 root system
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/power/state on
    onrestart restart media
    onrestart restart netd
    writepid /dev/cpuset/foreground/tasks

service 選項用于通知 init 進程啟動 zygote 程序,zygote 的執(zhí)行程序路徑為:/system/bin/app_process64哩罪,init 進程啟動 zygote 時授霸,傳遞的參數(shù)為: -Xzygote /system/bin --zygote --start-system-server。

class 的作用是用于批量管理 service际插。因此這里class main表示 zygote 服務(wù)屬于類main碘耳。后續(xù)啟動 main 的時候,就會啟動 zygote 服務(wù)框弛。

前面我們說過辛辨,init 進程在解析完 init.rc 文件后,會依次執(zhí)行觸發(fā)器:on early-init -> init -> late-init。我們再來看下 init.rc 文件內(nèi)容:

on late-init
···
on nonencrypted
    class_start main // zygote 屬于類 main
    class_start late_start
···

也就是說在 late-init 觸發(fā)時斗搞,就會啟動類 main 管理的服務(wù)指攒,也就是會啟動 zygote。

class_start 對應(yīng)的處理方法是 do_class_start,該方法定義在system\core\init\builtins.cpp 中:

int do_class_start(int nargs, char **args){
        /* Starting a class does not start services
         * which are explicitly disabled.  They must
         * be started individually.
         */
    service_for_each_class(args[1], service_start_if_not_disabled);
    return 0;
}

該方法內(nèi)部又會調(diào)用 service_for_each_class
/system/core/init/init_parser.cpp

void service_for_each_class(const char *classname,
                            void (*func)(struct service *svc))
{
    struct listnode *node;
    struct service *svc;
    list_for_each(node, &service_list) {
        svc = node_to_item(node, struct service, slist);
        if (!strcmp(svc->classname, classname)) {
            func(svc);
        }
    }
}

該方法遍歷 service_list 鏈表僻焚,每找到一個 classnamemain 的 service,就調(diào)用 service_start_if_not_disabled 來進一步處理允悦,service_start_if_not_disabled 方法源碼如下:
/system/core/init/builtins.cpp

static void service_start_if_not_disabled(struct service *svc)
{
    if (!(svc->flags & SVC_DISABLED)) {
        service_start(svc, NULL);
    } else {
        svc->flags |= SVC_DISABLED_START;
    }
}

可以看到,最終是調(diào)用 service_start 來啟動服務(wù)溅呢。service_start 源碼如下:
/system/core/init/init.cpp

void service_start(struct service *svc, const char *dynamic_args)
{
    ···
    // 如果Service已經(jīng)運行,則不啟動
    if (svc->flags & SVC_RUNNING) {
        return;
    }
    ···
    struct stat s;
    // 判斷Service要啟動的執(zhí)行文件是否存在
    if (stat(svc->args[0], &s) != 0) {
        ERROR("cannot find '%s', disabling '%s'\n", svc->args[0], svc->name);
        svc->flags |= SVC_DISABLED;
        return;
    }
    ···
    NOTICE("Starting service '%s'...\n", svc->name);

    // fork 函數(shù)創(chuàng)建子進程
    pid_t pid = fork();
    if (pid == 0) { // 運行在子進程當(dāng)中
        struct socketinfo *si;
        struct svcenvinfo *ei;
        char tmp[32];
        int fd, sz;

        umask(077);
        ··· 
        if (!dynamic_args) {
            // 加載可執(zhí)行文件
            if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0) {
                ERROR("cannot execve('%s'): %s\n", svc->args[0], strerror(errno));
            }
        ···
        _exit(127);
    }
    ···
}

該函數(shù)會通過調(diào)用 fork 函數(shù)來創(chuàng)建子進程猿挚,并在子進程中調(diào)用 execve 來加載可執(zhí)行文件咐旧,對于 zygote 服務(wù)來說,也就是會執(zhí)行 /system/bin/app_process64绩蜻。那接下來該可執(zhí)行文件的 main 函數(shù)就會被調(diào)用:
framework/cmds/app_process/app_main.cpp

int main(int argc, char* const argv[])
{
    ···
    // Process command line arguments
    // ignore argv[0]
    argc--;
    argv++;

    // --zygote : Start in zygote mode
    // --start-system-server : Start the system server.
    // --application : Start in application (stand alone, non zygote) mode.
    // --nice-name : The nice name for this process.
    ··· 
    // Parse runtime arguments.  Stop at first unrecognized option.
    ···
    // 解析參數(shù)
    ++i;  // Skip unused "parent dir" argument.
    while (i < argc) {
        const char* arg = argv[i++];
        if (strcmp(arg, "--zygote") == 0) {
            zygote = true;
            niceName = ZYGOTE_NICE_NAME;
        } else if (strcmp(arg, "--start-system-server") == 0) {
            startSystemServer = true;
        } else if (strcmp(arg, "--application") == 0) {
            application = true;
        } else if (strncmp(arg, "--nice-name=", 12) == 0) {
            niceName.setTo(arg + 12);
        } else if (strncmp(arg, "--", 2) != 0) {
            className.setTo(arg);
            break;
        } else {
            --i;
            break;
        }
    }
    ···
    if (zygote) {
        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
    } else if (className) {
        runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
    } else {
        fprintf(stderr, "Error: no class name or --zygote supplied.\n");
        app_usage();
        LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
        return 10;
    }
}

該函數(shù)會先對參數(shù)進行解析铣墨,對照 init.zygote64.rc 傳遞的參數(shù):-Xzygote /system/bin --zygote --start-system-server,可以解析得出zygote=true,startSystemServer =true办绝。于是最終會通過 AppRuntime.start 方法啟動 zygote伊约。

到此,init 進程就成功啟動了 zygote 進程孕蝉。

總結(jié)

init 進程啟動時屡律,總共經(jīng)歷兩個階段:內(nèi)核態(tài)用戶態(tài)

內(nèi)核態(tài) 階段下降淮,init 進程主要做掛載一些必要的設(shè)備節(jié)點(tmpfs超埋,devpts,proc佳鳖,sysfs····)霍殴,初始化 selinux 安全策略等操作,然后切換到 用戶態(tài)(通過傳遞參數(shù) --second-stage系吩,并調(diào)用 execv 進行重啟切換)来庭;

用戶態(tài) 階段下,init 進程最主要的一個操作就是解析 init.rc 文件穿挨,并按照順序先后執(zhí)行觸發(fā)器:on early-init -> init -> late-init月弛。

init 進程每遇到一個服務(wù)(service)時,就會通過 fork 出一個子進程來啟動該服務(wù)科盛。

zygote 在 init.rc 中是作為一個服務(wù)(service)存在尊搬,當(dāng) init.rc 文件被解析完成后,init 進程最終會在執(zhí)行觸發(fā)器 late-init 的過程中啟動 zygote 服務(wù)(zygote 服務(wù)進程就是 init 進程 fork 出來的一個子進程土涝,在該進程最后佛寿,會通過 AppRuntime.start 真正啟動 zygote)。

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市冀泻,隨后出現(xiàn)的幾起案子常侣,更是在濱河造成了極大的恐慌,老刑警劉巖弹渔,帶你破解...
    沈念sama閱讀 216,997評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件胳施,死亡現(xiàn)場離奇詭異,居然都是意外死亡肢专,警方通過查閱死者的電腦和手機舞肆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來博杖,“玉大人椿胯,你說我怎么就攤上這事√旮” “怎么了哩盲?”我有些...
    開封第一講書人閱讀 163,359評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長狈醉。 經(jīng)常有香客問我廉油,道長,這世上最難降的妖魔是什么苗傅? 我笑而不...
    開封第一講書人閱讀 58,309評論 1 292
  • 正文 為了忘掉前任抒线,我火速辦了婚禮,結(jié)果婚禮上渣慕,老公的妹妹穿的比我還像新娘十兢。我一直安慰自己,他們只是感情好摇庙,可當(dāng)我...
    茶點故事閱讀 67,346評論 6 390
  • 文/花漫 我一把揭開白布旱物。 她就那樣靜靜地躺著,像睡著了一般卫袒。 火紅的嫁衣襯著肌膚如雪宵呛。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,258評論 1 300
  • 那天夕凝,我揣著相機與錄音宝穗,去河邊找鬼。 笑死码秉,一個胖子當(dāng)著我的面吹牛逮矛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播转砖,決...
    沈念sama閱讀 40,122評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼须鼎,長吁一口氣:“原來是場噩夢啊……” “哼鲸伴!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起晋控,我...
    開封第一講書人閱讀 38,970評論 0 275
  • 序言:老撾萬榮一對情侶失蹤汞窗,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后赡译,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體仲吏,經(jīng)...
    沈念sama閱讀 45,403評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,596評論 3 334
  • 正文 我和宋清朗相戀三年蝌焚,在試婚紗的時候發(fā)現(xiàn)自己被綠了裹唆。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,769評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡只洒,死狀恐怖许帐,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情红碑,我是刑警寧澤舞吭,帶...
    沈念sama閱讀 35,464評論 5 344
  • 正文 年R本政府宣布泡垃,位于F島的核電站析珊,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏蔑穴。R本人自食惡果不足惜忠寻,卻給世界環(huán)境...
    茶點故事閱讀 41,075評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望存和。 院中可真熱鬧奕剃,春花似錦、人聲如沸捐腿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽茄袖。三九已至操软,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間宪祥,已是汗流浹背聂薪。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蝗羊,地道東北人藏澳。 一個月前我還...
    沈念sama閱讀 47,831評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像耀找,于是被迫代替她去往敵國和親翔悠。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,678評論 2 354