我理解的Binder通信机制(一)——Binder通信通道的建立

2,398 阅读10分钟

在文章开始前,我必须把我参照的这篇文章先贴出来: Android Bander设计与实现 - 设计篇【1】(是的,作者标题里的Binder还写错了😂。这里标注一下,后面篇幅中提到【1】就指这篇文章。)

必须说一下,这是我看过的目前为止,讲Binder通信最为透彻的一篇。文章通篇没有涉及具体代码的讲解,作者基于Binder源码做了高度抽象,但又没完全脱离代码层面通篇理论。当然,这也不是说这篇文章通俗易懂,如果想要理解细节,则必须有一些积累(Linux进程的虚拟内存,Linux一些系统调用函数 ioctl, mmap等)。作为学习者而言,脱离了具体的代码,只看理论,则怎样都不能说凭借这篇就想完全理清Binder脉络,勿论掌握。所以,这里还推荐Binder系列博客gityuan.com/2015/10/31/…,结合着来看,对弄懂Binder通信有着极大作用。

所以,在读过这些文章后,我觉得有必要把一些要点或者自认为重要的点整理出来。这样,往后再回过头来看时,说不定会有不一样的收获。

1 C/S结构

整个Binder通信机制被设计成Client/Server结构。Android系统将大量的核心功能放在不同的Service组件中实现,视为Server端。通过提供相关接口为Client端使用Server端提供的功能。通常,把Service组件所在的进程称为Server进程,把使用Service组件的进程称为Client进程。

2 关于Binder通信模型

通信模型这个词,是引自【1】,这个词听上去好像存在着一个官方标准或者规范的东西。但如果去Android官方文档去查,好像也不存在这样的一个词汇(我还在继续查资料),在我看过的一些Frameworks的书中也没见过这个词。暂且认为是【1】作者对Binder机制做的一个概念总结吧。

3 Binder通信机制建立

servicemanager可以说是Binder模型中一个特殊的存在,它本身就是一个service,只是要先于其他系统service启动。在Binder机制中,它同样作为一个server,为其他server进程提供功能,具体的就是系统服务注册、服务获取。这里存在一个悖论:servicemanager用来实现C/S结构的Binder通信机制,可是实现过程中还要使用C/S结构的Binder通信机制,这个要怎么处理?Android实现起来也是朴实无华,server进程中servicemanager的binder引用固定为0,也就是说,一个server(在servicemanager看来是client)若要向servicemanager注册自己,就通过0这个引用号,相当于获取了servicemanger的Binder引用,这样就解决了和servicemanager通信问题。servicemanager程序入口为service_manager.c,看一下main函数,

int main(int argc, char** argv)
{
    struct binder_state *bs;
    char *driver;

    if (argc > 1) {
        driver = argv[1];
    } else {
        driver = "/dev/binder";
    }
    //打开Binder字符设备
    bs = binder_open(driver, 128*1024);
	...
    //当前进程变为 service manager
    if (binder_become_context_manager(bs)) {
        ALOGE("cannot become context manager (%s)\n", strerror(errno));
        return -1;
    }

	...
    //进入循环,准备接收   
    binder_loop(bs, svcmgr_handler);
    return 0;
}

service_manager main函数做了三件事: ① binder_open,打开binder设备; ② binder_become_context_manager,当前进程注册为上下文管理者,变为service manager进程; ③ binder_loop 开启无限循环,等待处理IPC请求消息;

3.1 binder_open
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;
    }

    //打开binder字符设备
    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;
    }

    //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;//mapsize = 128 * 1024
	
    //内核中开辟出一块128K大小的缓冲区,用来存放Binder通信的数据,并建立缓冲区与binder设备的一一映射
    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;
}

binder_open主要工作可以分为三部分: ① 创建binder_state结构体类型的bs,分配内存; ② open系统调用打开binder字符设备; ③ 在内核中开辟出一块128K大小的缓冲区,建立缓冲区与binder设备的映射。 binder_open中有对Binder信息的检查,使用的是ioctl函数,这里援引【1】的一段话,对Server、Client进程同binder驱动交互方式做一些说明:

