源码分析 ---------openHarmony IPC数据传输情景分析

满堂花醉三千客,一剑霜寒十四洲。这篇文章主要讲述源码分析 ---------openHarmony IPC数据传输情景分析相关的知识,希望能为你提供帮助。
1.前情概览我们在前片博客中讲述了 proxy - stub 架构的一般编程范式,这篇文章关注驱动自身的数据传输,做一次完整的数据分析。由于IPC通信流程比较复杂,我们先开启上帝视角,将一些数据结构和数据流向直接阐述出来,然后再结合源码调用流查看是否具体是这样的。
2.数据结构简述 2.1messageParcel
messageParcel 用户态数据,可以写入一般类型,也可以写入iremoteObject。为什么要区分这两种类型,这里先卖一个关子。

class MessageParcel : public Parcel public: MessageParcel(); ~MessageParcel(); explicit MessageParcel(Allocator *allocator); bool WriteRemoteObject(const sptr< IRemoteObject> & object); sptr< IRemoteObject> ReadRemoteObject(); bool WriteFileDescriptor(int fd); int ReadFileDescriptor(); bool ContainFileDescriptors() const; bool WriteInterfaceToken(std::u16string name); std::u16string ReadInterfaceToken(); bool WriteRawData(const void *data, size_t size); const void *ReadRawData(size_t size); bool RestoreRawData(std::shared_ptr< char> rawData, size_t size); const void *GetRawData() const; size_t GetRawDataSize() const; size_t GetRawDataCapacity() const; void WriteNoException(); int32_t ReadException(); bool WriteAshmem(sptr< Ashmem> ashmem); sptr< Ashmem> ReadAshmem(); void ClearFileDescriptor(); void SetClearFdFlag()needCloseFd_ = true; ; private: #ifndef CONFIG_IPC_SINGLE bool WriteDBinderProxy(const sptr< IRemoteObject> & object, uint32_t handle, uint64_t stubIndex); #endif static constexpr size_t MAX_RAWDATA_SIZE = 128 * 1024 * 1024; // 128M static constexpr size_t MIN_RAWDATA_SIZE = 32 * 1024; // 32k bool needCloseFd_ = false; std::vector< sptr< Parcelable> > holders_; int writeRawDataFd_; int readRawDataFd_; void *kernelMappedWrite_; void *kernelMappedRead_; std::shared_ptr< char> rawData_; size_t rawDataSize_; ;

2.2 flat_binder_object
这是内核描述iremoteObject对象信息的结构体。当调用 object-> Marshalling(*this)的时候会将messageParcel 转化为flat_binder_object。同理还有一个UnMarshalling方法
struct flat_binder_object unsigned longtype; // binder类型:可以为BINDER_TYPE_BINDER或BINDER_TYPE_HANDLE等类型 unsigned longflags; // 标记union void*binder; // 当type=BINDER_TYPE_BINDER时,它指向Binder对象位于C++层的本地Binder对象的弱引用。 signed long handle; // 当type=BINDER_TYPE_HANDLE时,它等于Binder对象在Binder驱动中对应的Binder实体的Binder引用的描述。 ; void*cookie; // 当type=BINDER_TYPE_BINDER时才有效,它指向Binder对象位于C++层的本地Binder对象。 ;

2.3 binder_transaction_data
这个数据不仅包含了flat_binder_object中的纯数据,也包含的cmdID以及发送者的其他信息。
struct binder_transaction_data union size_thandle; // 当binder_transaction_data是由用户空间的进程发送给Binder驱动时, // handle是该事务的发送目标在Binder驱动中的信息,即该事务会交给handle来处理; // handle的值是目标在Binder驱动中的Binder引用。 void*ptr; // 当binder_transaction_data是有Binder驱动反馈给用户空间进程时, // ptr是该事务的发送目标在用户空间中的信息,即该事务会交给ptr对应的服务来处理; // ptr是处理该事务的服务的服务在用户空间的本地Binder对象。 target; // 该事务的目标对象(即,该事务数据包是给该target来处理的) void*cookie; // 只有当事务是由Binder驱动传递给用户空间时,cookie才有意思,它的值是处理该事务的Server位于C++层的本地Binder对象 unsigned intcode; // 事务编码。如果是请求,则以BC_开头;如果是回复,则以BR_开头。unsigned intflags; pid_tsender_pid; uid_tsender_euid; size_tdata_size; // 数据大小 size_toffsets_size; // 数据中包含的对象的个数union struct const void*buffer; const void*offsets; ptr; uint8_t buf[8]; data; // 数据 ;

2.4 binder_write_read
驱动真正读写的数据,也就是binder_transaction_data 会转化为binder_write_read交给驱动真正的读写。
struct binder_write_read signed long write_size; signed long write_consumed; unsigned longwrite_buffer; signed long read_size; signed long read_consumed; unsigned longread_buffer; ;

2.5 binder_ref
binder_ref是描述Binder引用的结构体。
struct binder_ref int debug_id; struct rb_node rb_node_desc; // 关联到binder_proc-> refs_by_desc红黑树中 struct rb_node rb_node_node; // 关联到binder_proc-> refs_by_node红黑树中 struct hlist_node node_entry; // 关联到binder_node-> refs哈希表中 struct binder_proc *proc; // 该Binder引用所属的Binder进程 struct binder_node *node; // 该Binder引用对应的Binder实体 uint32_t desc; // 描述 int strong; int weak; struct binder_ref_death *death; ;

