Binder学习(一)

来源:互联网 发布:java线程池 callable 编辑:程序博客网 时间:2024/05/29 16:44
目录:
1、 IPC通信
2、 Binder通信
3、 从Binder driver看Binder通信

一、IPC通信
Inter Process Communication(IPC),进程间通信。Linux系统不允许进程间直接访问彼此的内存空间,进程间需要通信则必须借助IPC相关的技术,比如:socket 、 pipe 、message queue等。而Android基于Linux系统,进程间的通信采用了更加有效的技术Binder。Binder建立在Linux中共享内核空间这一基础上,通过抽象驱动Binder Driver来达到进程间的通信。

二、Binder通信
每个进程有自己独立的用户内存空间,其他的进程无法直接访问,从而保证的进程中数据的安全。但是很多时候我们需要跨进程操作,比如:应用服务A需要调用音频播放,那么必须在A这个进程中去调用系统的AudioFlinger服务,而AudioFlinger服务在自己独立的进程中。
2.1 Binder通信原理
Linux进程除了用户内存空间外还有公共的内核空间,内核空间的数据所有进程都可以访问。基于这点,Binder可以大显身手了。通信原理如图1所示:
 
图1 Binder通信原理
客户端想调用Service server中的方法foo()需要以下步骤:
(1) 将请求封装成IPC数据并发送给Binder Driver
(2) Binder Driver接收客户端发来的IPC数据,解析得到想要调用的目标服务
(3) Binder Driver将客户端的请求封装成IPC数据发给目标服务
(4) 目标服务接收到IPC数据,解析后调用服务函数foo()并发送回应信息

2.2 IPC数据
IPC数据结构如图2所示:
 
图2 IPC数据
其中handle对应的就是我们的目标服务的索引信息,RPC代码和数据分别对应调用的方法名和参数。
2.3 binder相关的结构体
此处只列出部分结构体包括:
(1)struct binder_state 

int fd; //打开的文件描述符
void *mapped; //通过mmap把"/dev/binder"设备文件映射到进程虚拟空间的地址(用户空间)
unsigned mapsize; 
}; 
作用:一般用来记录open("/dev/binder")打开binder设备的句柄以及通过mmap把该设备文件映射到进程的用户空间的起始地址。

(2) struct binder_proc {
struct hlist_node proc_node;//连入总链表的点.
struct rb_root threads; // 红黑树的节点,所有的服务都有一个note节点)
struct rb_root nodes;
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;
int deferred_work;
void *buffer;//表示要映射的物理内存在内核空间中的起始位置
//内核使用的虚拟地址与进程使用的虚拟地址之间的差值,即如果某个物理页面在内核空间中对应的虚拟地址是addr的话, 
//那么这个物理页面在进程空间对应的虚拟地址就为addr + user_buffer_offset 
ptrdiff_t user_buffer_offset; 
struct list_head buffers;//通过mmap映射的内存空间.
struct rb_root free_buffers;//空闲的binder_buffer通过成员变量rb_node连入到struct binder_proc中的free_buffers表示的红黑树中去,
struct rb_root allocated_buffers;//正在使用的binder_buffer通过成员变量rb_node连入到structbinder_proc中的allocated_buffers表 示的红黑树中去。
size_t free_async_space;
struct page **pages;// struct 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;
int requested_threads;
int requested_threads_started;
int ready_threads;
long default_priority;
struct dentry *debugfs_entry;
};
作用:用来保存进程的相关信息,每个进程只能打开一个binder设备,所以对于一个进程来说在Binder driver中会创建唯一的一个binder_proc,这是一个非常重要的结构体,IPC数据的传递与回应都与之有密切的关系。

(3)struct binder_node { 
int debug_id; 
struct binder_work work; 
union { 
struct rb_node rb_node; 
struct hlist_node dead_node; 
}; 
struct binder_proc *proc; 
struct hlist_head refs; 
int internal_strong_refs; 
int local_weak_refs; 
int local_strong_refs; 
void __user *ptr; 
void __user *cookie; 
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; 
int min_priority : 8; 
struct list_head async_todo; 
};
作用:描述一个Binder实体,注意这个binder实体是在驱动也就是内核中表示一个Binder。

