首先声明一下这是一个讨论帖,我只是论述一下个人的观点,欢迎大家讲事实摆道理。
前言
大家都知道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);
}
总结
以上就是我觉得看似合理的两个观点的反驳,如果你们有新的观点,或者觉得我的反驳中的论据有问题。
欢迎留言交流。
网友评论