ServiceManager是一个守护进程。它的main()函数源码如下:
frameworks/native/cmds/servicemanager/service_manager.c
int main(int argc, char **argv)
{
struct binder_state *bs;
void *svcmgr = BINDER_SERVICE_MANAGER;
bs = binder_open(128*1024);
if (binder_become_context_manager(bs)) {
ALOGE("cannot become context manager (%s)\n", strerror(errno));
return -1;
}
svcmgr_handle = svcmgr;
binder_loop(bs, svcmgr_handler);
return 0;
}
/frameworks/native/cmds/servicemanager/binder.c
struct binder_state *binder_open(unsigned mapsize)
{
struct binder_state *bs;
bs = malloc(sizeof(*bs));
...
bs->fd = open("/dev/binder", O_RDWR);
...
bs->mapsize = mapsize;
bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
...
return bs;
}
kernel/drivers/staging/android/binder.c
static int binder_open(struct inode *nodp, struct file *filp)
{
struct binder_proc *proc;
binder_debug(BINDER_DEBUG_OPEN_CLOSE, "binder_open: %d:%d\n",
current->group_leader->pid, current->pid);
// 为proc分配内存
proc = kzalloc(sizeof(*proc), GFP_KERNEL);
if (proc == NULL)
return -ENOMEM;
get_task_struct(current);
// 将proc->tsk指向当前线程
proc->tsk = current;
// 初始化proc的待处理事务列表
INIT_LIST_HEAD(&proc->todo);
// 初始化proc的等待队列
init_waitqueue_head(&proc->wait);
// 设置proc的进程优先级为当前线程的优先级
proc->default_priority = task_nice(current);
binder_lock(__func__);
binder_stats_created(BINDER_STAT_PROC);
// 将该进程上下文信息proc保存到"全局哈希表binder_procs"中
hlist_add_head(&proc->proc_node, &binder_procs);
// 设置进程id
proc->pid = current->group_leader->pid;
INIT_LIST_HEAD(&proc->delivered_death);
// 将proc添加到私有数据中。
// 这样,mmap(),ioctl()等函数都可以通过私有数据获取到proc,即该进程的上下文信息
filp->private_data = proc;
binder_unlock(__func__);
if (binder_debugfs_dir_entry_proc) {
char strbuf[11];
snprintf(strbuf, sizeof(strbuf), "%u", proc->pid);
proc->debugfs_entry = debugfs_create_file(strbuf, S_IRUGO,
binder_debugfs_dir_entry_proc, proc, &binder_proc_fops);
}
return 0;
}
创建binder_proc对象,并把当前进程等信息保存到binder_proc对象,该对象管理IPC所需的各种信息并拥有其他结构体的根结构体;再把binder_proc对象保存到文件指针filp,以及把binder_proc加入到全局链表binder_procs。
kernel/drivers/staging/android/binder.c
//vm_area_struct则是描述进程虚拟地址信息的结构体。
//vm_struct是描述内核虚拟地址信息的结构体。
static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
{
int ret;
struct vm_struct *area; //内核虚拟空间
struct binder_proc *proc = filp->private_data;
const char *failure_string;
struct binder_buffer *buffer;
if (proc->tsk != current)
return -EINVAL;
if ((vma->vm_end - vma->vm_start) > SZ_4M)
vma->vm_end = vma->vm_start + SZ_4M; //保证映射内存大小不超过4M
mutex_lock(&binder_mmap_lock); //同步锁
//采用IOREMAP方式,分配一个连续的内核虚拟空间,与进程虚拟空间大小一致
area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP);
if (area == NULL) {
ret = -ENOMEM;
failure_string = "get_vm_area";
goto err_get_vm_area_failed;
}
proc->buffer = area->addr; //指向内核虚拟空间的地址
//地址偏移量 = 用户虚拟地址空间 - 内核虚拟地址空间
proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer;
mutex_unlock(&binder_mmap_lock); //释放锁
...
//分配物理页的指针数组,数组大小为vma的等效page个数;
proc->pages = kzalloc(sizeof(proc->pages[0]) * ((vma->vm_end - vma->vm_start) / PAGE_SIZE), GFP_KERNEL);
if (proc->pages == NULL) {
ret = -ENOMEM;
failure_string = "alloc page array";
goto err_alloc_pages_failed;
}
// 内核虚拟空间的内存大小 = 进程虚拟地址区域(用户空间)的内存大小
proc->buffer_size = vma->vm_end - vma->vm_start;
vma->vm_ops = &binder_vm_ops;
vma->vm_private_data = proc;
//分配物理页面,同时映射到内核空间和进程空间,先分配1个物理页
if (binder_update_page_range(proc, 1, proc->buffer, proc->buffer + PAGE_SIZE, vma)) {
ret = -ENOMEM;
failure_string = "alloc small buf";
goto err_alloc_small_buf_failed;
}
buffer = proc->buffer; //binder_buffer对象 指向proc的buffer地址
INIT_LIST_HEAD(&proc->buffers); //创建进程的buffers链表头
list_add(&buffer->entry, &proc->buffers); //将binder_buffer地址 加入到所属进程的buffers队列
buffer->free = 1;
//将空闲buffer放入proc->free_buffers中
binder_insert_free_buffer(proc, buffer);
//异步可用空间大小为buffer总大小的一半。
proc->free_async_space = proc->buffer_size / 2;
barrier();
proc->files = get_files_struct(current);
proc->vma = vma;
proc->vma_vm_mm = vma->vm_mm;
return 0;
...// 错误flags跳转处,free释放内存之类的操作
return ret;
}
static int binder_update_page_range(struct binder_proc *proc, int allocate,
void *start, void *end,
struct vm_area_struct *vma)
{
void *page_addr;
unsigned long user_page_addr;
struct page **page;
struct mm_struct *mm; // 内存结构体
if (vma)
mm = NULL; //binder_mmap过程vma不为空,其他情况都为空
else
mm = get_task_mm(proc->tsk); //获取mm结构体
if (mm) {
down_write(&mm->mmap_sem); //获取mm_struct的写信号量
vma = proc->vma;
}
//此处allocate为1,代表分配过程。如果为0则代表释放过程
if (allocate == 0)
goto free_range;
for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {
int ret;
page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE];
//分配一个page的物理内存
*page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO);
//物理空间映射到虚拟内核空间
ret = map_kernel_range_noflush((unsigned long)page_addr,
PAGE_SIZE, PAGE_KERNEL, page);
flush_cache_vmap((unsigned long)page_addr, (unsigned long)page_addr + PAGE_SIZE);
user_page_addr = (uintptr_t)page_addr + proc->user_buffer_offset;
//物理空间映射到虚拟进程空间
ret = vm_insert_page(vma, user_page_addr, page[0]);
}
if (mm) {
up_write(&mm->mmap_sem); //释放内存的写信号量
mmput(mm); //减少mm->mm_users计数
}
return 0;
free_range:
... //释放内存的流程
return -ENOMEM;
}
binder_update_page_range主要完成工作:分配物理空间,将物理空间映射到内核空间,将物理空间映射到进程空间
在Binder通信机制中,mmap()会将Server进程的虚拟地址和内核虚拟地址映射到同一个物理页面。那么当Client进程向Server进程发送请求时,只需要将Client的数据拷贝到内核空间即可!由于Server进程的地址和内核空间映射到同一个物理页面,因此,Client中的数据拷贝到内核空间时,也就相当于拷贝到了Server进程中。因此,Binder通信机制中,数据传输时,只需要1次内存拷贝!
http://qiangbo.space/2017-01-15/AndroidAnatomy_Binder_Driver/
http://wangkuiwu.github.io/2014/09/03/Binder-ServiceManager-Daemon/
http://gityuan.com/2015/11/01/binder-driver/