Binder驱动是Binder IPC的基石,学习了解Binder驱动的实现,有助于深入理解Binder。以下基于Orangepi3lts提供的源代码,分析Binder驱动的实现。
binder设备使用方法
了解Binder的使用方法,对理解驱动的设计思路有帮助。Binder是一种进程间通信的方法,采用主从结构,客户端可以借助Binder驱动给服务端发送消息,或者接收服务端的消息。自然可以得到下图。
但是有一个问题,客户端怎么知道服务端的handle?所以Android引入了一个叫做ServiceManager的进程,这是一个非常特殊的服务端,其handle为0,主要功能是注册和查询Service。所以上图就扩展演变成了下图。由于SM的handle为0,所以服务端可以通过SM注册成为一个Service,客户端可以通过SM查询Service的handle。当得到服务端的handle后,客户端就可以直接与服务端通信了。
通过上面的分析,Binder有以下的使用场景:
服务端注册
客服端查询
客户端向服务端发起通信
当然,还需要知道如何打开Binder驱动。下面以ServiceManager为例,展示了一个进程如何初始化Binder驱动。初始化大致分为以下3步。ServiceManager比较特殊,需要调用命令BINDER_SET_CONTEXT_MGR,整个系统也只有ServiceManager需要调用这个命令。
打开设备驱动节点
执行mmap映射内存
调用ioctl读写数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 int main (int argc, char ** argv) { struct binder_state *bs ; char *driver = "/dev/binder" ; ... bs = binder_open(driver, 128 *1024 ); binder_become_context_manager(bs); binder_loop(bs, svcmgr_handler); return 0 ; } struct binder_state *binder_open (const char * driver, size_t mapsize) { struct binder_state *bs ; bs = malloc (sizeof (*bs)); bs->fd = open(driver, O_RDWR | O_CLOEXEC); bs->mapsize = mapsize; bs->mapped = mmap(NULL , mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0 ); return bs; } int binder_become_context_manager (struct binder_state *bs) { return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0 ); }
代码结构
binder驱动代码在kernel源码的drivers/android目录下,头文件在linux-4.9/include/uapi/linux/android目录下,代码可通过linux-orangepi 获取。binder驱动代码主要分为两个文件,binder.c 包含misc设备、fops等的实现代码,以及关键数据结构的定义。与内存分配有关的代码和数据结构都定义在binder_alloc.c 中。
与binder有关的编译选项主要有两个,编译选项CONFIG_ANDROID_BINDER_IPC决定是否将binder编译进系统;CONFIG_ANDROID_BINDER_DEVICES提供了设备名字列表,用逗号分割,binder会根据这个列表提供的设备名,注册若干个设备。下面是orangepi3lts的编译选项设置。
1 2 CONFIG_ANDROID_BINDER_IPC=y CONFIG_ANDROID_BINDER_DEVICES="binder,hwbinder,vndbinder"
设备驱动初始化
在binder.c 的最底部,device_initcall()注册了binder的初始化函数binder_init(),下面是初始化函数的主体结构,删除了局部变量声明、错误处理相关的代码。首先在debugfs目录下创建文件夹和文件,然后根据编译选项的配置创建若干个设备。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 static char *binder_devices_param = CONFIG_ANDROID_BINDER_DEVICES;module_param_named(devices, binder_devices_param, charp, S_IRUGO); static int __init binder_init (void ) { binder_debugfs_dir_entry_root = debugfs_create_dir("binder" , NULL ); if (binder_debugfs_dir_entry_root) binder_debugfs_dir_entry_proc = debugfs_create_dir("proc" , binder_debugfs_dir_entry_root); if (binder_debugfs_dir_entry_root) { } device_names = kzalloc(strlen (binder_devices_param) + 1 , GFP_KERNEL); strcpy (device_names, binder_devices_param); while ((device_name = strsep(&device_names, "," ))) { ret = init_binder_device(device_name); } return ret; } device_initcall(binder_init);
设备驱动节点的初始化由函数init_binder_device()完成,参数为设备名。首先为设备申请了一个设备结构体binder_device,然后就是对设备结构体的初始化。
首先初始化成员miscdev,这表明binder是一个misc字符设备,相关的操作函数存储在binder_fops,这是一个非常重要的结构体。然后调用misc_register()向系统注册binder设备。
context表示binder设备的使用环境,主要记录了contex mgr相关的信息。可以从Android Binder文档 了解到不同binder设备的用途。
最后,通过hlist将所有的设备结构体链在一起,通过链表头binder_devices可以遍历所有的binder设备。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 static HLIST_HEAD (binder_devices) ;struct binder_device { struct hlist_node hlist ; struct miscdevice miscdev ; struct binder_context context ; }; static int __init init_binder_device (const char *name) { int ret; struct binder_device *binder_device ; binder_device = kzalloc(sizeof (*binder_device), GFP_KERNEL); binder_device->miscdev.fops = &binder_fops; binder_device->miscdev.minor = MISC_DYNAMIC_MINOR; binder_device->miscdev.name = name; binder_device->context.binder_context_mgr_uid = INVALID_UID; binder_device->context.name = name; mutex_init(&binder_device->context.context_mgr_node_lock); ret = misc_register(&binder_device->miscdev); hlist_add_head(&binder_device->hlist, &binder_devices); return ret; }
现在来验证一下binder驱动的初始化。在debugfs的挂载目录下可以看到binder创建的目录和文件。这些文件的作用后面再说。/dev目录下也可以看到binder驱动创建的三个设备节点。从Android Binder文档 可以了解到每个设备节点的用途。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 console:/ # mount | grep debugfs debugfs on /sys/kernel/debug type debugfs (rw,seclabel,relatime) console:/ # ls -l /sys/kernel/debug/binder total 0 -r--r--r-- 1 root root 0 1970-01-01 08:00 failed_transaction_log drwxr-xr-x 2 root root 0 2022-03-20 17:39 proc -r--r--r-- 1 root root 0 1970-01-01 08:00 state -r--r--r-- 1 root root 0 1970-01-01 08:00 stats -r--r--r-- 1 root root 0 1970-01-01 08:00 transaction_log -r--r--r-- 1 root root 0 1970-01-01 08:00 transactions console:/ # ls /dev/*binder* -l crw-rw-rw- 1 root root 10, 54 1970-01-01 08:00 /dev/binder crw-rw-rw- 1 root root 10, 53 1970-01-01 08:00 /dev/hwbinder crw-rw-rw- 1 root root 10, 52 1970-01-01 08:00 /dev/vndbinder
IPC 域
说明
/dev/binder
框架/应用进程之间的 IPC,使用 AIDL 接口
/dev/hwbinder
框架/供应商进程之间的 IPC,使用 HIDL 接口 供应商进程之间的 IPC,使用 HIDL 接口
/dev/vndbinder
供应商/供应商进程之间的 IPC,使用 AIDL 接口
另外,在/sys/module/binder/parameters目录下,还可以看到binder驱动定义的参数。
1 2 3 4 5 console:/ # ls -l /sys/module/binder/parameters/ total 0 -rw-r--r-- 1 root root 4096 2022-03-20 19:51 debug_mask -r--r--r-- 1 root root 4096 2022-03-20 19:51 devices -rw-r--r-- 1 root root 4096 2022-03-20 19:51 stop_on_user_error
open函数
字符设备有一个非常重要的结构体,struct file_operations,以下是binder的定义。在对binder设备节点执行对应的系统调用时,最终会调用到设备驱动提供的函数。使用binder驱动的第一步是调用open()函数,所以需要先分析binder_open()。
1 2 3 4 5 6 7 8 9 10 static const struct file_operations binder_fops = { .owner = THIS_MODULE, .poll = binder_poll, .unlocked_ioctl = binder_ioctl, .compat_ioctl = binder_ioctl, .mmap = binder_mmap, .open = binder_open, .flush = binder_flush, .release = binder_release, };
binder_open()主要是围绕结构体binder_proc来做文章,这里记录了一些打开binder的进程的信息,然在debugfs/binder/proc目录下创建一个以pid命名的文件。binder_open()的绝大部分代码都非常好理解,个人觉得以下代码比较关键和难懂。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 static int binder_open (struct inode *nodp, struct file *filp) { ... binder_dev = container_of(filp->private_data, struct binder_device, miscdev); proc->context = &binder_dev->context; binder_alloc_init(&proc->alloc); ... filp->private_data = proc; mutex_lock(&binder_procs_lock); hlist_add_head(&proc->proc_node, &binder_procs); mutex_unlock(&binder_procs_lock); ... return 0 ; }
mmap函数
设备驱动的mmap函数需要协助kernel完成struct vmware_area_struct结构体的设置,mmap函数就非常好理解了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 static int binder_mmap (struct file *filp, struct vm_area_struct *vma) { int ret; struct binder_proc *proc = filp->private_data; const char *failure_string; if (proc->tsk != current->group_leader) return -EINVAL; if ((vma->vm_end - vma->vm_start) > SZ_4M) vma->vm_end = vma->vm_start + SZ_4M; vma->vm_flags = (vma->vm_flags | VM_DONTCOPY) & ~VM_MAYWRITE; vma->vm_ops = &binder_vm_ops; vma->vm_private_data = proc; ret = binder_alloc_mmap_handler(&proc->alloc, vma); return ret; }
binder_alloc_mmap_handler()申请了一块连续的内核虚拟内存,为每个页面申请了struct binder_lru_page。
TODO:这些数据结构有什么用?物理内存是什么时候申请的?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 int binder_alloc_mmap_handler (struct binder_alloc *alloc, struct vm_area_struct *vma) { int ret; struct vm_struct *area ; const char *failure_string; struct binder_buffer *buffer ; mutex_lock(&binder_alloc_mmap_lock); area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP); alloc->buffer = area->addr; alloc->user_buffer_offset = vma->vm_start - (uintptr_t )alloc->buffer; mutex_unlock(&binder_alloc_mmap_lock); alloc->pages = kzalloc(sizeof (alloc->pages[0 ]) * ((vma->vm_end - vma->vm_start) / PAGE_SIZE), GFP_KERNEL); alloc->buffer_size = vma->vm_end - vma->vm_start; buffer = kzalloc(sizeof (*buffer), GFP_KERNEL); buffer->data = alloc->buffer; list_add(&buffer->entry, &alloc->buffers); buffer->free = 1 ; binder_insert_free_buffer(alloc, buffer); alloc->free_async_space = alloc->buffer_size / 2 ; barrier(); alloc->vma = vma; alloc->vma_vm_mm = vma->vm_mm; atomic_inc (&alloc->vma_vm_mm->mm_count); return 0 ; }
ioctl函数
binder_ioctl()是binder驱动的关键,主要功能都是在这个函数实现。首先从private_data拿到binder_proc,然后获取binder_thread,这个结构体表示执行ioctl的线程。如果是第一次执行,binder_get_thread()会创建相关的结构体并初始化。进程所有的线程以红黑树的形式存储在proc->threads。
1 2 3 4 5 6 7 static long binder_ioctl (struct file *filp, unsigned int cmd, unsigned long arg) { int ret; struct binder_proc *proc = filp->private_data; struct binder_thread *thread ; ... thread = binder_get_thread(proc);
然后就是一个大的switch语句,根据不同的cmd执行不同的函数。下表列出了所有cmd、对应的参数,以及功能。其中最关键的命令非BINDER_WRITE_READ莫属。
cmd
arg
说明
BINDER_WRITE_READ
指向struct binder_write_read的指针。
使用函数binder_ioctl_write_read()处理。
BINDER_SET_MAX_THREADS
整数。
设置proc->max_threads。
BINDER_SET_CONTEXT_MGR
None
ServiceManager进程的专属命令。
BINDER_THREAD_EXIT
None
从proc->threads中删除线程,释放相关资源
BINDER_VERSION
指向struct binder_version的指针
版本号存储在宏BINDER_CURRENT_PROTOCOL_VERSION
BINDER_GET_NODE_DEBUG_INFO
指向struct binder_node_debug_info的指针
BINDER_SET_CONTEXT_MGR
ServiceManager在执行完mmap()函数后,会调用此命令。这个命令由函数binder_ioctl_set_ctx_mgr()处理。首先检查调用的进程是否符合要求,然后新建一个binder_node,并保存到context->binder_context_mgr_node。
binder_context与binder设备有关,保存在设备结构体binder_device中,主要保存了mgr有关的信息。binder_node与Service有关,每个node表示一个service。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 static int binder_ioctl_set_ctx_mgr (struct file *filp) { int ret = 0 ; struct binder_proc *proc = filp->private_data; struct binder_context *context = proc->context; struct binder_node *new_node ; kuid_t curr_euid = current_euid(); mutex_lock(&context->context_mgr_node_lock); new_node = binder_new_node(proc, NULL ); binder_node_lock(new_node); new_node->local_weak_refs++; new_node->local_strong_refs++; new_node->has_strong_ref = 1 ; new_node->has_weak_ref = 1 ; context->binder_context_mgr_node = new_node; binder_node_unlock(new_node); binder_put_node(new_node); out: mutex_unlock(&context->context_mgr_node_lock); return ret; }
BINDER_WRITE_READ
BINDER_WRITE_READ命令通过函数binder_ioctl_write_read()来处理,前三个参数来自于ioctl()函数的参数,thread是binder_get_thread(proc)的返回值。函数开头,首先将参数复制到结构体struct binder_write_read,这个结构体比较好理解,包含了读写的字节数以及对应的缓冲区指针。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 struct binder_write_read { binder_size_t write_size; binder_size_t write_consumed; binder_uintptr_t write_buffer; binder_size_t read_size; binder_size_t read_consumed; binder_uintptr_t read_buffer; }; static int binder_ioctl_write_read (struct file *filp, unsigned int cmd, unsigned long arg, struct binder_thread *thread) { struct binder_proc *proc = filp->private_data; void __user *ubuf = (void __user *)arg; struct binder_write_read bwr ; ... if (copy_from_user(&bwr, ubuf, sizeof (bwr))) { ret = -EFAULT; goto out; } ...
然后根据bwr.write_size和bwr.read_size的值决定是否调用binder_thread_write()、binder_thread_read()做进一步处理。最后将修改后的bwr结构体复制到用户空间,因为driver会更改bwr.write_consumed和bwr.read_consumed。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 if (bwr.write_size > 0) { ret = binder_thread_write(proc, thread, bwr.write_buffer, bwr.write_size, &bwr.write_consumed); ... } if (bwr.read_size > 0) { ret = binder_thread_read(proc, thread, bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK); if (!binder_worklist_empty_ilocked(&proc->todo)) binder_wakeup_proc_ilocked(proc); ... } ... if (copy_to_user(ubuf, &bwr, sizeof(bwr))) { ret = -EFAULT; goto out; } ... }
binder_thread_write()
函数binder_thread_write()非常长,但好在结构比较清晰。指针ptr是一个用户空间指针,始终指向待处理的字节。不断的从ptr取出cmd,然后根据cmd进行不同的处理,最后更新consumed,直到buffer所有的数据处理完毕。也就是说,bwr.write_buffer由若干个cmd和紧随其后的可选固定长度参数构成,参数的长度根据cmd的不同而不同。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 static int binder_thread_write (struct binder_proc *proc, struct binder_thread *thread, binder_uintptr_t binder_buffer, size_t size, binder_size_t *consumed) { uint32_t cmd; struct binder_context *context = proc->context; void __user *buffer = (void __user *)(uintptr_t )binder_buffer; void __user *ptr = buffer + *consumed; void __user *end = buffer + size; while (ptr < end && thread->return_error.cmd == BR_OK) { int ret; if (get_user(cmd, (uint32_t __user *)ptr)) return -EFAULT; ptr += sizeof (uint32_t ); ... switch (cmd) { ... *consumed = ptr - buffer; } } return 0 ;
下表列出了binder_thread_write支持的所有cmd。后面结合具体的使用场景,再来分析这些命令的作用。
binder command protocol
param
说明
BC_INCREFS BC_ACQUIRE BC_RELEASE BC_DECREFS
无符号32位整数,target
增加或减少引用计数
BC_INCREFS_DONE BC_ACQUIRE_DONE
两个指针参数,依次是node_ptr和cookie
BC_ATTEMPT_ACQUIRE BC_ACQUIRE_RESULT
None
这两个命令不支持
BC_FREE_BUFFER
一个指针参数,data_ptr
BC_TRANSACTION_SG BC_REPLY_SG
有一个struct binder_transaction_data_sg参数
BC_TRANSACTION BC_REPLY
有一个struct binder_transaction_data参数
BC_REGISTER_LOOPER
None
BC_ENTER_LOOPER
None
BC_EXIT_LOOPER
None
BC_REQUEST_DEATH_NOTIFICATION BC_CLEAR_DEATH_NOTIFICATION
一个无符号32位整数,target; 一个指针参数,cookie
BC_DEAD_BINDER_DONE
一个指针参数,cookie
TODO:详细理解每一个cmd的作用
binder_thread_read()
binder_thread_read()函数比较长,不是很好理解。主要工作流程如下:
进入阻塞状态,等待binder_work
如果队列不为空,取出一个binder_work
根据work的不同类型,进行不同的处理
给读取的线程回复一个BR命令,某些命令还会带一个参数。
下面来分析实现代码。首先,设置线程的等待标志位。Binder同时支持阻塞和非阻塞模式,假设是阻塞模式,则调用binder_wait_for_work()等待一个binder_work的到来。binder_wait_for_work()基于Linux内核的等待队列实现,等待队列是thread->wait。如果当前线程已经有binder_work,则不会阻塞,直接返回。binder_work是写入线程发来的。
1 2 3 4 5 6 7 8 thread->looper |= BINDER_LOOPER_STATE_WAITING; if (non_block) { if (!binder_has_work(thread, wait_for_proc_work)) ret = -EAGAIN; } else { ret = binder_wait_for_work(thread, wait_for_proc_work); } thread->looper &= ~BINDER_LOOPER_STATE_WAITING;
然后进入一个死循环,根据work的不同类型进行不同的处理,具体参考下方代码块的注释。处理完毕后给读取线程的缓冲区写入一个BR命令,部分BR命令还有一个参数。BINDER_WORK_TRANSACTION的处理过于复杂,所以放在了switch语句的后面处理。暂时先不具体分析每个work type是怎么处理的,后面结合具体的使用场景再分析。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 while (1 ) { w = binder_dequeue_work_head_ilocked(list ); switch (w->type) { case BINDER_WORK_TRANSACTION: { t = container_of(w, struct binder_transaction, work); } break ; case BINDER_WORK_RETURN_ERROR: { struct binder_error *e = container_of( w, struct binder_error, work); } break ; case BINDER_WORK_TRANSACTION_COMPLETE: { cmd = BR_TRANSACTION_COMPLETE; put_user(cmd, (uint32_t __user *)ptr): ptr += sizeof (uint32_t ); } break ; case BINDER_WORK_NODE: { struct binder_node *node = container_of(w, struct binder_node, work); } break ; case BINDER_WORK_DEAD_BINDER: case BINDER_WORK_DEAD_BINDER_AND_CLEAR: case BINDER_WORK_CLEAR_DEATH_NOTIFICATION: { struct binder_ref_death *death ; death = container_of(w, struct binder_ref_death, work); } break ; } if (!t) continue ; break ; }
BR命令的全程是binder driver return protocol,由binder driver发送给service进程。下表列出了所有的BR命令及其对应的参数。
binder return protocol
param
说明
BR_OK
None
BR_TRANSACTION BR_REPLY
struct binder_transaction_data
the received command.
BR_DEAD_REPLY
None
The target of the last transaction (either a bcTRANSACTION or a bcATTEMPT_ACQUIRE) is no longer with us.
BR_INCREFS BR_ACQUIRE BR_RELEASE BR_DECREFS
struct binder_ptr_cookie
ptr to binder, cookie for binder
BR_NOOP
None
什么都不做,检查下一个命令。 它的存在主要是可以用 BR_SPAWN_LOOPER 命令替换它。
BR_SPAWN_LOOPER
None
驱动程序已确定进程没有线程等待为传入事务提供服务。 当一个进程接收到这个命令时,它必须产生一个新的服务线程并通过 bcENTER_LOOPER 注册它。
BR_DEAD_BINDER
binder_uintptr_t, cookie
BR_CLEAR_DEATH_NOTIFICATION_DONE
binder_uintptr_t, cookie
BR_FAILED_REPLY
None
最后一个事务(bcTRANSACTION 或 bcATTEMPT_ACQUIRE)失败(例如内存不足)。
Binder协议
之前的分析,更多是语法层面的分析,还没有深入理解分析Binder的数据结构。所以,接下来根据具体的通信实例,来深入理解Binder协议,理解BC、BR命令及其参数的含义。
注册Service
binder设备使用方法 提到过,进程或线程可以向ServiceManager注册成为Service。所以现在来看看注册为Service需要发送什么样的数据。下面的代码片段来自MediaPlayerService.cpp,defaultServiceManager()会获取一个handle为0的BpBinder对象,并以此创建出一个BpServiceManager对象,BpBinder对象保存在mRemote。
1 2 3 4 5 void MediaPlayerService::instantiate () { defaultServiceManager ()->addService ( String16 ("media.player" ), new MediaPlayerService ()); }
下面是BpServiceManager::addService()的实现。首先使用Parcel打包数据。Parcel是一种数据容器,提供了非常多的接口来打包各种类型的数据,打包和解包需要使用对应的接口即可。然后调用remote()->transact()发送并接收返回的消息。remote()返回mRemote,而mRemote是handle等于0的BpBinder对象,所以remote()->transact()实际上是将消息发送给ServiceManager。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class BpServiceManager : public BpInterface<IServiceManager> { virtual status_t addService (const String16& name, const sp<IBinder>& service, bool allowIsolated, int dumpsysPriority) { Parcel data, reply; data.writeInterfaceToken (IServiceManager::getInterfaceDescriptor ()); data.writeString16 (name); data.writeStrongBinder (service); data.writeInt32 (allowIsolated ? 1 : 0 ); data.writeInt32 (dumpsysPriority); status_t err = remote ()->transact (ADD_SERVICE_TRANSACTION, data, &reply); return err == NO_ERROR ? reply.readExceptionCode () : err; } }
BpBinder::transact()实际上会借助IPCThreadState::transact()来完成实际的发送动作,IPCThreadState对象每个线程有且只有一个。下面是函数的具体实现,handle=0,code=ADD_SERVICE_TRANSACTION,data在BpServiceManager::addService()函数中写入了一些对象,flags是默认值0,reply是ServiceManager返回的值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 status_t IPCThreadState::transact (int32_t handle, uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { status_t err; flags |= TF_ACCEPT_FDS; err = writeTransactionData (BC_TRANSACTION, flags, handle, code, data, NULL ); if ((flags & TF_ONE_WAY) == 0 ) { if (reply) { err = waitForResponse (reply); } else { Parcel fakeReply; err = waitForResponse (&fakeReply); } } else { err = waitForResponse (NULL , NULL ); } return err; }
writeTransactionData()负责将待发送数据的相关信息组合成binder_transaction_data结构,然后将cmd和binder_transaction_data结构写入到mOut对象,这也是一个Parcel对象。如下代码所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 status_t IPCThreadState::writeTransactionData (int32_t cmd, uint32_t binderFlags, int32_t handle, uint32_t code, const Parcel& data, status_t * statusBuffer) { binder_transaction_data tr; tr.target.ptr = 0 ; tr.target.handle = handle; tr.code = code; tr.flags = binderFlags; tr.cookie = 0 ; tr.sender_pid = 0 ; tr.sender_euid = 0 ; tr.data_size = data.ipcDataSize (); tr.data.ptr.buffer = data.ipcData (); tr.offsets_size = data.ipcObjectsCount ()*sizeof (binder_size_t ); tr.data.ptr.offsets = data.ipcObjects (); mOut.writeInt32 (cmd); mOut.write (&tr, sizeof (tr)); return NO_ERROR; }
IPCThreadState::waitForResponse()主要完成以下两个工作:
调用talkWithDriver(),将mOut发送到driver,然后等待driver回复的信息并写入到mIn
根据mIn中的数据,进行不同的处理
由于现在还不知道driver会返回什么数据,所以这个函数先不分析。先来看看talkWithDriver()。首先判断mProcess对象的文件描述符是否大于0,然后根据mIn和mOut来构建binder_write_read结构体,最后调用ioctl的BINDER_WRITE_READ命令将数据发送到driver。不妨回头看看前面对BINDER_WRITE_READ 的分析,两个地方对上了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 status_t IPCThreadState::talkWithDriver (bool doReceive) { binder_write_read bwr; const bool needRead = mIn.dataPosition () >= mIn.dataSize (); const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize () : 0 ; bwr.write_size = outAvail; bwr.write_buffer = (uintptr_t )mOut.data (); if (doReceive && needRead) { bwr.read_size = mIn.dataCapacity (); bwr.read_buffer = (uintptr_t )mIn.data (); } else { bwr.read_size = 0 ; bwr.read_buffer = 0 ; } if ((bwr.write_size == 0 ) && (bwr.read_size == 0 )) return NO_ERROR; bwr.write_consumed = 0 ; bwr.read_consumed = 0 ; status_t err; do { if (ioctl (mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0 ) err = NO_ERROR; else err = -errno; } while (err == -EINTR); return err; }
下图展示了从addService()到talkWithDriver()是如何对数据封装的,箭头表示指针指向对应的数据结构。前面提到过,不同的BC指令会带有不同的参数,BC_TRANSACTION的参数是一个binder_transaction_data结构体,这个结构体表明了要发送的数据、目标以及如何处理数据。
driver使用函数binder_transaction()来处理BC_TRANSACTION,这个函数非常的长(600行左右),并且和BC_REPLAY共用一个函数,所以分析起来有点困难。当replay=0是,函数的主要流程如下:
根据target.handle找到target_node / target_proc
构建结构体 binder_transaction
依次取出tr->data.buffer中的binder对象,并根据不同的对象类型进行不同的处理,并处理结果附加到binder_transaction
t->work.type = BINDER_WORK_TRANSACTION
调用binder_proc_transaction(),将t->work附加到target_proc->todo队列,然后唤醒target_proc。
addService()写入了一个类型为BINDER_TYPE_HANDLE的binder对象,其存储了即将注册的service的handle值。binder_translate_handle()的工作是获取注册service的binder_node,并增加引用。现在,这里的target_proc是service manager,唤醒目标进程后,binder_thread_read()会继续运行。注意这里的work type是BINDER_WORK_TRANSACTION。
TODO:binder_node是什么时候在driver注册的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 static void binder_transaction ( struct binder_proc *proc, struct binder_thread *thread, struct binder_transaction_data *tr, int reply, binder_size_t extra_buffers_size) { if (reply) { } else { if (tr->target.handle) { } else { target_node = context->binder_context_mgr_node; target_node = binder_get_node_refs_for_txn(target_node, &target_proc, &return_error); } } t->code = tr->code; t->buffer->target_node = target_node; for (; offp < off_end; offp++) { struct binder_object_header *hdr ; hdr = (struct binder_object_header *)(t->buffer->data + *offp); switch (hdr->type) { case BINDER_TYPE_HANDLE: { struct flat_binder_object *fp ; fp = to_flat_binder_object(hdr); ret = binder_translate_handle(fp, t, thread); } } } t->work.type = BINDER_WORK_TRANSACTION; if (reply) { } else if (!(t->flags & TF_ONE_WAY)) { binder_proc_transaction(t, target_proc, target_thread); } else { binder_proc_transaction(t, target_proc, NULL ); } }
从前面binder_thread_read() 的分析看,BINDER_WORK_TRANSACTION的处理在binder_thread_read()函数的最后面,其主要工作是根据binder_transaction结构体构造binder_transaction_data结构体,然后写回到用户空间的buffer。注意这里的命令是BR_TRANSACTION,tr.code是ADD_SERVICE_TRANSACTION。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 while (1 ) { if (t->buffer->target_node) { struct binder_node *target_node = t->buffer->target_node; tr.target.ptr = target_node->ptr; tr.cookie = target_node->cookie; cmd = BR_TRANSACTION; } else { } tr.code = t->code; tr.flags = t->flags; tr.data_size = t->buffer->data_size; tr.offsets_size = t->buffer->offsets_size; tr.data.ptr.buffer = (binder_uintptr_t ) ((uintptr_t )t->buffer->data + binder_alloc_get_user_buffer_offset(&proc->alloc)); tr.data.ptr.offsets = tr.data.ptr.buffer + ALIGN(t->buffer->data_size, sizeof (void *)); put_user(cmd, (uint32_t __user *)ptr); ptr += sizeof (uint32_t ); copy_to_user(ptr, &tr, sizeof (tr)); ptr += sizeof (tr); break ; }
最后看service manager对消息的处理,历经千难万险,SM终于拿到了消息,并完成了添加service的动作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 int svcmgr_handler (struct binder_state *bs, struct binder_transaction_data *txn, struct binder_io *msg, struct binder_io *reply) { switch (txn->code) { case SVC_MGR_ADD_SERVICE: s = bio_get_string16 (msg, &len); if (s == NULL ) { return -1 ; } handle = bio_get_ref (msg); allow_isolated = bio_get_uint32 (msg) ? 1 : 0 ; dumpsys_priority = bio_get_uint32 (msg); if (do_add_service (bs, s, len, handle, txn->sender_euid, allow_isolated, dumpsys_priority, txn->sender_pid)) return -1 ; break ; } }
获取service
TODO
参考资料