引言
在博客Android Binder机制(3)本地服务注册过程这篇博客中我们详细讲解了本地服务的注册过程,除了一个地方之外,那就是IPCThreadState::waitForResponse()方法中的talkWithDriver(),而在talkWithDriver()中调用了binder_ioctl(),由于内容太多,所以专门写一篇博客进行分析。
实际上,不只是在服务注册过程中会调用到Binder Driver中的binder_ioctl(),在服务检索、服务使用阶段都会调用到binder_ioctl(),所以这篇博客将分这3个阶段详细讲解binder_ioctl()方法。
并且,这3个阶段均可按Binder数据的传递划分为6个阶段,如下所示:
本文中对于服务注册、服务检索、服务使用都分这6个阶段进行讲解。
另外,由于在博客Android Binder机制(2) ContextManager注册过程分析中详细分析过binder_open和binder_mmap()函数,这里就不再讲解了,不了解的小伙伴可以移步到那篇Blog中。
在开始之前,我们先补充一些基础知识。
0.1 ioctl的BINDER_WRITE_READ命令用来请求Binder Driver发送或接收IPC数据以及IPC应答数据
0.2 binder_ioctl()函数负责在两个进程间收发IPC数据以及IPC应答数据。因此,在分析binder_ioctl()函数时,必然要涉及到发送IPC数据和接收IPC数据的两个进程。
0.3 binder_write_read结构体的定义如下:
struct binder_write_read- write_buffer成员变量用于发送IPC数据或IPC应答数据。而read_buffer成员变量用来接收来自Binder Driver的数据,即Binder Driver在接收IPC数据或IPC应答数据后,将它们保存到read_buffer中,而后再传递到用户空间中。
- write_size与read_size分别用来指定write_buffer与read_buffer的数据大小
- write_consumed与read_consumed分别用来设定write_buffer与read_buffer中被处理的数据大小
- 在前面的学习中,我们知道IPC数据与IPC应答数据由Handle、RPC代码、RPC数据、Binder协议构成。其中Handle、RPC代码、RPC数据保存在名称为binder_transaction_data的结构体中。如下图所示:
-
如上图所示,binder_write_read的write_buffer指向含有Binder协议和binder_transaction_data的内存区域,在Binder Driver处理IPC数据时它们被作为一个处理单元。通常这种处理单元能被连续加载到write_buffer中,但为了说明的方便,只考虑有一个IPC数据的情形。同样,read_buffer指向由Binder协议与binder_transaction_data指向的内存区域
-
binder_write_read结构体中包含着用户空间中生成的IPC数据,Binder Driver也拥有一个相同的结构体。用户空间设置完binder_write_read结构体的数据后,调用ioctl()函数传递给Binder Driver,Binder Driver调用copy_from_user()函数将用户空间中的数据拷贝到自身的binder_write_read结构体中。相反,在传递IPC应答数据时,Binder Driver将调用copy_to_user()函数,将自身binder_write_read结构体中的数据拷贝到用户空间。
1.服务注册
1.1 过程概览
服务注册过程的示意图如下所示:
通过前面的博客我们知道,Context Manager先于其他所有Service Server进行,它最先使用Binder Driver,进入待机状态,之后等待Service Server的服务注册请求或者Client的服务检索请求。
上图所示过程如下:
1)Context Manager通过open()函数调用binder_open()函数,Binder Driver使用该函数为Context Manager生成并初始化binder_proc结构体;
2)Context Manager在内核空间中开辟一块Buffer,用于接收IPC数据。Context Manager通过mmap()函数调用Binder Driver的binder_mmap()函数。Binder Driver使用binder_mmap()函数在内核空间中分配一块用于接收IPC数据的Buffer,其大小为调用mmap()函数时指定的大小,并被保存到binder_buffer结构体中。而后binder_buffer变量被注册到binder_proc结构体中;
3)Context Manager通过ioctl()调用Binder Driver的binder_ioctl()函数。在binder_ioctl()函数中,Context Manager将处于待机状态,直到另一个进程(在服务注册阶段是指Service Server)向共发送IPC数据;
4)Service Server通过open()调用Binder Driver的binder_open()函数,Binder Driver为Service Server生成binder_proc结构体,并将其初始化;
5)Service Server通过mmap()调用Binder Driver的binder_mmap()函数.Binder Driver开辟一块用于接收IPC应答数据的Buffer,并将其保存到binder_buffer结构体中。而后Service Server生成IPC数据,用来调用Context Manager的服务注册函数;
6)为了注册服务,Service Server将IPC数据传递给Binder Driver。Binder Driver将根据IPC数据中的Handle查找到Context Manager的binder_node结构体,以及binder_proc结构体;
7)为Service Server要注册的服务生成binder_node结构体。Binder Driver将binder_node分别注册到Service Server的binder_proc结构体以及Context Manager的binder_proc结构体中;
8)Binder Driver将在6)中查找到的Context Manager的binder_proc结构体中查找注册在binder_buffer结构体中的Buffer,并传递IPC数据;
9)Binder Driver会记下IPC数据发送进程(Service Server)的binder_proc结构体,以便在Context Manager向Service Server发送应答数据时查找Serivce Server的binder_proc结构体;
10)Binder Driver让Service Server处于待机状态,并将Context Manager从待机状态中唤醒,传递IPC数据。从待机状态中唤醒的Context Manager经由Binder Driver接收来自Service Server的IPC数据。Context Manager根据接收的IPC数据注册指定的服务,而后生成IPC应答数据(通知服务注册完成),并将其传递给Binder Driver;
11)Binder Driver会在9)中的Service Server的binder_proc结构体中查找注册在binder_buffer结构体中的Buffer,并传送IPC应答数据,唤醒接收端的Service Server。Service Server被唤醒后,接收IPC应答数据,并根据接收的IPC应答数据进行相应处理。
1.2 传递Binder数据的阶段1
Context Manager调用binder_open()与binder_mmap()函数,初始化binder_proc结构体,创建用于接收IPC数据的binder_buffer结构体。
在用户空间中生成一块Buffer,将read_buffer注册到binder_write_read结构体中,而后调用ioctl()函数,此时,binder_write_read结构体的read_size指的是用户空间的Buffer的大小。此时binder_ioctl()中调用的主要代码如下:
binder_ioctl()binder_ioctl()函数的第二个参数cmd是ioctl()的命令,第三个参数arg是binder_write_read结构体,它是调用ioctl()函数时使用的参数。
- flip->private_data是在binder_open()函数中创建的binder_proc结构体
- 由于proc的线程红黑树中还没有新建相应的线程,故在binder_get_thread()函数中新建一个binder_thread结构体
- 分析ioctl()命令,前面已经说过,此时传递的命令是BINDER_WRITE_READ
- 调用copy_from_user()方法将Context Manager持有的用户空间的binder_write_read结构体复制到内核空间中。而后根据read_size与write_size变量判断是发送IPC数据,还是接收IPC应答数据。Context Manager在接收IPC数据时,将生成相应的read_buffer,并且read_size的值将大于0
- 由于read_size>0,故调用binder_thread_read()函数
1.2.1 binder_thread_read()分析
binder_thread_read()中调用到的代码如下所示:
binder_thread_read()Context Manager在binder_thread_read()函数中调用wait_event_interruptible_exclusive()函数。wait_event_interruptible_exclusive()函数通过当前进程的binder_proc结构体的wait变量将当前任务注册到待机队列中。而后将task_struct结构体内的state变量由TASK_RUNNING更改为TASK_INTERRUPTIBLE,再调用schedule()函数。当Context Manager的任务阙云太为TASK_INTERRUPTIBLE时,Context Manager就不再继续运行,它将进入待机状态,直至接收到IPC数据。
1.3 传递Binder数据的阶段2
这个阶段其实对应的是诸如MediaPlayerService::instantiate();这样的过程,只不过我们这里只从Binder Driver的角度考虑问题,所以只讨论ioctl()函数。
这个阶段的示意图如下:
Service Server也会生成binder_write_read结构体,包含write_buffer与read_buffer两个成员变量。其中,read_buffer变量用于接收IPC应答数据,write_buffer变量用于发送IPC数据。binder_write_read结构体内容如下图所示:
此时binder_ioctl()函数中执行的主要代码如下:
binder_ioctl()