(4)struct binder_ref {//就是 refs_by_desc、refs_by_node 这两个引用树种的数据结构.
int debug_id;
struct rb_node rb_node_desc;//连接des的引用树.
struct rb_node rb_node_node;//连接node的引用树.
struct hlist_node node_entry;
struct binder_proc *proc; //该binder引用所属的进程,
struct binder_node *node;//和远程的binder实体binder_node关联的地方.相对应.
uint32_t desc;
int strong;
int weak;
struct binder_ref_death *death;
};
作用: 描述一个Binder实体的引用。(底层用binder实体和binder引用来相对的称呼,相当于用户空间binder代理)

(5) struct binder_buffer {
struct list_head entry; //连入 binder_proc的buffers
//空闲的binder_buffer通过成员变量rb_node连入到binder_proc中的free_buffers表示的红黑树中.
//正在使用的binder_buffer通过成员变量rb_node连入到binder_proc中的allocated_buffers表示的红黑//树中去。
struct rb_node rb_node; 
unsigned free:1; //每一个binder_buffer又分为正在使用的和空闲的,通过free成员变量来区分.
unsigned allow_user_free:1;
unsigned async_transaction:1;
unsigned debug_id:29;
struct binder_transaction *transaction;
struct binder_node *target_node;
size_t data_size;
size_t offsets_size;
uint8_t data[0];
};
作用:描述一段通过mmap映射的内存空间,定义在binder驱动binder.c当中.  每一个binder_buffer通过其成员entry按从低址到高地址连入到struct binder_proc中的buffers表示的链表中去.

(6) binder_thread
线程状态如下:
enum {  
    BINDER_LOOPER_STATE_REGISTERED  = 0x01,  
    BINDER_LOOPER_STATE_ENTERED     = 0x02,  
    BINDER_LOOPER_STATE_EXITED      = 0x04,  
    BINDER_LOOPER_STATE_INVALID     = 0x08,  
    BINDER_LOOPER_STATE_WAITING     = 0x10,  
    BINDER_LOOPER_STATE_NEED_RETURN = 0x20  
};
struct binder_thread {
struct binder_proc *proc; //当前线程所属的进程。
struct rb_node rb_node; //来连入binder_proc的threads红黑树.
int pid;
int looper;//表示线程的状态  就是上面enum的类型。
struct binder_transaction *transaction_stack; //表示线程正在处理的事务
struct list_head todo; //表示发往该线程的数据列表待处理的一次通信事务.
uint32_t return_error; /* Write failed, return error code in read buf */
uint32_t return_error2; /* Write failed, return error code in read */
/* buffer. Used when sending a reply to a dead process that */
/* we are also waiting on */
wait_queue_head_t wait;  //用来阻塞线程等待某个事件的发生
struct binder_stats stats; //用来保存一些统计信息
};
作用:描述执行当前binder通信事物的线程,在驱动中定义。

(7) struct binder_transaction {
int debug_id;
struct binder_work work;//当前在进行什么类型的工作项的处理
struct binder_thread *from; //来自于哪一个binder线程
struct binder_transaction *from_parent;//来自于哪一个binder事务
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;
};
作用:client端和server端通信时,用来记录通信信息,表示一次通信.

(8)struct binder_write_read {
binder_size_t write_size; /* bytes to write */
binder_size_t write_consumed; /* bytes consumed by driver */
binder_uintptr_t write_buffer;
binder_size_t read_size; /* bytes to read */
binder_size_t read_consumed; /* bytes consumed by driver */
binder_uintptr_t read_buffer;
};
作用:传输数据的封装

