Android 入门系列之 Binder(二)

 

binder_read的一些内容,明天详解如何parse和回调处理;然后分析binder驱动的写。后天计划解析进程间通讯在驱动层的实现。...



看看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);// 参照鄙人ioctl文档,这是一个宏,获取用户传入数据大小

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))) {// 参照鄙人ioctl文档,这里调copy_from_user拷贝到了bwr

ret = -EFAULT;

goto out;

}

if (bwr.write_size > 0) {// 要写,就调用binder_thread_write

ret = binder_thread_write(proc, thread,

bwr.write_buffer,

bwr.write_size,

&bwr.write_consumed);

……

}

if (bwr.read_size > 0) {// 要读,就调binder_thread_read

ret = binder_thread_read(proc, thread, bwr.read_buffer,

bwr.read_size,

&bwr.read_consumed,

filp->f_flags & O_NONBLOCK);

……

}

if (copy_to_user(ubuf, &bwr, sizeof(bwr))) { // 读写完毕,用copy_to_user再把这个bwr拷贝回去

ret = -EFAULT;

goto out;

}

out:

return ret;

}

上述流程很显然是去写,直接看binder_thread_write:

static int binder_thread_write(struct binder_proc *proc,

struct binder_thread *thread,

binder_uintptr_t binder_buffer, size_t size,

binder_size_t *consumed)

{

uint32_t cmd;

void __user *buffer = (void __user *)(uintptr_t)binder_buffer;// 不可解引用的binder_buffer

void __user *ptr = buffer + *consumed;// binder_buffer一部分已经被使用过了,加偏移

void __user *end = buffer + size;// 开始加长度即要写的结尾

while (ptr < end && thread->return_error == BR_OK) {// 还够用

if (get_user(cmd, (uint32_t __user *)ptr))// 参照鄙人ioctl文档,从用户空间处获取简单类型赋给cmd

return -EFAULT;

ptr += sizeof(uint32_t);// 加一个int32的偏移,因为命令也要占用空间

if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.bc)) {// 从命令获取序数,参照鄙人ioctl文档,它表示命令

binder_stats.bc[_IOC_NR(cmd)]++;// 统计数据

proc->stats.bc[_IOC_NR(cmd)]++;

thread->stats.bc[_IOC_NR(cmd)]++;

}

switch (cmd) {// 序数即命令,下面是教科书式的ioctl使用

case BC_INCREFS:// 这组四个命令增加或减少Binder的引用计数,用以实现强指针或弱指针的功能

case BC_ACQUIRE:

case BC_RELEASE:

case BC_DECREFS: {

uint32_t target;

struct binder_ref *ref;// 前文的binder_ref结构体,维护在两个红黑树中,且有一个binder_proc、binder_node

const char *debug_string;

if (get_user(target, (uint32_t __user *)ptr))// get_user获取下一个uint32,即目标

return -EFAULT;

ptr += sizeof(uint32_t);

if (target == 0 && binder_context_mgr_node &&// 目标为0,即上下文管理者

(cmd == BC_INCREFS || cmd == BC_ACQUIRE)) {// 增加强引用或者获取binder弱引用

ref = binder_get_ref_for_node(proc,

binder_context_mgr_node);// 获取binder_context_mgr_node对应的binder_ref

if (ref->desc != target) {// 拿到的不是想要的

……

}

} else

ref = binder_get_ref(proc, target);// 根据target获取binder_ref

if (ref == NULL) { // 没拿到

……

break;

}

……// 操作引用数,之后详细分析

break;

}

……

case BC_ENTER_LOOPER:

……

thread->looper |= BINDER_LOOPER_STATE_ENTERED; // 更新binder_thread的线程状态

break;

……

default:

pr_err("%d:%d unknown command %d
",

proc->pid, thread->pid, cmd);

return -EINVAL;

}

*consumed = ptr - buffer;// 更新已使用的计数

}

return 0;

}

这样,binder_thread进入looper状态,继续看looper循环,将会去循环读取,根据上面的分析,最终调用binder_thread_read方法:

