进程间通讯b i n d e r

来源:互联网 发布:源码168 编辑:程序博客网 时间:2024/05/21 19:49
一、初始化
1. init:
创建延时工作队列:负责一些扫尾工作
注册一个misc设备
在proc下创建:
proc/binder
proc/binder/proc->binder_read_proc_proc()
proc/state -> binder_read_proc_state()
proc/stats -> binder_read_proc_stats()
proc/transactions->binder_read_proc_transactions()
proc/transaction_log->binder_read_proc_transaction_log()
proc/failed_transaction_log->binder_read_proc_transaction_log()


2. 功能函数:打印出相关结构
print_binder_buffer()
print_binder_node()
print_binder_proc()
print_binder_proc_stats()
print_binder_ref()
print_binder_stats()
print_binder_thread()
print_binder_transaction()
print_binder_transaction_log_entry()
print_binder_work()


二、打开和映射驱动文件句柄
1.应用层
fd = open(“/dev/binder”, O_RDWR);
mmap(NULL, MAP_SIZE, PROT_READ, MAP_PRIVATE, fd, 0);
2. open()
a. 为进程生成binder_proc对象
b. 记录统计数据
c. 将创建的binder_proc对象加入全局进程信息列表binder_procs
d. 将binder_proc保存在file结构体的私有数据中
e. 以本进程pid创建proc/binder/proc/pid的proc文件系统入口
  f. 进程打开设备后,一定会有一个binder_proc在内核中,但如果没有ioctl访问的话,不会有binder_thread与之对应
  如果有了binder_thread结构,那么此结构会通过rb_node连入进程的threads域
三、IO控制函数 
1. 取到本进程信息filp->private_data
2. 从进程信息中查找线程信息binder_get_thread(proc),如果没有则新建一个binder_thread
  内部根据task current->pid来标识一个线程id
3. 处理命令BINDER_WRITE_READ(其它命令比较简单),先写后读
  binder_thread_write()、binder_thread_read()
4. 在读完成后,如果proc->wait有事务未完成则唤醒proc.wait空闲线程继续读操作
5. 读操作binder_thread_write()
a. 参数有:proc结构、thread结构、binder_write_read结构等
  b. 虽然binder_write_read在内核,但read、write的buffer还在用户空间
c. 由于buffer中的内容都是cmd+数据的格式,并且这种组合可能有很多个,所以需要一个循环来处理
d. 对于BC_TRANSACTION和BC_REPLY命令对应的数据是binder_transaction_data,但它在buffer中处于
  用户空间,需要先用copy_from_user复制到内核中来
e. struct  binder_transaction_data 是用于在用户空间中表示传输的数据,struct binder_transaction是
  binder驱动在内核空间中来表示传输的数据。binder_thread_write需要完成从前者向后者的转换而
  binder_thread_read()则主要是完成从后者向前者转换。
f. 接着就进入了binder_transaction( *proc,*thread, struct binder_transaction_data *tr, int reply)


6. binder_transaction()
a. 先看是不是reply操作,如果不是,则再检查tr->target.handle是否>0,大于0说明不是SM
b. 以binder_get_ref(proc,handle)找到ref,再用ref找到引用的binder_node
c. 如果handle==0说明是取sm的node直接把全局变量传给target_node就成
d. 从target_node取得target_proc,
  优化是为了选择to_thread 参考:if (!(tr->flags & TF_ONE_WAY) && thread->transaction_stack)
e. binder_transaction一般在发送方(binder_node)创建,在接收方通过work域反向得到binder_transaction的指针,
  最后在发送方接收到接收方的回复后将其释放。
f. 如果是同步传输的发送方: if (!reply && !(tr->flags & TF_ONE_WAY)),则将当前thread记录到transaction的
  from域中。以便接收方可以返回消息。
