Binder客户端和驱动端通信流程实例分析----以acquireWakeLock()函数为例 (二)

来源:互联网 发布:四川大学成教学院网络 编辑:程序博客网 时间:2024/06/15 04:23

在上一篇文章《Binder客户端和驱动端通信流程实例分析----以acquireWakeLock()函数为例(一)》中,我们介绍了亮屏锁WakeLock类的acquire()函数如何将Binder请求从app上层一步步向下传递到frameworknative层的IPCThreadState类的transact()函数。

IPCThreadState类的transact()函数主要做的事情如下:

1. 调用IPCThreadState::writeTransactionData()函数把包含Binder请求信息的parcel内容拆解重组到一个binder_transaction_data结构体的变量中,再把命令码cmd和binder_transaction_data写入到mOut变量,mOut变量在后续的处理中将被包含在一个binder_write_read结构体变量中,通过ioctl()传给Binder驱动;

2. 调用IPCThreadState::waitForResponse()进入一个与Binder驱动进行沟通的while循环,该循环主要代码如下(因篇幅所限,进行了缩略):

    while (1) {        if ((err=talkWithDriver()) < NO_ERROR) break;        err = mIn.errorCheck();        if (err < NO_ERROR) break;        if (mIn.dataAvail() == 0) continue;        cmd = (uint32_t)mIn.readInt32();        switch (cmd) {        case BR_TRANSACTION_COMPLETE:            if (!reply && !acquireResult) goto finish;            break;        case BR_DEAD_REPLY:            err = DEAD_OBJECT;            goto finish;        case BR_FAILED_REPLY:            err = FAILED_TRANSACTION;            goto finish;        case BR_ACQUIRE_RESULT:            …            If (…) continue;            goto finish;        case BR_REPLY:            …        default:            err = executeCommand(cmd);            if (err != NO_ERROR) goto finish;            break;        }    }

该段循环代码调用talkWithDriver()函数与Binder驱动进行通信,然后根据返回的cmd命令码,执行不同的操作,如果判断该次事务已经完成,则退出while循环,否则继续调用talkWithDriver()与Binder驱动进行通信。通过在该循环中添加打印,可以发现在一次Binder事务中一般会调用五六次talkWithDriver(),当服务端完全处理好该次事务,并把处理结果通过Binder驱动返回后,就可成功退出循环。

 

现在我们看一下talkWithDriver()函数。talkWithDriver()是framework native层和设备文件驱动层的过渡函数,再往下就是设备文件驱动层了。talkWithDriver()在函数开头声明了一个binder_write_read结构体变量,然后把mIn和mOut的内存地址写入该变量。mIn是接收返回数据的载体,mOut则是Binder请求信息的载体。现在,关键的数据载体都打包在binder_write_read结构体中。数据打包完成后,talkWithDriver()执行以下代码,将binder_write_read结构体向下传递给设备文件:

    if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)        err = NO_ERROR;

其中,bwr是binder_write_read结构体变量,BINDER_WRITE_READ是请求类型。mProcess是一个ProcessState实例,在Android Framework中属于单例实现(即一个进程只有一个mProcess),其成员mDriverFD是一个设备文件描述符,当mProcess实例被创建时,会调用open_driver()函数打开路径为"/dev/binder"的Binder驱动设备,返回的文件描述符就被保存在mDriverFD。通过这样的机制,当程序需要和Binder驱动通信时,只需要调用ioctl,把mDriverFD作为设备文件描述符,就可以把请求类型和请求数据准确无误地传递给Binder驱动。

 

现在,我们已经到了framework native层的边界,再往下走就会通过ioctl()进入linux kernel的领域。如果我们在安卓源代码中搜索ioctl()的定义,会搜出来至少几十个结果,很难判断哪个是talkWithDriver()里面调用的ioctl()。

这时候不要慌,静下心来思考。既然它是一个操作设备驱动文件的函数,那么不妨到内核文件系统路径,也就是kernel-3.18/fs/目录下搜索。我们发现,kernel-3.18/fs/ioctl.c这个文件里面实现了ioctl()函数,但是它的定义方式比较特殊,是通过宏SYSCALL_DEFINE3进行定义的,如下所示:

SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, unsigned long, arg){int error;struct fd f = fdget(fd);if (!f.file)return -EBADF;error = security_file_ioctl(f.file, cmd, arg);if (!error)error = do_vfs_ioctl(f.file, fd, cmd, arg);fdput(f);return error;}

如果我们在这个函数里面添加打印,就会发现,talkWithDriver()调用的ioctl(),正好就是kernel-3.18/fs/ioctl.c文件里面的ioctl()。

现在我们已经进入linux kernel层。

在ioctl()的实现代码中,最终会通过如下调用顺序到达Binder驱动:

ioctl()→do_vfs_ioctl()→vfs_ioctl()→f_op->unlocked_ioctl()

现在观察f_op->unlocked_ioctl()。在kernel-3.18/fs/ioctl.c的ioctl()中,有以下代码:

struct fd f_op = fdget(fd);

可见f_op就是指向Binder设备文件"/dev/binder"的一个文件描述结构体,在上述调用顺序中,最后一个环节是调用该文件描述结构体的unlocked_ioctl()函数。

这时候如果我们打开Binder驱动的实现文件kernel-3.18/drivers/staging/android/binder.c,就可以看到unlocked_ioctl()其实是一个函数映射:

static const struct file_operations binder_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,};

可以看到,它是binder_ioctl()的映射。

在binder_ioctl()中,由于前面传递的请求类型是BINDER_WRITE_READ,故会执行以下命令分支:

case BINDER_WRITE_READ:ret = binder_ioctl_write_read(filp, cmd, arg, thread);if (ret)goto err;break;

在这个分支中,主要的动作是调用binder_ioctl_write_read()。

在binder_ioctl_write_read()的实现代码中,首先调用binder_thread_write()对请求数据包进行解包,逐个执行包中的命令,其中最主要的命令是BC_TRANSACTION,会触发执行binder_transaction()。

在binder_transaction()函数中,最主要的动作是遍历红黑树,找到服务端的工作队伍,将Binder请求插入该队伍,相当于加入一个新的工作任务。

插入完成后,线程就会返回到binder_ioctl_write_read()函数体中,执行binder_thread_read()。

binder_thread_read()会执行一个while循环,在不需要等待服务端返回数据的情况下,该循环立刻退出,如果需要等待,则当服务端返回数据时才退出。退出循环后,线程回退到talkWithDriver()的while循环。

当返回的结果符合条件时,线程退出talkWithDriver()的while循环,逐层向上返回,直到退出WakeLock的acquire()函数。

 

至此,我们完成了基于acquireWakeLock()函数的Binder客户端和驱动端的通信流程分析。由于Binder机制具有全局通用性,其它api的Binder请求都可以通过类似的方法和流程进行分析。

阅读全文
0 0
原创粉丝点击