static int binder_thread_read(struct binder_proc *proc,

struct binder_thread *thread,

binder_uintptr_t binder_buffer, size_t size,

binder_size_t *consumed, int non_block)

{

void __user *buffer = (void __user *)(uintptr_t)binder_buffer;// __user,无需再说

void __user *ptr = buffer + *consumed;// 计算读的起始地址

void __user *end = buffer + size;// 结束地址

int ret = 0;

int wait_for_proc_work;// 等待进程的工作

if (*consumed == 0) {// 没消耗buffer

if (put_user(BR_NOOP, (uint32_t __user *)ptr)) // 拷贝给用户空间一个BR_NOOP表示操作完成

return -EFAULT;

ptr += sizeof(uint32_t);// 写了一个BR_NOOP后,已使用uint32_t长度的buffer

}

retry:

wait_for_proc_work = thread->transaction_stack == NULL &&

list_empty(&thread->todo); // 这两个列表均为空,即没有要等待的工作

if (thread->return_error != BR_OK && ptr < end) {

…… // 出错了且还有buffer空间

goto done;

}

thread->looper |= BINDER_LOOPER_STATE_WAITING; // 更新binder_thread状态为等待

if (wait_for_proc_work)// 不必等

proc->ready_threads++;// 更新统计数

binder_unlock(__func__);// 退出临界区,见鄙人Linux同步文档,注意这里进入临界区是在binder_ioctl的时候

if (wait_for_proc_work) {// 不需要等

if (!(thread->looper & (BINDER_LOOPER_STATE_REGISTERED |

BINDER_LOOPER_STATE_ENTERED))) {// binder_thread没有进入looper状态

wait_event_interruptible(binder_user_error_wait,

binder_stop_on_user_error < 2);// 等待执行错误的相关内容

}

binder_set_nice(proc->default_priority);// 设置优先级,-20最高,19最低,见鄙人的Linux进程管理文档

if (non_block) {// binder调用是否非阻塞的,一般是阻塞的

if (!binder_has_proc_work(proc, thread))// 这个方法表示有要做的工作

ret = -EAGAIN;// 因为没有要做的工作,返回一个EAGAIN错误码

} else// 阻塞的,调用wait_event_freezable_exclusive进入wait队列等待直到没有其他工作为止

ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread));

} else {// 要等

if (non_block) {// 非阻塞

if (!binder_has_thread_work(thread))

ret = -EAGAIN;

} else// 阻塞

ret = wait_event_freezable(thread->wait, binder_has_thread_work(thread));

}

binder_lock(__func__);// 进入临界区

if (wait_for_proc_work)// 不用等

proc->ready_threads--;

thread->looper &= ~BINDER_LOOPER_STATE_WAITING;// 去掉BINDER_LOOPER_STATE_WAITING状态

if (ret)

return ret;

