binder机制原理分析一共分5个部分,其实省了一点,但是分析到后面都差不多了,以后再补充吧。

1、ServiceManager 进程启动

2、普通Service注册到ServiceManager

3、从ServiceManager中获取服务

4、java层service的注册过程

5、Java层service的获取过程

前言:

要讲解分析binder,最好是从SM的启动开始分析讲解,不然就会感觉总有黑洞没有探索,会总惦记着,感觉缺少点啥;

ServiceMangerd的启动主要分为3步骤:

  1. 通过binder_open方法打开binder设备驱动文件,然后通过mmap机制实现地址双映射。
  2. 将通过binder_become_context_manager指令将自己设置成为系统服务的大管家。
  3. 通过binder_looper方法,通过ioctl不断与binder驱动进行读写交互,并通过binder_parse处理数据。
int main(){struct binder_state *bs;
1、service_manager.c在初始化第一步就通过binder.c打开这个binder驱动设备文件。所以这个文件非常的重要。bs = binder_open(128*1024);
2、第二步是将自己注册成功所有服务的管家
if (binder_become_context_manager(bs)) {ALOGE("cannot become context manager (%s)\n", strerror(errno));
return -1;
}
............
3、这里通过binder_looper等待客户端的请求,第二个单数是servicemanager和handler句柄binder_loop(bs, svcmgr_handler);return 0;
}

打开驱动文件binder_open(128*1024)

binder_open(128*1024);通过binder_open方法打开binder设备驱动文件:

1、先初始化binder_proc结构体:包含了四颗红黑树、state、buffer、buffersize、user_buffer_offset、pages、todo队列、default_priorety等等。
作用:(待补充);将binder_proc结构体赋给file—>private_data中,方便获取。
2、通过mmap机制申请内核虚拟共享空间,配分物理页地址,并将地址映射到内核空间和用户空间。
作用就是当查询时发现物理页缺页的情况下,执行1拷贝操作。
struct binder_state *binder_open(size_t mapsize){struct binder_state *bs;struct binder_version vers;bs = malloc(sizeof(*bs));if (!bs) {errno = ENOMEM;return NULL;}初始化binder_proc结构体binder_proc里的4棵树需要了解一下(待补充):proc—>task(保存使用了binder机制的进程信息)proc—>buffer(内核地址)proc—>buffer_size(内核地址大小)proc—>user_buffer_offset(用户进程地址偏移量)proc—>pages(物理页)物理页地址的双映射是binder实现1拷贝的关键。proc—>todo队列(保存IPC传递过来的任务)proc—>default_priorety(保存进程的优先级);1、将binder_proc注册到binder_procs中,通过binder_procs就行可以查询到所有的进程信息。2、将binder_proc注册到file结构体的private_data中,这样bingder的其他操作就能直接获取到binder_proc结构体,就比如说下面mmap()的操作;bs->fd = open("/dev/binder", O_RDWR | O_CLOEXEC);.................//通过mmap机制开辟虚拟内核共享空间。bs->mapsize = mapsize;bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);.................return NULL;
}

mmap的原理:

  1. 申请内核虚拟地址空间
    根据进程的start和end,在内核虚拟空间申请一块指定大小的内存area,area不能超过4M(end-start),且没有禁用映射。将area赋给proc—>buffer,将偏移量(start-buffer)—>user_buffer_offset;
  2. 构建物理页数组
    通过buffer/PAGE_SIZE赋给proc—>pages物理数组。
  3. 实现物理页地址双映射
    通过binder_update_page_range遍历给buffer~buffer+pagesize之间的每个物理页并通过alloc_page分配地址,将该地址映射到虚拟内核空间(map_kernel_range_noflush)和虚拟进程空间(vm_insert_page)。
static int binder_mmap(struct file *filp, struct vm_area_struct *vma){............
1、判断进程申请的虚拟空间大小是否超过4M、是否禁用了映射;但是在ProcessState中,也就是APP中已经设置了默认不超过(1*1024*1024)-(4096*2)也就是1M-8K的大小了,因此这里的判断对APP无效。
if ((vma->vm_end - vma->vm_start) > SZ_4M)vma->vm_end = vma->vm_start + SZ_4M;if (vma->vm_flags & FORBIDDEN_MMAP_FLAGS) {ret = -EPERM;failure_string = "bad vm_flags";goto err_bad_arg;}
2、在内核虚拟地址空间中申请分配指定大小的内存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;}
3、保存内核虚拟地址空间的起始值和偏移量以及物理页,构建物理页数组,这个物理页必须以PAGE_SIZE为单位,这也是mmap的一个缺点proc->buffer = area->addr;proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer;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;//将当前进程的binder_proc注册到虚拟地址空间描述符的vm_private_data中vma->vm_private_data = proc;// 分配实际的物理页面并同时映射到进程虚拟地址空间和内核虚拟地址空间中
4、为每个物理数组分配物理页地址,并且将物理页地址映射到虚拟内存空间和虚拟进程空间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;}//这段地址空间使用一个binder_buffer来描述,分别插入到proc->buffers链表和proc->free_buffers红黑树中去buffer = proc->buffer;INIT_LIST_HEAD(&proc->buffers);list_add(&buffer->entry, &proc->buffers);buffer->free = 1;binder_insert_free_buffer(proc, buffer);proc->free_async_space = proc->buffer_size / 2;barrier();proc->files = get_files_struct(current);proc->vma = vma;return 0;
。。。。。。。。。。。return ret;
}

