{//代表Binder进程
struct binder_proc *proc;//1
binder_debug(BINDER_DEBUG_OPEN_CLOSE, “binder_open: %d:%d\n”,
current->group_leader->pid, current->pid);
//分配内存空间
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结构体代表Binder进程,用于管理Binder的各种信息。注释2处用于为binder_proc分配内存空间。注释3处将binder_proc赋值给file指针的private_data变量,下面还会再次提到这个private_data变量。
2. 注册成为Binder机制的上下文管理者
binder_become_context_manager函数用于将servicemanager注册成为Binder机制的上下文管理者,这个管理者在整个系统中只有一个,代码如下所示。
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函数会调用Binder驱动的binder_ioctl函数,binder_ioctl函数的代码比较多,这里截取了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,这个private_data变量在binder_open函数中讲过,是一个binder_proc结构体。
注释2处的binder_get_thread函数用于获取binder_thread,binder_thread结构体指的是Binder线程,binder_get_thread函数内部会从传入的参数binder_proc中查找binder_thread,如果查询到则直接返回,如果查询不到则会创建一个新的binderthread并返回。
注释3处的全局变量binder_context_mgr_node代表Binder机制的上下文管理者对应的一个Binder对象,如果它不为NULL,说明此前自身已经被注册为Binder的上下文管理者了,Binder的上下文管理者是不能重复注册的,因此会goto到err标签。
注释4处的全局变量binder_context_mgr_uid代表注册了Binder机制上下文管理者的进程的有效用户ID,如果它的值不为-1,说明此前已经有进程注册Binder的上下文管理者了,因此在注释5处判断当前进程的有效用户ID是否等于binder_context_mgr_uid,如果不等于binder_context_mgr_uid就goto到err标签。
如果不满足注释4处的条件,说明此前没有进程注册Binder机制的上下文管理者,这时就会在注释6处将当前进程的有效用户ID赋值给全局变量binder_context_mgr_uid,另外还会在注释7处调用binder_new_node函数创建一个Binder对象并赋值给全局变量binder_context_mgr_node。
3. 循环等待和处理客户端发来的请求
servicemanager成功注册成为Binder机制的上下文管理者后,servicemanager就是Binder机制的“总管”了,它需要在系统运行期间处理客户端的请求,由于客户端的请求不确定何时发送,因此需要通过无限循环来实现,实现这一需求的函数就是binder_loop,代码如下所示。
frameworks/native/cmds/servicemanager/binder.c
{
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函数写入Binder驱动中,这样当前线程(ServiceManager的主线程)就成为一个Binder线程,这样就可以处理进程间的请求了。
在无限循环中不断调用注释2处的ioctl函数,ioctl函数使用BINDER_WRITE_READ指令查询Binder驱动中是否有新的请求,如果有新的请求就交给注释3处的binder_parse函数处理。如果没有新的请求,当前线程就会在Binder驱动中睡眠,等待新的进程间请求。
由于binder_write函数的调用链中涉及了内核空间和用户空间的交互,因此这里着重讲解这部分内容,代码如下所示。
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结构体,接下来的代码对bwr进行赋值,其中需要注意的是,注释2处data的值为BC_ENTER_LOOPER。注释3处的ioctl函数会将bwr中的数据发送给binder驱动,我们已经知道了ioctl函数在Kernel Binder中对应的函数为binder_ioctl,此前分析过这个函数,这里截取BINDER_WRITE_READ命令处理部分,代码如下所示。
kernel/goldfish/drivers/staging/android/binder.c
{
…
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函数用于将用户空间数据ubuf复制出来并保存到内核数据bwr(binder_write_read结构体)中。
注释2处的bwr的输入缓存区有数据时,会调用注释3处的binder_thread_write函数来处理BC_ENTER_LOOPER协议,其内部会将目标线程的状态设置为BINDER_LOOPER_ STATE_ENTERED,这样目标线程就是一个Binder线程。
注释4处通过copyto_user函数将内核空间数据bwr复制到用户空间。
Android及Binder相关学习资源
其实客户端开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。
这里再分享一下我面试期间的复习路线:(以下体系的复习资料是我从各路大佬收集整理好的)
《Android开发七大模块核心知识笔记》
尾声
最后,我再重复一次,如果你想成为一个优秀的 Android 开发人员,请集中精力,对基础和重要的事情做深度研究。
对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。 整理的这些架构技术希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。
这里,笔者分享一份从架构哲学的层面来剖析的视频及资料分享给大家梳理了多年的架构经验,筹备近6个月最新录制的,相信这份视频能给你带来不一样的启发、收获。
Android进阶学习资料库
一共十个专题,包括了Android进阶所有学习资料,Android进阶视频,Flutter,java基础,kotlin,NDK模块,计算机网络,数据结构与算法,微信小程序,面试题解析,framework源码!
-
自行下载直达领取链接:点击这里前往GitHub
这里,笔者分享一份从架构哲学的层面来剖析的视频及资料分享给大家梳理了多年的架构经验,筹备近6个月最新录制的,相信这份视频能给你带来不一样的启发、收获。
[外链图片转存中…(img-5SR5bpVs-1644045952659)]
Android进阶学习资料库
一共十个专题,包括了Android进阶所有学习资料,Android进阶视频,Flutter,java基础,kotlin,NDK模块,计算机网络,数据结构与算法,微信小程序,面试题解析,framework源码!
[外链图片转存中…(img-dQwmjB3X-1644045952661)]
-
自行下载直达领取链接:点击这里前往GitHub