前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Binder 机制】Native 层 Binder 机制分析 ( service_manager.c | 开启 Binder | 注册 Binder 进程上下文 | 开启 Binder 循环 )

【Binder 机制】Native 层 Binder 机制分析 ( service_manager.c | 开启 Binder | 注册 Binder 进程上下文 | 开启 Binder 循环 )

作者头像
韩曙亮
发布2023-03-29 17:23:44
6280
发布2023-03-29 17:23:44
举报

文章目录

一、系统服务


System Server 是由 Zygote 进程 fork() 出来的 ;

Init 进程 启动 Zygote , Zygote 启动 System Server 进程 ;

System Server 注册服务时 , 会执行 service_manager.c 中的方法 ;

二、系统服务主函数


service_manager.c 从 int main(int argc, char** argv) 方法开始执行 , 在该 main 函数中 , 执行了

3

个重要操作 :

  • ① 开启 Binder : 获取 Binder 驱动 , 并且申请了 128 KB 的内存映射地址 , bs = binder_open(driver, 128*1024); ;
  • ② 注册上下文 : 将自己注册成 Binder 进程的上下文 , binder_become_context_manager(bs) ;
  • ③ 开启 Binder 循环 : 不停的轮询 , 是否有消息发送 , binder_loop(bs, svcmgr_handler); ;
代码语言:javascript
复制
int main(int argc, char** argv)
{

	// 开启 Binder , 获取 Binder 驱动 , 并且申请了 128 KB 的内存映射地址
    bs = binder_open(driver, 128*1024);
    

	// 将自己注册成 Binder 进程的上下文
    if (binder_become_context_manager(bs)) {
        ALOGE("cannot become context manager (%s)\n", strerror(errno));
        return -1;
    }

	// 开启 Binder 循环 
    binder_loop(bs, svcmgr_handler);

    return 0;
}

完整代码参考 /frameworks/native/cmds/servicemanager/service_manager.c ;

三、开启 Binder


通过调用 bs = binder_open(driver, 128*1024); 开启 Binder 的方法 struct binder_state *binder_open(const char* driver, size_t mapsize) , 定义在 /frameworks/native/cmds/servicemanager/binder.c 中 ;

首先分析下 binder_state 结构体 , 其中有

3

个参数 ,

  • int fd : Binder 驱动的文件描述
  • void *mapped : 内存映射首地址
  • size_t mapsize : 内存映射的大小 , 128 KB

方法最终返回一个 binder_state 结构体 ;

代码语言:javascript
复制
struct binder_state
{
    int fd;			// Binder 驱动的文件描述
    void *mapped;	// 内存映射首地址
    size_t mapsize; // 内存映射的大小 , 128 KB 
};

// 打开 Binder , 并获取 Binder 的状态描述
struct binder_state *binder_open(const char* driver, size_t mapsize)
{
    struct binder_state *bs;	// 返回的结构体
    struct binder_version vers;

    bs = malloc(sizeof(*bs));	// 为返回值申请内存
    if (!bs) {
        errno = ENOMEM;
        return NULL;
    }

    bs->fd = open(driver, O_RDWR | O_CLOEXEC);
    if (bs->fd < 0) {
        fprintf(stderr,"binder: cannot open %s (%s)\n",
                driver, strerror(errno));
        goto fail_open;
    }

    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);
    if (bs->mapped == MAP_FAILED) {
        fprintf(stderr,"binder: cannot map device (%s)\n",
                strerror(errno));
        goto fail_map;
    }

    return bs;

fail_map:
    close(bs->fd);
fail_open:
    free(bs);
    return NULL;
}

完整代码参考 /frameworks/native/cmds/servicemanager/binder.c#97;

四、注册上下文


通过调用 binder_become_context_manager(bs) , 将自己注册成 Binder 进程的上下文 ; 其中调用的 ioctl 方法是内核中的方法 , 这是 IO Control 的简称 ; 这个代码得去内核中查找 ;

代码语言:javascript
复制
int binder_become_context_manager(struct binder_state *bs)
{
    return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}

完整代码参考 /frameworks/native/cmds/servicemanager/binder.c#147

【Binder 机制】分析 Android 内核源码中的 Binder 驱动源码 binder.c ( googlesource 中的 Android 内核源码 | 内核源码下载 ) 博客中分析了该 ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0) 方法对应的源码 ;

五、开启 Binder 循环


在 service_manager.c 的 main 函数中 , 调用 binder_loop(bs, svcmgr_handler) 开启 Binder 循环 ;

binder_loop 方法定义在 /frameworks/native/cmds/servicemanager/binder.c 中 ;

binder_loop 方法中 , 先调用了 binder_write 方法 , 然后开启了死循环 ;

代码语言:javascript
复制
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 (;;) {
        bwr.read_size = sizeof(readbuf);
        bwr.read_consumed = 0;
        bwr.read_buffer = (uintptr_t) readbuf;

        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);

        if (res < 0) {
            ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
            break;
        }
		
		// 如果收到 Binder 读写消息信息 , 调用 binder_parse 方法处理 ;
        res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
        if (res == 0) {
            ALOGE("binder_loop: unexpected reply?!\n");
            break;
        }
        if (res < 0) {
            ALOGE("binder_loop: io error %d %s\n", res, strerror(errno));
            break;
        }
    }
}

