一、初始化
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;
};
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;
};