Android Binder原理(四)ServiceManager的啟動(dòng)過程

本文首發(fā)于微信公眾號(hào)「后廠技術(shù)官」

關(guān)聯(lián)系列
Android AOSP基礎(chǔ)系列
Android系統(tǒng)啟動(dòng)系列
應(yīng)用進(jìn)程啟動(dòng)系列
Android深入四大組件系列
Android深入理解Context系列
Android深入理解JNI系列
Android解析WindowManager
Android解析WMS系列
Android解析AMS系列
Android包管理機(jī)制系列
Android輸入系統(tǒng)系列

前言

在上一篇文章中蚤霞,我們以MediaPlayerService為例窖壕,講解了系統(tǒng)服務(wù)是如何注冊(cè)的(addService)袖牙,既然有注冊(cè)就勢(shì)必要有獲取,但是在了解獲取服務(wù)前,我們最好先了解ServiceManager的啟動(dòng)過程,這樣更有助于理解系統(tǒng)服務(wù)的注冊(cè)和獲取的過程。

另外還有一點(diǎn)需要說明的是,要想了解ServiceManager的啟動(dòng)過程檩小,需要查看Kernel Binder部分的源碼烟勋,這部分代碼在內(nèi)核源碼中阻肿,AOSP源碼是不包括內(nèi)核源碼的畜疾,因此需要單獨(dú)下載,見Android AOSP基礎(chǔ)(二)AOSP源碼和內(nèi)核源碼下載這篇文章忿偷。

1.ServiceManager的入口函數(shù)

ServiceManager是init進(jìn)程負(fù)責(zé)啟動(dòng)的茶凳,具體是在解析init.rc配置文件時(shí)啟動(dòng)的,init進(jìn)程是在系統(tǒng)啟動(dòng)時(shí)啟動(dòng)的,因此ServiceManager亦是如此,不理解init進(jìn)程和init.rc的可以看Android系統(tǒng)啟動(dòng)流程(一)解析init進(jìn)程啟動(dòng)過程這篇文章。
rc文件內(nèi)部由Android初始化語(yǔ)言編寫(Android Init Language)編寫的腳本,它主要包含五種類型語(yǔ)句:Action择膝、Commands、Services窃页、Options和Import畦木。
在Android 7.0中對(duì)init.rc文件進(jìn)行了拆分勾栗,每個(gè)服務(wù)一個(gè)rc文件绣夺。ServiceManager的啟動(dòng)腳本在servicemanager.rc中:
frameworks/native/cmds/servicemanager/servicemanager.rc

service servicemanager /system/bin/servicemanager
    class core animation
    user system  //1
    group system readproc
    critical //2
    onrestart restart healthd  
    onrestart restart zygote
    onrestart restart audioserver
    onrestart restart media
    onrestart restart surfaceflinger
    onrestart restart inputflinger
    onrestart restart drm
    onrestart restart cameraserver
    onrestart restart keystore
    onrestart restart gatekeeperd
    writepid /dev/cpuset/system-background/tasks
    shutdown critical

service用于通知init進(jìn)程創(chuàng)建名為servicemanager的進(jìn)程,這個(gè)servicemanager進(jìn)程執(zhí)行程序的路徑為/system/bin/servicemanager烈钞。
注釋1的關(guān)鍵字user說明servicemanager是以用戶system的身份運(yùn)行的泊碑,注釋2處的critical說明servicemanager是系統(tǒng)中的關(guān)鍵服務(wù),關(guān)鍵服務(wù)是不會(huì)退出的毯欣,如果退出了馒过,系統(tǒng)就會(huì)重啟,當(dāng)系統(tǒng)重啟時(shí)就會(huì)啟動(dòng)用onrestart關(guān)鍵字修飾的進(jìn)程酗钞,比如zygote腹忽、media、surfaceflinger等等砚作。

servicemanager的入口函數(shù)在service_manager.c中:
frameworks/native/cmds/servicemanager/service_manager.c

int main(int argc, char** argv)
{
    struct binder_state *bs;//1
    union selinux_callback cb;
    char *driver;

    if (argc > 1) {
        driver = argv[1];
    } else {
        driver = "/dev/binder";
    }

    bs = binder_open(driver, 128*1024);//2
    ...
    if (binder_become_context_manager(bs)) {//3
        ALOGE("cannot become context manager (%s)\n", strerror(errno));
        return -1;
    }
    ...
    if (getcon(&service_manager_context) != 0) {
        ALOGE("SELinux: Failed to acquire service_manager context. Aborting.\n");
        abort();
    }
    binder_loop(bs, svcmgr_handler);//4

    return 0;
}