g. 如果是 异步 或 BC_REPLY操作,就不需要记录返回目的地(task),t->from=NULL
h. 记录binder_transaction t的
t->euid=proc->tsk->cred->euid
t->to_proc=target_proc
t->to_thread = target_thread;           //可以为NULL(优化不成,即直接被唤醒的线程)
t->code = tr->code;             //保持不变,驱动不会关心它
t->flags = tr->flags;    //保持不变,驱动不会关心它
t->priority = task_nice(current);//保存当前处于发送状态的task的nice值
t->buffer = binder_alloc_buf()//从目标进程的接收缓冲区分配数据
t->buffer->allow_user_free = 0;//先关,后面再打开
t->buffer->transaction = t;//让这块buffer知道自己属于哪个transaction
t->buffer->target_node = target_node;   //内存所属的节点,可以等于NULL(reply时?)


i.  offp指向?
j. 只有具有binder实体的进程才有权利发送这类binder对象->在本进程中找或新建一个binder的node
k. 在目标进程搜索或新建这个node的ref:ref = binder_get_ref_for_node(target_proc, node);
  此时,本进程在内核有一个node结点,而在目标进程有一个本节点的引用。本进程内核node的handle赋值成ref.desc
l. 对case BINDER_TYPE_HANDLE:执行操作
m. 对于case BINDER_TYPE_FD:区分是否为reply(若是再判是否目标接受文件形式binder)、目标进程是否接收文件形式的binder
  如果接受,则file=fget(fp->handle);申请一个文件描述符target_fd,并将相关信息存入target_proc的files字段
  再将binder的handle修改成目标进程的fd: fp->handle=target_fd
  这样文件就被共享了,一个进程操作文件,另一个进程的文件指针也会变化:read()/write()/seek()


n. 最后处理transaction_stack



  1). 本次发起传输之前,当前task没有处于通讯状态的话,此值为null
  2). 第一次发起传输时,此值为null
  3). 如果之前有异步传输未处理完,此值不为null
  4). 如果之前task正处理"接收"请求(task是接收方),此值不为null
  
  分为reply过程、同步发送、异步发送
  是reply:


  是同步
  t->need_reply = 1; 说明需要回复
  t->from_parent = thread->transaction_stack;

  thread->transaction_stack = t;
  transaction_stack保存的是传输中间数据,由上面两句可知,它是一个链表,只不过它永远指向最新的。
  是异步:
需要异步分流,异步操作只有一个在(?应该是进程的)to-do队列中,其它的都在node的ansy_todo中。

7. binder_transaction()的最后转化阶段分析(switch(fp->type)):
7.1 case xxxBINDER:说明是传送本进程的binder对象
a. 说明传输的是一个本进程binder对象
b. 用binder_get_node(proc, fp->binder)或binder_new_node(proc, fp->binder, fp->cookie)
  为这个本地对象创建一个内核节点
c. 其中fp->binder是这个传输中flat_binder_object在进程空间的地址
d. 因为要传输到目标进程中去,所以为目标进程建立本binder_node的引用:
  ref = binder_get_ref_for_node(target_proc, node)
e. 此时对目标进程来说,它能看到的是一个引用,引用是连接几个结构体的桥梁
  fp->handle = ref->desc;引用号desc对应的是handle,目标进程通过这个引用号就能找到内核
  中对应的binder_node。
f. 几个结构对应关系
proc中nodes以节点在用户空间地址ptr为key
     refs_by_desc以节点的引用号(desc,handle)为key找引用
     refs_by_node以节点内核地址为key找引用
引用中有节点的引用号desc,和节点内核地址*node
binder_node中有 它在创建自己的进程的用户空间地址
所以,给定一个proc结构,就能通过handle(引用号)在refs_by_desc查找节点引用
也能通过内核地址在refs_by_node查找节点引用,也能通过binder在本进程中的地址
找到具体node。


7.2 case BINDER_TYPE_HANDLE:与上面的过程相反




