-
-
[原创][AOSP] 一句话原理 - Binder细节
-
发表于: 2026-7-14 19:51 1135
-
Env: pixel6 - android 15 - kernel 5.10
binder的内核态其实并没有新的进程/线程用来专门处理binder传输过程中的内存分配。其原理是复用的client/server用户态的内核栈来执行。
1. 其实用户态到内核态的转变并没有切换线程/进程,因为内核态的地址索引是全局共享的,不是类似用户空间的地址范围,也不是以进程/线程进行切割访问权限控制的,陷入内核态就是进入另一个空间。虽然用户态和内核态都是基于MMU进行虚拟地址访问控制的,但是内核态的mmu在kmalloc的时候范围只在内核态,而不是用户态。
2. 用户态进程隔离访问控制是基于CPU页表的。正常一个进程访问/proc/self/maps是虚拟地址,比方说A进程有0xaaaa地址,B进程也有这个地址。那么这两个进程资源访问的切换不重叠的原因就是CPU页表的不同才能让A访问0xaaaa地址的时候访问的不是B进程的0xaaaa地址
3. binder使用/dev/binder设备文件节点,在open("/dev/binder")的时候其实内核逻辑就会区分当前节点是否是设备节点 还是vfs的普通文件节点,正常文件节点是基于红黑树进行寻址的(这里的寻址是基于硬盘的),而设备树节点在注册的时候会注册某个syscall函数对应使用某个设备文件节点的handle。比方说binder.c中,binder设备的mmap,open处理函数就是binder.c中的binder_open函数。:
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,
};
4. 因为b进程可以通过provider等方式把ibinder对象传给a进程,但是由于虚拟地址mmu的控制 a进程的地址空间不能直接访问b的地址空间,所以需要在传输的时候通过内核态来给a进程分配handle,不直接把ibinder对象放到a进程中。如果a进程想与b进程通信则a进程中的ibinder对象其实就是内核的handle
#### Tips
这里其实思考了下为啥binder/跨进程传递数据都要经过内核态。以及不经过内核态是否可行。
1. 本质上内核栈是每个进程都有的东西。内核不是一个进程,是一片code和数据。可以认为是一个运行环境,但是平时除了调度(内核线程)以外都是外部调用(陷入内核态或者syscall)。一旦涉及到陷入内核态执行的时候就会通过syscall接口来从用户态到内核态的转变。syscall本身就是用户态->内核态的接口。从用户态到内核态的转变本质上是CPU状态的转换。跨进程传递数据本身就需要经过复制和拷贝。从进程b拷贝到内核再拷贝到进程a完成了一次跨进程数据传递(Binder单次拷贝是因为进程a的Binder Server内存映射出来当handle,连接建立完成进程拷贝,但是返回值是需要在Client端开辟内存空间映射给server的)。
2. 从用户态指针数据(字符串,数据指针)陷入内核态执行的时候是需要从用户态拷贝到内核态的,而不是将指针拷贝到内核态进行虚拟地址转换的(这里以printf函数为例,其入参在陷入内核态的时候已经完成了对字符串长度的获取。这里也是由bionic指定的'\0'作为分割字符进行取长度的)
3. 不经过内核态传递数据是否可行?答案是可行的。用共享内存创建一片内存空间,通过open vfs创建文件描述符 通过进程间数据传递把文件描述符传递给另一个进程。当然在跨进程传输数据的时候(也就是传递文件描述符的时候)还是需要陷入内核态来进行数据的安全传递的。然后就可以通过这个唯一的fd+自旋读写来进程非通知式的跨进程数据传递
目前在通读AOSP和Kernel代码,如有问题请在评论区指正,谢谢
#AOSP #Kernel #Binder
[内核课程]《Windows内核攻防实战》!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。