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

版权声明:本文为博主原创文章,如需转载请注明出处。因博主水平有限,如有疏忽遗漏,敬请指出。 https://blog.csdn.net/ShadowN1ght/article/details/77653372

在上一篇文章《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请求都可以通过类似的方法和流程进行分析。

猜你喜欢

转载自blog.csdn.net/ShadowN1ght/article/details/77653372