mmap机制图解总结一下:

绑定SM:binder_become_context_manager()

这是ServiceManager启动的第二步,在binder驱动中为ServiceManager创建binder实体binger_node和binder_thread。整个binder通信机制是通过ioctl设备命令操作符来完成的。这里就是通过设备命令操作符,将ServiceManager设置为上下文。
实现步骤:

  1. 从proc—>threads红黑树中(一共有四棵树,这是其中一颗)查找获取是否有线程thread,没有的话就创建一个binder_thread,然后插入到红黑树中去。
  2. 通过ioctl传入BINDER_SET_CONTEXT_MGR指令,遍历proc—>nodes红黑树是否有node,没有创建一个binder_node并赋值给binder_context_mgr_node,同时设置binder_context_mgr_uid,此id唯一。
int binder_become_context_manager(struct binder_state *bs){//ServiceManager通过BINDER_SET_CONTEXT_MGR指令在binder驱动中注册SMgr,
//系统中只能有一个MSgr存在,除非它调用close关闭。其他服务才能来注册。return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}
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;unsigned int size = _IOC_SIZE(cmd);void __user *ubuf = (void __user *)arg;ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);if (ret)return ret;mutex_lock(&binder_lock);
1、从binder_proc中取出binder_thread线程描述符thread = binder_get_thread(proc);if (thread == NULL) {ret = -ENOMEM;goto err;}
2、处理不同的binder命令switch (cmd) {case BINDER_SET_CONTEXT_MGR:ret = binder_ioctl_set_ctx_mgr(filp);break;goto err;}ret = 0;
err:if (thread)
3、设置binder线程状态thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN;mutex_unlock(&binder_lock);wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);if (ret && ret != -ERESTARTSYS)printk(KERN_INFO "binder: %d:%d ioctl %x %lx returned %d\n", proc->pid, current->pid, cmd, arg, ret);return ret;
}
主要目的就是设置context->binder_context_mgr_node和id的值,这个是唯一的
static int binder_ioctl_set_ctx_mgr(struct file *filp){int ret = 0;struct binder_proc *proc = filp->private_data;       通过filp->private_data获取procstruct binder_context *context = proc->context;kuid_t curr_euid = current_euid();if (context->binder_context_mgr_node) {pr_err("BINDER_SET_CONTEXT_MGR already set\n");  存在就报错,保证唯一性ret = -EBUSY;goto out;}
主要就是这两句话,设置UID和设置node,两个值唯一;context->binder_context_mgr_uid = curr_euid;context->binder_context_mgr_node = binder_new_node(proc, 0, 0);
}

等待用户请求阶段

通过binder_loop(bs, svcmgr_handler);

  1. 首先写入write操作,设置cmd为BINDER_ENTRY_LOOPER,在binder_thread_write中设置了binder_thread的状态looper为BINDER_LOOPER_STATE_ENTERED,表示当前binder线程进入循环状态。
  2. 开起for循环,并设置read_size大小表示可读,通过ioctl执行binder_thread_read操作读取数据,读取时,如果thread->transaction_stack和thread->todo都为空,当前进程就通过wait_event_interruptible_exclusive函数进入休眠状态。
  3. 通过binder_parse方法解析读取到的消息。
void binder_loop(struct binder_state *bs, binder_handler func){int res;struct binder_write_read bwr;uint32_t readbuf[32];bwr.write_size = 0;bwr.write_consumed = 0;bwr.write_buffer = 0;readbuf[0] = BC_ENTER_LOOPER;binder_write(bs, readbuf, sizeof(uint32_t));
开启for循环,readsize赋值,然后通过ioctl作用一个永远读取操作。for ( ;; ) {开个读空间bwr.read_size = sizeof(readbuf);bwr.read_consumed = 0;bwr.read_buffer = (uintptr_t) readbuf;
读空间不为空,进行读写操作;res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
读取到数据后,进行解析res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);}
}

ServiceManager进程启动总结:

  1. 通过open_binder()打开binder驱动设备文件。
  2. 通过mmap()在内核中开辟一块共享虚拟内存,同时将根据进程大小创建物理页,并将物理页地址同时映射到进程虚拟空间和内核虚拟空间。
  3. 通过ioctl的BINDER_SET_CONTEXT_MGR指令在内核中为ServiceManager创建binder_node实体,设置全局的binder_context_mgr_node和binder_context_mgr_uid两个全局变量。
  4. 设置ioctl的BINDER_WRITE_READ指令进行读写操作。分别调用copy_from_user —> binder_thread_write(binder_thread_read)—>copy_to_user三步走,并且别分设置binder_thread的looper线程状态为执行状态——BINDER_LOOPER_STATE_ENTERED以及睡眠状态BINDER_LOOPER_STATE_WAITING,这个状态也就是ServiceManager进程的状态。
  5. 如果thread的transaction_stack或者todo队列为空,那么就进入BINDER_LOOPER_STATE_WAITING状态,否正,唤醒进程并进入BINDER_LOOPER_STATE_ENTEYED状态。

