首页 > 其他分享 >Android Binder 机制之 ServiceManager 模块

Android Binder 机制之 ServiceManager 模块

时间:2024-06-03 14:34:07浏览次数:14  
标签:node ... bwr struct binder Binder ServiceManager Android proc

ServiceManager 启动源码分析

以 Android 9.0 代码为例介绍

Init 拉起 ServiceManager 进程

init 进程通过 init.rc 脚本拉起 Native 层的 ServiceManager 进程

  1. init.rc
// system/core/rootdir/init.rc
on late-init    
    ...
    trigger post-fs # late_init 事件触发 post_fs 事件
    ...
on post-fs
    load_system_props
    start logd
    start servicemanager    # post-fs 事件中启动三个服务管理进程
    start hwservicemanager
    start vndservicemanager
  1. init.cpp
// system/core/init/init.cpp:740
int main(int argc, char** argv) {
    std::string bootmode = GetProperty("ro.bootmode", "");
    if (bootmode == "charger") {
        am.QueueEventTrigger("charger");
    } else {
        am.QueueEventTrigger("late-init");  // 当前的 bootmode 不是 charger 时,将触发 late-init 事件
    }
    ...
    am.ExecuteOneCommand(); // 执行该事件里设定好的命令,即拉起 servicemanager 进程
}

ServiceManager 启动

ServiceManager 启动后完成两件事

  1. 打开 binder 驱动
  2. 将自己注册到 binder 驱动中,设置自己为守护进程,等待服务端和客户端的调用

然后 ServiceManager 进程陷入循环,等待请求

// frameworks/native/cmds/servicemanager/service_manager.c
int main(int argc, char** argv){
    ...
    driver = "/dev/binder";
    bs = binder_open(driver, 128*1024); // 打开 binder 驱动
    ...
    if (binder_become_context_manager(bs)) {    // 将当前进程设置为守护进程
        ...
    }
    ...
    binder_loop(bs, svcmgr_handler);    // 在 svcmgr_handler() 函数中循环等待 client 发过来的请求
    ...
}

1. ServiceManager 打开 binder 驱动

在 binder_open(),完成两件事

  1. 获取 binder 驱动的句柄文件
  2. 和 binder 内核完成共享内存步骤

binder_open() 打开驱动

system_manager 会在 main.cpp 中,调用 open 系统调用

// frameworks/native/cmds/servicemanager/binder.c
struct binder_state *binder_open(const char* driver, size_t mapsize){
    struct binder_state *bs;    // 记录 binder 的结构体
    ...
    bs->fd = open(driver, O_RDWR | O_CLOEXEC);  // 打开文件节点的文件描述符
    ...
    // 写 BINDER_VERSION 到 Binder 内核
    if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) ||
        (vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION)) {
        fprintf(stderr,
                "binder: kernel driver version (%d) differs from user space version (%d)\n",
                vers.protocol_version, BINDER_CURRENT_PROTOCOL_VERSION);
        goto fail_open;
    }
    ...
    bs->mapsize = mapsize;                    // 分配的内存映射的空间大小
    bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);    // 分配的内存的空间地址
}

binder_open() 内核处理

上层通过系统调用接口 open() 调用到 binder 内核中的 binder_open() 函数,调用过程如下:

  1. 应用进程调用 open() 系统调用,请求并打开指定文件
  2. 系统调用转为内核中的 do_sys_open() 函数调用
  3. 该函数在 VFS(Virtual File System) 层查找并打开指定的文件设备,并创建一个新的 file 结构体实例
  4. VFS 设置 file 结构体的各个字段,包含与设备文件相关联的操作函数(read/write/ioctl)
  5. do_sys_open() 函数返回一个文件描述符,该文件描述符与新创建的 file 结构体关联
  6. 如果请求的 Binder 设备文件,VFS 会调用 binder_open 函数,并将 file 结构体作为参数传递过去。

