Android Zygote

一、前言

本文主要講解內(nèi)容
1、系統(tǒng)啟動(dòng)zygote宜岛、zygote的構(gòu)造流程、主要做了什么
2功舀、如何創(chuàng)建一個(gè)新的進(jìn)程
3萍倡、systemserver的ams創(chuàng)建應(yīng)用如何建立socket聯(lián)系,以及如何收發(fā)消息

先簡(jiǎn)單提一下開機(jī)流程

init.rc -> zygote.rc -> app_main.cpp  ->  AndroidRuntime.cpp (啟動(dòng)虛擬機(jī)辟汰,注冊(cè)jni列敲,啟動(dòng)zygote) -> ZygoteInit.java ->  SystemServer進(jìn)程

其中init是我們系統(tǒng)啟動(dòng)的第一個(gè)進(jìn)程,正是它通過(guò)linux的forck方法帖汞,創(chuàng)建我們系統(tǒng)的最重要的進(jìn)程之一zygote.
zygote的啟動(dòng)過(guò)程以及作用:?jiǎn)?dòng)dalvik虛擬機(jī)戴而,加載系統(tǒng)必須的一些資源,啟動(dòng)framework的systemserver進(jìn)程翩蘸。最后等待app請(qǐng)求創(chuàng)建應(yīng)用進(jìn)程
zygote在fork一個(gè)新的進(jìn)程時(shí)會(huì)克隆出和之前zygote幾乎一樣的進(jìn)程包含zygote的資源所意,新進(jìn)程不需要進(jìn)行初始化操作,只會(huì)修改一些必要參數(shù)
由于源碼過(guò)多催首,本文的代碼都會(huì)精簡(jiǎn)要點(diǎn)扶踊,最好結(jié)合源碼閱讀,源碼基于androidR,各安卓版本代碼可能有小區(qū)別但整體不會(huì)變化很大

二翅帜、啟動(dòng)流程

2.1姻檀、init.rc啟動(dòng)的地方

關(guān)于zygote的rc文件有幾個(gè)地方:

./system/core/rootdir/init.zygote32_64.rc
./system/core/rootdir/init.zygote32.rc
./system/core/rootdir/init.zygote64_32.rc
./system/core/rootdir/init.zygote64.rc

他們的32和64分別對(duì)應(yīng)兩個(gè)啟動(dòng)的地方app_process32和app_process64
例如:zygote64_32.rc,那么它以app_process64為主涝滴,app_process32為輔绣版,它兩都會(huì)啟動(dòng)胶台,也就是有兩個(gè)zygote進(jìn)程
./system/core/rootdir/init.zygote64.rc

service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
    class main
    priority -20
    user root
    group root readproc reserved_disk
    socket zygote stream 660 root system
    socket usap_pool_primary stream 660 root system
    onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse
    onrestart write /sys/power/state on
    onrestart restart audioserver
    onrestart restart cameraserver
    onrestart restart media
    onrestart restart netd
    onrestart restart wificond
    writepid /dev/cpuset/foreground/tasks

上面就是開始啟動(dòng)zygote的地方,不管是啟動(dòng)32/64,都比較類似杂抽,執(zhí)行手機(jī)里的/system/bin/app_process它對(duì)應(yīng)的地方

2.2诈唬、app_main init主入口

app_main.cpp    frameworks\base\cmds\app_process    11671   2022/2/23   241

app_main.cpp的main函數(shù)就成了入口,它主要做了如下幾個(gè)事情:

//1缩麸、構(gòu)建runtime
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));

//2铸磅、解析運(yùn)行參數(shù)
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;
        }
    }

//3、runtime start zygoteinit
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.");
    }

這里的runtime只是一個(gè)調(diào)用杭朱,實(shí)現(xiàn)在
AndroidRuntime.cpp frameworks\base\core\jni