while (1) {

uint32_t cmd;// 命令

struct binder_transaction_data tr;// 用户传入数据的结构体

struct binder_work *w;// binder工作状态结构体

struct binder_transaction *t = NULL;// 进程间传输的结构体

if (!list_empty(&thread->todo)) {·// 有要执行的工作

w = list_first_entry(&thread->todo, struct binder_work,

entry);// 从todo取第一个

} else if (!list_empty(&proc->todo) && wait_for_proc_work) {

w = list_first_entry(&proc->todo, struct binder_work,

entry);// 取todo第一个

} else {

/* no data added */

if (ptr - buffer == 4 &&

!(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN))// 没数据且read操作不需要返回,即重试

goto retry;

break;

}

if (end - ptr < sizeof(tr) + 4)// 读取的长度不及一个binder_transaction结构体的长度;为何+4?

break;// 见鄙人ioctl文档,4是ioctl的cmd的第三段,即用户数据长度

switch (w->type) {

case BINDER_WORK_TRANSACTION: {// 1,获取包含binder_work的binder_transaction

t = container_of(w, struct binder_transaction, work);// 获取binder_transaction

} break;

case BINDER_WORK_TRANSACTION_COMPLETE: {// 获取完毕

cmd = BR_TRANSACTION_COMPLETE;

if (put_user(cmd, (uint32_t __user *)ptr))// 回传BR_TRANSACTION_COMPLETE给用户

return -EFAULT;

ptr += sizeof(uint32_t);// 增加偏移量

binder_stat_br(proc, thread, cmd);// 统计相关

list_del(&w->entry);// 链表删掉这个binder_work

kfree(w);// 释放它

binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE);// 删除状态

} break;

……

if (!t)

continue;

BUG_ON(t->buffer == NULL);

if (t->buffer->target_node) {// 取到了binder_transaction且取到了目标binder_node

struct binder_node *target_node = t->buffer->target_node;// 获取读取的目标binder_node

tr.target.ptr = target_node->ptr;// 目标node中获取要传输的数据

tr.cookie =  target_node->cookie;// 获取cookir

t->saved_priority = task_nice(current);// 当前的进程优先级设置给binder_transaction

if (t->priority < target_node->min_priority &&

!(t->flags & TF_ONE_WAY))

binder_set_nice(t->priority);

else if (!(t->flags & TF_ONE_WAY) ||

t->saved_priority > target_node->min_priority)

binder_set_nice(target_node->min_priority); // binder通讯总是取最高优先级(nice越小优先级越高)

cmd = BR_TRANSACTION;

} else {// 返回数据包

tr.target.ptr = 0;

tr.cookie = 0;

cmd = BR_REPLY;

}

tr.code = t->code;// 命令

tr.flags = t->flags;// flags

tr.sender_euid = from_kuid(current_user_ns(), t->sender_euid);// 发送者的euid

if (t->from) {// 赋值发送者的pid

struct task_struct *sender = t->from->proc->tsk;

tr.sender_pid = task_tgid_nr_ns(sender,

task_active_pid_ns(current));

} else {

tr.sender_pid = 0;

}

tr.data_size = t->buffer->data_size;// 大小

tr.offsets_size = t->buffer->offsets_size;// 偏移量

tr.data.ptr.buffer = (binder_uintptr_t)(// 偏移

(uintptr_t)t->buffer->data +

proc->user_buffer_offset);

tr.data.ptr.offsets = tr.data.ptr.buffer +// 以void *为上界对齐data_size,也即void *的整数倍,大于等于ALIGN(t->buffer->data_size,// data_size

sizeof(void *));

if (put_user(cmd, (uint32_t __user *)ptr))// 将cmd写入ptr

return -EFAULT;

ptr += sizeof(uint32_t);// 增加uint32_t长度的偏移

if (copy_to_user(ptr, &tr, sizeof(tr)))// 这次复杂数据copy_to_user,把binder_transaction_data复制给用户

return -EFAULT;

ptr += sizeof(tr);// 增加偏移

trace_binder_transaction_received(t);

binder_stat_br(proc, thread, cmd);// 更新统计数据

list_del(&t->work.entry);// 工作列表删除这个work

t->buffer->allow_user_free = 1;// 允许用户释放buffer

if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) {// TF_ONE_WAY是异步交互,即client发送请求后立刻返回

t->to_parent = thread->transaction_stack;// 否则Server会返回BC_REPLY数据包,Client端必须等

t->to_thread = thread;// 待接收完该数据包

thread->transaction_stack = t;// 设置目标thread和它的transaction_stack

} else {// 直接初始化,然后调用kfree,释放binder_transaction,因为无需再次回传给client数据包了

t->buffer->transaction = NULL;

kfree(t);

binder_stats_deleted(BINDER_STAT_TRANSACTION);

}

break;

}

done:

*consumed = ptr - buffer;// 消耗的buffer

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)) {

/** 该消息用于接收方线程池管理。当驱动发现接收方所有线程都处于忙碌状态且线程池里的线程总数没有超过BINDER_SET_MAX_THREADS设置的最 *大线程数时,向接收方发送该命令要求创建更多线程以备接收数据。*/

proc->requested_threads++;

if (put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer))

return -EFAULT;

binder_stat_br(proc, thread, BR_SPAWN_LOOPER);

}

return 0;

}


    关注 来哥的技术栈


微信扫一扫关注公众号

0 个评论

要回复文章请先登录注册