1 总览
想要使用binder完成进程间通信(IPC)或者完成远程过程调用(RPC),那么我们需要有如下三个要素:
- 源:即调用者(Client)
- 目的:即服务提供者(Server)。这里会有一个问题,client怎么知道我要向哪里发送数据呢?这里就需要用到ServiceManager,Server需要先注册到ServiceManager中,Client再向ServiceManager查询服务获得一个handle。
- 数据:Client想要调用Server的哪个方法,传输什么参数,返回什么结果,需要事先约定好协议,放在一个Buffer当中。
我绘制了一张Client、Server、ServiceManager以及Binder Driver四者的关系图,图中虚线都是RPC调用,实际都是通过ioctl与binder驱动进行交互,实现Client和Server通讯的功能。从图上可以看到,ServiceManager与Server/Client之间的调用也是虚线,也就说ServiceManager也是一个binder service,只不过是一个特殊的service。
2 Binder Driver
从上面可以看到,client和server之间的通讯都是通过与binder驱动的交互来完成的,所以如果不了解binder驱动,那么是很难理解binder的。binder驱动相关的代码参考 binder.c。
const struct file_operations binder_fops = {
.owner = THIS_MODULE,
.poll = binder_poll,
.unlocked_ioctl = binder_ioctl,
.compat_ioctl = compat_ptr_ioctl,
.mmap = binder_mmap,
.open = binder_open,
.flush = binder_flush,
.release = binder_release,
};
我们在用户态调用的mmap、open、iotcl实际调用的是binder.c中定义的binder_mmap、binder_open、binder_ioctl。
接下来我们将以MediaServer为例,初步了解与binder驱动相关的系统调用。MediaServer的代码参考 main_mediaserver.cpp
int main(int argc __unused, char **argv __unused)
{
// 1. 打开binder驱动,mmap
sp<ProcessState> proc(ProcessState::self());
// 2. 获取servicemanager
sp<IServiceManager> sm(defaultServiceManager());
// 3. 注册到servicemanager
MediaPlayerService::instantiate();
// 4. 开始监听
::android::hardware::configureRpcThreadpool(16, false);
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
::android::hardware::joinRpcThreadpool();
}
2.1 binder_open
创建ProcessState
单例对象时,会先调用open打开binder驱动,代码可以参考 ProcessState.cpp
int fd = open(driver, O_RDWR | O_CLOEXEC);
这里就会进入内核态调用binder驱动的binder_open方法。
static HLIST_HEAD(binder_procs);
static int binder_open(struct inode *nodp, struct file *filp)
{
// 1. 创建一个binder_proc指针并为其开辟空间
struct binder_proc *proc, *itr;
struct binder_device *binder_dev;
proc = kzalloc(sizeof(*proc), GFP_KERNEL);
// 初始化binder_proc的成员
get_task_struct(current->group_leader);
proc->tsk = current->group_leader;
proc->cred = get_cred(filp->f_cred);
INIT_LIST_HEAD(&proc->todo);
// 2. 获取binder driver里的binder_device
binder_dev = container_of(filp->private_data, struct binder_device, miscdev);
refcount_inc(&binder_dev->ref);
// 将binder_proc中的binder_context指向binder驱动的binder_context
proc->context = &binder_dev->context;
// 3. 初始化binder_alloc
binder_alloc_init(&proc->alloc);
// 4. 记录当前调用进程的pid
proc->pid = current->group_leader->pid;
INIT_LIST_HEAD(&proc->delivered_death);
INIT_LIST_HEAD(&proc->waiting_threads);
// 5. 将binder_proc记录到fd当中
filp->private_data = proc;
// 6. 将binder_proc加入到全局链表binder_procs当中
hlist_add_head(&proc->proc_node, &binder_procs);
return 0;
}
我这里删除了很多代码内容,只留下了我认为比较重要的步骤:
- 为调用进程创建一个
binder_proc
对象,binder驱动会为每个进程都创建这样一个对象; - 获取binder驱动中的
binder_device
,该对象是属于binder设备的,于binder_init中创建并初始化。这里获取binder_device
是因为它里面存储有一个binder_context
成员,这个成员就是ServiceManager
,这个后面会讲到; - 为
binder_proc
初始化成员binder_alloc
,后续mmap分配的内存将会记录在该成员中; - 将当前进程pid记录到
binder_proc
,有了它binder驱动就可以从茫茫进程中找到目标进程了; - 将
binder_proc
记录到fd当中,后续每次iotcl都会通过该fd获取到binder_proc
; - 将
binder_proc
加入到全局链表binder_procs
当中;
2.2 binder_mmap
执行完binder_open之后,ProcessState
会用回传的fd执行mmap
mVMStart = mmap(nullptr, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE,opened.value(), 0);
这里就会进入内核态调用binder驱动的binder_mmap方法。
static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
{
struct binder_proc *proc = filp->private_data;
if (proc->tsk != current->group_leader)
return -EINVAL;
vma->vm_flags |= VM_DONTCOPY | VM_MIXEDMAP;
vma->vm_flags &= ~VM_MAYWRITE;
vma->vm_ops = &binder_vm_ops;
vma->vm_private_data = proc;
// 核心工作
return binder_alloc_mmap_handler(&proc->alloc, vma);
}
binder_mmap的核心工作在binder_alloc_mmap_handler中完成,由于比较复杂,我对驱动也不了解,所以就不贴代码了。
我们要了解的是通过binder_mmap可以为当前进程开辟一块共享内存,可由用户态和内核态共享,每个进程都会有这样一块内存,内存信息存储在binder_proc
的binder_alloc
成员中,通过这块内存可以完成其他博文中所说的一次拷贝的功能,如何拷贝的我们后面再看。
2.3 binder_ioctl
通过binder_ioctl,用户态就可以与binder驱动实现通信了。
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret;
// 找到当前进程的binder_proc
struct binder_proc *proc = filp->private_data;
struct binder_thread *thread;
unsigned int size = _IOC_SIZE(cmd);
void __user *ubuf = (void __user *)arg;
if (ret)
goto err_unlocked;
// 获取到线程,这里不在本次阅读的重点中,暂时忽略
thread = binder_get_thread(proc);
if (thread == NULL) {
ret = -ENOMEM;
goto err;
}
// 执行cmd
switch (cmd) {
......
}
binder驱动会根据传入的fd、cmd以及对应的参数来执行对应的操作,这里暂时不去看里面具体执行了什么。
到这里binder驱动就初步了解结束,我认为这篇笔记比较重要的点在于理解binder_proc
的作用:binder驱动会为每个调用进程(无论是源进程还是目标进程)都创建一个binder_proc
,并且记录到全局变量当中,后续可以通过binder_proc
中的pid实现目标进程的查找。
可能这里对新同学来说还比较抽象,不过不要着急,后面我们慢慢看。文章来源:https://www.toymoban.com/news/detail-509542.html
根据MediaServer的调用顺序,打开binder驱动后,会去获取ServiceManager,所以接下来我们先去了解ServiceManager的创建,获取以及调用。文章来源地址https://www.toymoban.com/news/detail-509542.html
到了这里,关于Android 13(T) - binder阅读(1)- binder driver的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!