3.5 receive async requet
接收异步方式发送过来的请求和接收同步方式发来的请求基本一样,不同的是,在将binder_transaction的数据转换到binder_transaction_data之后,将会释放掉binder_transaction数据结构的空间。
由于binder驱动对异步通信做了分流的处理,如果当前目标进程已经有一个异步通信正在处理,那么为了保证同步通信的实时性,所以会将后来发给该进程的异步通信任务放在一个等待队列async_todo中,直到前面那个异步通信任务完成后才会从异步等待队列中取出一个任务放进前次处理异步任务的task的todo队列中去。
(从这里可以看出,如果某个线程正在处理异步任务,当完成的时候发现异步等待队列中还有异步任务需要处理,那么这个等待的异步任务也会被当前这个线程处理,直到这个时间段内的异步任务处理完。隔了段时间之后,如果再有异步任务到来的话,此时驱动可能会分配其他的线程来处理接下来时间段内的异步任务。简单点说,在某线程执行任何一个异步任务未完成之前就已经排到异步等待队列中来的异步任务,都将会由这个线程来执行。)。
不过,我们在binder_thread_read()函数的最后没有看到将异步任务移入线程的todo队列中的动作,这个函数和异步请求接收相关的只有如下地方:
static int binder_thread_read(struct binder_proc *proc,
struct binder_thread *thread,
void __user *buffer, int size,
signed long *consumed, int non_block)
{
…
while (1) {
if (!t) // 非 BINDER_WORK_TRANSACTION 的情况,放弃执行后面的重新循环
continue;
…
if (t->from) { // 记录发送线程的binder_thread
… // 同步传输时
} else { // reply 或者异步传输时
tr.sender_pid = 0;
}
…
list_del(&t->work.entry);
t->buffer->allow_user_free = 1;
if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) {// 同步传输
t->to_parent = thread->transaction_stack;
t->to_thread = thread;
thread->transaction_stack = t;
} else { /* 如果收到的是回复数据或者而是异步请求,这里将会释放掉这次单边
传输的binder_transaction结构体,另外所有的传输的binder_buffer结构体空间都是通过上层发送命令BC_FREE_BUFFER来通知binder驱动释放的,因为这部分空间是驱动在管理。*/
t->buffer->transaction = NULL;
kfree(t);
binder_stats_deleted(BINDER_STAT_TRANSACTION);
}
break;
}// while(1)
}
那究竟是在哪里移入下一个异步等待任务的呢?其实我们可以想一下,这个binder_thread_read()函数执行完的时候,异步任务还没开始执行,驱动还会将binder_transaction_data结构体传回上层程序,上层程序才真正开始执行异步任务,不过通常上层应用程序在执行完异步任务(其实不只是异步任务,应该是所有类型的任务)被执行完,都应该发送BC_FREE_BUFFER这个命令到binder驱动,通知驱动释放掉一次单边传输时的binder_buffer内存空间。到这里之后,这个异步任务才算得上真正完成。所以我们的前面提到的移入异步任务的事情就是在这个时候做的,请看源码:
int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread,void __user *buffer, int size, signed long *consumed)
{
uint32_t cmd;
void __user *ptr = buffer + *consumed;
void __user *end = buffer + size;
while (ptr < end && thread->return_error == BR_OK) {
…
switch (cmd) {
…
case BC_FREE_BUFFER: { // BC_FREE_BUFFER = _IOW('c', 3, int),
// cmd | data_ptr (data_ptr是指binder_buffer.data开始地址)
void __user *data_ptr;
struct binder_buffer *buffer;
if (get_user(data_ptr, (void * __user *)ptr))