前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[026]Zygote中Socket通信能否替换成Binder通信?

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

作者头像
王小二
发布2020-06-08 10:51:34
1.8K0
发布2020-06-08 10:51:34
举报

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

前言

大家都知道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通信功能。

代码语言:javascript
复制
/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线程而已。

代码语言:javascript
复制
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);
}

总结

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

欢迎留言交流。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 我的观点
  • 我的论据
  • 别人观点
  • 观点1:并发问题
  • 观点2:父子进程共享FD问题(其实这个是我以前早期的观点)
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档