Binder 内核在 binder_open() 函数中做了 4 件事,分别是

  1. 创建 binder_proc 结构体,存储于 Binder 相关的进程信息,每个使用 Binder 的进程都有一份独立的 binder_proc 实例
  2. 初始化数据结构,包括初始化 todo 队列和等待队列,这些队列用于管理 Binder 事务和进程间通信
  3. 记录 binder_proc,将创建的 binder_proc 添加到内核的 binder_procs 列表中,这样系统就可以在需要时访问和管理所有的 Binder 进程
  4. 分配文件描述符,binder_open() 会为应用进程分配一个文件描述符,以便应用进程可以通过该描述符与 Binder 驱动进行通信
// device/renesas/kernel/drivers/android/binder.c
static int binder_open(struct inode *nodp, struct file *filp)
{
    struct binder_proc *proc;   
    ...
    proc = kzalloc(sizeof(*proc), GFP_KERNEL);  // 初始化 binder_proc
    ...
    INIT_LIST_HEAD(&proc->todo);                // 初始化 todo 队列
    ...
    INIT_LIST_HEAD(&proc->waiting_threads);     // 初始化 等待队列
    ...
    filp->private_data = proc;                  // 将 binder_proc 记录到 file 实例中
    ....
    hlist_add_head(&proc->proc_node, &binder_procs);    // 将 binder_proc 记录到 binder_procs 列表中
    ...
}

binder_mmap() 内核的内存映射处理

mmap 系统调用最终会到 binder_mmap 函数中

binder_mmap 完成以下几个工作:

  1. 内存映射:binder_mmap 会在内核虚拟地址空间中申请一块与用户虚拟地址内存相同大小的内存
  2. 物理分配内存:它会申请一块页面大小的物理内存
  3. 映射同步:将同一块物理内存分别映射到内核虚拟地址空间和用户虚拟内存空间,实现用户空间的 Buffer 和内核空间的 Buffer 同步操作
// device/renesas/kernel/drivers/android/binder.c
static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
{
    ...
    // 获取对应进程的信息
   	struct binder_proc *proc = filp->private_data;
    ... 
    // 设置了一块内存的操作接口,打开关闭和缺页等
    // 并且将该块内存和指定进程绑定
    vma->vm_ops = &binder_vm_ops;
	vma->vm_private_data = proc;
    ...
    ret = binder_alloc_mmap_handler(&proc->alloc, vma);
    ...
}
// device/renesas/kernel/drivers/android/binder_alloc.c
int binder_alloc_mmap_handler(struct binder_alloc *alloc,
			      struct vm_area_struct *vma)
{
    ...
    // 在内核虚拟地址空间中申请一块与用户虚拟内存大小相同的内存
    // 将该内存与当前进程绑定
    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->user_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;
	binder_alloc_set_vma(alloc, vma);
	mmgrab(alloc->vma_vm_mm);   
    ...
}

需要注意的是:以上代码是早期版本内核的 mmap 处理步骤,等到 4.19 版本之后,内存不再申请一块内存地址了,而 binder_mmap 也不再做映射了,等到进程通信数据传递的时候,才会完成用户空间虚拟地址到物理地址的映射,然后再把数据拷贝到这块物理内存中完成数据传递。

架构图

如上,ServiceManager 的 binder 驱动已打开,并且已经做好 ServiceManager 和 Binder 内核的共享内存申请

架构图如下:

// todo

2. ServiceManager 注册自己为守护进程

在 Native 层

// frameworks/native/cmds/servicemanager/binder.c
int binder_become_context_manager(struct binder_state *bs)
{
    // 通过 ioctl 将 binder 描述符传入,并且通过指令 BINDER_SET_CONTEXT_MGR 设置为守护进程
    return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}

在 Binder 内核中完成三件事

  • 在 Binder 驱动层创建 binder_node 结构体对象
  • 将当前的 binder_proc 加入到 binder_node 的 node->proc
  • 创建 binder_node 的 async_todo 和 binder_work 两个队列