驱动和应用程序之间定义了一套接口协议,主要功能由ioctl()接口实现,不提供read(),write()接口,因为ioctl()灵活方便,且能够一次调用实现先写后读以满足同步交互

3.2 binder_become_context_manager
int binder_become_context_manager(struct binder_state *bs)
{
    return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}

在3.1中,对ioctl函数的使用进行了说明,所以binder_become_context_manager是当前进程向binder驱动发送形式“命令+参数”的数据,BINDER_SET_CONTEXT_MGR命令生效后,当前进程就摇身一变成为servicermanager进程。

3.2.1 binder_ioctl

ioctl函数经过系统调用,对应于Binder驱动层的binder_ioctl函数。

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) {
    binder_lock(__func__);
    switch (cmd) {
      case BINDER_WRITE_READ:
      case BINDER_SET_MAX_THREADS:
      case BINDER_THREAD_EXIT:
      case BINDER_VERSION: 
          ...
      case BINDER_SET_CONTEXT_MGR:
          ret = binder_ioctl_set_ctx_mgr(filp);
          break;
      }
      ...
    }
    binder_unlock(__func__);
}

进入BINDER_SET_CONTEXT_MGR分支,再看binder_ioctl_set_ctx_mgr(filp);

static int binder_ioctl_set_ctx_mgr(struct file *filp)
{
	int ret = 0;
	struct binder_proc *proc = filp->private_data;
	kuid_t curr_euid = current_euid();
        //根据错误信息知道,BINDER_SET_CONTEXT_MGR命令只需设置一次,
        //也就是binder_context_mgr_node只需创建一次
	if (binder_context_mgr_node != NULL) {
		pr_err("BINDER_SET_CONTEXT_MGR already set\n");
		ret = -EBUSY;
		goto out;
	}
	if (uid_valid(binder_context_mgr_uid)) {
		...
	} else {
		binder_context_mgr_uid = curr_euid;
	}
        //创建ServiceManager在binder驱动中对应的实体节点
	binder_context_mgr_node = binder_new_node(proc, 0, 0);
	if (binder_context_mgr_node == NULL) {
		ret = -ENOMEM;
		goto out;
	}
	binder_context_mgr_node->local_weak_refs++;
	binder_context_mgr_node->local_strong_refs++;
	binder_context_mgr_node->has_strong_ref = 1;
	binder_context_mgr_node->has_weak_ref = 1;
out:
	return ret;
}

创建struct binder_node类型的binder_context_mgr_node,并将binder_context_mgr_node的强弱引用各加1。

再回头看binder_ioctl函数,其可以处理的命令还有BINDER_WRITE_READ、BINDER_SET_MAX_THREADS、BINDER_THREAD_EXIT、BINDER_VERSION。这些命令是Binder协议的一部分。除了BINDER_VERSION命令在3.1小节binder_open函数见过,其余的在接下来内容中也会接触到。

3.3 binder_loop

servicemanager本身是一个可以处理client进程请求的server进程。为了应对来自client进程的请求,servicemanager会进入一个无限循环等待处理client进程的IPC请求。

void binder_loop(struct binder_state *bs, binder_handler func)
{
    int res;
    //创建binder_write_read
    struct binder_write_read bwr;
    uint32_t readbuf[32];

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

    //发送BC_ENTER_LOOPER命令给binder驱动
    readbuf[0] = BC_ENTER_LOOPER;
    binder_write(bs, readbuf, sizeof(uint32_t));

    //进入无限循环,等待处理IPC读写请求
    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;
        }

        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;
        }
    }
}

这里,我们看一下结构体binder_write_read

struct binder_write_read {
    signed long write_size;
    signed long write_consumed;
    unsigned long write_buffer;
    signed long read_size;
    signed long read_consumed;
    unsigned long read_buffer;
};

binder_write_read是应用程序与Binder驱动交互直接使用的数据包。参数分为两段:写部分和读部分。如果write_size不为0,就先将write_buffer里的数据写入Binder;如果read_size不为0,从Binder中读取数据存入read_buffer中。write_consumed和read_consumed表示操作完成时Binder驱动实际写入或读取的数据个数。