注釋1處的binder_state結(jié)構(gòu)體用來存儲(chǔ)binder的三個(gè)信息:

struct binder_state
{
    int fd; //binder設(shè)備的文件描述符
    void *mapped; //binder設(shè)備文件映射到進(jìn)程的地址空間
    size_t mapsize; //內(nèi)存映射后窘奏,系統(tǒng)分配的地址空間的大小,默認(rèn)為128KB
};

main函數(shù)主要做了三件事:
1.注釋2處調(diào)用binder_open函數(shù)用于打開binder設(shè)備文件葫录,并申請(qǐng)128k字節(jié)大小的內(nèi)存空間着裹。
2.注釋3處調(diào)用binder_become_context_manager函數(shù),將servicemanager注冊(cè)成為Binder機(jī)制的上下文管理者米同。
3.注釋4處調(diào)用binder_loop函數(shù)骇扇,循環(huán)等待和處理client端發(fā)來的請(qǐng)求。

現(xiàn)在對(duì)這三件事分別進(jìn)行講解面粮。

1.1 打開binder設(shè)備

binder_open函數(shù)用于打開binder設(shè)備文件少孝,并且將它映射到進(jìn)程的地址空間,如下所示但金。

frameworks/native/cmds/servicemanager/binder.c

struct binder_state *binder_open(const char* driver, size_t mapsize)
{
    struct binder_state *bs;
    struct binder_version vers;

    bs = malloc(sizeof(*bs));
    if (!bs) {
        errno = ENOMEM;
        return NULL;
    }

    bs->fd = open(driver, O_RDWR | O_CLOEXEC);//1
    if (bs->fd < 0) {
        fprintf(stderr,"binder: cannot open %s (%s)\n",
                driver, strerror(errno));
        goto fail_open;
    }
    //獲取Binder的version
    if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) ||
        (vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION)) {//2
        fprintf(stderr,
                "binder: kernel driver version (%d) differs from user space version (%d)\n",
                vers.protocol_version, BINDER_CURRENT_PROTOCOL_VERSION);
        goto fail_open;
    }

    bs->mapsize = mapsize;
    bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);//3
    if (bs->mapped == MAP_FAILED) {
        fprintf(stderr,"binder: cannot map device (%s)\n",
                strerror(errno));
        goto fail_map;
    }
    return bs;

fail_map:
    close(bs->fd);
fail_open:
    free(bs);
    return NULL;
}

注釋1處用于打開binder設(shè)備文件韭山,后面會(huì)進(jìn)行分析郁季。
注釋2處的ioctl函數(shù)用于獲取Binder的版本冷溃,如果獲取不到或者內(nèi)核空間和用戶空間的binder不是同一個(gè)版本就會(huì)直接goto到fail_open標(biāo)簽,釋放binder的內(nèi)存空間梦裂。
注釋3處調(diào)用mmap函數(shù)進(jìn)行內(nèi)存映射似枕,通俗來講就是將binder設(shè)備文件映射到進(jìn)程的地址空間,地址空間的大小為mapsize年柠,也就是128K凿歼。映射完畢后會(huì)將地址空間的起始地址和大小保存在binder_state結(jié)構(gòu)體中的mapped和mapsize變量中。

這里著重說一下open函數(shù),它會(huì)調(diào)用Kernel Binder部分的binder_open函數(shù)答憔,這部分源碼位于內(nèi)核源碼中味赃,這里展示的代碼版本為goldfish3.4。