// device/renesas/kernel/drivers/android/binder.c
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    switch (cmd) {
        ...
        case BINDER_SET_CONTEXT_MGR:
		ret = binder_ioctl_set_ctx_mgr(filp, NULL);
		if (ret)
			goto err;
		break;
        ...
    }
}
static int binder_ioctl_set_ctx_mgr(struct file *filp,
				    struct flat_binder_object *fbo)
{
    ...
    // 确保守护进程只注册一次
    if (context->binder_context_mgr_node) {
		pr_err("BINDER_SET_CONTEXT_MGR already set\n");
		ret = -EBUSY;
		goto out;
	}
    ...
    if (uid_valid(context->binder_context_mgr_uid)) {
        // 检查 uid 有效
	} else {
		context->binder_context_mgr_uid = curr_euid;    // 无效的话将当前线程 euid 设置为 service_manager
	}
    ...
    new_node = binder_new_node(proc, fbo);      // 创建 service_manager 实体
    ...
    new_node->local_weak_refs++;    // 强弱引用 +1
	new_node->local_strong_refs++;
    ...
}
static struct binder_node *binder_new_node(struct binder_proc *proc,
					   struct flat_binder_object *fp) {
    // 分配内核空间
    struct binder_node *new_node = kzalloc(sizeof(*node), GFP_KERNEL);
    node = binder_init_node_ilocked(proc, new_node, fp);    // 处理 binder_node
    ...
}

static struct binder_node *binder_init_node_ilocked(
						struct binder_proc *proc,
						struct binder_node *new_node,
						struct flat_binder_object *fp)
{
    ...
	node = new_node;
	binder_stats_created(BINDER_STAT_NODE);
	node->tmp_refs++;
	rb_link_node(&node->rb_node, parent, p);    // 将节点放入到红黑数树中
	rb_insert_color(&node->rb_node, &proc->nodes);
	node->debug_id = atomic_inc_return(&binder_last_id);
	node->proc = proc;                          // 初始化 binder_node
	node->ptr = ptr;
	node->cookie = cookie;          
	node->work.type = BINDER_WORK_NODE;         // 设置 binder_work 的type
    ...
}

3. ServiceManager 进入循环等待

在 native 层,ServiceManager 发送 looper 指令告诉内核

// 发送 BC_ENTER_LOOPER 指令到 Binder 内核
// frameworks/native/cmds/servicemanager/binder.c
void binder_loop(struct binder_state *bs, binder_handler func)
{

    bwr.write_size = 0;
    bwr.write_consumed = 0;
    bwr.write_buffer = 0;

    uint32_t readbuf[32];
    readbuf[0] = BC_ENTER_LOOPER;
    binder_write(bs, readbuf, sizeof(uint32_t));    // 写一次 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);   // 再写,只读不写
        ...
        // 消息处理,func 
        res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
    }
}

// 将 BC_ENTER_LOOPER 写入到 binder 内核中
int binder_write(struct binder_state *bs, void *data, size_t len)
{
    struct binder_write_read bwr;
    int res;

    bwr.write_size = len;       // 只写
    bwr.write_consumed = 0;
    bwr.write_buffer = (uintptr_t) data;
    bwr.read_size = 0;          // 不读
    bwr.read_consumed = 0;
    bwr.read_buffer = 0;
    res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
    if (res < 0) {
        fprintf(stderr,"binder_write: ioctl failed (%s)\n",
                strerror(errno));
    }
    return res;
}

// frameworks/native/cmds/servicemanager/service_manager.c
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_GET_SERVICE:
    case SVC_MGR_CHECK_SERVICE:
        ...
    case SVC_MGR_ADD_SERVICE:
        ...
    case SVC_MGR_LIST_SERVICES:
        ...
    }
}

binder 内核收到 BINDER_WRITE_READ 请求处理

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    ...
	switch (cmd) {
	case BINDER_WRITE_READ:
		ret = binder_ioctl_write_read(filp, cmd, arg, thread);
		if (ret)
			goto err;
		break;
    }
    ...
}

