【Binder系列课】二、Binder通信服务应知应会

Binder问答

概括Binder机制
1.整体业务层CS架构,BpBinder/BnBinder
2.通信层Binder驱动,writeStrongBinder/readStrongBinder完成路由
其他的代码都是业务逻辑,没必要剖析。

一次Binder请求的数据封装/传输流程

听说你Binder机制学的不错,来面试下这几个问题(二)
在这里插入图片描述

数据结构

Android Binder框架实现之Binder中的数据结构

binder_proc

binder_proc是内核中描述Binder进程上下文信息的结构体,其在用户空间相对应的是ProcessState

  • threads,它是包含该进程内用于处理用户请求的所有线程的红黑树。
  • nodes,它是包含该进程内的所有Binder实体所组成的红黑树。
  • refs_by_desc,它是包行该进程内的所有Binder引用所组成的红黑树。通过一个引用的desc可以在这里找到对应的ref
  • buffer,它是该进程内核虚拟内存的起始地址。而user_buffer_offset,则是该内核虚拟地址和进程虚拟地址之间的差值。在Binder驱动中,将进程的内核虚拟地址和进程虚拟地址映射到同一物理页面,该user_buffer_offset则是它们之间的差值;这样,已知其中一个,就可以根据差值算出另外一个。
  • todo wait,todo,是该进程的待处理事务队列,而wait则是等待队列。 没看懂什么区别。
  • struct list_head buffers; // 和binder_buffer->entry关联到同一链表,从而对Binder内存进行管理

binder_thread

binder_thread是内核层描述Binder线程的结构体,在用户层与其相对应的是IPCThreadState

binder_node

binder_node是内核层描述Binder实体的结构体,用户层与之相对应的是具体的Binder Server。

  • binder_uintptr_t ptr; // Binder实体在用户空间的地址(为Binder实体对应的Server在用户空间的本地Binder的引用)
  • binder_uintptr_t cookie; // Binder实体在用户空间的其他数据(为Binder实体对应的Server在用户空间的本地Binder自身)
  • proc,它是binder_proc(进程上下文信息)结构体对象。目的是保存该Binder实体的进程。

binder_ref

binder_ref是内核层描述Binder引用的结构体,用户层与之相对应的是某个具体服务的远程代理BpBinder或者BinderProxy。

  • struct rb_node rb_node_desc; //关联到所属进程binder_proc->refs_by_desc红黑树中
  • struct rb_node rb_node_node; // 关联到所属进程binder_proc->refs_by_node红黑树中
  • node是该Binder引用所引用的Binder实体。
  • proc,它是binder_proc(进程上下文信息)结构体对象。
  • desc是Binder引用的描述,实际上它就是Binder驱动为该Binder分配的一个唯一的int型整数。通过该desc,可以在binder_proc->refs_by_desc中找到该Binder引用,进而可以找到该Binder引用所引用的Binder实体等信息。
    在这里插入图片描述

binder_buffer

Binder驱动将Binder进程的内存分成一段一段进行管理,而这每一段则是使用binder_buffer来描述的。

  • struct list_head entry; // 和binder_proc->buffers关联到同一链表,从而使Binder进程对内存进行管理。

flat_binder_object

flat_binder_object是描述Binder对象信息的结构体。binder通过此结构体传输binder对象。

struct flat_binder_object {
   
	/* 8 bytes for large_flat_header. */
	__u32		type;		 		// 可以为BINDER_TYPE_BINDER或BINDER_TYPE_HANDLE等类型
	__u32		flags;		 		// 标记
	/* 8 bytes of data. */
	union {
   
		binder_uintptr_t	binder;		// 当type=BINDER_TYPE_BINDER时,它指向Binder对象位于C++层的本地Binder对象(即BBinder对象)的弱引用。 
		__u32			handle;			// 当type=BINDER_TYPE_HANDLE时,它等于Binder对象在Binder驱动中对应的Binder实体的Binder引用的描述。就是binder_ref->desc对象,可以通过desc在内核中找到binder_node
	};

	/* extra data associated with local object */
	binder_uintptr_t	cookie;			// 当type=BINDER_TYPE_BINDER时才有效,它指向Binder对象位于C++层的本地Binder对象(即BBinder对象)。
};

它在用户空间被使用时(例如,Server发送添加服务请求给ServiceManager),flat_binder_object就是记录的Server位于用户空间的Binder对象的信息的结构体;此时的type的值一般都是BINDER_TYPE_BINDER类型,对应的union中的binder的值是该Binder对象在用户空间的本地Binder(即BBinder对象)的引用;同时,cookie则是本地Binder自身。 而当flat_binder_object在Binder驱动中被使用(例如,当Binder驱动收到发送服务请求时),它会将type修改为BINDER_TYPE_HANDLE,然后将联合体中的handle修改为"该Server对应的Binder实体的Binder引用"的描述;根据Binder引用的描述就能找到该Server。