用戶態(tài)和內(nèi)核態(tài)
臨時(shí)插入一個(gè)知識(shí)點(diǎn):用戶態(tài)和內(nèi)核態(tài)
Intel的X86架構(gòu)的CPU提供了0到3四個(gè)特權(quán)級(jí)虐拓,數(shù)字越小心俗,權(quán)限越高,Linux操作系統(tǒng)中主要采用了0和3兩個(gè)特權(quán)級(jí)蓉驹,分別對(duì)應(yīng)的就是內(nèi)核態(tài)與用戶態(tài)城榛。用戶態(tài)的特權(quán)級(jí)別低,因此進(jìn)程在用戶態(tài)下不經(jīng)過系統(tǒng)調(diào)用是無(wú)法主動(dòng)訪問到內(nèi)核空間中的數(shù)據(jù)的态兴,這樣用戶無(wú)法隨意的進(jìn)入所有進(jìn)程共享的內(nèi)核空間狠持,起到了保護(hù)的作用。下面來介紹下什么是用戶態(tài)和內(nèi)核態(tài)瞻润。
當(dāng)一個(gè)進(jìn)程在執(zhí)行用戶自己的代碼時(shí)處于用戶態(tài)喘垂,比如open函數(shù),它運(yùn)行在用戶空間绍撞,當(dāng)前的進(jìn)程處于用戶態(tài)王污。
當(dāng)一個(gè)進(jìn)程因?yàn)橄到y(tǒng)調(diào)用進(jìn)入內(nèi)核代碼中執(zhí)行時(shí)就處于內(nèi)核態(tài),比如open函數(shù)通過系統(tǒng)調(diào)用(__open()函數(shù))楚午,查找到了open函數(shù)在Kernel Binder對(duì)應(yīng)的函數(shù)為binder_open昭齐,這時(shí)binder_open運(yùn)行在內(nèi)核空間,當(dāng)前的進(jìn)程由用戶態(tài)切換到內(nèi)核態(tài)矾柜。

kernel/goldfish/drivers/staging/android/binder.c

static int binder_open(struct inode *nodp, struct file *filp)
{   //代表Binder進(jìn)程
    struct binder_proc *proc;//1
    binder_debug(BINDER_DEBUG_OPEN_CLOSE, "binder_open: %d:%d\n",
             current->group_leader->pid, current->pid);
    //分配內(nèi)存空間
    proc = kzalloc(sizeof(*proc), GFP_KERNEL);//2
    if (proc == NULL)
        return -ENOMEM;
    get_task_struct(current);
    proc->tsk = current;
    INIT_LIST_HEAD(&proc->todo);
    init_waitqueue_head(&proc->wait);
    proc->default_priority = task_nice(current);
    //binder同步鎖
    binder_lock(__func__);

    binder_stats_created(BINDER_STAT_PROC);
    hlist_add_head(&proc->proc_node, &binder_procs);
    proc->pid = current->group_leader->pid;
    INIT_LIST_HEAD(&proc->delivered_death);
    filp->private_data = proc;//3
    //binder同步鎖釋放
    binder_unlock(__func__);
    ...
    return 0;
}

注釋1處的binder_proc結(jié)構(gòu)體代表binder進(jìn)程阱驾,用于管理binder的各種信息。注釋2處用于為binder_proc分配內(nèi)存空間怪蔑。注釋3處將binder_proc賦值給file指針的private_data變量里覆,后面的1.2小節(jié)會(huì)再次提到這個(gè)private_data變量。

1.2 注冊(cè)成為Binder機(jī)制的上下文管理者

binder_become_context_manager函數(shù)用于將servicemanager注冊(cè)成為Binder機(jī)制的上下文管理者缆瓣,這個(gè)管理者在整個(gè)系統(tǒng)只有一個(gè)喧枷,代碼如下所示。
frameworks/native/cmds/servicemanager/binder.c

int binder_become_context_manager(struct binder_state *bs)
{
    return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}

