ARC的binder实现

ARC的背景知识参考这篇文章

从背景知识可以知道,ARC是一个跨平台的Android runtime。Android前前后后修改了不少linux内核的东西,其中就包括binder。(PS:什么是binder?使用场景?这里这里)。binder中有一个模块用到了内核驱动。(PS:如何写linux内核驱动?这里)这个模块是必不可少的IPC通讯模块。那么ARC如何处理这个模块的呢?

实现的代码在这里:
https://chromium.googlesource.com/chromium/src/+/69.0.3497.117/chromeos/binder/

(PS:熟悉代码的话,直接看代码就完事了,我这里写得不对的地方也欢迎校正。)

这里简单描述下实现。

ARC要用NaCl重新编译代码,其中包括libc,在libc中增加这部分的处理。
在用户空间上开一个共享内存,然后再构建一层IPC通讯机制。
把原来作为内核驱动的模块,在用户空间上做了实现。

提几个问题,带着问题来解释实现:

  • 如何拦截open("/dev/binder",XXXX)、ioctl的系统调用?
  • binder原来的功能是如何设计?
  • 重新实现之后有哪些出入?
  • 如何独立的测试/修改这部分功能?

如何拦截原来的系统调用

const char kDriverPath[] = "/dev/binder";

bool Driver::Initialize() {
  base::AssertBlockingAllowed();
  // Open binder driver.
  fd_.reset(HANDLE_EINTR(open(kDriverPath, O_RDWR | O_CLOEXEC | O_NONBLOCK)));
  if (!fd_.is_valid()) {
    PLOG(ERROR) << "Failed to open";
    return false;
  }
  // Version check.
  int version = 0;
  if (HANDLE_EINTR(ioctl(fd_.get(), BINDER_VERSION, &version)) != 0 ||
      version != BINDER_CURRENT_PROTOCOL_VERSION) {
    PLOG(ERROR) << "Version check failure: version = " << version;
    return false;
  }

  ...
}

注意到这里有一个 HANDLE_EINTR,它对这个 /dev/binder 文件的系统调用 ( open、ioctl、poll )替换下实现?

// TODO test

binder driver原来的设计

binder driver为跨进程的数据交换做了支持,其中主要是如下几个方法。

参考
http://gityuan.com/2016/09/04/binder-start-service/#三binder-driver

binder_ioctl

首先,根据传递过来的文件句柄指针获取相应的binder_proc结构体, 再从中查找binder_thread,如果当前线程已经加入到proc的线程队列则直接返回, 如果不存在则创建binder_thread,并将当前线程添加到当前的proc.

当返回值为-ENOMEM,则意味着内存不足,往往会出现创建binder_thread对象失败;
当返回值为-EINVAL,则意味着CMD命令参数无效;

binder_ioctl_write_read/binder_thread_write

此时arg是一个binder_write_read结构体,mOut数据保存在write_buffer,所以write_size>0,但此时read_size=0。首先,将用户空间bwr结构体拷贝到内核空间,然后执行binder_thread_write()操作.
不断从binder_buffer所指向的地址获取cmd, 当只有BC_TRANSACTION或者BC_REPLY时, 则调用binder_transaction()来处理事务.

binder_transaction

  • 查询目标进程的过程: handle -> binder_ref -> binder_node -> binder_proc
  • 将BINDER_WORK_TRANSACTION添加到目标队列target_list:
    call事务, 则目标队列target_list=target_proc->todo;
    reply事务,则目标队列target_list=target_thread->todo;
    async事务,则目标队列target_list=target_node->async_todo.
  • 数据拷贝
    将用户空间binder_transaction_data中ptr.buffer和ptr.offsets拷贝到目标进程的binder_buffer->data;
    这就是只拷贝一次的真理所在;
  • 设置事务栈信息
    BC_TRANSACTION且非oneway, 则将当前事务添加到thread->transaction_stack;
  • 事务分发过程:
    将BINDER_WORK_TRANSACTION添加到目标队列(此时为target_proc->todo队列);
    将BINDER_WORK_TRANSACTION_COMPLETE添加到当前线程thread->todo队列;
  • 唤醒目标进程target_proc开始执行事务。

该方法中proc/thread是指当前发起方的进程信息,而binder_proc是指目标接收端进程。 此时当前线程thread的todo队列已经有事务, 接下来便会进入binder_thread_read来处理相关的事务.

重新实现之后的出入

// TODO

代码测试于验证

// TODO

相关推荐
©️2020 CSDN 皮肤主题: 深蓝海洋 设计师:CSDN官方博客 返回首页