binder机制原理分析(一):ServiceManager 进程启动相关推荐

  1. ServiceManager 进程启动源码分析

    Service Manager是整个Binder机制的守护进程,用来管理开发者创建的各种Server,并且向Client提供查询Server远程接口的功能.Service Manager作为本地服务由 ...

  2. Android锁屏机制原理分析

    转载自:http://www.2cto.com/kf/201401/273898.html 春节前最后几天了,工作上几乎没有什么要做.大致整理下之前工作中写的文档,PPT,手册. 由于去年一年完全转到 ...

  3. Binder机制原理、源码、AIDL,IBinder,Binder,IInterface,BinderDriver,需要的都在这里了

    导读: 本文分为三个阶段, 第一阶段,原理概述,力争说人话的基础上,讲明白Binder机制在搞什么,为什么这样搞,以及具体是怎么搞的. 第二阶段,代码层面描述,主要描述了,AIDL.IBinder.B ...

  4. Binder源码分析之ServiceManager(原)

    ServiceManager作为Native层Service的管理员,有着极其重要的作用,主要表现两个方面:         1.对于服务端来说,系统所有的服务提供者都需要向ServiceManage ...

  5. 理解Android Binder机制原理

    原文地址: http://blog.csdn.net/universus/article/details/6211589 Binder是Android系统进程间通信(IPC)方式之一.Linux已经拥 ...

  6. Spring IoC 源码系列(三)Spring 事件发布机制原理分析

    在 IoC 容器启动流程中有一个 finishRefresh 方法,具体实现如下: protected void finishRefresh() {clearResourceCaches();init ...

  7. redis发布订阅c接口_Redis 发布/订阅机制原理分析

    序:使用订阅发布功能的时候想查一下客户端是如何接收消息的(客户端订阅了频道之后也会注册一个回调函数,服务端publish消息之后回调函数会获取到消息.这块没看到太多内容只有C++的源码),无意中查到这 ...

  8. Framework原理分析之——Zygote进程流程分析

    在启动Zygote进程的分析中,我们知道其实是通过系统调用fork()函数来创建一个进程,然后执行Zygote.rc中的执行文件,从而开始Zygote进程业务. 本篇文章我们就来分析大名鼎鼎的Zygo ...

  9. Linux select/poll机制原理分析

    转载一篇文章,讲解select和poll机制的,分享给大家. 前言 Read the fucking source code!  --By 鲁迅 A picture is worth a thousa ...

最新文章

  1. 《预训练周刊》第9期:TABBIE:表格数据的预训练表示、「视觉预训练神作」:不用图片却训出图像识别SOTA?...
  2. Android SlideAndDragListView,一个可排序可滑动item的ListView
  3. PMCAFF原创作者人气榜,快来看看你排第几?
  4. SEO:如何做好软文推广
  5. 修改UBOOT和LINUX调试串口(TI达芬奇芯片--DM6467)
  6. JAVA List集合转Page(分页对象) java 分页 PageModel 测试类TestPagerModel
  7. 分辨垃圾材质自动分类 支付宝升级垃圾分类AI回收箱
  8. Kotlin 基础语法(四)
  9. OC---Math公式
  10. c语言stdio函数大全,初学者常用的stdio库,原来还有这么多知识点
  11. sigmoid/softmax指数运算溢出问题的解决方法
  12. 什么是PERT网络分析?
  13. python寻找所有三位数素数_寻找所有的素数的python实现
  14. 如何将音乐复制到您的Android手机
  15. 汇正财经骗局?科创50大涨
  16. 一文搞定计算机网络面试题
  17. ftp上传工具中文版,有好用的中文版ftp上传工具吗?ftp上传工具中文版
  18. C语言程序设计会员计费系统,C语言课程设计酒店房间登记与计费管理系统总结报告...
  19. oppo手机怎么查计算机记录,oppo怎样查通话记录(oppo手机怎么查以前的通话记录)...
  20. C71500(BFe30-1-1)镍白铜锻件 带材

热门文章

  1. rust卡领地柜权限_rust一个领地柜有多大范围 | 手游网游页游攻略大全
  2. Spark 实时处理 总文章
  3. mysql 1443_MySQL 1443:這是什么意思?
  4. Teamviewer控制安卓手机远程打卡实现
  5. CDISC SDTMIG4.1.4.3 时间间隔和持续时间变量DUR的使用
  6. linux 系统启动故障处理(an error occurred during the file system check)
  7. Linq之GroupBy用法
  8. 格式化代码_格式化代码是什么意思
  9. Android 百度地图获取定位信息并实时传给后台
  10. Yii2 Elasticsearch 操作Demo