美文网首页
[026]Zygote中Socket通信能否替换成Binder通

[026]Zygote中Socket通信能否替换成Binder通

作者: 王小二的技术栈 | 来源:发表于2020-02-18 18:42 被阅读0次

    首先声明一下这是一个讨论帖,我只是论述一下个人的观点,欢迎大家讲事实摆道理。

    前言

    大家都知道App进程是AMS通过通过Socket通信通知Zygote孵化出来的,借用gityuan的图就是图中的第2步,能否用Binder通信替换Socket通信?我们只讨论技术上实现的可能性,不讨论两者性能上的差异。


    我的观点

    能替换成Binder通信。

    我的论据

    我实在是想不出用Binder通信替换Socket通信的缺陷在哪里?

    别人观点

    既然我想不出,肯定网上有人持否定态度,我们看看他们说的有没有道理。

    观点1:并发问题

    链接:

    https://blog.csdn.net/qq_39037047/article/details/88066589

    观点描述:

    怕父进程binder线程有锁,然后子进程的主线程一直在等其子线程(从父进程拷贝过来的子进程)的资源,但是其实父进程的子进程并没有被拷贝过来,造成死锁,所以fork不允许存在多线程。而非常巧的是Binder通讯偏偏就是多线程,所以干脆父进程(Zygote)这个时候就不使用binder线程

    反驳:

    我们完全可以将Zygote进程的主线程作为唯一的Binder线程,这样子也就没有这个问题了。

    观点2:父子进程共享FD问题(其实这个是我以前早期的观点)

    观点描述:

    因为Zygote在open("dev/binder")中带有的flag是O_CLOEXEC,fork之后,在子进程执行EXEC的时候,会因为O_CLOEXEC的条件关闭这个共享FD,就会调用binder_release的代码,顺带清空父进程的FD对应file结构体中private_data对象保存的binder_proc,影响父进程的Binder通信功能。

    /frameworks/native/libs/binder/ProcessState.cpp
    static int open_driver(const char *driver)
    {
        int fd = open(driver, O_RDWR | O_CLOEXEC);
        return fd;
    }
    
    drivers/staging/android/binder.c
    static int binder_release(struct inode *nodp, struct file *filp)
    {
        struct binder_proc *proc = filp->private_data;
        debugfs_remove(proc->debugfs_entry);
        binder_defer_work(proc, BINDER_DEFERRED_RELEASE);//调用到代码1.1
        return 0;
    }
    
    //1.1
    binder_defer_work(struct binder_proc *proc, enum binder_deferred_state defer)
    {
        mutex_lock(&binder_deferred_lock);
        proc->deferred_work |= defer;
        if (hlist_unhashed(&proc->deferred_work_node)) {
            hlist_add_head(&proc->deferred_work_node,
                    &binder_deferred_list);
            //发起一个workqueue去执行binder_deferred_work,也就是代码1.2
            queue_work(binder_deferred_workqueue, &binder_deferred_work);
        }
        mutex_unlock(&binder_deferred_lock);
    }
    
    //1.2
    static DECLARE_WORK(binder_deferred_work, binder_deferred_func);
    static void binder_deferred_func(struct work_struct *work)
    {
        ...
        do {
            ...
            if (defer & BINDER_DEFERRED_RELEASE)
                binder_deferred_release(proc); /* frees proc */ //代码1.3
                    ...
        } while (proc);
    }
    
    //1.3
    static void binder_deferred_release(struct binder_proc *proc)
    {
        ...
        kfree(proc);//这里会释放父进程的binder_proc
    }
    
    
    反驳:

    链接:https://blog.csdn.net/scarecrow_byr/article/details/91410131

    上述的观点对O_CLOEXEC的理解有些偏差,正确的理解应该是在linux系统中,父进程打开一个文件fd可以带上O_CLOEXEC标志位,fork之后,子进程得到父进程的完整拷贝,对于父进程已经open的文件,子进程也可以得到一样的fd。内核里,子进程只是把fd对应的file指针指向父进程fd对应的struct file,并且把file的引用加1。子进程中用exec系列系统调用加载新的可执行程序之前,会关闭子进程中父进程O_CLOEXEC标志打开的fd。子进程关闭该fd时候,但是因为父进程还持有fd的引用计数,所以这个关闭的动作只会执行fops的flush回调函数,并没有真正调用fops的release回调函数。

    看Binder驱动中实现的flush回调函数binder_flush,最后调用的binder_deferred_flush方法中,并没有释放binder_proc,只是唤醒一下父进程的Binder线程而已。

    static void binder_deferred_flush(struct binder_proc *proc)
    {
        struct rb_node *n;
        int wake_count = 0;
    
        for (n = rb_first(&proc->threads); n != NULL; n = rb_next(n)) {
            struct binder_thread *thread = rb_entry(n, struct binder_thread, rb_node);
    
            thread->looper |= BINDER_LOOPER_STATE_NEED_RETURN;
            if (thread->looper & BINDER_LOOPER_STATE_WAITING) {
                wake_up_interruptible(&thread->wait);
                wake_count++;
            }
        }
        wake_up_interruptible_all(&proc->wait);
    
        binder_debug(BINDER_DEBUG_OPEN_CLOSE,
                 "binder_flush: %d woke %d threads\n", proc->pid,
                 wake_count);
    }
    

    总结

    以上就是我觉得看似合理的两个观点的反驳,如果你们有新的观点,或者觉得我的反驳中的论据有问题。

    欢迎留言交流。

    相关文章

      网友评论

          本文标题:[026]Zygote中Socket通信能否替换成Binder通

          本文链接:https://www.haomeiwen.com/subject/fkawfhtx.html