void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
    //這句日志實(shí)際輸出02-21 11:29:59.487   496   496 D AndroidRuntime: >>>>>> START com.android.internal.os.ZygoteInit uid 0 <<<<<<
    //達(dá)標(biāo)className = com.android.internal.os.ZygoteInit
    ALOGD(">>>>>> START %s uid %d <<<<<<\n",
            className != NULL ? className : "(unknown)", getuid());
    //打印關(guān)鍵日志boot_progress_start 表示開機(jī)過(guò)程上層開始
    for (size_t i = 0; i < options.size(); ++i) {
        if (options[i] == startSystemServer) {
            primary_zygote = true;
           /* track our progress through the boot sequence */
           const int LOG_BOOT_PROGRESS_START = 3000;
           LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START,  ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
        }
    }

    1阅仔、啟動(dòng)虛擬機(jī)
     /* start the virtual machine */
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;
    if (startVm(&mJavaVM, &env, zygote, primary_zygote) != 0) {
        return;
    }
    onVmCreated(env);

    2、注冊(cè)JNI
    /*
     * Register android functions.
     */
    if (startReg(env) < 0) {
        ALOGE("Unable to register all android natives\n");
        return;
    }

    3弧械、調(diào)用ZygoteInit的main函數(shù)
    //這就是上面?zhèn)渥⒌腸lassName = ZygoteIni
    char* slashClassName = toSlashClassName(className != NULL ? className : "");
    jclass startClass = env->FindClass(slashClassName);
    if (startClass == NULL) {
        ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
        /* keep going */
    } else {
        jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
            "([Ljava/lang/String;)V");

2.3八酒、ZygoteInit.java#main zygote入口

public static void main(String argv[]) {
        1、加載進(jìn)程的資源和類(這里可以多線程加載class文件優(yōu)化開機(jī)速度)
        preload(bootTimingsTraceLog);
                
        2刃唐、創(chuàng)建zygoteServer
        zygoteServer = new ZygoteServer(isPrimaryZygote);

            if (startSystemServer) {
                3羞迷、把sokeckname傳進(jìn)去systemserver中,并開始運(yùn)行systemserver
                Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);
                if (r != null) {
                    r.run();
                    return;
                }
        4画饥、最后開啟loop等待消息
        caller = zygoteServer.runSelectLoop(abiList);
    }

看一下forkSystemServer中關(guān)鍵的兩個(gè)地方

private static Runnable forkSystemServer(String abiList, String socketName,
            ZygoteServer zygoteServer) {
        1衔瓮、zygote調(diào)用forkSystemServer這個(gè)native方法創(chuàng)建出系統(tǒng)進(jìn)程
        pid = Zygote.forkSystemServer(
                    parsedArgs.mUid, parsedArgs.mGid,
                    parsedArgs.mGids,
                    parsedArgs.mRuntimeFlags,
                    null,
                    parsedArgs.mPermittedCapabilities,
                    parsedArgs.mEffectiveCapabilities);

        2、給應(yīng)用創(chuàng)建新的進(jìn)程抖甘,走h(yuǎn)andleSystemServerProcess邏輯
        /* For child process */
        if (pid == 0) {
            if (hasSecondZygote(abiList)) {
                waitForSecondaryZygote(socketName);
            }

            zygoteServer.closeServerSocket();
            return handleSystemServerProcess(parsedArgs);
        }

}

這里下一步就開始啟動(dòng)我們熟悉的systemserver服務(wù)热鞍。關(guān)于Systemserver就不過(guò)多闡述了,它主要啟動(dòng)了我們整個(gè)android系統(tǒng)的各種service

三衔彻、zygote創(chuàng)建子進(jìn)程流程

3.1碍现、調(diào)用流程

創(chuàng)建一個(gè)應(yīng)用流程這里就不詳述了,都大同小異米奸,這里直接跟蹤到關(guān)鍵點(diǎn),當(dāng)我們跟蹤啟動(dòng)流程跟蹤到
ActivityStack.java frameworks\base\services\core\java\com\android\server\wm
ActivityStack#resumeTopActivityInnerLocked

mStackSupervisor.startSpecificActivity(next, true, false);

startSpecificActivity主要判斷當(dāng)前應(yīng)用是否進(jìn)程已經(jīng)在運(yùn)行,會(huì)跟蹤改到ActivityTaskManager#startProcessAsync方法
final Message m = PooledLambda.obtainMessage(ActivityManagerInternal::startProcess, mAmInternal, activity.processName, activity.info.applicationInfo, knownToBeDead, isTop, hostingType, activity.intent.getComponent());
即來(lái)到ActivityManagerServie#startProcess