2.6 binder_proc
struct binder_proc struct hlist_node proc_node; // 根据proc_node,可以获取该进程在"全局哈希表binder_procs(统计了所有的binder proc进程)"中的位置 struct rb_root threads; // binder_proc进程内用于处理用户请求的线程组成的红黑树(关联binder_thread-> rb_node) struct rb_root nodes; // binder_proc进程内的binder实体组成的红黑树(关联binder_node-> rb_node) struct rb_root refs_by_desc; // binder_proc进程内的binder引用组成的红黑树,该引用以句柄来排序(关联binder_ref-> rb_node_desc) struct rb_root refs_by_node; // binder_proc进程内的binder引用组成的红黑树,该引用以它对应的binder实体的地址来排序(关联binder_ref-> rb_node) int pid; // 进程id struct vm_area_struct *vma; // 进程的内核虚拟内存 struct mm_struct *vma_vm_mm; struct task_struct *tsk; // 进程控制结构体(每一个进程都由task_struct 数据结构来定义)。 struct files_struct *files; // 保存了进程打开的所有文件表数据 struct hlist_node deferred_work_node; int deferred_work; void *buffer; // 该进程映射的物理内存在内核空间中的起始位置 ptrdiff_t user_buffer_offset; // 内核虚拟地址与进程虚拟地址之间的差值// 内存管理的相关变量 struct list_head buffers; // 和binder_buffer-> entry关联到同一链表,从而对Binder内存进行管理 struct rb_root free_buffers; // 空闲内存,和binder_buffer-> rb_node关联。 struct rb_root allocated_buffers; // 已分配内存,和binder_buffer-> rb_node关联。 size_t free_async_space; struct page **pages; // 映射内存的page页数组,page是描述物理内存的结构体 size_t buffer_size; // 映射内存的大小 uint32_t buffer_free; struct list_head todo; // 该进程的待处理事件队列。 wait_queue_head_t wait; // 等待队列。 struct binder_stats stats; struct list_head delivered_death; int max_threads; // 最大线程数。定义threads中可包含的最大进程数。 int requested_threads; int requested_threads_started; int ready_threads; long default_priority; // 默认优先级。 struct dentry *debugfs_entry; ;

2.7 binder_node
binder_node是描述Binder实体的结构体。
struct binder_node int debug_id; struct binder_work work; union struct rb_node rb_node; // 如果这个Binder实体还在使用,则将该节点链接到proc-> nodes中。 struct hlist_node dead_node; // 如果这个Binder实体所属的进程已经销毁,而这个Binder实体又被其它进程所引用,则这个Binder实体通过dead_node进入到一个哈希表中去存放 ; struct binder_proc *proc; // 该binder实体所属的Binder进程 struct hlist_head refs; // 该Binder实体的所有Binder引用所组成的链表 int internal_strong_refs; int local_weak_refs; int local_strong_refs; void __user *ptr; // Binder实体在用户空间的地址(为Binder实体对应的Server在用户空间的本地Binder的引用) void __user *cookie; // Binder实体在用户空间的其他数据(为Binder实体对应的Server在用户空间的本地Binder自身) unsigned has_strong_ref:1; unsigned pending_strong_ref:1; unsigned has_weak_ref:1; unsigned pending_weak_ref:1; unsigned has_async_transaction:1; unsigned accept_fds:1; unsigned min_priority:8; struct list_head async_todo; ;

3.情景分析 3.1 数据流向
其实结合上文,一次的数据发送。其实就是 messageParcel -> flat_binder_object -> binder_transaction_data -> binder_write_read。上文中我们说到普通数据和iremoteObjiect有区别,那区别是啥,就是发送的数据里面包含iremoteObJiect,驱动程序就会在全局哈希表binder_procs中注册一个binder_proc。那普通数据和iremote数据是如何区分呢?答案是他有两个指针一个指向普通的数据,另一个指向iremoteObject。也就是binder_transaction_data的buffer指针和offsets指针。那binder驱动在这个数据发送的过程中做了什么呢?
3.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链表中。
源码调用图解
源码分析 ---------openHarmony IPC数据传输情景分析

文章图片

client和stub的交互过程就是 client - > stub -> client 这样看起来就像自身进程调用自己的方法一样。上图中ipcObjectStub -> OnRemoteRequest()就会向binder驱动放松数据,这也是为什么继承的子类需要重写OnRemoteRequest方法。
这里就是大概ipc调用的过程,对驱动自身的阐述比较浅薄,驱动中的多线程模型还有自身数据结构的复杂性并没有阐述。如果有机会研究后,再讲讲dsoftbus驱动和binder驱动的不同和相似之处,讲讲iremoteObJect是如何适配这两种不同驱动模型的。
想了解更多关于鸿蒙的内容,请访问:
【源码分析 ---------openHarmony IPC数据传输情景分析】51CTO和华为官方合作共建的鸿蒙技术社区
https://ost.51cto.com/#bkwz
::: hljs-center
源码分析 ---------openHarmony IPC数据传输情景分析

文章图片

:::

    推荐阅读