3.3.1 binder_write
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;
    //写入的data为BC_ENTER_LOOPER
    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);
    return res;
}

binder_write函数比较简单,填充binder_write_read变量后,向binder驱动发送BC_ENTER_LOOPER命令。回看3.2.1小节,binder_ioctl函数中BINDER_WRITE_READ分支,调用binder_ioctl_write_read

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    case BINDER_WRITE_READ:
		ret = binder_ioctl_write_read(filp, cmd, arg, thread);
		if (ret)
			goto err;
		break;
    ...
}
3.3.2 binder_ioctl_write_read
static int binder_ioctl_write_read(struct file *filp,
                                   unsigned int cmd, unsigned long arg,
                                   struct binder_thread *thread)
{
    int ret = 0;
    //当前情形下,binder_proc表示smgr进程
    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;
    }
    //从用户空间拷贝数据到内核缓冲区 ubuf表示数据
    if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
        ret = -EFAULT;
        goto out;
    }
    ...
    //bwr.write_size大于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;
        }
    }
    //bwr.read_size大于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);
        if (!list_empty(&proc->todo))
            wake_up_interruptible(&proc->wait);
        if (ret < 0) {
            if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
                ret = -EFAULT;
            goto out;
        }
    }
    ...
    // 从内核缓冲区拷贝数据到用户空间,ubuf表示数据
    if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
        ret = -EFAULT;
        goto out;
    }
    out:
    return ret;
}

根据3.3.1可知,bwr.write_size > 0,执行binder_thread_write函数。

3.3.3 binder_thread_write
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) {
  uint32_t cmd;
  void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
  void __user *ptr = buffer + *consumed;
  void __user *end = buffer + size;
  
  while (ptr < end && thread->return_error == BR_OK) {
    //获取具体的命令
    get_user(cmd, (uint32_t __user *)ptr); 
    ... 
    switch (cmd) {
      case BC_ENTER_LOOPER:
          //设置该线程的looper状态
          thread->looper |= BINDER_LOOPER_STATE_ENTERED;
          break;
    ...
    }
  }    
}

binder_thread_write函数,会进入BC_ENTER_LOOPER分支,thread是在binder_ioctl函数中通过如下方式获得的,

        //thread为servicemanager所在线程
	thread = binder_get_thread(proc);

进入该分支,servicemanager所在线程的looper标记设置为BINDER_LOOPER_STATE_ENTERED,表示该线程进入循环状态,即servicemanager马上要进入binder_looper的无限循环开始处理IPC请求。binder_thread_write函数执行完,最终返回到3.3小节部分,binder_loop函数中继续执行,进入无限for循环中,开始等待IPC请求。

先不去管无限for循环中逻辑,该函数截至至此,Binder通道已经打通,可以在server进程和client进程之间进行自由通信了。当然binder_loop方法还没完,binder_parse的细节,后续篇幅会深入。

4 特殊的Service——ServiceManager

Binder通道建好后,是不是就能进行数据通信了?是的,但是还要提一个重要的概念:ServiceManager。ServiceManager在系统服务注册(client为server进程)、获取(client为client进程)的过程中充当着server的角色。所以,这一节重点讲一下ServiceManager。获取Service Manager是通过defaultServiceManager()方法来完成。

4.1 defaultServiceManager
sp<IServiceManager> defaultServiceManager()
{
    if (gDefaultServiceManager != NULL) return gDefaultServiceManager;
    {
        AutoMutex _l(gDefaultServiceManagerLock); 
        while (gDefaultServiceManager == NULL) {
            gDefaultServiceManager = interface_cast<IServiceManager>(
                ProcessState::self()->getContextObject(NULL));
            if (gDefaultServiceManager == NULL)
                sleep(1);
        }
    }
    return gDefaultServiceManager;
}

逐级深入,