static int binder_ioctl_write_read(struct file *filp,
				unsigned int cmd, unsigned long arg,
				struct binder_thread *thread)
{
    ...
    // 将传入的参数,从用户空间拷贝到内核空间
    void __user *ubuf = (void __user *)arg;
    if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
		ret = -EFAULT;
		goto out;
	}
    ...
    // 有写入的数据,即用户进程向内核进程写数据,对应 binder_loop() 的第一次 ioctl
    if (bwr.write_size > 0) {
		ret = binder_thread_write(proc, thread,
					  bwr.write_buffer,
					  bwr.write_size,
					  &bwr.write_consumed);
		trace_binder_write_done(ret);
		if (ret < 0) {
			bwr.read_consumed = 0;
			if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
				ret = -EFAULT;
			goto out;
		}
	}
    // 如果有需要读取的数据,即内核向用户写数据,对应 binder_loop() 的 for 循环中的 ioctl
	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);
		trace_binder_read_done(ret);
		binder_inner_proc_lock(proc);
		if (!binder_worklist_empty_ilocked(&proc->todo))
			binder_wakeup_proc_ilocked(proc);
		binder_inner_proc_unlock(proc);
		if (ret < 0) {
			if (copy_to_user(ubuf, &bwr, sizeof(bwr)))  // 读取完拷贝到用户空间
				ret = -EFAULT;
			goto out;
		}
	}
    ...
}

// 第一次 ioctl 写 BC_ENTER_LOOPER 处理逻辑
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)
{
	while (ptr < end && thread->return_error.cmd == BR_OK) {
		int ret;
        // 从 ptr 中获取数据,即 BC_ENTER_LOOPER
		if (get_user(cmd, (uint32_t __user *)ptr))
			return -EFAULT;
        ...
        switch (cmd) {
            ...
            // 将指定线程设置为 loop 状态,该线程是从 device 文件中获取。
            case BC_ENTER_LOOPER:
			binder_debug(BINDER_DEBUG_THREADS,
				     "%d:%d BC_ENTER_LOOPER\n",
				     proc->pid, thread->pid);
			if (thread->looper & BINDER_LOOPER_STATE_REGISTERED) {
				thread->looper |= BINDER_LOOPER_STATE_INVALID;
				binder_user_error("%d:%d ERROR: BC_ENTER_LOOPER called after BC_REGISTER_LOOPER\n",
					proc->pid, thread->pid);
			}
			thread->looper |= BINDER_LOOPER_STATE_ENTERED;  // 设置该线程的状态为 BINDER_LOOPER_STATE_ENTERED
			break;
            ...
        }
    }
    ...
}

// for 循环中只读不写的处理
static int binder_thread_read(struct binder_proc *proc,
			      struct binder_thread *thread,
			      binder_uintptr_t binder_buffer, size_t size,
			      binder_size_t *consumed, int non_block)
{
    ...
    // for 循环中的 read_consumed 值,是 0
    if (*consumed == 0) {
		if (put_user(BR_NOOP, (uint32_t __user *)ptr))  // 将 BR_NOOP 设置到 ptr 中
			return -EFAULT;
		ptr += sizeof(uint32_t);
	}
    ...

    // binder 线程是否能工作,此时肯定是 true
	binder_inner_proc_lock(proc);
	wait_for_proc_work = binder_available_for_proc_work_ilocked(thread);
	binder_inner_proc_unlock(proc);
    ...

    // filp->f_flags 是 O_RDWR | O_CLOEXEC( 02000000 | 02)
    // non_block = filp->f_flags & O_NONBLOCK(00004000 & 02000002 = 0)
	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); // 将传入的线程阻塞在此处
	}
}

static int binder_wait_for_work(struct binder_thread *thread,
				bool do_proc_work)
{
    ...
	for (;;) {
		prepare_to_wait(&thread->wait, &wait, TASK_INTERRUPTIBLE);
		if (binder_has_work_ilocked(thread, do_proc_work))
			break;
        /*
        * 调度函数,从运行队列的链表中找到一个进程,随后 CPU 分配给这个进程。
        * 如果当前进程因不能获得必须的资源而要被阻塞时,需要使用调度函数
        * 1. 先将当前进程插入到适当的等待队列
        * 2. 将当前进程状态修改为 TASK_INTERRUPTIBLE 或者 TASK_UNINTERRUPTIBLE
        * 3. 调用 schedule()
        * 4. 检查资源是否可能,如果不可用转到步骤 2
        * 5. 如果资源可用就从等待队列中删除当前进程
        */
		schedule();
        ...
	}
    ...
}

