进程间通信——Binder

作者: 某昆 | 来源:发表于2018-04-22 19:13 被阅读79次

本文主要内容

  • Binder驱动接口
  • ServiceManager的启动
  • Binder设计理念
  • 总结

Binder驱动接口

Binder驱动一共有3个重要的接口:

  • binder_open,打开binder驱动
  • binder_mmap,内存映射
  • binder_ioctl,与binder驱动通信

binder_mmap是整套机制的基础,通过内存映射,不同进程间有一块共享内存,得以实现IPC。

binder_ioctl,类似于文件读写方法,它承担了Binder驱动的大部分业务,与Binder驱动通信。

关于以上3个方法,后续再补充详细说明。

ServiceManager的启动

ServiceManager是非常重要的系统服务,它类似于网络世界中的DNS(请参见进程能通信——智能指针)。
它是在init.rc中启动的,代码位于/framework/native/cmds/servicemanager 目录中,sm启动代码如下:

  //service_manager.c
int main(int argc, char** argv)
{
struct binder_state *bs;
char *driver;
if (argc > 1) {
    driver = argv[1];
} else {
    driver = "/dev/binder";
}
//打开驱动
bs = binder_open(driver, 128*1024);
//将自己设置成context manager角色,ServiceManager系统中肯定只有一个
if (binder_become_context_manager(bs)) {
    ALOGE("cannot become context manager (%s)\n", strerror(errno));
    return -1;
}
//进入循环
binder_loop(bs, svcmgr_handler);
}

代码较类似于socket通信server端代码,新建ServerSocket,启动死循环,等待连接。查看binder_open代码:

struct binder_state *binder_open(const char* driver, size_t mapsize)
{
  struct binder_state *bs;
  bs = malloc(sizeof(*bs));
  //真正地打开binder驱动
  bs->fd = open(driver, O_RDWR | O_CLOEXEC);
  //内存映射
  bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
  return bs;
}

上一章中提到的binder驱动三大方法中,现在已经出现俩了,binder_open方法中打开binder驱动,并且完成内存映射。

接下来看看binder_loop方法:

void binder_loop(struct binder_state *bs, binder_handler func)
{
  for (;;) {
      //调用ioctl,与binder驱动通信,从驱动中读取数据,并存放在bwr指针中
      res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
      //处理读取的指令
      res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
  }
}

binder_loop中存在一个死循环,循环中不断从驱动中读取指令,并处理指令。接着看binder_parse方法:

int binder_parse(struct binder_state *bs, struct binder_io *bio,
             uintptr_t ptr, size_t size, binder_handler func)
{
  while (ptr < end) {
      switch(cmd) {
      case BR_TRANSACTION: 
          struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr;
          if (func) {
              //func是一个函数指针,在此调用函数并返回,此函数类似于onTransact
              res = func(bs, txn, &msg, &reply);
              if (txn->flags & TF_ONE_WAY) {
                  binder_free_buffer(bs, txn->data.ptr.buffer);
              } else {
                 binder_send_reply(bs, &reply, txn->data.ptr.buffer, res);
              }
          }
          ptr += sizeof(*txn);
          break;
      }
  }
}

binder_parse方法中,根据cmd的值执行对应的方法,当客户端调用transact方法时,ServiceManager会回调func函数。func是一个函数指针,它实质上是:

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:
      //调用do_find_service,查找对应的service binder
      handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid);
  case SVC_MGR_ADD_SERVICE:
      //添加service
      do_add_service(bs, s, len, handle, txn->sender_euid,allow_isolated, txn->sender_pid)
  case SVC_MGR_LIST_SERVICES: {
      //链表数据结构,返回节点
      si = svclist;
      while ((n-- > 0) && si)
          si = si->next;
  }
  return 0;
}

所以,当service manager从binder驱动中读取指令,并根据不同的指令执行不同的方法,最后将结果返回给binder驱动。

service manager与binder交互的逻辑,非常类似于socket通信,先打开binder驱动,再调用mmap方法映射内存,最后启动死循环,调用ioctl方法从binder驱动中获取指令,根据指令调用对应方法,返回结果给binder驱动。

Binder设计理念

如果是我们自己来设计binder,那么我们该怎么做?

很明显,参照service manager,在server端,用户需要打开binder驱动,执行内存映射,建议死循环,与binder驱动通信等等。client端应用还是需要执行以上步骤。过程相当繁锁,必须要进行相应封装,以使用用户开发。

而且以上代码均是c或c++代码,android上层用户更多地使用java,需要给java用户提供更友好的使用方式。

binder主要在两个方面进行封装:

  • 封装与binder驱动交互的部分
  • 使用代理,proxy

1、ProcessState和IPCThreadState

ProcessState,专门管理每个应用进程中的Binder操作,包括与打开驱动、内存映射等等,每个进程中只需要打开一次驱动,执行一次内存映射即可。

应用进程中肯定会有多个线程,每个线程都可能需要进行进程间通信,负责这个工作的就是IPCThreadState。实际上ProcessState只是负责打开驱动、内存映射等,而与binder驱动实质进行通信的是IPCThreadState。

2、proxy

binder中proxy无处不在,用户不便直接与service manager打交道,尤其是在java端,所以设计出proxy,从而更方便客户调用。

总结

关于binder机制,其实以前也曾写过一篇文章,Android binder机制(native服务篇),关于客户端部分如何与server端交互,请参照上文,今天再次阅读《深入理解android内核设计思想》,有了新的感悟,果然是常看常新。

有时往往代码特别多,不要害怕,宏大的代码,无数的文件往往只是作者在掩盖一些具体细节,在封装,方便用户调用而已,而繁琐的背后只是那一点点特别简单的道理,比如binder机制,核心原理和socket通信几乎没有差别。只要我们抓住主线,不要怕麻烦,一切都能尽在掌握当中

请允许我装逼一下,一切有为法,皆梦幻泡影,如雾亦如电,应作如是观。

相关文章

  • Binder机制

    进程间通信方式 Android Binder框架图 Android系统框架 IPC 进程通信原理 Binder原理...

  • ServiceManager 的启动过程

    ServiceManager 是 Binder 进程间通信的核心组件之一,扮演者 Binder 进程间通信机制的上...

  • Android Binder机制

    Binder简介 binder使用内存映射(mmap)来实现进程间传递数据,比较传统的进程间通信. binder只...

  • Android 高级之跨进程通信(IPC)

    先看一下 Android 中跨进程通信的方式: Binder Binder 是 Android 系统进程间通信(I...

  • Android--IPC

    Binder IPC (Inter-Process Communication) 进程间通信,支持应用间通信、应用...

  • Android进程间通信之bindService

    在Android中binder是一种非常重要的进程间通信方式。基于binder实现的进程间通信形态非常多,其中An...

  • Binder机制总结

    一、Binder机制概述 在Android开发中,很多时候我们需要用到进程间通信,所谓进程间通信,实现进程间通信的...

  • Android

    ContentProvider 作用 进程间数据共享 即跨进程通信 原理 Binder进程间通信结合匿名共享内存(...

  • Binder进程间通信-JAVA层

    进程间通信基本原理 进程间通信原理 Binder是什么? IPC(Inter-Process Communicat...

  • Android bindService 流程分析

    进程间通信 Binder是什么 IPC间的通信机制。 什么时候需要进程间通信 大图片加载 // 防止出现内存不够 ...

网友评论

    本文标题:进程间通信——Binder

    本文链接:https://www.haomeiwen.com/subject/tfsclftx.html