共计 2481 个字符,预计需要花费 7 分钟才能阅读完成。
三个传输空间限制
Android 中,由 Zygote 孵化的进程是通过 ProcessState 来创建 Binder 实体的。实体创建过程中会映射一段内存空间用于数据传输,其大小设置为 ((1*1024*1024) – (4096*2))。
1.---> ProcessState.cpp
2.
3. #define BINDER_VM_SIZE ((1*1024*1024) - (4096 *2))
4. . . . . . .
5.ProcessState::ProcessState()
6. : mDriverFD(open_driver()) // 打开了 Binder 设备
7. , mVMStart(MAP_FAILED) // 内存映射的真实地址
8. , mManagesContexts(false)
9. , mBinderContextCheckFunc(NULL)
10. , mBinderContextUserData(NULL)
11. , mThreadPoolStarted(false)
12. , mThreadPoolSeq(1)
13.{14. if (mDriverFD >= 0) {
15. // 对 Binder 设备进行内存映射
16. mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
17. if (mVMStart == MAP_FAILED) {18. close(mDriverFD);
19. mDriverFD = -1;
20. }
21. }
22.}
在 Binder 机制中,有一个非常特殊的 Service,就是 Service Manager。它做为 Binder 机制的守护进程,管理着其他的 Services。它的 Binder buffer 大小也不同,为 (128*1024)。因为它只是用于管理 services,提供 addServcie,getService 等功能,所以不会涉及大数据传输,因为没有分配较大的传输空间。
1.---> service_manager.c
2.
3.int main(int argc, char **argv)
4.{
5. struct binder_state *bs;
6.
7. bs = binder_open(128*1024);
8. ......
9.}
Kernel 中对 Binder 传输的大小也有个限制,大小为 4M。这个限制使得所有的应用都无法申请大于 4M 的传输空间。
1.---> binder.c
2.
3.static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
4.{
5. ......
6. if ((vma->vm_end - vma->vm_start) > SZ_4M)
7. vma->vm_end = vma->vm_start + SZ_4M;
8. ......
9. proc->buffer_size = vma->vm_end - vma->vm_start;
10. ......
11. proc->free_async_space = proc->buffer_size / 2;
12. ....
13.}
目前,我们得到的三个对 Binder 传输空间的限制。
- (128*1024):对 Service Manager 的限制。
- ((1*1024*1024) – (4096 *2)):对普通 Android services 的限制。
- 4M:kernel 可以接受的最大空间。
所以,实际应用可以申请的最大 Binder Buffer 是 4M。但考虑实际传输的需要和节省内存,Android 中申请的是 (1M – 8K)。这个值是可以增加的,只要不大于 4M 就可以。
1M – 8K
Binder Buffer 为什么会是 (1M – 8K),二不是整数值 1M?这里看起来很奇怪,8K 空间到底时用来做什么的?从 GIT 提交的记录可以看到下面的 commits。
Modify the binder to request 1M – 2 pages instead of 1M. The backing store in the kernel requires a guard page, so 1M allocations fragment memory very badly. Subtracting a couple of pages so that they fit in a power of two allows the kernel to make more efficient use of its virtual address space.
这段解释还没有充分理解,其大致的意思是:kernel 的“backing store”需要一个保护页,这使得 1M 用来分配碎片内存时变得很差,所以这里减去两页来提高效率,因为减去一页就变成了奇数。至于保护页如何影响内存分配,需要后续分析内存管理时再进一步研究。
同步和异步
Binder 通信可以分为同步传输和异步传输,在设置传输空间大小时,将异步传输空间设置为同步传输的一半。
proc->free_async_space = proc->buffer_size / 2;
Binder 通过红黑树进行 buffer 管理,将分配使用的 buffer 放入 allocated_buffers,将回收的 buffer 放入 free_buffers。无论同步还是异步,这里的操作都是相同的。
1.---> binder.c
2.
3.struct binder_proc {
4. ......
5. struct list_head buffers;
6. struct rb_root free_buffers;
7. struct rb_root allocated_buffers;
8. size_t free_async_space;
9. ......
10.}
而 free_async_space 只是用来统计异步传输时已经分配了多少空间。初始化时将其设置为全部 buffer 空间的一半,更多的是想对异步传输做一个限制,不希望异步传输消耗掉所有的 buffer。相比较而言,同步传输可能会承载更重要的数据,应该尽量保证同步传输有可用 buffer。并且异步传输是不需要等待返回的,连续的异步传输很可能将 buffer 耗光。