至此,ServiceManager 启动,到打开 Binder 内核,注册自己为守护进程,然后自己进入 loop 循环,而 Binder 内核也进入中断等待请求到来的逻辑分析完毕

疑问:

  1. binder 内核处进程阻塞后, BR_NOOP 是什么时候被通知到 ServiceManager 中的

标签:node,...,bwr,struct,binder,Binder,ServiceManager,Android,proc
From: https://www.cnblogs.com/wanghao-boke/p/18228852

相关文章

  • java springboot基于Android平台的诗词学习系统APP小程序万字文档和PPT(源码+lw+部署
    前言......
  • Android 10.0 Launcher3禁用widget微件功能实现
    1.前言在10.0的系统rom定制化开发中,在一些Launcher3的定制化功能中,有些产品禁用appwidget微件功能,要求Launcher去掉加载widget微件功能,接下来具体分析下widget微件的加载流程2.Launcher3禁用widget微件功能实现的核心类packages/apps/Launcher3/src/com/android/launcher3/......
  • Android Studio踩坑记录
    一、5issueswerefoundwhencheckingAARmetadata:  1. Dependency'androidx.appcompat:appcompat-resources:1.7.0'requireslibrariesandapplicationsthat    dependonittocompileagainstversion34orlaterofthe    AndroidAPIs.诸如......
  • Android基础-UI布局
    在Android开发中,UI布局是构建用户界面(UserInterface)的基础。不同的布局方式可以适应不同的界面需求,实现多样化的界面效果。下面将详细阐述Android中几种常见的UI布局方式,包括它们的功能、优势和劣势,以便开发者在选择布局方式时能够做出更明智的决策。1.LinearLayout(线性布局......
  • Android Bluetooth page timeout问题
    Android蓝牙连接超时时间蓝牙配对时间过长https://blog.51cto.com/u_13019/7750311测试机与辅助机配对蓝牙成功后,关闭辅助机蓝牙开关,测试机给辅助机通过蓝牙分享一张图片,提示"蓝牙共享,未发送文件"间隔时间应当5秒左右测试步骤:1.测试机与辅助机配对蓝牙成功2.进入设置->蓝牙,选......
  • Android启动窗口SplashScreen
    Android启动窗口SplashScreen首先介绍下什么是启动窗口,对于大部分应用冷启动时的场景都会有启动窗口,为了让效果更明显,在如下代码中(只是一个基本的可以运行的应用即可)添加了sleep5s的代码,在按recent键移除应用后,再点击桌面图标,即可看到启动窗口效果,即使点击后界面内容显示出来前的......
  • 基于Android的XX校园交流APP
    摘要这个App的设计主要包括前台页面的设计和方便用户互动的后端数据库,而前端软件的开发则需要良好的数据处理能力、友好的界面和易用的功能。数据要被工作人员通过界面操作传输至数据库中。通过研究采用MVP结构设计,使用Java开发语言开发,采用ssm架构以及MySQL数据库进行设......
  • springboot基于Android的记录生活APP
    摘要本文拟采用Android平台进行开发,使用java技术和Springboot搭建系统框架,后台使用MySQL数据库进行信息管理,设计开发的记录生活APP。通过调研和分析,系统拥有管理员和用户两个角色,主要具备登录注册,个人信息修改,用户管理,运动项目管理,食物类型管理,新闻资讯管理,食品分析管理,套......
  • 基于Android的跳蚤市场
    摘要伴随着我国社会的发展,人民生活质量日益提高。互联网逐步进入千家万户,改变传统的管理方式,以互联网为基础,利用java技术,结合SpringBoot框架和MySQL数据库开发设计一套跳蚤市场,提高工作效率的同时,减轻管理者工作方面的压力,使他们能够将更多精力投入到市场上,更好的完成用户......
  • Android基础-数据库
    在Android系统中,数据库扮演着至关重要的角色,它负责存储、管理和检索应用程序所需的数据。随着移动应用的日益复杂和功能的不断增加,对数据库的需求也日益提高。在Android中,有多种数据库管理系统和工具可供选择,其中最为常见和广泛使用的是SQLite数据库。下面将详细介绍Android系......