binder_write_read

binder_write_read是用来描述Binder在驱动层和用户层进行数据读写交互的的结构体。用户层和内核层均有此结构体。

  • write_size,是写内容的总大小;write_consumed,是已写内容的大小;write_buffer,是写的内容的虚拟地址。
  • read_size,是读内容的总大小;read_consumed,是已读内容的大小;read_buffer,是读的内容的虚拟地址。

binder_transaction_data

binder_transaction_data是描述Binder事务交互的数据格式的结构体。它也属于内核空间和用户空间的通信结构体。

struct binder_transaction_data {
   
	union {
   
		__u32	handle;		// 当binder_transaction_data是由用户空间的进程发送给Binder驱动时,
							// handle是该事务的发送目标在Binder驱动中的信息,即该事务会交给handle来处理;	
							// handle的值是目标在Binder驱动中的Binder引用,Binder的引用在代码中也叫句柄(handle)
		binder_uintptr_t ptr;	//当binder_transaction_data是由Binder驱动反馈给用户空间进程时
								//ptr是该事务的发送目标在用户空间中的信息,即该事务会交给ptr对应的服务来处理;
								// ptr是处理该事务的服务在用户空间的本地Binder对象,即指向Binder对象内存的指针
	} target;					//该事务的目标对象(即,该事务数据包是给该target来处理的,具体是由handle还是ptr来制定,根据实际情况来确定)
	binder_uintptr_t	cookie;	//只有当事务是由Binder驱动传递给用户空间时,cookie才有意思,它的值是处理该事务的Server位于C++层的本地Binder对象
	__u32		code;			//该成员存放收发双方约定的命令码,驱动完全不关心该成员的内容.如果是请求,则以BC_开头;如果是回复,则以BR_开头。

	__u32	        flags;
	pid_t		sender_pid;
	uid_t		sender_euid;
	binder_size_t	data_size;		//数据大小
	binder_size_t	offsets_size;	//数据包中包含的对象的个数

	union {
   
		struct {
   
			binder_uintptr_t	buffer;		//真正保存通信数据的缓冲区
			binder_uintptr_t	offsets;	//记录Binder对象偏移的数组	
		} ptr;
		__u8	buf[8];
	} data;							//数据
};

在这里插入图片描述

binder_write_read 和 binder_transaction_data分工不同

  • binder_transaction_data 记录了具体的数据
  • binder_write_read 记录了处理的状态 比如 write_consumed,是已写内容的大小

binder_transaction

binder_transaction用来描述进程间通信过程,这个过程又称为一个事务,进程通信事务。

struct binder_transaction {
   
	int debug_id;
	struct binder_work work;			//设置事务类型
	struct binder_thread *from;			//发起事务的线程,称为源线程
	struct binder_transaction *from_parent;	//该事务所依赖的事务
	struct binder_proc *to_proc;			//处理该事务的进程
	struct binder_thread *to_thread;		//处理该事务的线程
	struct binder_transaction *to_parent;	//目标线程的下一个事务
	unsigned need_reply:1;					//用来区分一个事务是同步的还是异步的
	/* unsigned is_dead:1; */	/* not used at the moment */

	struct binder_buffer *buffer;			//为该事务分配的一块内核缓冲区,该缓冲区保存了通信数据
	unsigned int	code;					//进程间通信代码
	unsigned int	flags;					//标志位,描述进程间通信行为的特征
	long	priority;						//发起事务的线程优先级
	long	saved_priority;					//保存处理该事务的线程原有优先级
	kuid_t	sender_euid;					//发起事务的euid
};

ServiceManager#binder_state

struct binder_state
{
   
    int fd;			//文件节点"/dev/binder"的句柄
    void *mapped;	//映射内存的起始地址
    size_t mapsize;	//映射内存的大小
};

ServiceManager#binder_io

binder_io是与binder_transaction_data对应的轻量级结构体。

不清楚具体的使用方式

ServiceManager#svcinfo

svcinfo是保存"注册到ServiceManager中的服务"的相关信息的结构体。它是一个单链表,

struct svcinfo
{
   
    struct svcinfo *next; //svcinfo中的next是指向下一个服务的节点
    uint32_t handle; //服务在Binder驱动中Binder引用的描述
    struct binder_death death;
    int allow_isolated;
    size_t len;
    uint16_t name[0]; //name则是服务的名称。
};

Parcel