(9)struct binder_transaction_data {
/* The first two are only used for bcTRANSACTION and brTRANSACTION,
* identifying the target and contents of the transaction.
*/
union {
/* target descriptor of command transaction */
__u32 handle;//当通信命令的目标对象不是本地Binder实体时,使用handle来表示这个Binder实体的引用
/* target descriptor of return transaction */
binder_uintptr_t ptr;//当通信命令的目标对象是本地Binder实体时,就使用ptr来表示这个对象在本进程中的地址.
} target;
//只有目标对象是Binder实体时,cookie成员变量才有意义,表示一些附加数据,由Binder实体来解释这个个附加数据.
binder_uintptr_t cookie; 
__u32 code;//通信的命令码如:BC_XXXX、BR_XXXXX等等.
/* General information about the transaction. */
__u32 flags; //前面列举的flags
pid_t sender_pid;
uid_t sender_euid;
binder_size_t data_size; //data.buffer缓冲区的大小
binder_size_t offsets_size; //表示data.offsets缓冲区的大小

/* If this transaction is inline, the data immediately
* follows here; otherwise, it ends with a pointer to
* the data buffer.
*/
union {
struct {
/* transaction data */
binder_uintptr_t buffer;//真正要传输的数据保存的地方.
/* offsets from buffer to flat_binder_object structs */
binder_uintptr_t offsets;
} ptr;
__u8 buf[8];
} data;
};
作用:一次binder通信事务,封装了RPC数据和服务的编号handle

(10) struct flat_binder_object {
/* 8 bytes for large_flat_header. */
__u32 type; //handle 标识检索已有的binder_note ;binder表示新建一个binder_note
__u32 flags; 
/* 8 bytes of data. */
union {
binder_uintptr_t binder; //binder表示这是一个Binder实体
__u32 handle; //handle表示这是一个Binder引用.
};
/* extra data associated with local object */
binder_uintptr_t cookie;//当这是一个Binder实体时,cookie才有意义,表示附加数据,由进程自己解
};
作用:传输过程中的每一个Binder实体或者引用

2.4 Binder Driver
Binder Driver是运行在Linux内存空间中的抽象驱动程序,在进程间通信中充当中转的角色。服务程序的注册、检索、调用都需要与Binder Driver打交道,接下来将站在Binder Driver的角度上来分析程序的注册、检索、调用三个过程中Binder机制是如何运行的。
2.4.1 服务注册
Linux内部通过一个Context Manager进程来统一管理系统的所有服务,所有的服务都要注册到Context Manager的服务列表中。图3展示了从Binder Driver分析服务注册的过程。
 
图3 注册服务是Binder Driver的行为
(1)~(3) Context Manager在所有服务启动前最先运行,它会通过调用binder_open()函数在Binder Driver中初始化一个binder_proc的结构体,并在内核空间中开辟一块buffer用于接收IPC数据,然后进入等待状态,等待buffer中有IPC数据过来再唤醒读取IPC数据

(4) ~(5)Context Manager处于等待状态后,Service server进程也通过binder_open()函数来初始该进程对应的binder_proc结构体和开辟一块buffer用于接收IPC应答。Service server生成IPC数据,用来调用Context Manager进程的注册函数

(6) Service server将IPC数据发送给Binder Driver,Context Manager对应的handle=0,通过解析IPC中的handle可以获得Context Manager对应的binder_note和binder_proc。每一个服务都会在内核控件中创建一个binder_note节点,该节点包含了改服务的相关信息,此处不详细介绍。

(7) Binder Driver为需要注册的服务创建binder_note节点,并将该节点分别注册到服务进程的binder_proc和Context Manager的binder_proc中。

(8)~(9) Binder Driver将IPC数据传入Context Manager开辟的read_buffer中,等待Context Manager读取,然后将Service server的binder_proc记录下来,以便发送Context Manager的应答信息。

(10) Binder Driver让Service server进入等待状态,并将Context Manager从等待状态唤醒,接收来自Service server的IPC数据,Context Manager解析IPC数据后将Service server注册到服务列表后并发送回应信息

(11) Binder Driver查找记录下来的Service server的binder_proc结构体中的buffer,并将来自Context Manager的回应IPC数据传入其中,然后唤醒Service server读取回应信息。Service server被唤醒后读取回应信息并处理,整个注册过程就完成了。


文章大部分内容来自《Android框架揭秘》

参考文献:http://blog.csdn.net/zy00000000001/article/details/53443151


0 0
原创粉丝点击