该系列文章总纲链接:专题分纲目录 android 系统核心机制 binder
本章关键点总结 & 说明:
思维导图在系统核心机制binder这一部分中也是 持续不断迭代的,随着对binder的不断分析和讲解,导图内容也不断增多。这里主要关注➕ Binder驱动 部分即可。
本章节我们主要从驱动的角度来解读binder,首先要搞清楚的就是IPC通信机制,这就少不了基础知识,因此第一部分对基础做一个讲解,接下来对驱动的关键3大流程进行讲解,内容拷贝机制,最后谈谈transaction stack机制(数据传输时从源 发给 目标,目标怎么知道是 源发的呢)和server端的多线程(Binder的多线程到底是如何实现的,如何创建呢?这个是系统底层实现的)。
1 Binder,进程间的IPC通信机制
1.1 handle的含义
客户端表示为A,服务端表示为B
@1 handle表示客户端A对服务端B的引用(服务的引用),是binder IPC机制中对应驱动层的binder_ref
@2 handle对于每个不同的客户端进程A0、A1、A2,均指向服务端B的服务,但handle值不同。这类似于多个进程访问同一个文件,都会得到自己的文件描述符,但因为每个进程访问文件描述符的顺序不同而导致不同(虽然不同,但指向的是同一个文件)。如下图所示:
说明:图左边每个进程中的小球对应进程中的一个个handle(后面会讲到对应驱动中的binder_ref,而很多binder_ref可以对应一个binder_node),右边是不同进程文件描述符与文件之间的关系。
1.2 驱动层关键的结构体
这里主要对 驱动层的几个关键结构体进行说明,binder_ref表示的引用, binder_node表示实体,binder_proc表示所在进程, binder_thread表示处理线程;这些结构体也是binder驱动层 分析的重点。
@1 binder_ref代码如下:
struct binder_ref {
/* Lookups needed: */
/* node + proc => ref (transaction) */
/* desc + proc => ref (transaction, inc/dec ref) */
/* node => refs + procs (proc exit) */
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;//对应服务端的节点
uint32_t desc;
int strong;
int weak;
struct binder_ref_death *death;
};
如上所示,handle对应驱动层的binder_ref,表示binder_node(服务端的实体)的引用(对服务的引用),可以多对一。
@2 binder_node代码如下:
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;//多个binder_ref对应一个binder_node
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;
unsigned min_priority:8;
struct list_head async_todo;
};
binder_node表示服务的实体,通过binder_node,可以找到binder_proc(表示服务所在进程)。
@3 binder_proc代码如下:
struct binder_proc {
struct hlist_node proc_node;
struct rb_root threads;//多个线程
struct rb_root nodes;
struct rb_root refs_by_desc;
struct rb_root refs_by_node;
int pid;
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;
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 dentry *debugfs_entry;
};
binder_proc表示服务所在进程,一个进程可以有多个服务。同时包含红黑树结构的thread(每个thread对应一个binder_thread),一般会有多个客户端同时来获取服务,服务端会开启多个线程对这些请求进行处理。
@4 binder_thread代码如下:
struct binder_thread {
struct binder_proc *proc;
struct rb_node rb_node;
int pid;
int looper;
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_thread 来表示。
1.3 驱动层binder逻辑流程
这里对binder3个关键流程:注册服务、获取服务、使用服务来说明
图的说明:绿色对应服务端,紫色部分对应servicemanager,蓝色对应客户端(后面的图都是这个特点)
@1 注册服务
@2 获取服务
@3 使用服务
1.4 数据传输过程(客户端和服务端)
binder的客户端和服务端进行数据传输过程如下所示:
1.5 数据复制过程
@1 一般的IPC (比如socket)数据拷贝过程如下:
@2 binder关键数据拷贝过程如下:
@3 binder 数据拷贝的全貌(结构变量+数据)
在bctest.c中使用ioctl读或写时一定会传入一个结构体binder_write_read类型的变量,而binder中关键内容的内存拷贝则会采用mmap的方式来实现。
@4 数据的跨进程传递,只需要一次拷贝就可以完成的原理:当把同一块物理页面同时映射到进程空间和内核空间,这时在两者之间传递数据,只需要其中任意一方把数据拷贝到物理页面,另一方直接读取即可。
@5 总结:结构变量拷贝两次,内存buf拷贝与映射各一次。
2 binder驱动层的3个关键流程
这里首先对binder传递的数据进行说明,数据传递本身涉及的binder_io、binder_transaction_data、binder_write_read等结构体,但本质上传递的是binder_io结构体中的data数据,在bctest.c的svrmgr_publish中的代码中这里对binder_io数据进行初始化,代码如下所示:
int svcmgr_publish(struct binder_state *bs, void *target, const char *name, void *ptr)
{
unsigned status;
unsigned iodata[512/4];
struct binder_io msg, reply;
bio_init(&msg, iodata, sizeof(iodata), 4);//初始化binder_io结构体变量msg,偏移4个字节
/*
从这里开始向binder_io中写入数据,实际上是对binder_io中的data变量进行写入操作
这里先向binder_io结构体中写入4个字节的0
写入字符串SVC_MGR_NAME与name(均先写入长度,再写入字符串)
最后写入flat_binder_object,底层需要据此创建binder_node节点
*/
bio_put_uint32(&msg, 0); // strict mode header
bio_put_string16_x(&msg, SVC_MGR_NAME);
bio_put_string16_x(&msg, name);
bio_put_obj(&msg, ptr);
if (binder_call(bs, &msg, &reply, target, SVC_MGR_ADD_SERVICE))
return -1;
status = bio_get_uint32(&reply);
binder_done(bs, &msg, &reply);
return status;
}
这里涉及的三个函数bio_put_uint32(),bio_put_string16_x(),bio_put_obj()中实际都是在向binder_io结构体中的data变量写入数据,在驱动中也是通过data变量获取信息的,即binder_io中的data就是所谓的buffer,这里将代码
bio_put_uint32(&msg, 0); // strict mode header
bio_put_string16_x(&msg, SVC_MGR_NAME);
bio_put_string16_x(&msg, name);
bio_put_obj(&msg, ptr);
转换为图像的模式,让大家看的更明白一些,如下所示:
其中offs1 offs2 off3 off4分别表示一个地址值,存储的是flat_binder_object的地址值,最多可以存储四个,代码是通过内部逻辑确定具体有几个flat_binder_object的,这里不多描述。有了这个基础,接下来主要就是看binder的ioctl流程来对binder驱动有更深的了解了。
2.1 服务注册流程
@1 服务注册流程中ioctl命令交互流程
该部分是驱动中打印的ioctl 命令的交互流程,在驱动中打印,(下面交互图均如此),关键的命令流程如下所示:
@2 进程间通信的关键命令
从上面的图可知,在Binder的命令集中,只有BC_TRANSACTION、BC_REPLY、BR_TRANSACTION、BR_REPLY涉及两个进程之间的通信,其他所有的CMD都是APP与驱动之间的交互,用于改变/报告状态。抽象出来,进程间通信的关键信息如下:
@3 传递数据说明
注册服务时binder传递的关键数据是 android.os.IServiceManager(前缀)、hello(服务名称)、flat_binder_object(驱动据此创建一个binder_node节点),获得的回复数据为0时表示正常。
2.2 服务获取流程
@1 服务获取的ioctl 流程如下图所示:
@2 传递数据说明
获取服务时binder传递的关键数据是android.os.IServiceManager(前缀)与hello(服务名称),获得的回复数据是flat_binder_object (驱动据此创建一个binder_ref节点)
2.3 服务使用流程
@1 使用服务的ioctl 流程如下图所示:
@2 传递数据说明
使用服务时binder传递的关键数据是参数值(即传递的函数参数,对于一般的方法int say_hello_to(char* reference)来讲是一个字符串值),获得的回复数据是服务端函数执行后的返回值
2.4 总结及归纳
无论是注册服务过程还是获取服务,本质上都是一次进程间通信,关注黄色方块部分,都要有一次BC_TRANSACTION--->BR_TRANSACTION--->BC_REPLY--->BR_REPLY的过程,这个过程是最关键的,所谓注册服务,获取服务,使用服务就是三次进程间通信的过程。
第一次是服务端与ServiceManager之间通信,注册服务。
第二次是客户端与ServiceManager之间通信,获取服务。
第三次是客户端与服务端之间通信,使用服务。
3 transaction_stack机制解读
@1 具体的发送者和接收者是谁?
针对数据传输时,有多个线程的情况,这里关注两个问题:
发给谁(handle可以表示一个进程,那么那具体是该进程的哪个线程呢?)
说明:binder_proc下面有自己的todo链表,binder_thread下面也有自己的todo链表。
一般情况下是放在binder_proc中,之后唤醒等待于binder_proc.wait的空闲线程。
对于双向传输而言,则是则放在binder_thread.todo里面,唤醒该线程(为何是双向传输,通过transaction_stack来判断)
回复给谁(对于回复来讲,没有handle,那具体回复给具体哪一个进程或者进程下面的线程呢?)
回复过程中是没有handle表示进程的,那么必定有某个地方记录之前的发送者,这就是transaction_stack,对应的结构体是binder_transaction。
@2 binder_transaction一般情况下的传输流程
传输流程的详细过程如下:
忽略其中详细繁琐的细节,最核心传输的框架如下:
至此,针对一般情况,给出答案:
发送给谁:从客户端test_client发送到服务端test_server,有handle作为进程的标识。
回复给谁:从服务端test_server回复到客户端test_client有binder_transaction存储作为标识。
@3 binder_transaction双向传输的说明
考虑这样一种情景,具体如下:
进程P1提供S1服务,同时创建有T1-1,T1-2,T1-3几个线程,同理,进程P2,P3...。
此时,P1提供S1服务,需要P2提供的S2服务,而P2提供的S2服务需要P3提供的S3服务,而P3提供的S3服务需要P1提供的S1服务。那么,P1提供的S1服务会让自己的线程T1-1向P2提供的S2服务发出请求,T2-1处理请求,P2提供的S2服务会让自己的线程T2-1向P3提供的S3服务发出请求,T3-1处理请求,P3提供的S3服务会让自己的线程T3-1向P1提供的S1服务发出请求,此时是谁来处理呢?是正在等待的T1-1还是其他的线程T1-2或T1-3呢?,一般情况一定是使用一个新线程来做,这样方便,但为了优化资源,Binder的双向传输机制使用了T1-1而不是线程T1-2或T1-3。那么具体如何做呢?关键代码如下所示:
static void binder_transaction(struct binder_proc *proc , struct binder_thread *thread , struct binder_transaction_data *tr, int reply)
{
...
if (reply) {
...
} else {
...
if (!(tr->flags & TF_ONE_WAY) && thread->transaction_stack) {
struct binder_transaction *tmp;
tmp = thread->transaction_stack;
...
while (tmp) { //在这里会轮询查找链表上的项,如果满足双向传输机制,一定可以找到该线程并使用它
if (tmp->from && tmp->from->proc == target_proc)
target_thread = tmp->from;
tmp = tmp->from_parent;
}
}
}
...
}
此部分代码也是双向传输的核心,也是双向传输的判定关键点,如果判定为双向传输,则会按照如下所示的流程进行传输,如图:
同时TR1,TR2,TR3之间的关系如下所示:
T2.from_parent = T1.to_parent;
T3.from_parent = T2.to_parent;
T1.from_parent = T3.to_parent;
总结:双向通信设计的意义在于节省资源,流程上主要依赖于一般的传输流程(3@2)和进程通信的关键命令模型(2.1@2)
4 server的多线程分析
@1 何时创建,怎样创建
何时创建新线程?新当足够多的客户端同时向服务端发送请求,服务端忙不过来时,会创建新的线程来处理。
创建新线程的流程如下:
驱动判断服务端忙不过来
驱动向APP发送“创建新线程请求”
APP创建新线程
@2 驱动向APP发送“创建新线程请求”的条件
驱动中对应代码以及相关分析如下:
static int binder_thread_read(struct binder_proc *proc,
struct binder_thread *thread,
void __user *buffer, int size,
signed long *consumed, int non_block)
{
void __user *ptr = buffer + *consumed;
void __user *end = buffer + size;
...
done:
*consumed = ptr - buffer;
/*
条件解读如下:
proc->requested_threads:未处理的新线程请求数量为0
proc->ready_threads:空闲线程数为0
proc->requested_threads_started < proc->max_threads:已经启动的线程数量要少于max_threads
(thread->looper & (BINDER_LOOPER_STATE_REGISTERED |BINDER_LOOPER_STATE_ENTERED)):已经注册的binder线程或者主线程
*/
if (proc->requested_threads + proc->ready_threads == 0 &&
proc->requested_threads_started < proc->max_threads &&
(thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
BINDER_LOOPER_STATE_ENTERED)) /* the user-space code fails to */
/*spawn a new thread if we leave this out */) {
proc->requested_threads++;
if (put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer))
return -EFAULT;
}
return 0;
}
满足了以上条件,驱动才会给APP发送“创建新线程”的消息
@3 怎样基于原来的驱动来写APP
- 在进程中设置MAX_THREAD
- 在binder_parse中收到BR_SPAWN_LOOPER后在用户层创建新线程
- 新线程执行ioctl向binder驱动发出 BC_REGISTER_LOOPER,同时和主线程一样,进入一个循环处理数据
其中,第一步关键代码如下:
void binder_set_maxthreads(struct binder_state *bs, int threads)
{
ioctl(bs->fd, BINDER_SET_MAX_THREADS, &threads);
}
第二步的参考代码如下所示:
int binder_parse(struct binder_state *bs, struct binder_io *bio,
uintptr_t ptr, size_t size, binder_handler func)
{
int r = 1;
uintptr_t end = ptr + (uintptr_t) size;
while (ptr < end) {
uint32_t cmd = *(uint32_t *) ptr;
ptr += sizeof(uint32_t);
switch(cmd) {
case BR_NOOP:
break;
case BR_TRANSACTION_COMPLETE:
break;
case BR_INCREFS:
case BR_ACQUIRE:
case BR_RELEASE:
case BR_DECREFS:
ptr += sizeof(struct binder_ptr_cookie);
break;
case BR_SPAWN_LOOPER: {//关键部分代码实现,根据驱动发来的请求,创建一个新的线程
pthread_t thread;
struct binder_thread_desc btd;
btd.bs = bs;
btd.func = func;
pthread_create(&thread, NULL, binder_thread_routine, &btd);
break;
}
case BR_TRANSACTION: {
struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr;
if ((end - ptr) < sizeof(*txn)) {
ALOGE("parse: txn too small!\n");
return -1;
}
binder_dump_txn(txn);
if (func) {
unsigned rdata[256/4];
struct binder_io msg;
struct binder_io reply;
int res;
bio_init(&reply, rdata, sizeof(rdata), 4);
bio_init_from_txn(&msg, txn);
res = func(bs, txn, &msg, &reply);
binder_send_reply(bs, &reply, txn->data.ptr.buffer, res);
}
ptr += sizeof(*txn);
break;
}
case BR_REPLY: {
struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr;
if ((end - ptr) < sizeof(*txn)) {
ALOGE("parse: reply too small!\n");
return -1;
}
binder_dump_txn(txn);
if (bio) {
bio_init_from_txn(bio, txn);
bio = 0;
} else {
/* todo FREE BUFFER */
}
ptr += sizeof(*txn);
r = 0;
break;
}
case BR_DEAD_BINDER: {
struct binder_death *death = (struct binder_death *)(uintptr_t) *(binder_uintptr_t *)ptr;
ptr += sizeof(binder_uintptr_t);
death->func(bs, death->ptr);
break;
}
case BR_FAILED_REPLY:
r = -1;
break;
case BR_DEAD_REPLY:
r = -1;
break;
default:
ALOGE("parse: OOPS %d\n", cmd);
return -1;
}
}
return r;
}
第三步的参考代码如下所示:
void binder_thread_loop(struct binder_state *bs, binder_handler func)
{
int res;
struct binder_write_read bwr;
uint32_t readbuf[32];
bwr.write_size = 0;
bwr.write_consumed = 0;
bwr.write_buffer = 0;
readbuf[0] = BC_REGISTER_LOOPER;
binder_write(bs, readbuf, sizeof(uint32_t));//对于新创建的线程,需要通过ioctl注册到驱动中
for (;;) {
bwr.read_size = sizeof(readbuf);
bwr.read_consumed = 0;
bwr.read_buffer = (uintptr_t) readbuf;
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);//和主线程binder_loop类似,不断读取驱动中的数据
if (res < 0) {
ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
break;
}
res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);//解析数据并处理
if (res == 0) {
ALOGE("binder_loop: unexpected reply?!\n");
break;
}
if (res < 0) {
ALOGE("binder_loop: io error %d %s\n", res, strerror(errno));
break;
}
}
}
// struct binder_state *bs, binder_handler func
struct binder_thread_desc {
struct binder_state *bs;
binder_handler func;
};
static void * binder_thread_routine(struct binder_thread_desc *btd)
{
binder_thread_loop(btd->bs, btd->func);
return NULL;
}
总结下流程:底层binder_proc.wait没有空闲线程,忙不过来的时候,就会向上层APP发送BR_SPAWN_LOOPER 命令,上层daemon 收到命令后 在上层创建一个线程出来,注册到驱动层,并开始 和binder驱动进行交互。
5 驱动层部分源码解读
说明:这里的代码分析仅仅是为了帮助大家在看驱动个代码时有个参考
5.1 驱动层代码框架:
@1 关键文件所在路径:
kernel/drivers/android/binder.c
kernel/include/uapi/linux/android/binder.h
@2 关键的几个点要多注意分析:
binder_ioctl: 与用户空间进行数据交互,参数cmd区分不同的请求,BINDER_WRITE_READ表示读写数据。
binder_thread_write: copy_to_user服务,get_user从用户空间获取请求并发送或者返回用户空间进程产生的结果;该函数调用binder_transaction函数:转发请求并返回结果,当收到请求时,binder_transcation函数通过对象handle找到对象所在进程。将请求放到目标进程的队列中,等待目标进程读取。
- 数据的解析工作在binder_parse()中实现。
- binder_thread_read:copy_from_user 服务。 put_user读取结果。
Binder主要通过ioctl命令对底层进行调用,不会直接用read和write对其进行操作
@3 misc 设备结构
binder对应的设备节点是/dev/binder;属于misc设备;对应的misc设备结构和实现的file_operations如下代码所示:
static struct miscdevice binder_miscdev = {
.minor = MISC_DYNAMIC_MINOR, //动态获得主设备号
.name = "binder", //设备名
.fops = &binder_fops //file_oprations
};
static const struct file_operations binder_fops = {//fops实现的几个操作
.owner = THIS_MODULE,
.poll = binder_poll,
.unlocked_ioctl = binder_ioctl,
.compat_ioctl = binder_ioctl,
.mmap = binder_mmap,
.open = binder_open,
.flush = binder_flush,
.release = binder_release,
};
@4 接下来一个部分会专门针对 驱动代码部分函数进行分析,分析思路如下:
binder_init->fops()
binder_open
binder_release
binder_mmap
binder_update_page_range
binder_ioctl
binder_ioctl_set_ctx_mgr
binder_new_node
binder_ioctl_write_read
binder_thread_write *
binder_thread_read *
binder_free_thread
binder_release_work
binder_flash
binder_poll
binder_get_thread
binder_has_proc_work
binder_has_thread_work
5.2 代码分析
@1 binder_init分析
static int __init binder_init(void)
{
int ret;
#ifdef BINDER_MONITOR
struct task_struct *th;
th = kthread_create(binder_bwdog_thread, NULL, "binder_watchdog");
if (IS_ERR(th)) {
pr_err("fail to create watchdog thread "
"(err:%li)\n", PTR_ERR(th));
} else {
wake_up_process(th);
}
binder_transaction_log_failed.entry = &entry_failed[0];
binder_transaction_log_failed.size = ARRAY_SIZE(entry_failed);
binder_transaction_log.entry = &entry_t[0];
binder_transaction_log.size = ARRAY_SIZE(entry_t);
#endif
binder_deferred_workqueue = create_singlethread_workqueue("binder");
if (!binder_deferred_workqueue)
return -ENOMEM;
/*创建debugfs的设备树和设备节点,主要用于debugfs start*/
binder_debugfs_dir_entry_root = debugfs_create_dir("binder", NULL);
if (binder_debugfs_dir_entry_root)
binder_debugfs_dir_entry_proc = debugfs_create_dir("proc",
binder_debugfs_dir_entry_root);
ret = misc_register(&binder_miscdev);
if (binder_debugfs_dir_entry_root) {
debugfs_create_file("state",S_IRUGO,binder_debugfs_dir_entry_root,NULL,&binder_state_fops);
debugfs_create_file("stats",S_IRUGO,binder_debugfs_dir_entry_root, NULL,&binder_stats_fops);
debugfs_create_file("transactions",S_IRUGO,binder_debugfs_dir_entry_root, NULL,&binder_stats_fops);
debugfs_create_file("transaction_log",S_IRUGO,binder_debugfs_dir_entry_root,&binder_transaction_log,&binder_stats_fops);
debugfs_create_file("failed_transaction_log",S_IRUGO,binder_debugfs_dir_entry_root,&binder_transaction_log_failed,&binder_transaction_log_fops);
#ifdef BINDER_MONITOR
debugfs_create_file("transaction_log_enable",(S_IRUGO | S_IWUSR | S_IWGRP), binder_debugfs_dir_entry_root,NULL,&binder_transaction_log_enable_fops);
debugfs_create_file("log_level",(S_IRUGO | S_IWUSR | S_IWGRP),binder_debugfs_dir_entry_root,NULL,&binder_log_level_fops);
debugfs_create_file("timeout_log",S_IRUGO,binder_debugfs_dir_entry_root,&binder_timeout_log_t,&binder_timeout_log_fops);
#endif
#ifdef BINDER_PERF_EVAL
debugfs_create_file("perf_evalue",(S_IRUGO | S_IWUSR | S_IWGRP),binder_debugfs_dir_entry_root,NULL,&binder_perf_evalue_fops);
debugfs_create_file("perf_stats",S_IRUGO,binder_debugfs_dir_entry_root,NULL,&binder_perf_stats_fops);
#endif
#ifdef MTK_BINDER_PAGE_USED_RECORD
debugfs_create_file("page_used",S_IRUGO,binder_debugfs_dir_entry_root,NULL,&binder_page_used_fops);
#endif
}
/*创建debugfs的设备树和设备节点,主要用于debugfs end*/
return ret;
}
这里主要是binder初始化相关的操作,关键点 关注➕ 注释即可
@2 binder_open分析
static int binder_open(struct inode *nodp, struct file *filp)
{
struct binder_proc *proc; //保存调用Binder的各个进程和线程的信息,进程线程ID,Binder状态等,以便于其他文件操作可以引用
binder_debug(BINDER_DEBUG_OPEN_CLOSE, "binder_open: %d:%d\n",current->group_leader->pid, current->pid);
proc = kzalloc(sizeof(*proc), GFP_KERNEL); //kzalloc效果等同于先是用kmalloc()申请空间 , 然后用memset()来初始化 ,申请元素都被初始化为0
if (proc == NULL)
return -ENOMEM;
get_task_struct(current); //增加引用计数,获取当前进程的task信息,在Linux中current就是指的当前进程
proc->tsk = current; //保持引用计数,将当前进程task信息保存在binder_proc的tsk成员中
INIT_LIST_HEAD(&proc->todo); /初始化todo队列,binder驱动在接收到IPC数据后,会将要执行的任务保存到todo队列中
init_waitqueue_head(&proc->wait); //初始化待机队列以便将打开binder驱动的进程切换到待机状态
proc->default_priority = task_nice(current); //将当前进程的优先级保存到binder_proc的default_priority成员中
#ifdef RT_PRIO_INHERIT
proc->default_rt_prio = current->rt_priority;
proc->default_policy = current->policy;
#endif
binder_lock(__func__);
binder_stats_created(BINDER_STAT_PROC); //增加BINDER_STAT_PROC
hlist_add_head(&proc->proc_node, &binder_procs); //将当前创建的binder_proc挂载到全局链表binder_procs中,这样就可以通过binder_procs查看所有打开binder驱动的进程
proc->pid = current->group_leader->pid; //设置binder_proc的pid信息
INIT_LIST_HEAD(&proc->delivered_death); //初始化delivered_death链表
filp->private_data = proc;//将当前创建的binder_proc注册到file结构体的private_data成员变量中,以便在binder驱动的其他操作中可以直接取出binder_proc结构体
binder_unlock(__func__);
if (binder_debugfs_dir_entry_proc) {//创建proc/binder/proc/$pid文件,在该目录下生成文件显示Binder IPC相关信息
char strbuf[11];
snprintf(strbuf, sizeof(strbuf), "%u", proc->pid);
proc->debugfs_entry = debugfs_create_file(strbuf, S_IRUGO,
binder_debugfs_dir_entry_proc, proc, &binder_proc_fops);
}
return 0;
}
binder_open的逻辑步骤如下:
- 创建并分配一个binder_proc空间来保持binder数据。增加当前进程/线程的引用计数,并保存到binder_proc的tsk。
- 初始化binder_proc队列,包括使用INIT_LIST_HEAD初始化链表头todo,使用init_waitqueue_head初始化等待序列wait,设置默认优先级为当前进程的nice值。
- 增加BINDER_STAT_PROC的对象计数,通过hlist_add_head把创建的binder_proc对象添加到全局binder_proc哈希表中,这样任何进程都可以访问其他进程的binder_proc对象了。
- 将当前线程组的pid赋值给proc的pid字段,即thread_group指向线程组中的第一个线程的task_struct结构,同时把创建的binder_proc对象指针赋值给flip的private_data对象。
- 创建文件/proc/binder/proc/$pid,用来输出当前binder proc对象的状态,文件名以pid命名。Pid不是当前进程/线程的id,而是线程组的pid,即线程组中第一个线程的pid。另外,创建该文件时,也指定了操作该文件的函数接口为binder_read_proc参数即创建的binder_proc对像proc。
@3 binder_release分析
static int binder_release(struct inode *nodp, struct file *filp)
{
struct binder_proc *proc = filp->private_data; //与opne对应,获取proc指针
debugfs_remove(proc->debugfs_entry); //通过proc获取进程,线程的pid, 然后删除/proc/binder/proc/$pid文件
binder_defer_work(proc, BINDER_DEFERRED_RELEASE); //释放binder_proc对象的数据和分配的空间
return 0;
}
@4 binder_mmap分析
//特殊说明1:
struct vm_area_struct表示的地址空间范围是0~3G,而struct vm_struct表示的地址空间范围是(3G + 896M + 8M) ~ 4G。
struct vm_struct表示的地址空间范围为什么不是3G~4G呢?
3G ~ (3G + 896M)范围的地址是用来映射连续的物理页面的,这个范围的虚拟地址和对应的实际物理地址有着简单的对应关系,即对应0~896M的物理地址空间,
而(3G + 896M) ~ (3G + 896M + 8M)是安全保护区域(例如,所有指向这8M地址空间的指针都是非法的),
因此struct vm_struct使用(3G + 896M + 8M) ~ 4G地址空间来映射非连续的物理页面。
//特殊说明2:
为什么会同时使用进程虚拟地址空间和内核虚拟地址空间来映射同一个物理页面呢?
同一个物理页面,一方映射到进程虚拟地址空间,一方映射到内核虚拟地址空间,这样,进程和内核之间就可以减少一次内存拷贝了,提到了进程间通信效率。
举个例子如,Client要将一块内存数据传递给Server,一般的做法是,Client将这块数据从它的进程空间拷贝到内核空间中,然后内核再将这个数据从内核空间拷贝到Server的进程空间,这样,Server就可以访问这个数据了。但是在这种方法中,执行了两次内存拷贝操作,而采用我们上面提到的方法,只需要把Client进程空间的数据拷贝一次到内核空间,然后Server与内核共享这个数据就可以了,整个过程只需要执行一次内存拷贝,提高了效率。
代码分析如下:
//mmap把设备内存(内核空间)映射到用户空间,从而用户程序可以在用户空间操作内核空间的地址;
//binder_mmap能映射的最大内存空间为4M,而且不能映射具有写权限的内存区域;
//binder_mmap现在内核虚拟映射表上获取一块可以使用的区域,然后分配物理页,再把物理页映射到虚拟映射表上;
//由于设备内存的映射在mmap中实现,因此每个进程/线程都只能执行一次映射操作。
static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
{
//struct vm_area_struct表示用户空间进程虚拟地址,地址空间范围是0~3G
//struct vm_struct表示内核空间虚拟地址,地址空间范围是(3G + 896M + 8M) ~ 4G
//它们对应的物理页面都可以是不连续的。
int ret;
struct vm_struct *area; //定义描述即将分配的内核缓存区的变量
//通过filp->private_data得到在打开设备文件/dev/binder时创建的struct binder_proc结构
struct binder_proc *proc = filp->private_data;
const char *failure_string;
struct binder_buffer *buffer;
if (proc->tsk != current)
return -EINVAL;
/× ========进程虚拟地址空间的设置=========== ×/
//判断进程虚拟地址大小是否大于4M,如果大于则设置为4M
if ((vma->vm_end - vma->vm_start) > SZ_4M)
vma->vm_end = vma->vm_start + SZ_4M;
binder_debug(BINDER_DEBUG_OPEN_CLOSE,
"binder_mmap: %d %lx-%lx (%ld K) vma %lx pagep %lx\n",
proc->pid, vma->vm_start, vma->vm_end,
(vma->vm_end - vma->vm_start) / SZ_1K, vma->vm_flags,
(unsigned long)pgprot_val(vma->vm_page_prot));
//判断进程虚拟地址空间是否被设置为禁止映射
if (vma->vm_flags & FORBIDDEN_MMAP_FLAGS) {
ret = -EPERM;
failure_string = "bad vm_flags";
goto err_bad_arg;
}
//设置进程虚拟地址空间的标志位
vma->vm_flags = (vma->vm_flags | VM_DONTCOPY) & ~VM_MAYWRITE;
mutex_lock(&binder_mmap_lock);
/× ========内核虚拟地址空间的分配========= ×/
//判断内核缓存区在内核虚拟地址空间的起始值是否等于NULL,即判断是否已经mmap过
if (proc->buffer) {
ret = -EBUSY;
failure_string = "already mapped";
goto err_already_mapped;
}
area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP);//在内核虚拟地址空间中申请分配指定大小的内存
if (area == NULL) {
ret = -ENOMEM;
failure_string = "get_vm_area";
goto err_get_vm_area_failed;
}
proc->buffer = area->addr; //保存内核虚拟地址空间的起始值
proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer;//计算进程虚拟地址起始值与内核虚拟地址的起始值之间的偏移
mutex_unlock(&binder_mmap_lock);
#ifdef CONFIG_CPU_CACHE_VIPT
if (cache_is_vipt_aliasing()) {
while (CACHE_COLOUR((vma->vm_start ^ (uint32_t)proc->buffer))) {
pr_info("binder_mmap: %d %lx-%lx maps %p bad alignment\n", proc->pid, vma->vm_start, vma->vm_end, proc->buffer);
vma->vm_start += PAGE_SIZE;
}
}
#endif
//分配描述所有物理页面的数组,一个物理页面用page来表示,缓存区所需物理页面个数为((vma->vm_end - vma->vm_start) / PAGE_SIZE),所以
//描述所有物理页面信息需要sizeof(proc->pages[0]) * ((vma->vm_end - vma->vm_start) / PAGE_SIZE)大小的存储空间
proc->pages = kzalloc(sizeof(proc->pages[0]) * ((vma->vm_end - vma->vm_start) / PAGE_SIZE), GFP_KERNEL);
//判断描述物理页面的数组起始地址是否等于NULL
if (proc->pages == NULL) {
ret = -ENOMEM;
failure_string = "alloc page array";
goto err_alloc_pages_failed;
}
proc->buffer_size = vma->vm_end - vma->vm_start; //计算分配的缓存区大小
vma->vm_ops = &binder_vm_ops;//注册进程虚拟地址空间的操作函数
vma->vm_private_data = proc; //将当前进程的binder_proc注册到虚拟地址空间描述符的vm_private_data中
/× =====分配实际的物理页面并同时映射到进行虚拟地址空间和内核虚拟地址空间中====== ×/
//为虚拟地址空间proc->buffer ~ proc->buffer + PAGE_SIZE分配一个空闲的物理页面
if (binder_update_page_range(proc, 1, proc->buffer, proc->buffer + PAGE_SIZE, vma)) {//真正开始分配物理空间
ret = -ENOMEM;
failure_string = "alloc small buf";
goto err_alloc_small_buf_failed;
}
//这段地址空间使用一个binder_buffer来描述,分别插入到proc->buffers链表和proc->free_buffers红黑树中去
buffer = proc->buffer;
INIT_LIST_HEAD(&proc->buffers);
list_add(&buffer->entry, &proc->buffers);
buffer->free = 1;
binder_insert_free_buffer(proc, buffer);
proc->free_async_space = proc->buffer_size / 2;
barrier();
proc->files = get_files_struct(current);
proc->vma = vma;
proc->vma_vm_mm = vma->vm_mm;
return 0;
err_alloc_small_buf_failed:
kfree(proc->pages);
proc->pages = NULL;
err_alloc_pages_failed:
mutex_lock(&binder_mmap_lock);
vfree(proc->buffer);
proc->buffer = NULL;
err_get_vm_area_failed:
err_already_mapped:
mutex_unlock(&binder_mmap_lock);
err_bad_arg:
pr_err("binder_mmap: %d %lx-%lx %s failed %d\n",
proc->pid, vma->vm_start, vma->vm_end, failure_string, ret);
return ret;
}
总结binder_mmap顺序:
- 检测内存映射条件,即映射内存大小(4MB),flags,是否已经映射过;
- 获取地址空间,并把此空间的地址记录到进程信息buffer中;
- 分配物理页面并记录下来;
- 将buffer插入到进程信息的buffer列表中;
- 调用buffer_update_page_range函数将分配的物理页面和vm空间对应起来;
- 通过binder_insert_free_buffer函数把此进程的buffer插入到进程信息中。
其中,binder_update_page_range的实现如下所示:
//参数allocate用于区分是分配物理页面还是释放物理页面,1为分配物理页面,0为释放物理页面
static int binder_update_page_range(struct binder_proc *proc, int allocate,void *start, void *end,struct vm_area_struct *vma){
void *page_addr;
unsigned long user_page_addr;
struct vm_struct tmp_area;
struct page **page;
struct mm_struct *mm;
binder_debug(BINDER_DEBUG_BUFFER_ALLOC,"%d: %s pages %p-%p\n", proc->pid,allocate ? "allocate" : "free", start, end);
if (end <= start)
return 0;
trace_binder_update_page_range(proc, allocate, start, end);
if (vma)
mm = NULL;
else
mm = get_task_mm(proc->tsk);
if (mm) {
down_write(&mm->mmap_sem);
vma = proc->vma;
if (vma && mm != proc->vma_vm_mm) {
pr_err("%d: vma mm and task mm mismatch\n",
proc->pid);
vma = NULL;
}
}
//如果allocate等于0,释放物理页面
if (allocate == 0)
goto free_range;
if (vma == NULL) {
pr_err("%d: binder_alloc_buf failed to map pages in userspace, no vma\n",proc->pid);
goto err_no_vma;
}
//循环分配PAGE_SIZE大小的物理页面
for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {
int ret;
struct page **page_array_ptr;
//计算当前分配的物理页在物理页面数组pages中的索引号(page_addr - proc->buffer) / PAGE_SIZE
page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE];
BUG_ON(*page);
//分配物理页面
*page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO);//关键步骤1:alloc_page 分配页面
if (*page == NULL) {
pr_err("%d: binder_alloc_buf failed for page at %p\n",
proc->pid, page_addr);
goto err_alloc_page_failed;
}
#ifdef MTK_BINDER_PAGE_USED_RECORD
binder_page_used++;
proc->page_used++;
if(binder_page_used > binder_page_used_peak)
binder_page_used_peak = binder_page_used;
if (proc->page_used > proc->page_used_peak)
proc->page_used_peak = proc->page_used;
#endif
//映射物理页面到内核虚拟地址空间中,利用page初始化struct vm_struct tmp_area结构体
tmp_area.addr = page_addr;
tmp_area.size = PAGE_SIZE + PAGE_SIZE /* guard page? */;
page_array_ptr = page;
//将这个物理页面page插入到tmp_area描述的内核空间
ret = map_vm_area(&tmp_area, PAGE_KERNEL, &page_array_ptr); //关键步骤2:map_vm_area 为分配的内存做映射关系
if (ret) {
pr_err("%d: binder_alloc_buf failed to map page at %p in kernel\n",
proc->pid, page_addr);
goto err_map_kernel_failed;
}
//根据内核虚拟地址与进程虚拟地址之间的偏移计算该物理页在进程中的虚拟地址空间值
user_page_addr = (uintptr_t)page_addr + proc->user_buffer_offset;
//将这个物理页面插入到进程地址空间
ret = vm_insert_page(vma, user_page_addr, page[0]); //关键步骤3:vm_insert_page 把分配的物理页插入到用户VMA区域
if (ret) {
pr_err("%d: binder_alloc_buf failed to map page at %lx in userspace\n",
proc->pid, user_page_addr);
goto err_vm_insert_page_failed;
}
/* vm_insert_page does not seem to increment the refcount */
}
if (mm) {
up_write(&mm->mmap_sem);
mmput(mm);
}
return 0;
free_range:
for (page_addr = end - PAGE_SIZE; page_addr >= start;
page_addr -= PAGE_SIZE) {
page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE];
if (vma)
zap_page_range(vma, (uintptr_t)page_addr +
proc->user_buffer_offset, PAGE_SIZE, NULL);
err_vm_insert_page_failed:
unmap_kernel_range((unsigned long)page_addr, PAGE_SIZE);
err_map_kernel_failed:
__free_page(*page);
*page = NULL;
#ifdef MTK_BINDER_PAGE_USED_RECORD
if(binder_page_used > 0)
binder_page_used--;
if (proc->page_used > 0)
proc->page_used--;
#endif
err_alloc_page_failed:
;
}
err_no_vma:
if (mm) {
up_write(&mm->mmap_sem);
mmput(mm);
}
return -ENOMEM;
}
@5 binder_ioctl分析
binder ioctl中的命令有以下5个,定义在binder.h中,如下所示:
#define BINDER_WRITE_READ _IOWR('b',1, struct binder_write_read)
#define BINDER_SET_MAX_THREADS _IOW('b', 5, size_t)
#define BINDER_SET_CONTEXT_MGR _IOW('b', 7, int)
#define BINDER_THREAD_EXIT _IOW('b', 8, int)
#define BINDER_VERSION _IOWR('b',9, struct binder_version)
接下来对ioctl的分析主要是对这5个命令的分析,代码如下:
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret;
//通过filp->private_data得到在打开设备文件/dev/binder时创建的struct binder_proc结构
struct binder_proc *proc = filp->private_data;
struct binder_thread *thread;
unsigned int size = _IOC_SIZE(cmd);
void __user *ubuf = (void __user *)arg;
/*pr_info("binder_ioctl: %d:%d %x %lx\n", proc->pid, current->pid, cmd, arg);*/
trace_binder_ioctl(cmd, arg);
ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
if (ret)
goto err_unlocked;
binder_lock(__func__);
//从binder_proc中取出binder_thread线程描述符
thread = binder_get_thread(proc);
if (thread == NULL) {
ret = -ENOMEM;
goto err;
}
//处理不同的binder命令
switch (cmd) {
case BINDER_WRITE_READ:
ret = binder_ioctl_write_read(filp, cmd, arg, thread);
if (ret)
goto err;
break;
case BINDER_SET_MAX_THREADS:
if (copy_from_user(&proc->max_threads, ubuf, sizeof(proc->max_threads))) {
ret = -EINVAL;
goto err;
}
break;
case BINDER_SET_CONTEXT_MGR://若一个进程/线程能被成功设置成binder_context_mgr_node对象,称该进程/线程为Context Manager.即设置驱动中的全局变量binder_context_mgr_uid为当前进程的uid.并初始化一个Binder_node并赋值给全局变量binder_context_mgr_node
//只有创建binder_context_mgr_node对象的Binder上下文管理进程/线程才有权限重新设置这个对象
ret = binder_ioctl_set_ctx_mgr(filp, thread);
if (ret)
goto err;
break;
case BINDER_THREAD_EXIT:
binder_debug(BINDER_DEBUG_THREADS, "%d:%d exit\n",proc->pid, thread->pid);
binder_free_thread(proc, thread);
thread = NULL;
break;
case BINDER_VERSION: {
struct binder_version __user *ver = ubuf;
if (size != sizeof(struct binder_version)) {
ret = -EINVAL;
goto err;
}
if (put_user(BINDER_CURRENT_PROTOCOL_VERSION,
&ver->protocol_version)) {
ret = -EINVAL;
goto err;
}
break;
}
default:
ret = -EINVAL;
goto err;
}
ret = 0;
err:
//设置binder线程状态
if (thread)
thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN;
binder_unlock(__func__);
wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
if (ret && ret != -ERESTARTSYS)
pr_info("%d:%d ioctl %x %lx returned %d\n", proc->pid, current->pid, cmd, arg, ret);
err_unlocked:
trace_binder_ioctl_done(ret);
return ret;
}
@@5.1 从命令BINDER_SET_CONTEXT_MGR开始分析:其中,binder_ioctl_set_ctx_mgr的实现如下所示:
static int binder_ioctl_set_ctx_mgr(struct file *filp, struct binder_thread *thread)
{
int ret = 0;
struct binder_proc *proc = filp->private_data;
kuid_t curr_euid = current_euid();
//binder_context_mgr_node是一个全局变量,首先判断该变量是否为空以验证是否已经为ServiceManager进程创建了binder实体对象
if (binder_context_mgr_node != NULL) {
pr_err("BINDER_SET_CONTEXT_MGR already set\n");
ret = -EBUSY;
goto out;
}
ret = security_binder_set_context_mgr(proc->tsk);
if (ret < 0)
goto out;
//binder_context_mgr_uid也是一个全局变量,判断是否与当前ServiceManager进程的uid相同
if (uid_valid(binder_context_mgr_uid)) {
if (!uid_eq(binder_context_mgr_uid, curr_euid)) {
pr_err("BINDER_SET_CONTEXT_MGR bad uid %d != %d\n",
from_kuid(&init_user_ns, curr_euid),
from_kuid(&init_user_ns,
binder_context_mgr_uid));
ret = -EPERM;
goto out;
}
} else {
//设置ServiceManager进程的uid到binder_context_mgr_uid全局变量中
binder_context_mgr_uid = curr_euid;
}
//为当前ServiceManager进程创建一个binder实体对象,并保存到binder_context_mgr_node全局变量中
binder_context_mgr_node = binder_new_node(proc, 0, 0);
if (binder_context_mgr_node == NULL) {
ret = -ENOMEM;
goto out;
}
#ifdef BINDER_MONITOR
strcpy(binder_context_mgr_node->name, "servicemanager");
pr_debug("%d:%d set as servicemanager uid %d\n",
proc->pid, thread->pid, binder_context_mgr_uid);
#endif
//初始化binder_node节点数据
binder_context_mgr_node->local_weak_refs++;
binder_context_mgr_node->local_strong_refs++;
binder_context_mgr_node->has_strong_ref = 1;
binder_context_mgr_node->has_weak_ref = 1;
out:
return ret;
}
在binder_ioctl_set_ctx_mgr中,binder_new_node的实现如下所示:
static struct binder_node *binder_new_node(struct binder_proc *proc,binder_uintptr_t ptr,binder_uintptr_t cookie)
{
struct rb_node **p = &proc->nodes.rb_node;
struct rb_node *parent = NULL;
struct binder_node *node;
//从进程描述符binder_proc的nodes红黑树中查找是否存在指定的binder实体对象,即查找第一个叶节点
while (*p) {
parent = *p;
node = rb_entry(parent, struct binder_node, rb_node);
if (ptr < node->ptr)
p = &(*p)->rb_left;
else if (ptr > node->ptr)
p = &(*p)->rb_right;
else
return NULL;
}
node = kzalloc(sizeof(*node), GFP_KERNEL);//创建binder_node节点
if (node == NULL)
return NULL;
binder_stats_created(BINDER_STAT_NODE);
rb_link_node(&node->rb_node, parent, p); //插入节点操作1
rb_insert_color(&node->rb_node, &proc->nodes); //插入节点操作2
/*node初始化数据,start*/
node->debug_id = ++binder_last_id;
node->proc = proc;
node->ptr = ptr;
node->cookie = cookie;
node->work.type = BINDER_WORK_NODE;
/*node初始化数据,end*/
INIT_LIST_HEAD(&node->work.entry); //初始化链表头
INIT_LIST_HEAD(&node->async_todo);
binder_debug(BINDER_DEBUG_INTERNAL_REFS,
"%d:%d node %d u%016llx c%016llx created\n",
proc->pid, current->pid, node->debug_id,
(u64)node->ptr, (u64)node->cookie);
return node;
}
binder_new_node的说明:binder_proc的成员node是binder_node的根节点,这是一颗红黑树,该函数首先根据规则找到第一个叶节点作为新插入的节点的父节点,然后创建binder_node节点并插入。
@@5.2 从命令BINDER_WRITE_READ开始分析:其中binder_ioctl_write_read是关键,函数实现如下:
static int binder_ioctl_write_read(struct file *filp,unsigned int cmd, unsigned long arg,struct binder_thread *thread)
{
int ret = 0;
struct binder_proc *proc = filp->private_data;
unsigned int size = _IOC_SIZE(cmd);
void __user *ubuf = (void __user *)arg;
struct binder_write_read bwr;
if (size != sizeof(struct binder_write_read)) { //判断数据完整性
ret = -EINVAL;
goto out;
}
if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {//从用户空间复制数据到binder_write_read中
ret = -EFAULT;
goto out;
}
binder_debug(BINDER_DEBUG_READ_WRITE,
"%d:%d write %lld at %016llx, read %lld at %016llx\n",
proc->pid, thread->pid,
(u64)bwr.write_size, (u64)bwr.write_buffer,
(u64)bwr.read_size, (u64)bwr.read_buffer);
if (bwr.write_size > 0) { //执行写操作
ret = binder_thread_write(proc, thread,
bwr.write_buffer,
bwr.write_size,
&bwr.write_consumed);
trace_binder_write_done(ret);
if (ret < 0) {
bwr.read_consumed = 0;
if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
ret = -EFAULT;
goto out;
}
}
if (bwr.read_size > 0) { //执行读操作
ret = binder_thread_read(proc, thread, bwr.read_buffer,
bwr.read_size,
&bwr.read_consumed,
filp->f_flags & O_NONBLOCK);
trace_binder_read_done(ret);
if (!list_empty(&proc->todo)){
if (thread->proc != proc) {
int i;
unsigned int *p;
printk(KERN_ERR "binder: "
"thread->proc != proc\n");
printk(KERN_ERR "binder: thread %p\n",
thread);
p = (unsigned int *)thread - 32;
for (i = -4; i <= 3; i++, p+=8) {
printk(KERN_ERR "%p %08x %08x "
"%08x %08x %08x %08x "
"%08x %08x\n",
p, *(p), *(p+1), *(p+2),
*(p+3), *(p+4), *(p+5),
*(p+6), *(p+7));
}
printk(KERN_ERR "binder: thread->proc "
"%p\n", thread->proc);
p = (unsigned int *)thread->proc - 32;
for (i = -4; i <= 5; i++, p+=8) {
printk(KERN_ERR "%p %08x %08x "
"%08x %08x %08x %08x "
"%08x %08x\n",
p, *(p), *(p+1), *(p+2),
*(p+3), *(p+4), *(p+5),
*(p+6), *(p+7));
}
printk(KERN_ERR "binder: proc %p\n",
proc);
p = (unsigned int *)proc - 32;
for (i = -4; i <= 5; i++, p+=8) {
printk(KERN_ERR "%p %08x %08x "
"%08x %08x %08x %08x "
"%08x %08x\n",
p, *(p), *(p+1), *(p+2),
*(p+3), *(p+4), *(p+5),
*(p+6), *(p+7));
}
BUG();
}
wake_up_interruptible(&proc->wait);
}
if (ret < 0) {
if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
ret = -EFAULT;
goto out;
}
}
binder_debug(BINDER_DEBUG_READ_WRITE,
"%d:%d wrote %lld of %lld, read return %lld of %lld\n",
proc->pid, thread->pid,
(u64)bwr.write_consumed, (u64)bwr.write_size,
(u64)bwr.read_consumed, (u64)bwr.read_size);
if (copy_to_user(ubuf, &bwr, sizeof(bwr))) { //将数据复制到用户空间
ret = -EFAULT;
goto out;
}
out:
return ret;
}
在binder_ioctl_write_read中,最关键的两个处理函数是binder_thread_read和binder_thread_write,其中两个函数实现如下:
@3 从命令BINDER_THREAD_EXIT开始分析:其中binder_free_thread是关键,函数实现如下:
static int binder_free_thread(struct binder_proc *proc,struct binder_thread *thread)
{
struct binder_transaction *t;
struct binder_transaction *send_reply = NULL;
int active_transactions = 0;
rb_erase(&thread->rb_node, &proc->threads);//将当前线程从红黑树上删除
t = thread->transaction_stack; //取得binder_transaction数据
if (t && t->to_thread == thread)//判断要释放的是否是“回复”进程
send_reply = t;
while (t) { //释放所有的binder_transaction
active_transactions++;
binder_debug(BINDER_DEBUG_DEAD_TRANSACTION,
"release %d:%d transaction %d %s, still active\n",
proc->pid, thread->pid,
t->debug_id,
(t->to_thread == thread) ? "in" : "out");
#ifdef MTK_BINDER_DEBUG
pr_err("%d: %p from %d:%d to %d:%d code %x flags %x "
"pri %ld r%d "
#ifdef BINDER_MONITOR
"start %lu.%06lu"
#endif
,
t->debug_id, t,
t->from ? t->from->proc->pid : 0,
t->from ? t->from->pid : 0,
t->to_proc ? t->to_proc->pid : 0,
t->to_thread ? t->to_thread->pid : 0,
t->code, t->flags, t->priority, t->need_reply
#ifdef BINDER_MONITOR
, (unsigned long)t->timestamp.tv_sec,
(t->timestamp.tv_nsec / NSEC_PER_USEC)
#endif
);
#endif
if (t->to_thread == thread) {
t->to_proc = NULL;
t->to_thread = NULL;
if (t->buffer) {
t->buffer->transaction = NULL;
t->buffer = NULL;
}
t = t->to_parent;
} else if (t->from == thread) {
t->from = NULL;
t = t->from_parent;
} else
BUG();
}
if (send_reply) //如果需要则发送失败回复
binder_send_failed_reply(send_reply, BR_DEAD_REPLY);
binder_release_work(&thread->todo);//释放binder_work
kfree(thread); //释放binder_thread
binder_stats_deleted(BINDER_STAT_THREAD);//改变binder状态
return active_transactions;
}
在binder_free_thread中,对binder_release_work做简要分析,它的函数实现如下所示:
static void binder_release_work(struct list_head *list)
{
struct binder_work *w;
while (!list_empty(list)) {
w = list_first_entry(list, struct binder_work, entry);
list_del_init(&w->entry);
switch (w->type) {
case BINDER_WORK_TRANSACTION: {
struct binder_transaction *t;
t = container_of(w, struct binder_transaction, work);
if (t->buffer->target_node &&
!(t->flags & TF_ONE_WAY)) {
binder_send_failed_reply(t, BR_DEAD_REPLY);
} else {
binder_debug(BINDER_DEBUG_DEAD_TRANSACTION,
"undelivered transaction %d\n",
t->debug_id);
t->buffer->transaction = NULL;
#ifdef BINDER_MONITOR
binder_cancel_bwdog(t);
#endif
kfree(t);
binder_stats_deleted(BINDER_STAT_TRANSACTION);
}
} break;
case BINDER_WORK_TRANSACTION_COMPLETE: {
binder_debug(BINDER_DEBUG_DEAD_TRANSACTION,"undelivered TRANSACTION_COMPLETE\n");
kfree(w);
binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE);
} break;
case BINDER_WORK_DEAD_BINDER_AND_CLEAR:
case BINDER_WORK_CLEAR_DEATH_NOTIFICATION: {
struct binder_ref_death *death;
death = container_of(w, struct binder_ref_death, work);
binder_debug(BINDER_DEBUG_DEAD_TRANSACTION,
"undelivered death notification, %016llx\n",
(u64)death->cookie);
kfree(death);
binder_stats_deleted(BINDER_STAT_DEATH);
} break;
default:
pr_err("unexpected work type, %d, not freed\n",w->type);
break;
}
}
}
@6 binder_flush分析
//关闭一个设备文件描述符复制到时候被调用
static int binder_flush(struct file *filp, fl_owner_t id)
{
struct binder_proc *proc = filp->private_data;
binder_defer_work(proc, BINDER_DEFERRED_FLUSH);//关闭一个设备文件描述符复制的时候被调用
return 0;
}
@7 binder_poll分析
//非阻塞型IO操作的内核驱动实现。当用户程序read数据时,如果驱动程序没有准备好数据,则阻塞该进程,直到数据准备好。称为阻塞型IO操作
static unsigned int binder_poll(struct file *filp,struct poll_table_struct *wait)
{
struct binder_proc *proc = filp->private_data;
struct binder_thread *thread = NULL;
int wait_for_proc_work;
binder_lock(__func__);
thread = binder_get_thread(proc); //获取当前进程/线程信息
wait_for_proc_work = thread->transaction_stack == NULL &&list_empty(&thread->todo) && thread->return_error == BR_OK;
binder_unlock(__func__);
if (wait_for_proc_work) { //proc_work工作方式 进程
if (binder_has_proc_work(proc, thread))
return POLLIN;
poll_wait(filp, &proc->wait, wait);
if (binder_has_proc_work(proc, thread))
return POLLIN;
} else { //proc_work工作方式 线程
if (binder_has_thread_work(thread))
return POLLIN;
poll_wait(filp, &thread->wait, wait);
if (binder_has_thread_work(thread))
return POLLIN;
}
return 0;
}
其中,binder_poll中涉及的binder_get_thread,binder_has_proc_work和binder_has_thread_work的实现如下所示:
static struct binder_thread *binder_get_thread(struct binder_proc *proc)
{
struct binder_thread *thread = NULL;
struct rb_node *parent = NULL;
struct rb_node **p = &proc->threads.rb_node; //取得队列中的线程
/从进程描述符binder_proc的threads红黑树中查找是否有线程描述符存在
while (*p) {
parent = *p;
thread = rb_entry(parent, struct binder_thread, rb_node);//取得将要比较的线程
if (current->pid < thread->pid)
p = &(*p)->rb_left;
else if (current->pid > thread->pid)
p = &(*p)->rb_right;
else
break;
}
if (*p == NULL) { //如果没有查找到
thread = kzalloc(sizeof(*thread), GFP_KERNEL);//创建一个线程描述符binder_thread
if (thread == NULL)
return NULL;
//初始化该线程描述符的各个成员变量
binder_stats_created(BINDER_STAT_THREAD);
thread->proc = proc; //插入到proc->threads所表示的红黑树中去,下次要使用时就可以从proc中找到
thread->pid = current->pid;
init_waitqueue_head(&thread->wait); //初始化等待队列
INIT_LIST_HEAD(&thread->todo); //初始化链表表头
rb_link_node(&thread->rb_node, parent, p);
rb_insert_color(&thread->rb_node, &proc->threads);
thread->looper |= BINDER_LOOPER_STATE_NEED_RETURN; //设置线程循环状态
thread->return_error = BR_OK;
thread->return_error2 = BR_OK;
}
return thread;
}
static int binder_has_proc_work(struct binder_proc *proc,struct binder_thread *thread)
{
return !list_empty(&proc->todo) ||(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN);
}
static int binder_has_thread_work(struct binder_thread *thread)
{
return !list_empty(&thread->todo) || thread->return_error != BR_OK ||(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN);
}
@8 注册相关
device_initcall(binder_init);
MODULE_LICENSE("GPL v2");
binder_init由device_initcall调用,而不是传统的module_init和module_exit。这是因为使binder驱动直接编译进内核,不支持动态编译,同时需要在Kernel中做镜像。
至此 binder驱动的参考代码分析分享结束。