4.2 ProcessState::self
sp<ProcessState> ProcessState::self()
{
    Mutex::Autolock _l(gProcessMutex);
    if (gProcess != nullptr) {
        return gProcess;
    }
    gProcess = new ProcessState(kDefaultDriver);
    return gProcess;
}

函数逻辑较简单,直接看ProcessState的构造函数

4.2.1 ProcssState构造函数
ProcessState::ProcessState(const char *driver)
        : mDriverName(String8(driver))
        //在初始化列表中打开binder驱动
        , mDriverFD(open_driver(driver))
        , mVMStart(MAP_FAILED)
        , mThreadCountLock(PTHREAD_MUTEX_INITIALIZER)
        , mThreadCountDecrement(PTHREAD_COND_INITIALIZER)
        , mExecutingThreadsCount(0)
        , mMaxThreads(DEFAULT_MAX_BINDER_THREADS)
        , mStarvationStartTimeMs(0)
        , mBinderContextCheckFunc(nullptr)
        , mBinderContextUserData(nullptr)
        , mThreadPoolStarted(false)
        , mThreadPoolSeq(1)
        , mCallRestriction(CallRestriction::NONE)
{
    if (mDriverFD >= 0) {
        // mmap the binder, providing a chunk of virtual address space to receive transactions.
        // 采用内存映射函数mmap,给binder分配一块虚拟地址空间,用来接收事务
        mVMStart = mmap(nullptr, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
        ...
    }
    ...
}
4.2.2 open_driver
static int open_driver(const char *driver)
{
    int fd = open(driver, O_RDWR | O_CLOEXEC);
    if (fd >= 0) {
        int vers = 0;
        //获取binder版本信息
        status_t result = ioctl(fd, BINDER_VERSION, &vers);
        ...
        size_t maxThreads = DEFAULT_MAX_BINDER_THREADS;
        result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);
        ...
        }
    }
    return fd;
}

open_driver函数调用ioctl函数向驱动获取binder版本信息,同时检查binder协议的版本信息。之后,open_driver会设置binder进程可以支持的最大线程数。看一下binder_ioctl函数中BINDER_SET_MAX_THREADS分支的逻辑,

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{

    ...
    case BINDER_SET_MAX_THREADS:
        //设置binder进程支持的最大线程数量,值为DEFAULT_MAX_BINDER_THREADS
		if (copy_from_user(&proc->max_threads, ubuf, sizeof(proc->max_threads))) {
			ret = -EINVAL;
			goto err;
		}
		break;
    ...
}
4.3 ProcessState::getContextObject

ProcessState::self之后,接着来看ProcessState::getContextObject会做什么,

sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/)
{
    sp<IBinder> context = getStrongProxyForHandle(0);
    ...
    return context;
}
4.3.1 getStrongProxyForHandle
sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
    sp<IBinder> result;

    AutoMutex _l(mLock);

    //从一个维护handle_entry类型的全局集合中返回handle值对应的handle_entry指针
    handle_entry* e = lookupHandleLocked(handle);

    if (e != nullptr) {
        // We need to create a new BpBinder if there isn't currently one, OR we
        // are unable to acquire a weak reference on this current one.
        IBinder* b = e->binder;

        //如果该handle_entry的binder指针为空指针 或者 无法获取当前binder弱引用
        if (b == nullptr || !e->refs->attemptIncWeak(this)) {

            //当前传入的handle == 0
            if (handle == 0) {
                // Special case for context manager...
                Parcel data;
                //确保context manager是否已经被注册,
                //即前面的BINDER_SET_CONTEXT_MGR命令已发送到binder驱动且生效
                status_t status = IPCThreadState::self()->transact(
                        0, IBinder::PING_TRANSACTION, data, nullptr, 0);
                if (status == DEAD_OBJECT)
                    return nullptr;
            }

            //新建一个BpBinder对象
            b = BpBinder::create(handle);
            e->binder = b;
            if (b) e->refs = b->getWeakRefs();
            result = b;
        } else {
            // This little bit of nastyness is to allow us to add a primary
            // reference to the remote proxy when this team doesn't have one
            // but another team is sending the handle to us.
            result.force_set(b);
            e->refs->decWeak(this);
        }
    }

    return result;
}