ioctl函數(shù)會(huì)調(diào)用Binder驅(qū)動(dòng)的binder_ioctl函數(shù)弓坞,binder_ioctl函數(shù)代碼比較多隧甚,這里截取BINDER_SET_CONTEXT_MGR的處理部分,代碼如下所示渡冻。
kernel/goldfish/drivers/staging/android/binder.c

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    int ret;
    struct binder_proc *proc = filp->private_data; //1
    struct binder_thread *thread;
    unsigned int size = _IOC_SIZE(cmd);
    void __user *ubuf = (void __user *)arg;
    trace_binder_ioctl(cmd, arg);

    ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
    if (ret)
        goto err_unlocked;

    binder_lock(__func__);
    thread = binder_get_thread(proc);//2
    if (thread == NULL) {
        ret = -ENOMEM;
        goto err;
    }

    switch (cmd) {
    ...
    case BINDER_SET_CONTEXT_MGR:
        if (binder_context_mgr_node != NULL) {//3
            printk(KERN_ERR "binder: BINDER_SET_CONTEXT_MGR already set\n");
            ret = -EBUSY;
            goto err;
        }
        ret = security_binder_set_context_mgr(proc->tsk);
        if (ret < 0)
            goto err;
        if (binder_context_mgr_uid != -1) {//4
            if (binder_context_mgr_uid != current->cred->euid) {//5
                printk(KERN_ERR "binder: BINDER_SET_"
                       "CONTEXT_MGR bad uid %d != %d\n",
                       current->cred->euid,
                       binder_context_mgr_uid);
                ret = -EPERM;
                goto err;
            }
        } else
            binder_context_mgr_uid = current->cred->euid;//6
        binder_context_mgr_node = binder_new_node(proc, NULL, NULL);//7
        if (binder_context_mgr_node == NULL) {
            ret = -ENOMEM;
            goto err;
        }
        binder_context_mgr_node->local_weak_refs++;
        binder_context_mgr_node->local_strong_refs++;
        binder_context_mgr_node->has_strong_ref = 1;
        binder_context_mgr_node->has_weak_ref = 1;
        break;
 ...
err_unlocked:
    trace_binder_ioctl_done(ret);
    return ret;
}

注釋1處將file指針中的private_data變量賦值給binder_proc戚扳,這個(gè)private_data變量在binder_open函數(shù)中講過,是一個(gè)binder_proc結(jié)構(gòu)體族吻。注釋2處的binder_get_thread函數(shù)用于獲取binder_thread帽借,binder_thread結(jié)構(gòu)體指的是binder線程珠增,binder_get_thread函數(shù)內(nèi)部會(huì)從傳入的參數(shù)binder_proc中查找binder_thread,如果查詢到直接返回砍艾,如果查詢不到會(huì)創(chuàng)建一個(gè)新的binder_thread并返回蒂教。
注釋3處的全局變量binder_context_mgr_node代表的是Binder機(jī)制的上下文管理者對(duì)應(yīng)的一個(gè)Binder對(duì)象,如果它不為NULL脆荷,說明此前自身已經(jīng)被注冊(cè)為Binder的上下文管理者了悴品,Binder的上下文管理者是不能重復(fù)注冊(cè)的,因此會(huì)goto到err標(biāo)簽简烘。
注釋4處的全局變量binder_context_mgr_uid代表注冊(cè)了Binder機(jī)制上下文管理者的進(jìn)程的有效用戶ID苔严,如果它的值不為-1,說明此前已經(jīng)有進(jìn)程注冊(cè)Binder的上下文管理者了孤澎,因此在注釋5處判斷當(dāng)前進(jìn)程的有效用戶ID是否等于binder_context_mgr_uid届氢,不等于就goto到err標(biāo)簽。
如果不滿足注釋4的條件覆旭,說明此前沒有進(jìn)程注冊(cè)Binder機(jī)制的上下文管理者退子,就會(huì)在注釋6處將當(dāng)前進(jìn)程的有效用戶ID賦值給全局變量binder_context_mgr_uid,另外還會(huì)在注釋7處調(diào)用binder_new_node函數(shù)創(chuàng)建一個(gè)Binder對(duì)象并賦值給全局變量binder_context_mgr_node型将。

1.3 循環(huán)等待和處理client端發(fā)來的請(qǐng)求

servicemanager成功注冊(cè)成為Binder機(jī)制的上下文管理者后寂祥,servicemanager就是Binder機(jī)制的“總管”了,它需要在系統(tǒng)運(yùn)行期間處理client端的請(qǐng)求七兜,由于client端的請(qǐng)求不確定何時(shí)發(fā)送丸凭,因此需要通過無(wú)限循環(huán)來實(shí)現(xiàn),實(shí)現(xiàn)這一需求的函數(shù)就是binder_loop腕铸。
frameworks/native/cmds/servicemanager/binder.c