Parcel是描述Binder在用户层服务代理端和服务端进行通信的信息的结构体。

    status_t            mError;                                   
    uint8_t*            mData;            // 数据
    size_t              mDataSize;        // 数据大小
    size_t              mDataCapacity;    // 数据容量
    mutable size_t      mDataPos;         // 数据指针的当前位置
    size_t*             mObjects;         // 对象在mData中的偏移地址
    size_t              mObjectsSize;     // 对象个数
    size_t              mObjectsCapacity; // 对象的容量

数据传输

Android Binder框架实现之Parcel详解之read/writeStrongBinder实现

Parcel负责Binder通信过程中的数据打包,在写入数据的时候 可以通过growData自动扩容。
简单的数据直接写入到mData里,IBinder打包成flat_binder_object。
上面的链接,详细讲述了IBinder打包成flat_binder_object的过程。
在这里插入图片描述

IPCThreadState会在 writeTransactionData 中将mOut(Parcel)打包到binder_transaction_data结构体中。然后在talkWithDriver中讲数据通过 ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr(binder_write_read)) 发送到驱动层
在这里插入图片描述

Parcel可以传递各种类型的数据对象:

基本数据类型:借助Parcel的方法writePrimitives将基本数据类型从用户空间(源进程)copy到kernel空间(Binder驱动中)再写回用户空间(目标进程,binder驱动负责寻找目标进程)
复杂对象将经过序列化的数据借助Parcel的方法writeParcelable()/writeSerializable()从用户空间(源进程)copy到kernel空间(Binder驱动中)再写回用户空间(目标进程,binder驱动负责寻找目标进程),然后再反序列化
大型数据(文件描述符):通过Parcel的方法writeFileDescriptor通过Binder传递匿名共享内存 (Ashmem)的FileDescriptor从而达到传递匿名共享内存的方式,即传递的是FileDescriptor而并不是真的是大数据,参见博客Android 匿名共享内存的使用
IBinder对象:通过Parcel的方法writeStrongBinder方法,然后经由Kernel binder驱动专门处理来完成IBinder的传递

APP的binder线程的如何启动的?

BootLoader-》Linux kernel—》Android init—》service_manager——》Zygote——》system_server

Android APP进程都是由Zygote进程孵化出来的。常见场景:点击桌面icon启动APP,或者startActivity启动一个新进程里面的Activity,最终都会由AMS去调用Process.start()方法去向Zygote进程发送请求,让Zygote去fork一个新进程,Zygote收到请求后会调用Zygote.forkAndSpecialize()来fork出新进程,之后会通过RuntimeInit.nativeZygoteInit来初始化Andriod APP运行需要的一些环境,而binder线程就是在这个时候新建启动的

RuntimeInit.nativeZygoteInit, 该方法经过 JNI 映射,最终会调用到 app_main.cpp 中的 onZygoteInit。
/base/cmds/app_process/app_main.cpp

    virtual void onZygoteInit()
    {
   
        sp<ProcessState> proc = ProcessState::self();
        //这里会创建Binder的主线程。
        proc->startThreadPool();
    }

ProcessState::self()函数会调用open()打开/dev/binder设备,这个时候Client就能通过Binder进行远程通信;其次,proc->startThreadPool()负责新建一个binder线程,监听Binder设备,这样进程就具备了作为Binder服务端的资格。每个APP的进程都会通过onZygoteInit打开Binder,既能作为Client,也能作为Server,这就是Android进程天然支持Binder通信的原因。

Binder 线程的创建流程图:
在这里插入图片描述

每次由 Zygote fork 出新进程的过程中,伴随着创建 Binder 线程池,调用 spawnPooledThread 来创建 Binder 主线程。 当线程执行 binderthreadread 的过程中,发现当前没有空闲线程,没有请求创建线程,且没有达到上限,则创建新的 Binder 线程。

Binder 系统中可分为 3 类 Binder 线程:

  • Binder 主线程:进程创建过程会调用 startThreadPool() 过程中再进入 spawnPooledThread(true),来创建 Binder 主线程。 编号从 1 开始,也就是意味着 Binder 主线程名为 binder1,并且主线程是不会退出的。
  • Binder 普通线程:是由 Binder Driver 来根据是否有空闲的 Binder 线程来决定是否创建 Binder 线程,回调spawnPooledThread(false) ,isMain=false, 该线程名格式为 binderx。
  • Binder 其他线程:其他线程是指并没有调用 spawnPooledThread 方法,而是直接调用 IPC.joinThreadPool(),将当前线程直接加入 Binder 线程队列。 例如: mediaserver 和 servicemanager 的主线程都是 Binder 线程,但 system_server 的主线程并非 Binder 线程。

services_manager进程的启动

以下参考自:Android Binder框架实现之servicemanager守护进程
Kernel启动加载完驱动之后,会启动Android的init

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值