该函数的主要工作已在代码中做了注释。还是逐级查看,

4.3.2 lookupHandleLocked
ProcessState::handle_entry* ProcessState::lookupHandleLocked(int32_t handle)
{
    const size_t N=mHandleToObject.size();
    if (N <= (size_t)handle) {
        handle_entry e;
        e.binder = nullptr;
        e.refs = nullptr;
        status_t err = mHandleToObject.insertAt(e, N, handle+1-N);
        if (err < NO_ERROR) return nullptr;
    }
    return &mHandleToObject.editItemAt(handle);
}

mHandleToObject是一个元素类型为handle_entry的容器,handle_entry是一个封装了binder指针的结构体。

struct handle_entry {
     IBinder* binder;
     RefBase::weakref_type* refs;
};

getContextObject函数调用结果最终返回一个BpBinder的实例对象。

4.4 interface_cast

getContextObject返回BpBinder实例后

gDefaultServiceManager = interface_cast<IServiceManager>(
                ProcessState::self()->getContextObject(NULL));

就相当于

gDefaultServiceManager = interface_cast<IServiceManager>(
                new BpBinder(0));

interface_cast方法定义在frameworks/native/include/binder/IInterface.h中,其代码如下,

template<typename INTERFACE>
inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj){
        return INTERFACE::asInterface(obj);
}

将泛型类型替换为IServiceManager,就变为,

template<typename INTERFACE>
inline sp<IServiceManager> interface_cast(const sp<IBinder>& obj){
        return IServiceManager::asInterface(obj);
}

最终相当于

 IServiceManager::asInterface(new BpBinder(0));
4.4.1 IServiceManager::asInterface
 android::sp<IServiceManager> IServiceManager::asInterface(const android::sp<android::IBinder>& obj)
{
       android::sp<IServiceManager> intr;
        if(obj != NULL) {
           intr = static_cast<IServiceManager *>(
               obj->queryLocalInterface(IServiceManager::descriptor).get());
           if (intr == NULL) {
               intr = new BpServiceManager(obj); 
            }
        }
       return intr;
}

该函数逻辑比较简单,其实直接返回的是BpServiceManager实例对象。

最后,defaultServiceManager返回的是BpServiceManager实例对象。这里我们再接着看一下BpServiceManager的构造函数。

4.4.2 BpServiceManager构造函数
explicit BpServiceManager(const sp<IBinder>& impl)
     : BpInterface<IServiceManager>(impl)
{ }

注意到,BpServiceManager构造函数是一个空函数,沿着继承结构看BpInterface的构造函数,

template<typename INTERFACE>
inline BpInterface<INTERFACE>::BpInterface(const sp<IBinder>& remote)
    : BpRefBase(remote)
{
}

依然不见IBinder指针类型的参数赋值,接着看BpRefBase的构造函数

BpRefBase::BpRefBase(const sp<IBinder>& o)
    : mRemote(o.get()), mRefs(nullptr), mState(0)
{
    extendObjectLifetime(OBJECT_LIFETIME_WEAK);

    if (mRemote) {
        mRemote->incStrong(this);           // Removed on first IncStrong().
        mRefs = mRemote->createWeak(this);  // Held for our entire lifetime.
    }
}

这里,BpBinder的实例对象赋给IBinder指针类型的mRemote变量。

5 后记

在Binder通道建立以及获得ServiceManager之后,应该说,基于Binder通信的通道已经完全打通,为后续的系统服务注册、系统服务获取做好了准备。到这里,【1】中提到的Binder通信模型中的Binder驱动、servicemanager我们都已经接触到了。下篇聚焦系统服务注册与获取,就会涉及到server、client。

参考资料

github.com/aosp-mirror…

aosp.tuna.tsinghua.edu.cn/platform/fr… clone)

androidxref.com/kernel_3.18…

blog.csdn.net/universus/a…

gityuan.com/2015/10/31/…

《Android的设计与实现 卷一》杨云君。

《深入理解Android 卷一》邓凡平。