3.2.2 Binder驱动的实现(6)
14.binder_flush
flush操作接口将在关闭一个设备文件描述符复制时被调用。该函数的实现十分简单,如代码清单3-21所示。
代码清单3-21 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;
}
正如上面所说的,通过调用一个workqueue来执行BINDER_DEFERRED_FLUSH操作,从而完成该flush操作,但是最终的处理还是交给了binder_defer_work函数。
15.binder_poll
poll函数是非阻塞型IO的内核驱动实现,所有支持非阻塞IO操作的设备驱动都需要实现poll函数。Binder的poll函数仅支持设备是否可以非阻塞地读(POLLIN),这里有两种等待任务:一种是proc_work,另一种是thread_work。同其他驱动的poll实现一样,这里也是通过调用poll_wait函数来实现的:具体实现如代码清单3-22所示。
代码清单3-22 binder_poll的实现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;
mutex_lock(&binder_lock);
//得到当前进程的信息
thread=binder_get_thread(proc);
wait_for_proc_work=thread->transaction_stack== NULL &&
list_empty(&thread->todo) && thread->return_error== BR_OK;
mutex_unlock(&binder_lock);
//proc_work方式
if (wait_for_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 {//thread_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_proc结构体的threads队列中,所以我们可以通过binder_get_thread(proc)来找到当前进程/线程的相关信息,稍后会详细分析查找原理。proc_work和thread_work方式如代码清单3-23所示。
代码清单3-23 proc_work和thread_work的实现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);
}
其中,主要是通过检测线程队列是否为空、线程的循环状态以及返回信息来判断所要采用的等待方式,最后通过调用poll_wait函数来实现poll操作。
16.binder_get_thread
该函数可以用于在threads队列中查找当前的进程信息,实现如代码清单3-24所示。
代码清单3-24 binder_get_thread的实现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;
while (*p) {
parent= *p;
// 取得将要进行比较的线程
thread=rb_entry(parent, struct binder_thread, rb_node);
//比较
if (current->pidpid)
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);
if (thread== NULL)
return NULL;
//初始化新进程的一系列数据信息
binder_stats.obj_created[BINDER_STAT_THREAD]++;
thread->procproc= 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;
}
实现原理是通过在队列中比较各个线程的pid与当前线程的pid是否相同,如果找到了,把它的线程信息返回;如果没找到,新建一个线程并把它加入到队列中,然后初始化就绪线程队列等。代码的注释已经把每一个步骤都讲清楚了。