完整代码参考 /frameworks/native/cmds/servicemanager/binder.c ;

六、binder_write 方法


binder_write 方法中 , 调用了内核中的 ioctl(bs->fd, BINDER_WRITE_READ, &bwr) 方法 ; 具体方法参考 【Binder 机制】分析 Android 内核源码中的 Binder 驱动源码 binder.c ( googlesource 中的 Android 内核源码 | 内核源码下载 ) 博客进行分析 ;

代码语言:javascript
复制
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/binder.c ;

七、binder_ioctl 内核方法


分析调用内核方法 ioctl(bs->fd, BINDER_WRITE_READ, &bwr) , 传入 BINDER_WRITE_READ 常量 , 走如下分支代码 ; 主要调用了 binder_ioctl_write_read 方法 ;

代码语言:javascript
复制
	case BINDER_WRITE_READ:
		ret = binder_ioctl_write_read(filp, cmd, arg, thread);
		if (ret)
			goto err;
		break;

android-mainline/drivers/android/binder.c 部分内核 Binder 驱动代码 :

代码语言:javascript
复制
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;
	/*pr_info("binder_ioctl: %d:%d %x %lx\n",
			proc->pid, current->pid, cmd, arg);*/
	binder_selftest_alloc(&proc->alloc);
	trace_binder_ioctl(cmd, arg);
	ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
	if (ret)
		goto err_unlocked;
	thread = binder_get_thread(proc);
	if (thread == NULL) {
		ret = -ENOMEM;
		goto err;
	}
	switch (cmd) {
	case BINDER_WRITE_READ:
		ret = binder_ioctl_write_read(filp, cmd, arg, thread);
		if (ret)
			goto err;
		break;
	default:
		ret = -EINVAL;
		goto err;
	}
	ret = 0;
err:
	if (thread)
		thread->looper_need_return = false;
	wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
	if (ret && ret != -EINTR)
		pr_info("%d:%d ioctl %x %lx returned %d\n", proc->pid, current->pid, cmd, arg, ret);
err_unlocked:
	trace_binder_ioctl_done(ret);
	return ret;
}

完整代码参考 https://android.googlesource.com/kernel/common/+/refs/heads/android-mainline/drivers/android/binder.c ;

八、binder_ioctl_write_read 内核方法


在 方法中 , 调用了

2

个重要的方法 , copy_from_user(&bwr, ubuf, sizeof(bwr)) 从用户空间的进程缓冲区中读取到内核空间缓冲区 , copy_to_user(ubuf, &bwr, sizeof(bwr)) 从内核空间写出到用户空间进程中 ;

如果写出的数据大于 0 , 则调用 binder_thread_write 方法 ;

如果读取的数据大于 0 , 则调用 binder_thread_read 方法 ;

代码语言:javascript
复制
static int binder_ioctl_write_read(struct file *filp,
				unsigned int cmd, unsigned long arg,
				struct binder_thread *thread)
{
	int ret = 0;
	struct binder_proc *proc = filp->private_data;
	unsigned int size = _IOC_SIZE(cmd);
	void __user *ubuf = (void __user *)arg;
	struct binder_write_read bwr;
	if (size != sizeof(struct binder_write_read)) {
		ret = -EINVAL;
		goto out;
	}
	// 从用户空间的进程缓冲区中读取到内核空间缓冲区 
	if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
		ret = -EFAULT;
		goto out;
	}
	binder_debug(BINDER_DEBUG_READ_WRITE,
		     "%d:%d write %lld at %016llx, read %lld at %016llx\n",
		     proc->pid, thread->pid,
		     (u64)bwr.write_size, (u64)bwr.write_buffer,
		     (u64)bwr.read_size, (u64)bwr.read_buffer);

	// 如果写出的数据大于 0 
	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;
		}
	}
	// 如果读取的数据大于 0 
	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;
		}
	}
	binder_debug(BINDER_DEBUG_READ_WRITE,
		     "%d:%d wrote %lld of %lld, read return %lld of %lld\n",
		     proc->pid, thread->pid,
		     (u64)bwr.write_consumed, (u64)bwr.write_size,
		     (u64)bwr.read_consumed, (u64)bwr.read_size);
	// 从内核空间写出到用户空间进程中 
	if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
		ret = -EFAULT;
		goto out;
	}
out:
	return ret;
}

完整代码参考 https://android.googlesource.com/kernel/common/+/refs/heads/android-mainline/drivers/android/binder.c ;

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-09-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 一、系统服务
  • 二、系统服务主函数
  • 三、开启 Binder
  • 四、注册上下文
  • 五、开启 Binder 循环
  • 六、binder_write 方法
  • 七、binder_ioctl 内核方法
  • 八、binder_ioctl_write_read 内核方法
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档