void binder_loop(struct binder_state *bs, binder_handler func)
{
    int res;
    struct binder_write_read bwr;
    uint32_t readbuf[32];

    bwr.write_size = 0;
    bwr.write_consumed = 0;
    bwr.write_buffer = 0;

    readbuf[0] = BC_ENTER_LOOPER;
    binder_write(bs, readbuf, sizeof(uint32_t));//1

    for (;;) {
        bwr.read_size = sizeof(readbuf);
        bwr.read_consumed = 0;
        bwr.read_buffer = (uintptr_t) readbuf;

        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);//2

        if (res < 0) {
            ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
            break;
        }

        res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);//3
        if (res == 0) {
            ALOGE("binder_loop: unexpected reply?!\n");
            break;
        }
        if (res < 0) {
            ALOGE("binder_loop: io error %d %s\n", res, strerror(errno));
            break;
        }
    }
}

注釋1處將BC_ENTER_LOOPER指令通過binder_write函數(shù)寫入到Binder驅(qū)動(dòng)中惜犀,這樣當(dāng)前線程(ServiceManager的主線程)就成為了一個(gè)Binder線程,這樣就可以處理進(jìn)程間的請(qǐng)求了狠裹。
在無(wú)限循環(huán)中不斷的調(diào)用注釋2處的ioctl函數(shù)虽界,它不斷的使用BINDER_WRITE_READ指令查詢Binder驅(qū)動(dòng)中是否有新的請(qǐng)求,如果有就交給注釋3處的binder_parse函數(shù)處理涛菠。如果沒有莉御,當(dāng)前線程就會(huì)在Binder驅(qū)動(dòng)中睡眠,等待新的進(jìn)程間請(qǐng)求俗冻。

由于binder_write函數(shù)的調(diào)用鏈中涉及到了內(nèi)核空間和用戶空間的交互礁叔,因此這里著重講解下。

frameworks/native/cmds/servicemanager/binder.c

int binder_write(struct binder_state *bs, void *data, size_t len)
{
    struct binder_write_read bwr;//1
    int res;

    bwr.write_size = len;
    bwr.write_consumed = 0;
    bwr.write_buffer = (uintptr_t) data;//2
    bwr.read_size = 0;
    bwr.read_consumed = 0;
    bwr.read_buffer = 0;
    res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);//3
    if (res < 0) {
        fprintf(stderr,"binder_write: ioctl failed (%s)\n",
                strerror(errno));
    }
    return res;
}

注釋1處定義binder_write_read結(jié)構(gòu)體言疗,接下來的代碼對(duì)bwr進(jìn)行賦值晴圾,其中需要注意的是颂砸,注釋2處的data的值為BC_ENTER_LOOPER噪奄。注釋3處的ioctl函數(shù)將會(huì)bwr中的數(shù)據(jù)發(fā)送給binder驅(qū)動(dòng)死姚,我們已經(jīng)知道了ioctl函數(shù)在Kernel Binder中對(duì)應(yīng)的函數(shù)為binder_ioctl,此前分析過這個(gè)函數(shù)勤篮,這里截取BINDER_WRITE_READ命令處理部分都毒。

kernel/goldfish/drivers/staging/android/binder.c

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{   
    ...
    void __user *ubuf = (void __user *)arg;
    ...
    switch (cmd) {
    case BINDER_WRITE_READ: {
        struct binder_write_read bwr;
        if (size != sizeof(struct binder_write_read)) {
            ret = -EINVAL;
            goto err;
        }
        if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {//1
            ret = -EFAULT;
            goto err;
        }
        binder_debug(BINDER_DEBUG_READ_WRITE,
                 "binder: %d:%d write %ld at %08lx, read %ld at %08lx\n",
                 proc->pid, thread->pid, bwr.write_size, bwr.write_buffer,
                 bwr.read_size, bwr.read_buffer);

        if (bwr.write_size > 0) {//2
            ret = binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed);//3
            trace_binder_write_done(ret);
            if (ret < 0) {
                bwr.read_consumed = 0;
                if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
                    ret = -EFAULT;
                goto err;
            }
        }
        ...
        binder_debug(BINDER_DEBUG_READ_WRITE,
                 "binder: %d:%d wrote %ld of %ld, read return %ld of %ld\n",
                 proc->pid, thread->pid, bwr.write_consumed, bwr.write_size,
                 bwr.read_consumed, bwr.read_size);
        if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {//4
            ret = -EFAULT;
            goto err;
        }
        break;
    }
   ...
    return ret;
}

