目录
- Binder进程间通讯机制(1)-简况
- Binder进程间通讯机制(2)-内核空间Binder驱动详解(基础数据结构)
- Binder进程间通讯机制(3)-内核空间Binder驱动详解(Binder驱动内存管理)
- Binder进程间通讯机制(4)-内核空间Binder驱动详解(Binder驱动库 C/C++接口简介)
- Binder进程间通讯机制(5)-从ServiceManager的角度分析IPC原理
Binder驱动存在于Linux kernel层,知识点如要包括基础数据结构(结构体),初始化过程,以及binder的打开open,映mmap,ioctl,管理内核缓冲区等操作.
基础数据结构
1.binder_work
struct binder_work {
struct list_head entry;
enum {
BINDER_WORK_TRANSACTION = 1,
BINDER_WORK_TRANSACTION_COMPLETE,
BINDER_WORK_NODE,
BINDER_WORK_DEAD_BINDER,
BINDER_WORK_DEAD_BINDER_AND_CLEAR,
BINDER_WORK_CLEAR_DEATH_NOTIFICATION
} type;
}
binder_work结构体用来描述待处理的工作项,这些工作项有可能属于一个进程,也可能属于某个进程的线程。成员entry 用来将这些结构体嵌入到宿主结构中,成员变量type用来描述工作项的类型。根据type的取值,binder驱动程序可以判断出一个binder_work嵌入到什么类型的宿主结构中。
2.binder_node
struct binder_node {
int debug_id;
struct binder_work work;
union {
struct rb_node rb_rode;//存活宿主的Binder实体对象
struct hlist_node dead_node;//保存死亡的宿主的Binder实体对象
};
struct binder_proc *proc;//Binder宿主进程
struct hlist_head refs;//Binder与Client的引用关系
int internal_strong_refs;//Binder实体对象的强引用计数
int local_weak_refs;//Binder实体对象的弱引用计数
int local_strong_refs;
void __user *ptr;//指向用户Service组件其中的引用计数对象
void __user *cookie; //指向用户空间的service组件
unsigned has_strong_ref:1;
//pending_strong_ref或pending_weak_ref 1:表示Binder实体对象请求service修改引用计数 0:service组件修改引用计数
unsigned pending_strong_ref:1;
unsigned has_weak_ref:1;
unsigned pending_weak_ref:1;
unsigned has_async_transaction:1; //用来表示是否在处理异步事务,1:是 0:否
unsigned accept_fds:1;
unsigned min_priority:8;
struct list_head async_todo;
}
结构体binder_node用来描述一个Binder实体对象,每一个Service组件在Binder驱动中都含有一个Binder实体对象.
proc : 指向Binder实体对象的宿主进程,并且这些宿主进程由结构体binder_proc描述,宿主进程使用一颗红黑树来维护内部的所有Binder实体对象,在联合体中的成员变量rb_node为这个红黑树的节点,如果宿主进程死亡,联合体中dead_node就会保存这个Binder实体对象.因为Binder本地对象不是存活就是死亡所以这里采用联合体使用同一块内存空间.
internal_strong_refs :描述Binder实体对象的强引用计数
local_weak_refs :描述Binder实体对象的弱引用计数
has_strong_ref,has_weak_ref :当前实体对象是否请求了一个service组件执行操作,当执行操作时,会增加强引用计数与弱引用计数,并且has_strong_ref与has_weak_ref会置位1.当一个service组件完成该组件的请求之后,会减少引用计数.
pending_strong_ref,pending_weak_ref :当Binder实体对象在请求service组件的过程中,引用计数器发生改变的过程中,会将pending_strong_ref与pending_weak_ref置为1.当service组件增加或修改引用计数字后,会将
pending_strong_ref与pending_weak_ref置为0.ptr,cookie :这两个都是指向用户空间的地址,用来描述用户空间的service组件,cookie指向service组件的地址,ptr指向service组件内部的一个引用计数对象(weakref_impl)的地址.这两个对象用来通过node寻找service组件,重要.
refs: Binder实体对象有可能被多个Client组件引用,所以需要使用refs来描述这些引用关系,并且将它们保存在同一个hash列表中。Binder驱动可以通过它知道那些client组件引用了同一个Binder实体对象。
当一个Binder实体对象的引用计数发生改变,会同时去请求改变Service组件的引用计数,这时候Binder驱动就会将改引用计数修改操作封装成一个binder_node工作项,添加到响应进程的todo队列去等待处理
3.binder_ref_death
struct binder_ref_death{
struct binder_work work;//死亡通知类型,用来判断service组件是否已经死亡
void _user *cookie; //用来保存接收死亡通知的对象地址
}
结构体binder_ref_death用来描述一个Service组件的死亡接收通知.Service组件会被多个Client组件进程持有,当Service组件因为一些原因意外死亡,client进程需要获取到Service组件的死亡通知,以便Client组件做出一些处理.
cookie :用来保存接收死亡通知的client对象地址.
work :用来保存死亡通知类型,通过work就可以知道所引用的Service组件是否已经死亡,死亡通知类型如下:
死亡类型 | 发生场景 |
---|---|
BINDER_WORK_DEAD_BINDER | (1)当Binder驱动监测到一个Service组件死亡,就会找到该Service组件对应的Binder实体对象,通过实体对象的成员变量refs找到引用了他的Client进程,向这个进程发送死亡通知,这时候死亡通知的类型为BINDER_WORK_DEAD_BINDER (2)当Client进程向Binder驱动注册一个死亡通知的时候,Service组件已经死亡,这时候Binder会将死亡通知的类型work设置为BINDER_WORK_DEAD_BINDER |
BINDER_WORK_CLEAR_DEATH_NOTIFICATION | 如果client向service发送死亡通知,相应的service组件还没有死亡,这时候会将work设置为BINDER_WORK_CLEAR_DEATH_NOTIFICATION |
BINDER_WORK_DEAD_BINDER_AND_CLEAR | 如果client向service发送死亡通知,想用的service组件已经死亡,work设置为BINDER_WORK_DEAD_BINDER_AND_CLEAR |
4.binder_ref
struct binder_ref {
int debug_id;
struct rb_node rb_node_desc;
struct rb_node rb_node_node;
struct hlist_node node_entry;
struct binder_proc *proc;
struct binder_node *node; //描述这个Binder引用对象所指向的Client组件
uint32_t desc; //句柄,方便Binder寻址找到这个结构体binder_ref所描述的Binder引用对象
int strong; //强引用计数
int weak; //弱引用计数
struct binder_ref_death *death;
};
结构体binder_ref用来描述一个Binder引用对象,既每个Client组件在Binder驱动中都有对应的引用对象
node :用来描述这个引用对象所关联的Binder实体对象,这个node就是Binder实体对象中变量refs的一个节点
desc :句柄,用来寻址,用来描述一个Binder引用对象,当Client进程的用户空间通过Binder访问Service的时候,Binder通过该句柄找到这个Binder引用对象,根据这个Binder引用对象的变量node找到Binder实体对象,通过Binder实体对象的ptr,cookie找到用户空间的Service组件,既要访问的Service组件.
proc :指向这个Binder引用对象所指向的宿主进程.
strong,weak :这个Binder引用对象的强引用与弱引用计数,用来维护这个Binder引用对象的生命周期.
death :指向service组件的死亡接收通知,当Client组件向Binder驱动住一个他所引用的Service组件的死亡通知时,Binder驱动会创建一个死亡通知结构体binder_ref_death,保存在death中.
5.binder_buffer
struct binder_buffer {
struct list_head entry; //内核缓冲区的一个节点
struct rb_node rb_node;
unsigned free:1; //标记该区域是否空闲
unsigned allow_user_free:1;
unsigned async_transaction:1;
unsigned debug_id:29;
struct binder_transaction *transaction; //client请求Service的一次事务
struct binder_node *target_node;
size_t data_size;
size_t offsets_size;
uint8_t data[0];//通信数据的数据缓冲区
};
结构体binder_buffer用来描述一块内核缓冲区,用来进程间通信传输数据.每一个使用Binder进程间通信的进程在Binder驱动中都有一块内核缓冲区列表,用来保存所分配的内核缓冲区.
entry :这个内核缓冲区列表的一个节点
free,rb_node :如果一块内核缓冲区为空闲状态,free的值为1,rb_node为空闲内核缓冲列表的一个节点,如果不空闲,rb_node为正在使用的内核缓冲列表的一个节点
transaction :结构体binder_transaction对象transaction用来描述一个事务,每个事务都关联一个Binder实体对象.当Client端请求Service端的时候,该事物会将数据传递给Binder实体对象,再由Binder实体对象传递给Service端,之后再释放该内核缓冲区.
data :用来保存通信数据的缓冲区
6.binder_proc
struct binder_proc {
struct hlist_node proc_node;
struct rb_root threads; //当前Binder进程所持有的Binder线程池
struct rb_root nodes; //当前进程Binder实体对象列表
struct rb_root refs_by_desc;
struct rb_root refs_by_node;
int pid; //使用该进程通信机制的进程组id
struct vm_area_struct *vma; //用户空间地址
struct mm_struct *vma_vm_mm;
struct task_struct *tsk; //任务控制块
struct files_struct *files; //打开文件结构体数组
struct hlist_node deferred_work_node;
ptrdiff_t user_buffer_offset; //用户空间地址与内核空间地址的偏移值
struct list_head buffers;
struct rb_root free_buffers;
struct rb_root allocated_buffers;
size_t free_async_space; struct page **pages;
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;//Binder驱动程序做多可以主动请求进程注册的线程数
int requested_threads_started;
int ready_threads; //当前进程空闲的线程数
long default_priority;
struct dentry *debugfs_entry;
};
结构体binder_proc用来描述一个正在使用的Binder进程间通信的进程.
proc_node :当一个进程调用open(“/dev/binder”),binder会创建一个binder_proc结构体.并将这个结构体保存到Binder全局的列表中.这个列表的节点就是proc_node.
buffer_size :Binder驱动为进程分配的内核缓冲区大小.
buffer,vma :成员变量buffer用来保存内核空间地址 ,vma用来保存用户空间地址,这两个地址存在一个偏移值user_buffer_offset,可以通过其中一个地址算出另一个地址的大小.
buffer :成员变量Buffer指向一块大的内核缓冲区,这个大的内核缓冲区Binder为了方便管理,又分成了小的内和缓冲区,这个小的内核缓冲区就是由结构体binder_buffer所描述.
threads :每个Binder进程间通信的进程都持有一个Binder线程池,这个threads用来组织一个进程的Binder线程池.
max_threads :Binder驱动程序做多可以主动请求进程注册的线程数
ready_threads :当前进程空闲的线程数
todo,wait :todo为当前进程的待处理工作队列,wait为空闲等待队列,
nodes,refs_by_desc,refs_by_node :一个进程内部包含了Binder引用对象列表与Binder实体对象列表,其中,nodes为当前进程Binder实体对象列表,他以Binder实体对象ptr作为关键字,refs_by_desc和refs_by_node为当前进程的Binder引用对象列表,前者refs_by_desc使用Binder引用对象的成员变量desc作为关键字,后者refs_by_node使用Binder引用对象成员变量node作为关键字.
7.binder_transaction
struct binder_transaction{
int debug_id;
struct binder_work work;
struct binder_thread *from; //发起事务的线程,源线程
sturct binder_proc *to_proc; //处理事务的进程,目标进程
sturct binder_thread *to_thread; //处理事务的线程,目标线程
struct binser_transaction *from_parent;
struct binder_transaction *to_parent;
unsigned need_reply : 1;//区分异步还是同步,同步1,异步0
struct binder_buffer *buffer; //用来分配内核缓冲区,并存放进程间通信的数据
unsigned int code;
unsigned int flags;
long priority;
long saved_priority;
uid_t sender_euid;
};
结构体binder_transaction用来描述进程间通信的过程,这个过程被称作一次事务.
need_reply :区分异步还是同步,同步1,异步0,同步事务需要等待回应.
from :发起事务的线程,成为源线程..
to_proc :处理事务的进程,目标进程
to_thread :处理事务的线程,目标线程
work :当前事务状态值,当Binder驱动为目标进程或者线程创建一个事务的时候,就会将work的状态设置为BINDER_WORK_TRANSACTION,并将它添加到目标进程或线程的todo队列等待处理.
sender_euid :源线程的用户id,目标线程用来识别身份
buffer :结构体binder_buffer,上面提到过用来描述一次事务传输的数据,会为该事物分配一块内核缓冲区.
8.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用来描述进程间通信传输的数据.包括输入数据和输出数据.
write_size,write_consumed,write_buffer :这三个用来描述输入数据,即从用户空间传入到内核空间Binder驱动的数据,write_buffer指向用户空间的缓存地址,里面保存着要传输的数据.缓冲区的大小由write_size指定.write_consumed用来描述内核空间处理了缓冲区多少字节的数据
read_size,read_consumed,read_buffer :描述输出数据,即从内核空间Binder驱动返回给用户空间的数据.为进程间通信返回的结果数据,read_buffer存放结果缓冲区地址,里面存放结果数据,read_size为结果缓冲区大小.
write_buffer与read_buffer作为缓冲区,由数组组成,数组的每个元素都是由一个协议和通讯数据组成,write_buffer所使用的是命令协议,read_buffer所使用的是返回协议.
9.binder_transaction_data
struct binder_transaction_data {
union {
size_t handle;
void *ptr;
} target;
void *cookie;
unsigned int code;
unsigned int flags;
pid_t sender_pid;
uid_t sender_euid;
size_t data_size;
size_t offsets_size;
union {
struct {
const void *buffer;//数据缓冲区
const void *offsets;
} ptr;
uint8_t buf[8];
} data;
};
结构体binder_transaction_data用来描述进程间通信过程中所传输的数据
target,ptr,handle :联合体target用来描述一个目标Binder实体对象或者Binder引用对象,若为实体对象,ptr就指向该Binder实体对象对应的一个serviec组件的弱引用计数对象(weakref_imol)的地址,若为引用对象,成员变量handle就指向Binder引用对象的句柄.
cookie :当Binder驱动使用返回命令协议向一个Server进程发送出进程间通讯请求,cookie指向目标Service组件的地址
sender_pid,sender_euid :发起进程间通信请求的pid和uid
data_size,offsets_size :通信数据缓冲区的大小以及偏移数组的大小.
data :通讯数据存放的缓冲区,其中结构体ptr的变量buffer指向一个数据缓冲区,真正用来保存数据的.它的大小由data_size决定.offsets为偏移数组,用来描述数据缓冲区每个Binder对象的位置.有了这个偏移数组,Binder驱动就能维护内部的Binder实体对象和引用对象的引用计数.每个Binder对象都使用一个flat_binder_object结构体描述.
buffer内存示意图:
data.ptr.buffer为数据开始位置,根据缓冲区大小data_size就可以获取到数据偏移量的值offerset_size中n1和n2的数组偏移量的值,根据n1和n2数组偏移量的值就可以从data.ptr.buffer的开始位置获取到flat_binder_objcet所描述的Binder对象.
10.flat_binder_object
struct flat_binder_object {
unsigned long type; //区分类型
unsigned long flags;
union {
void *binder; //binder实体对象的强引用计数地址
signed long handle; //binder引用对象句柄
};
void *cookie; //用来指向binder实体对象service组件地址
};
结构体flat_binder_object用来描述一个Binder实体对象或者Binder引用对象,还可以用来描述文件描述符,根据type字段区分.
- type :用来区分结构体flat_binder_object所描述的类型
type | 含义 |
---|---|
BINDER_TYPE_BINDER | 强引用类型的Binder实体对象 |
BINDER_TYPE_WEAK_BINDER | 弱引用类型的Binder实体对象 |
BINDER_TYPE_HANDLE | 强引用类型的Binder引用对象 |
BINDER_TYPE_WEAK_HANDLE | 弱引用类型的Binder引用对象 |
- binder :当结构体所描述的为实体对象的时候,变量Binder指向该Binder实体对象对应的一个service组件内部的弱引用计数地址(weakref_impl),并使用cookie指向该Service组件的地址,
- handle :当描述的为引用对象的时候,handle为该Binder引用对象的句柄.
Binder通信协议
1.binder_driver_command_protocoll 命令协议
enum binder_driver_command_protocol {
BC_TRANSACTION = _IOW('c', 0, struct binder_transaction_data),
BC_REPLY = _IOW('c', 1, struct binder_transaction_data),
BC_ACQUIRE_RESULT = _IOW('c', 2, __s32),
BC_FREE_BUFFER = _IOW('c', 3, binder_uintptr_t),
BC_INCREFS = _IOW('c', 4, __u32),
BC_ACQUIRE = _IOW('c', 5, __u32),
BC_RELEASE = _IOW('c', 6, __u32),
BC_DECREFS = _IOW('c', 7, __u32),
BC_INCREFS_DONE = _IOW('c', 8, struct binder_ptr_cookie),
BC_ACQUIRE_DONE = _IOW('c', 9, struct binder_ptr_cookie),
BC_ATTEMPT_ACQUIRE = _IOW('c', 10, struct binder_pri_desc),
BC_REGISTER_LOOPER = _IO('c', 11),
BC_ENTER_LOOPER = _IO('c', 12),
BC_EXIT_LOOPER = _IO('c', 13),
BC_REQUEST_DEATH_NOTIFICATION = _IOW('c', 14,
struct binder_handle_cookie),
BC_CLEAR_DEATH_NOTIFICATION = _IOW('c', 15,
struct binder_handle_cookie),
BC_DEAD_BINDER_DONE = _IOW('c', 16, binder_uintptr_t),
};
该协议主要由用户空间的组件向Binder内核发送指令
协议码类型:
协议码 | 含义 |
---|---|
BC_TRANSACTION | 命令协议码BC_TRANSACTION,BC_REPLY中的通讯数据使用结构体binder_transaction_data来描述.当一个进程请求另一个进程执行某项操作,源进程使用BC_TRANSACTION协议码请求Binder驱动程序将数据发给目标进程| |
BC_REPLY | 当目标进程返回结果数据,使用BC_REPLY协议码将数据请求Binder驱动将数据返回给源进程. |
BC_FREE_BUFFER | 当目标进程完成源进程通信请求之后,使用协议码BC_FREE_BUEFFER通知Binder驱动释放内核缓冲区 |
BC_INCREFS,BC_ACQUIRE ,BC_RELEASE ,BC_DECREFS | 这几个用来描述Binder引用对象的句柄值,分别用来减少和增强引用计数 |
BC_INCREFS_DONE ,BC_ACQUIRE_DONE | 这两个协议码所跟的结构体为binder_ptr_cookie,Binder驱动第一次增加一个Binder实体对象引用计数,就会使用返回协议码BR_ACQUIRE或者BR_increfs来请求对应的server进程增加service组件的引用计数,当server进程处理完请求之后,就会使用这两个协议码讲错做结果返回给Binder驱动 |
BC_REGISTER_LOOPER , BC_ENTER_LOOPER ,BC_EXIT_LOOPER | 当一个线程注册到Binder驱动之后,使用命令码BC_ENTER_LOOPER 通知binder驱动,已经准备好接收进程通讯请求,当Binder驱动主动请求进程注册一个新的线程到他的Binder线程池中处理通信请求,新创建的线程就会使用BC_REGISTER_LOOPER 通知Binder驱动,线程退出使用BC_EXIT_LOOPER 来通知Binder驱动不会再接收进程间通信请求 |
BC_REQUEST_DEATH_NOTIFICATION ,BC_CLEAR_DEATH_NOTIFICATION | 使用结构体binder_ptr_cookie描述,用来发送死亡通知 |
2.binder_driver_return_protocol 返回协议
enum binder_driver_return_protocol {
BR_ERROR = _IOR('r', 0, __s32)
,BR_OK = _IO('r', 1),
,BR_TRANSACTION = _IOR('r', 2, struct binder_transaction_data)
,BR_REPLY = _IOR('r', 3, struct binder_transaction_data)
,BR_TRANSACTION_COMPLETE = _IO('r', 6),
,BR_DEAD_REPLY = _IO('r', 5),BR_TRANSACTION_COMPLETE = _IO('r', 6)
,BR_INCREFS = _IOR('r', 7, struct binder_ptr_cookie),
BR_ACQUIRE = _IOR('r', 8, struct binder_ptr_cookie),
BR_RELEASE = _IOR('r', 9, struct binder_ptr_cookie),
BR_DECREFS = _IOR('r', 10, struct binder_ptr_cookie)
,BR_NOOP = _IO('r', 12)
,BR_SPAWN_LOOPER = _IO('r', 13)
,BR_FINISHED = _IO('r', 14)
,BR_DEAD_BINDER = _IOR('r', 15, binder_uintptr_t)
,BR_CLEAR_DEATH_NOTIFICATION_DONE = _IOR('r', 16, binder_uintptr_t)
,BR_FAILED_REPLY = _IO('r', 17),
};
该协议主要由Binder内核向用户空间的组件发送指令
协议码类型:
协议码 | 含义 |
---|---|
BR_ERROR | 用来描述错误代码,binder在处理应用程序发出的请求是发生了异常返回这个协议码 |
BR_OK | 请求成功 |
BR_TRANSACTION ,BR_REPLY | 使用结构体binder_transaction_data描述,当client请求service进程的时候,binder向service进程传递BR_TRANSACTION通知server进程处理进程间通信数据,当server进程完成后,binder进程会使用BR_REPLY 通知client进程并将结果返回 |
BR_DEAD_REPLY | 通知源进程目标进程或者目标线程死亡 |
BR_TRANSACTION_COMPLETE | 当Binder驱动接收到应用程序发送给Binder驱动的BC_TRANCATION或BC_REPLY时,会返回协议码BR_TRANSACTION_COMPLETE通知应用程序进程,该命令已经被接收.正在将数据分发给目标线程或者进程处理 |
BR_SPAWN_LOOPER | 当binder驱动发现一个进程没有足够空闲的BInder线程执行请求时,使用这个协议通知该进程增加一个线程到Binder线程池 |