public void startProcess(String processName, ApplicationInfo info, boolean knownToBeDead,
                boolean isTop, String hostingType, ComponentName hostingName) {

        startProcessLocked(processName, info, knownToBeDead, 0 /* intentFlags */,
                new HostingRecord(hostingType, hostingName, isTop),
                ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE, false /* allowWhileBooting */,
                false /* isolated */, true /* keepIfLarge */);

即來(lái)到ActivityManagerServie#startProcessLocked - ProcessList#startProcessLocked -
ProcessList.java frameworks\base\services\core\java\com\android\server\am

private Process.ProcessStartResult startProcess(HostingRecord hostingRecord, String entryPoint,
            ProcessRecord app, int uid, int[] gids, int runtimeFlags, int zygotePolicyFlags,
            int mountExternal, String seInfo, String requiredAbi, String instructionSet,
            String invokeWith, long startTime) {
    
    Process.ProcessStartResult startResult;
            if (hostingRecord.usesWebviewZygote()) {
                startResult = startWebView(entryPoint,
                        app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                        app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                        app.info.dataDir, null, app.info.packageName, app.mDisabledCompatChanges,
                        new String[]{PROC_START_SEQ_IDENT + app.startSeq});
            } else if (hostingRecord.usesAppZygote()) {
                final AppZygote appZygote = createAppZygoteForProcessIfNeeded(app);

                // We can't isolate app data and storage data as parent zygote already did that.
                startResult = appZygote.getProcess().start(entryPoint,
                        app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                        app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                        app.info.dataDir, null, app.info.packageName,
                        /*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, isTopApp,
                        app.mDisabledCompatChanges, pkgDataInfoMap, whitelistedAppDataInfoMap,
                        false, false,
                        new String[]{PROC_START_SEQ_IDENT + app.startSeq});
            } else {
                //modify for prefork blank process begin
                PreForkArgs preforkArgs = new PreForkArgs(entryPoint,
                        app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                        app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                        app.info.dataDir, invokeWith, app.info.packageName, zygotePolicyFlags,
                        isTopApp, app.mDisabledCompatChanges, pkgDataInfoMap,
                        whitelistedAppDataInfoMap, bindMountAppsData, bindMountAppStorageDirs,
                        new String[] {PROC_START_SEQ_IDENT + app.startSeq});
                startResult = mService.handlePreForkStartProcess(preforkArgs);
                if (startResult == null) {
                    startResult = Process.start(entryPoint,
                            app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                            app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                            app.info.dataDir, invokeWith, app.info.packageName, zygotePolicyFlags,
                            isTopApp, app.mDisabledCompatChanges, pkgDataInfoMap,
                            whitelistedAppDataInfoMap, bindMountAppsData, bindMountAppStorageDirs,
                            new String[]{PROC_START_SEQ_IDENT + app.startSeq});
                }
                //modify for prefork blank process end
            }
        
}

其中主要是appZygote.getProcess().start這一句爽篷,調(diào)用棧
ZygoteProcess中start - startViaZygote -
ygoteProces - zygoteSendArgsAndGetResult - attemptZygoteSendArgsAndGetResult

3.2悴晰、連接socket發(fā)送數(shù)據(jù)

1、sokeck建立的地方

startViaZygote - openZygoteSocketIfNeeded - attemptConnectionToPrimaryZygote

 @GuardedBy("mLock")
    private void attemptConnectionToPrimaryZygote() throws IOException {
            primaryZygoteState =
                    ZygoteState.connect(mZygoteSocketAddress, mUsapPoolSocketAddress);
            //構(gòu)建好消息這里發(fā)送
            maybeSetApiBlacklistExemptions(primaryZygoteState, false);
    }

static ZygoteState connect(@NonNull LocalSocketAddress zygoteSocketAddress,
     @Nullable LocalSocketAddress usapSocketAddress) {
          DataInputStream zygoteInputStream;
            BufferedWriter zygoteOutputWriter;
            final LocalSocket zygoteSessionSocket = new LocalSocket();

            if (zygoteSocketAddress == null) {
                throw new IllegalArgumentException("zygoteSocketAddress can't be null");
            }

            try {
                //關(guān)鍵點(diǎn)逐工,這里連接上了zygote的socket
                zygoteSessionSocket.connect(zygoteSocketAddress);
                zygoteInputStream = new DataInputStream(zygoteSessionSocket.getInputStream());
                zygoteOutputWriter =
                        new BufferedWriter(
                                new OutputStreamWriter(zygoteSessionSocket.getOutputStream()),
                                Zygote.SOCKET_BUFFER_SIZE);
 }

2铡溪、通過(guò)zygoteState的BufferedWriter,用socket發(fā)消息給zygote

 private Process.ProcessStartResult attemptZygoteSendArgsAndGetResult(
            ZygoteState zygoteState, String msgStr) throws ZygoteStartFailedEx {
        try {
            final BufferedWriter zygoteWriter = zygoteState.mZygoteOutputWriter;
            final DataInputStream zygoteInputStream = zygoteState.mZygoteInputStream;

            zygoteWriter.write(msgStr);
            zygoteWriter.flush();

            // Always read the entire result from the input stream to avoid leaving
            // bytes in the stream for future process starts to accidentally stumble
            // upon.
            Process.ProcessStartResult result = new Process.ProcessStartResult();
            result.pid = zygoteInputStream.readInt();
            result.usingWrapper = zygoteInputStream.readBoolean();

            if (result.pid < 0) {
                throw new ZygoteStartFailedEx("fork() failed");
            }

上面這段代碼泪喊,通過(guò)zygoteWriter把消息發(fā)給zygote棕硫,即發(fā)送流程

3.3、接收流程

接著上面的第二部分的ZygoteInit#main函數(shù)中袒啼,new了一個(gè)ZygoteServer

3.3.1 創(chuàng)建socket

ZygoteServer(boolean isPrimaryZygote) {
        mUsapPoolEventFD = Zygote.getUsapPoolEventFD();

        if (isPrimaryZygote) {
            mZygoteSocket = Zygote.createManagedSocketFromInitSocket(Zygote.PRIMARY_SOCKET_NAME);

static LocalServerSocket createManagedSocketFromInitSocket(String socketName) {
        int fileDesc;
        // fullSocketName = “ANDROID_SOCKET_” + “zygote";
        final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;

        try {
            String env = System.getenv(fullSocketName);
            fileDesc = Integer.parseInt(env);
        } catch (RuntimeException ex) {
            throw new RuntimeException("Socket unset or invalid: " + fullSocketName, ex);
        }

        try {
            FileDescriptor fd = new FileDescriptor();
            fd.setInt$(fileDesc);
            return new LocalServerSocket(fd);
        } catch (IOException ex) {
            throw new RuntimeException(
                "Error building socket from file descriptor: " + fileDesc, ex);
        }
    }

3.3.2 sokeck創(chuàng)建后消息發(fā)送到了哪里

這里又接著上面的第二部分的ZygoteInit#main函數(shù)中第四步zygoteServer.runSelectLoop(abiList);
ZygoteServer創(chuàng)建了socket哈扮,runSelectLoop會(huì)執(zhí)行

Runnable runSelectLoop(String abiList) {
    while(true) {
        try {
        ZygoteConnection connection = peers.get(pollIndex);
        // while循環(huán)到這句話會(huì)執(zhí)行forck操作纬纪,進(jìn)行新的進(jìn)程的創(chuàng)建
        final Runnable command = connection.processOneCommand(this);

        // TODO (chriswailes): Is this extra check necessary?
        if (mIsForkChild) {
            // We're in the child. We should always have a command to run at
            // this stage if processOneCommand hasn't called "exec".
            if (command == null) {
                throw new IllegalStateException("command == null");
            }

            return command;
        }
    }
}

3.4 創(chuàng)建子進(jìn)程

ZygoteConnection#processOneCommand
Runnable processOneCommand(ZygoteServer zygoteServer) {
     pid = Zygote.forkAndSpecialize(parsedArgs.mUid, parsedArgs.mGid, parsedArgs.mGids,
                parsedArgs.mRuntimeFlags, rlimits, parsedArgs.mMountExternal, parsedArgs.mSeInfo,
                parsedArgs.mNiceName, fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
                parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mIsTopApp,
                parsedArgs.mPkgDataInfoList, parsedArgs.mWhitelistedDataInfoList,
                parsedArgs.mBindMountAppDataDirs, parsedArgs.mBindMountAppStorageDirs);
    //穿件完后給ams回復(fù)
    return handleChildProc(parsedArgs, childPipeFd, parsedArgs.mStartChildZygote);
}

//Zygote#forkAndSpecialize
static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,
            int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
            int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
            boolean isTopApp, String[] pkgDataInfoList, String[] whitelistedDataInfoList,
            boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs) {
        ZygoteHooks.preFork();

        int pid = nativeForkAndSpecialize(
                uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
                fdsToIgnore, startChildZygote, instructionSet, appDataDir, isTopApp,
                pkgDataInfoList, whitelistedDataInfoList, bindMountAppDataDirs,
                bindMountAppStorageDirs);

子進(jìn)程的初始化操作就不詳述了大概說(shuō)下調(diào)用棧
handleChildProc - ZygoteInit.zygoteInit - RuntimeInit.applicationInit - RuntimeInit.findStaticMain

四、寫在最后

本文意在分析zygote的構(gòu)建滑肉,以及如何通過(guò)socke構(gòu)建新的進(jìn)程包各。下面說(shuō)兩個(gè)比較常規(guī)的問(wèn)題

為啥系統(tǒng)其它進(jìn)程都用binder而這里采用socket呢?
首先binder是多線程的,zygote的fork函數(shù)是不允許多線程的靶庙,不然容易造成死鎖(copy on write)

為啥運(yùn)行app不新建一個(gè)進(jìn)程而采用zygote fork问畅?
因?yàn)閼?yīng)用是獨(dú)立運(yùn)行在dalvik中,他們的進(jìn)程空間是分開的六荒。如果每個(gè)應(yīng)用都是新建進(jìn)程护姆,那么zygote加載的系統(tǒng)資源就會(huì)重復(fù)創(chuàng)建添加浪費(fèi)系統(tǒng)資源。且zygote孵化也可節(jié)約創(chuàng)建的時(shí)間掏击。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末卵皂,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子铐料,更是在濱河造成了極大的恐慌渐裂,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,378評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件钠惩,死亡現(xiàn)場(chǎng)離奇詭異柒凉,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)篓跛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,970評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門膝捞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人愧沟,你說(shuō)我怎么就攤上這事蔬咬。” “怎么了沐寺?”我有些...
    開封第一講書人閱讀 168,983評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵林艘,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我混坞,道長(zhǎng)狐援,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,938評(píng)論 1 299
  • 正文 為了忘掉前任究孕,我火速辦了婚禮啥酱,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘厨诸。我一直安慰自己镶殷,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,955評(píng)論 6 398
  • 文/花漫 我一把揭開白布微酬。 她就那樣靜靜地躺著绘趋,像睡著了一般颤陶。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上埋心,一...
    開封第一講書人閱讀 52,549評(píng)論 1 312
  • 那天指郁,我揣著相機(jī)與錄音,去河邊找鬼拷呆。 笑死闲坎,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的茬斧。 我是一名探鬼主播腰懂,決...
    沈念sama閱讀 41,063評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼项秉!你這毒婦竟也來(lái)了绣溜?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,991評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤娄蔼,失蹤者是張志新(化名)和其女友劉穎怖喻,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體岁诉,經(jīng)...
    沈念sama閱讀 46,522評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡锚沸,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,604評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了涕癣。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片哗蜈。...
    茶點(diǎn)故事閱讀 40,742評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖坠韩,靈堂內(nèi)的尸體忽然破棺而出距潘,到底是詐尸還是另有隱情,我是刑警寧澤只搁,帶...
    沈念sama閱讀 36,413評(píng)論 5 351
  • 正文 年R本政府宣布音比,位于F島的核電站,受9級(jí)特大地震影響氢惋,放射性物質(zhì)發(fā)生泄漏硅确。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,094評(píng)論 3 335
  • 文/蒙蒙 一明肮、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧缭付,春花似錦柿估、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,572評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)的妖。三九已至,卻和暖如春足陨,著一層夾襖步出監(jiān)牢的瞬間嫂粟,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,671評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工墨缘, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留星虹,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,159評(píng)論 3 378
  • 正文 我出身青樓镊讼,卻偏偏與公主長(zhǎng)得像宽涌,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蝶棋,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,747評(píng)論 2 361

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