8. binder对象发送端、接收端转换完成后的一些操作
if (reply) {//如果是回复

BUG_ON(t->buffer->async_transaction != 0);

binder_pop_transaction(target_thread, in_reply_to);

else if (!(t->flags & TF_ONE_WAY)) {//如果同步

BUG_ON(t->buffer->async_transaction != 0);

t->need_reply = 1;//设置需要给本事务的发送者进行回复

t->from_parent = thread->transaction_stack;//构成链表

thread->transaction_stack = t;//让线程的stack指向最新的


else {
//异步的话
BUG_ON(target_node == NULL);

BUG_ON(t->buffer->async_transaction != 1);

if (target_node->has_async_transaction) {//如果节点中已经有异步的请求

target_list = &target_node->async_todo;//将本次异步请求放到node的ansync_todo中

target_wait = NULL;
//清空等待,就是让下面的语句不再唤醒新线程服务本次异步操作
} //根据下面逻辑,第一个进来的异步操作会被放到todo里处理,后来的先放着
else {
target_node->has_async_transaction = 1;
//与上面相比,这里只置位没清除t_w,说明第一次异步会被加入到todo队列进行处理
}


}

t->work.type = BINDER_WORK_TRANSACTION;


list_add_tail(&t->work.entry, target_list);//t_l是进程或优化线程的todo队列,就是告诉目标进程/线程开始准备活动

tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;

list_add_tail(&tcomplete->entry, &thread->todo);//告诉发送者线程,这个事务发送了,同样是在线程的todo里
if (target_wait)

wake_up_interruptible(target_wait);//如果是同步,或第一个异步操作会再唤醒一个线程来工作





8. 包发送的优化
P1.t1向P2发的时候,会查一下,看P2中是否有一个线程T2也向T1发送过请求但T1还没有回复,如果有,说明
T1可能在等某些资源,也许T2处理完这次T1的请求,T1就返回上次的处理了。




9. task的binder_transaction这个链表为NULL, 它记录着本task上是否有传输正在进行


10. 异步接收请求


一个binder_transaction代表一个事务,一次交互过程。
对于同步请求而言,发送方发送了,生成一个binder_transaction在内核,直接接收方回复以后、发送方收到回复
才能释放binder_transaction;
而对于异步发送,这个事务的过程就是“发送方发出请求”,所以发出后,交给接收方后,这个结构会被释放掉。接收方
将这个结构转为binder_transaction_data。




11. 发送端、接收端同步问题
或者可以理解成读与写的问题
向驱动写都是BC_xxx
从驱动读都是BR_xxx
驱动负责将BC_xxx转换成BR_xxx

发送端 驱动接收端
BC_TRANSACTION->BR_TRANSACTION
BR_REPLY <- BC_REPLY


只有同步操作才会记录transaction t于stack中,接收者stack表示未完成的同步操作
list_add_tail(&t->work.entry, target_list);
每次接收着只处理一个,而发送者在等待处理结果,所以发送者的stack最上层一定和接收着相同(对应)。
发送端同步操作的实现好像是在应用层等,驱动层在stack中记录了未完成的transaction t;




接收端从驱动读,驱动负责把发送端(原接收端)的BC_xxx 型的transaction转换成BR_xxx
发送端向驱动写,命令为BC_xxx
对每个transaction,不论是同步还是异步,驱动都会给“写驱动者”线程回一个BINDER_WORK_TRANSACTION_COMPLETE表示
刚才发送的BC_xxx“被驱动收到了”

发送写时:t.work.entry被链入target_list (优化后的thread->todo或proc->todo)
tcomplete.entry被链入当前线程thread->todo,是驱动回复原发送线程工作状态的


(服务线程)读时候,如果是同步transaction时,因为同步需要回复,所以要记录t于stack中





























全局变量:
static DEFINE_MUTEX(binder_lock); 全局锁
static HLIST_HEAD(binder_procs); 所有binder进程
static struct binder_node *binder_context_mgr_node;
sm进程的binder节点
static uid_t binder_context_mgr_uid = -1; sm的uid
static int binder_last_id;
static struct proc_dir_entry *binder_proc_dir_entry_root;proc文件系统根目录
static struct proc_dir_entry *binder_proc_dir_entry_proc;proc文件系统binder的proc目录
static struct hlist_head binder_dead_nodes; 死亡节点
static HLIST_HEAD(binder_deferred_list); 延时工作列表

static DEFINE_MUTEX(binder_deferred_lock); 延时工作列表锁






struct binder_proc{
struct hlist_node proc_node;   构成链表:把本proc连入全局proc hlist全局查找进程
struct rb_root threads;      本进程所有线程查找进程的线程
struct rb_root nodes;      本进程所有binder_node查找进程节点,以node进程空间地址ptr为key
struct rb_root refs_by_desc;   本进程所有以desc(handle)为索引的ref查找进程引用
struct rb_root refs_by_node;   本进程所有以binder_node内核地址为索引的ref查找进程引用
int pid;       本进程pid
struct vm_area_struct *vma;    
struct task_struct *tsk;       在open时赋值为current
struct files_struct *files;    用在传输的binder是文件handle时
struct hlist_node deferred_work_node;
int deferred_work;
void *buffer;
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;              最大线程数
int requested_threads;
int requested_threads_started;
int ready_threads;
long default_priority;        默认优先级
}






struct binder_ref {

int debug_id;
调试相关
struct rb_node rb_node_desc;
构成链表:连入进程refs_by_desc表进程查找引用
struct rb_node rb_node_node;
构成链表:连入进程refs_by_node表进程查找引用
struct hlist_node node_entry;
构成链表:连入节点struct hlist_head refs节点查找引用
struct binder_proc *proc;
所属进程
struct binder_node *node;
所属节点 本引用所引用的节点
uint32_t desc;
节点handle
int strong;
强引用计数
int weak;
弱引用计数
struct binder_ref_death *death;死亡通知相关


 };




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;和cookie构成本地binder指针与flat_binder_object中相同字段对应
void __user *cookie;
和ptr构成本地binder指针与flat_binder_object中相同字段对应


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;是否本节点有异步事务与async_todo字段相关
unsigned accept_fds : 1;是否接受文件
int min_priority : 8;
最小优先级
struct list_head async_todo;异步事务链表异步事务都在此


};








struct binder_buffer {


struct list_head entry; /* free and allocated entries by addesss */


struct rb_node rb_node; /* free entry by size or allocated entry */

/* by address */


unsigned free : 1;

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];


};
struct binder_transaction {


int debug_id;


struct binder_work work;

struct binder_thread *from;
struct binder_transaction *from_parent;
  构成链表,thread以前的stack
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;
uid_t sender_euid;


};
struct binder_transaction_data {


/* The first two are only used for bcTRANSACTION and brTRANSACTION,


* identifying the target and contents of the transaction.
*/


union {

size_t handle; /* target descriptor of command transaction */

void *ptr;/* target descriptor of return transaction */

} target;




void *cookie;/* target object cookie */

unsigned int code; /* transaction command */






/* General information about the transaction. */

unsigned int flags;
pid_t sender_pid;

uid_t sender_euid;


size_t data_size; /* data.buffer 中数据长度*/


size_t offsets_size; /* 指出有多少个offsets来标识多少个obj */





/* If this transaction is inline, the data immediately


* follows here; otherwise, it ends with a pointer to
 
* the data buffer.
*/


union {


struct {/* transaction data */


const void *buffer;


/* offsets from buffer to flat_binder_object structs */

const void *offsets;

} ptr;

uint8_t buf[8];

} data;


};
struct flat_binder_object {

/* 8 bytes for large_flat_header. */

unsigned long type;


unsigned long flags;





/* 8 bytes of data. */


union {

void *binder;/* local object */

signed long handle; /* remote object */


};


/* extra data associated with local object */

void *cookie;
};

















原创粉丝点击