注釋1處的copy_from_user函數(shù),在本系列的第一篇文章Android Binder原理(一)學(xué)習(xí)Binder前必須要了解的知識(shí)點(diǎn)提過碰缔。在這里账劲,它用于將把用戶空間數(shù)據(jù)ubuf拷貝出來保存到內(nèi)核數(shù)據(jù)bwr(binder_write_read結(jié)構(gòu)體)中。
注釋2處金抡,bwr的輸入緩存區(qū)有數(shù)據(jù)時(shí)瀑焦,會(huì)調(diào)用注釋3處的binder_thread_write函數(shù)來處理BC_ENTER_LOOPER協(xié)議,其內(nèi)部會(huì)將目標(biāo)線程的狀態(tài)設(shè)置為BINDER_LOOPER_STATE_ENTERED梗肝,這樣目標(biāo)線程就是一個(gè)Binder線程榛瓮。
注釋4處通過copy_to_user函數(shù)將內(nèi)核空間數(shù)據(jù)bwr拷貝到用戶空間。

2.總結(jié)

ServiceManager的啟動(dòng)過程實(shí)際上就是分析ServiceManager的入口函數(shù)巫击,在入口函數(shù)中主要做了三件事禀晓,本篇文章深入到內(nèi)核源碼來對(duì)這三件逐一進(jìn)行分析,由于涉及的函數(shù)比較多坝锰,這篇文章只介紹了我們需要掌握的粹懒,剩余大家可以自行閱讀源碼,比如binder_thread_write顷级、copy_to_user函數(shù)凫乖。

更多的內(nèi)容請(qǐng)關(guān)注我的獨(dú)立博客的知識(shí)體系:
http://liuwangshu.cn/system/


分享前沿技術(shù)、技術(shù)資訊弓颈、行業(yè)秘聞拣凹,技術(shù)管理,助力10萬(wàn)+程序員成長(zhǎng)為技術(shù)官和架構(gòu)師恨豁。
[圖片上傳失敗...(image-6b3192-1581019793755)]

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末嚣镜,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子橘蜜,更是在濱河造成了極大的恐慌菊匿,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,000評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件计福,死亡現(xiàn)場(chǎng)離奇詭異跌捆,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)象颖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門佩厚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人说订,你說我怎么就攤上這事抄瓦〕逼浚” “怎么了?”我有些...
    開封第一講書人閱讀 168,561評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵钙姊,是天一觀的道長(zhǎng)毯辅。 經(jīng)常有香客問我,道長(zhǎng)煞额,這世上最難降的妖魔是什么思恐? 我笑而不...
    開封第一講書人閱讀 59,782評(píng)論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮膊毁,結(jié)果婚禮上胀莹,老公的妹妹穿的比我還像新娘。我一直安慰自己婚温,他們只是感情好嗜逻,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著缭召,像睡著了一般栈顷。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上嵌巷,一...
    開封第一講書人閱讀 52,394評(píng)論 1 310
  • 那天萄凤,我揣著相機(jī)與錄音,去河邊找鬼搪哪。 笑死靡努,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的晓折。 我是一名探鬼主播惑朦,決...
    沈念sama閱讀 40,952評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼漓概!你這毒婦竟也來了漾月?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,852評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤胃珍,失蹤者是張志新(化名)和其女友劉穎梁肿,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體觅彰,經(jīng)...
    沈念sama閱讀 46,409評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡吩蔑,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評(píng)論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了填抬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片烛芬。...
    茶點(diǎn)故事閱讀 40,615評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出赘娄,到底是詐尸還是另有隱情仆潮,我是刑警寧澤,帶...
    沈念sama閱讀 36,303評(píng)論 5 350
  • 正文 年R本政府宣布擅憔,位于F島的核電站鸵闪,受9級(jí)特大地震影響檐晕,放射性物質(zhì)發(fā)生泄漏暑诸。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評(píng)論 3 334
  • 文/蒙蒙 一辟灰、第九天 我趴在偏房一處隱蔽的房頂上張望个榕。 院中可真熱鬧,春花似錦芥喇、人聲如沸西采。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)械馆。三九已至,卻和暖如春武通,著一層夾襖步出監(jiān)牢的瞬間霹崎,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工冶忱, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留尾菇,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,041評(píng)論 3 377
  • 正文 我出身青樓囚枪,卻偏偏與公主長(zhǎng)得像派诬,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子链沼,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評(píng)論 2 359

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