更多内容请访问:鸿蒙科技社区联合成立与华为。com1。前情概览我们在上一篇博客中描述了proxy-stub架构的通用编程范式。本文着重于驱动自身的数据传输,并做一个完整的数据分析。由于IPC通信过程比较复杂,我们先打开上帝视角直接讲解一些数据结构和数据流程,再结合源码调用流程看看是不是这样。2.数据结构简述(1)messageParcelMessageParcel用户态数据,可以写成一般类型或iremoteObject。为什么我们需要区分这两种类型?这里首先是一个障碍。类MessageParcel:publicParcel{public:MessageParcel();~MessageParcel();显式MessageParcel(Allocator*allocator);boolWriteRemoteObject(constsptr&object);sptrReadRemoteObject();boolWriteFileDescriptor(intfd);intReadFileDescriptor();boolContainFileDescriptors()常量;boolWriteInterfaceToken(std::u16string名称);std::u16stringReadInterfaceToken();boolWriteRawData(constvoid*data,size_tsize);constvoid*ReadRawData(size_tsize);boolRestoreRawData(std::shared_ptrrawData,size_t大小);constvoid*GetRawData()const;size_tGetRawDataSize()常量;size_tGetRawDataCapacity()常量;voidWriteNoException();int32_t读取异常();boolWriteAshmem(sptrashmem);sptrReadAshmem();voidClearFileDescriptor();voidSetClearFdFlag(){needCloseFd_=true;};私有:#ifndefCONFIG_IPC_SINGLEboolWriteDBinderProxy(constsptr&object,uint32_thandle,uint64_tstubIndex);#endifstaticconstexprsize_tMAX_RAWDATA_SIZE=128*1024*1024;//128Mstaticconstexprsize_tMIN_RAWDATA_SIZE=32*1024;//32kboolneedCloseFd_=false;std::vector>holders_;intwriteRawDataFd_;intreadRawDataFd_;void*kernelMappedWrite_;void*kernelMappedRead_;std::shared_ptr原始数据_;size_trawDataSize_;这是描述iremoteObject对象信息的内核结构。当调用object->Marshalling(*this)时,messageParcel会被转换成一个flat_binder_object。同样,还有一个UnMarshalling方法。structflat_binder_object{unsignedlong类型;//活页夹类型:可以是BINDER_TYPE_BINDER或BINDER_TYPE_HANDLEunsignedlongflags;//标记union{void*binder;//当type=BINDER_TYPE_BINDER时,指向C++层Binder对象所在的本地Binder对象的弱引用。签名长柄;//当type=BINDER_TYPE_HANDLE时,等于Binder驱动中Binder对象对应的Binder实体的Binder引用的描述。};无效*cookie;//仅当type=BINDER_TYPE_BINDER时有效,指向C++层Binder对象所在的本地Binder对象。};(3)binder_transaction_data不仅包含flat_binder_object中的纯数据,还包含发送方的cmdID等信息。structbinder_transaction_data{union{size_thandle;//当binder_transaction_data被用户空间的进程发送给Binder驱动时,//handle是Binder驱动中事务发送目标的信息,即事务会交给handle处理;//handle的值是Binder驱动中target的Binder引用。无效*指针;//当binder_transaction_data被Binder驱动反馈给用户空间进程时,//ptr是事务的发送目标在用户空间的信息,即事务将交给对应的service用于处理的指针;//ptr是处理用户空间事务的服务的本地Binder对象。}目标;//交易的目标对象(即交易包被目标处理)void*cookie;//只有当事务被Binder驱动传递给用户空间时,cookie才有意义,它的值是位于C++层处理事务的Server本地Binder对象的unsignedint代码;//交易代码。如果是请求,则以BC_开头;如果是回复,则以BR_开头。无符号整数标志;pid_t发送者_pid;uid_t发件人_euid;size_t数据大小;//数据大小size_toffsets_size;//数据联合中包含的对象数{struct{constvoid*buffer;constvoid*偏移量;}指针;uint8_tbuf[8];}数据;//数据};(4)binder_write_readdriver真正读写数据,即binder_transaction_data会转化为binder_write_read交给driver进行真正的读写。structbinder_write_read{signedlongwrite_size;签名长write_consumed;无符号长写缓冲区;签名长读大小;签名长读消耗;unsignedlongread_buffer;};(5)binder_refbinder_ref是描述Binder引用的结构体。结构binder_ref{intdebug_id;结构rb_noderb_node_desc;//关联到binder_proc->refs_by_desc红黑树structrb_noderb_node_node;//关联到binder_proc->refs_by_node红黑树structhlist_nodenode_entry;//关联到哈希表中的binder_node->refsstructbinder_proc*proc;//Binder引用它所属的Binder进程structbinder_node*node;//Binder引用对应的Binder实体uint32_tdesc;//描述intstrong;内在弱;结构binder_ref_death*死亡;};(6)binder_procstructbinder_proc{structhlist_nodeproc_node;//根据proc_node,可以得到进程在“全局哈希表binder_procs(统计所有binderproc进程)”中的位置structrb_rootthreads;//binder_proc进程内部使用处理用户请求的线程组成的红黑树(关联binder_thread->rb_node)structrb_rootnodes;//binder_proc进程中binder实体组成的红黑树(关联binder_node->rb_node)structrb_rootrefs_by_desc;//binder_procprocess内部由binder引用组成的红黑树,引用按句柄排序(associatebinder_ref->rb_node_desc)structrb_rootrefs_by_node;//binder_proc进程中的binder引用组成的红黑树,按照其对应的binder实体地址排序(与binder_ref->rb_node关联)intpid;//进程IDstructvm_area_struct*vma;//进程内核虚拟内存structmm_struct*vma_vm_mm;结构task_struct*tsk;//进程控制结构(每个进程由task_struct数据结构定义)structfiles_struct*files;//保存进程打开的所有文件表数据structhlist_nodedeferred_work_node;intdeferred_work;无效*缓冲区;//进程映射的物理内存在内核空间的起始位置ptrdiff_tuser_buffer_offset;//内核虚拟地址和进程虚拟地址的区别//内存管理相关变量structlist_headbuffers;//与binder_buffer相关联->进入同一个链表管理Binder内存structrb_rootfree_buffers;//空闲内存,与binder_buffer->rb_node关联。结构rb_rootallocated_buffers;//分配的内存,与binder_buffer->rb_node关联。size_tfree_async_space;结构页面**页面;//映射内存的page数组,page是一个描述物理内存的结构体size_tbuffer_size;//映射内存的大小uint32_tbuffer_free;结构列表头待办事项;//进程事件队列的待处理。wait_queue_head_t等待;//等待队列。结构binder_stats统计信息;structlist_headdelivered_death;int最大线程;//最大线程数。定义线程中可以包含的最大进程数。intrequested_threads;intrequested_threads_started;intready_threads;长默认优先级;//默认优先级。结构dentry*debugfs_entry;};(7)binder_nodebinder_node是描述Binder实体的结构体。结构binder_node{intdebug_id;结构binder_work工作;union{结构rb_noderb_node;//如果Binder实体仍在使用中,则将节点链接到proc->nodes。结构hlist_node死节点;//如果这个Binder实体所属的进程已经被销毁,而这个Binder实体被其他进程引用,那么这个Binder实体会通过dead_node存储在一个哈希表中};结构binder_proc*proc;//Binder实体所属的Binder进程structhlist_headrefs;//Binder实体的所有Binder引用组成的链表intinternal_strong_refs;intlocal_weak_refs;intlocal_strong_refs;void__user*ptr;//Binder实体在用户空间的地址(供用户空间Binder实体对应的Server本地Binder的引用)void__user*cookie;//用户空间Binder实体的其他数据(用户空间Binder实体对应Server的本地Binder本身)unsignedhas_strong_ref:1;未签名的pending_strong_ref:1;未签名的has_weak_ref:1;未签名的pending_weak_ref:1;未签名的has_async_transaction:1;未签名的accept_fds:1;无符号最小优先级:8;结构list_headasync_todo;};发送的数据。其实就是messageParcel->flat_binder_object->binder_transaction_data->binder_write_read。上面我们提到了普通数据和iremoteObject是有区别的。有什么不同?即发送的数据中包含iremoteObject,驱动会在全局哈希表binder_procs中注册一个binder_proc。如何区分普通数据和远程数据?答案是他有两个指针,一个指向普通数据,一个指向iremoteObject。即binder_transaction_data的buffer指针和offsets指针。Binder驱动在发送数据的过程中做了什么?(2)驱动事件循环binder_ref->binder_proc->binder_node。对于发送方来说,要知道发送的数据需要传给谁,他自己持有一个服务进程的binder_ref,通过binder_ref找到对应的binder_proc,再通过binder_proc找到对应的binder_node(一个进程可以有多个binder_node,即是可以有多个服务)。binder_write_read中获取的数据也会被添加到宿主进程binder_proc的todo列表中。源码调用图可以看出client和stub的交互过程是client->stub->client,看起来是自己的进程调用自己的方法。上图中ipcObjectStub->OnRemoteRequest()会向binderdriver释放数据,这也是继承子类需要重写OnRemoteRequest方法的原因。下面是一般的ipc调用流程。驱动本身的讲解比较浅,驱动中的多线程模型和自身数据结构的复杂性没有讲解。如果有机会学习,那就说说dsoftbus驱动和binder驱动的异同,以及iremoteObJect是如何适配这两种不同的驱动模型的。更多信息请访问:与华为官方共建的鸿蒙技术社区https://ost.51cto.com