Android Binder机制分析(5) Binder_ioctl()分析

本文详细分析了Android Binder机制中关键的binder_ioctl()函数,涉及服务注册、检索和使用三个阶段。在每个阶段,文章都按照Binder数据的6个传递阶段展开,包括服务注册过程中的Binder数据交互、服务检索阶段的IPC数据发送和接收,以及服务使用阶段的通信流程。通过对binder_ioctl()的深入探讨,揭示了Android系统中服务交互的底层细节。
摘要由CSDN通过智能技术生成

引言

在博客Android Binder机制(3)本地服务注册过程这篇博客中我们详细讲解了本地服务的注册过程,除了一个地方之外,那就是IPCThreadState::waitForResponse()方法中的talkWithDriver(),而在talkWithDriver()中调用了binder_ioctl(),由于内容太多,所以专门写一篇博客进行分析。

实际上,不只是在服务注册过程中会调用到Binder Driver中的binder_ioctl(),在服务检索、服务使用阶段都会调用到binder_ioctl(),所以这篇博客将分这3个阶段详细讲解binder_ioctl()方法。

并且,这3个阶段均可按Binder数据的传递划分为6个阶段,如下所示:

binder_flow

本文中对于服务注册、服务检索、服务使用都分这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
1
2
3
4
5
6
7
8
9
10
11
 struct binder_write_read{
         
    //bytes to write
    signed long write_size;
    //bytes consumed by driver
    unsigned long write_buffer;
    //bytes to read
    signed long read_size;
    //bytes consumed by driver
    signed long read_consumed;
    unsigned long read_buffer;
}
  • 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_transaction_data_ipc

binder_write_read_vs_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 过程概览

服务注册过程的示意图如下所示:

service_register

通过前面的博客我们知道,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()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
static long binder_ioctl(struct file*flip,unsigned int cmd,unsigned long arg){
         
    struct binder_proc*proc=flip->private_data;
    void __user*ubuf=(void __user*)arg;
    thread=binder_get_thread(proc);
    switch(cmd){
         
        case BINDER_WRITE_READ:
            struct binder_write_read bwr;
            if(copy_from_user(&bwr,ubuf,sizeof(bwr)))
            if(bwr.write_size>0){
         
                ret=binder_thread_write(proc,thread,
                    (void __user*)bwr.write_buffer,bwr.write_size,
                    &bwr.write_consumed);
            }
            if(bwr.read_size>0){
         
                ret=binder_thread_read(proc,thread,
                    (void __user*)bwr.read_buffer,bwr.read_size,&bwr.read_consumed,
                    flip->f_flags & O_NONBLOCK);
            }

            ...
    }
}

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()
1
2
3
4
5
6
7
8
9
static int binder_thread_read(struct binder_proc *proc,
    struct binder_thread *thread,void __user*buffer,int size,
    signed long *consumed,int non_block)
{
         
    ...
    ret=wait_event_interruptible_exclusive(proc->wait,
        binder_has_proc_work(proc,thread));
    ...
}

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()函数。

这个阶段的示意图如下:

binder_flow_stage2

Service Server也会生成binder_write_read结构体,包含write_buffer与read_buffer两个成员变量。其中,read_buffer变量用于接收IPC应答数据,write_buffer变量用于发送IPC数据。binder_write_read结构体内容如下图所示:

binder_ipc

此时binder_ioctl()函数中执行的主要代码如下:

binder_ioctl()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
static long binder_ioctl(struct file*filp, unsigned int cmd,unsigned long arg)
{
         
    struct binder_proc*proc=filp->private_data;
    void __user *ubuf=(void __user*)arg;
    thread=binder_get_thread(proc);
    switch(cmd){
         
        case BINDER_WRITE_READ:{
         
            struct binder_write_read bwr;
            if(copy_from_user(&bwr,ubuf,sizeof(bwr))){
         
                ret=-EFAULT;
                goto err;
            }
            if(bwr.write_size>0){
         
                ret=binder_write(proc,thread,(void __user*)bwr.write_buffer,bwr.write_size,&bwr.write_consumed);
            }
            if(bwr.read_size>0)
                ret=binder_thread_read(proc,thread,(void __user*)bwr.
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值