binder驱动和内核交互笔记

进程只运行在进程固有的虚拟地址空间, 剩下的1G是内核空间 用户代码和相关库都运行在用户空间的代码区域. 两个进程共享的内核空间  binder driver是通信媒介
ipc由调用服务号,调用函数名,binder协议构成
handle是指服务号, 区分服务, binder driver通过handle值确定binder ipc数据传递到哪个服务中
RPC代码表示待调函数 RPC数据 是传递的参数
文件运算符函数  open系统调用到内核binder_open  __open()是系统调用
mmap函数,在内核中开辟一块区域,存放接收IPC数据. 调用ioctl()方法,将IPC数据作为参数,传递给Binder
        BINDER_WRITE_READ   进程间接收发送Binder IPC数据  struct binder_write_read
        BINDER_SET_MAX_THREADS  设定注册到binder driver中binder线程的最大个数   size_t
        BINDER_SET_CONTEXT_MGR   设定服务管理者
        BINDER_THREAD_EXIT  删除Binder线程
        根据Binder协议,决定是否传输协议
IPC层传递给binder driver的BINDER_COMMAND_PROTOCOL 通过Binder driver向接收端发送IPC数据时使用
binder driver传递给ipc层的BINDER_RETURN_PROTOCOL
/home/mec/goldfish/drivers/staging/android/bind.h
驱动收到信息会查询ipc数据中的handle信息查找相应的Service,然后driver将协议更改为BR_TRANSACTION,并将其重新插入IPC数据中,然后传递到server中  具体调用IPC数据中的哪个函数由ipc数据中的RPC代码决定
IPC应答数据  BR_REPLY   BC_REPLY
调用一个已经定义好的函数,通常需要提出函数的名称,并传递相应的参数.
binder驱动通过handle查找server,我们称为Binder寻址. 为了顺利实现寻址,server一般必须先把自身服务的访问信息注册到Manager中.  注册的过程中 server会向binder驱动传输IPC数据,其中包含RPC代码 ADD_SERVICE RPC数据(注册服务名称)
并且handle值设置为0, service会将IPC数据传递给Manager,然后driver驱动会生成一个binder节点.用来表示server中的服务A.
接下来生成binder节点引用数据,以便manager识别binder节点,并将相关节点连接起来,根据生成的顺序,引用数据会被编号.这种编号会将插入IPC数据中,传递给Manager, manager会根据IPC数据中服务的名称和BInder节点编号,将服务注册到自身的服务目录列表中
manager根据请求的服务名称 在自身持有的服务列表中查找相对应的服务编号.  驱动将客户端的引用数据和manager引用数据所指的binder节点连接起来.
manager(接收IPC数据,发送IPC应答数据)中mmap函数在内核中开辟一块用于接收ipc数据的buffer 再调用ioctl函数进入待机状态 等待ipc数据.
server((发送IPC数据,接收IPC应答数据)中调用mmap函数在内核中开辟一块用于接收ipc 应答数据 的buffer  移动的数据是RPC数据和binder协议,ipc数据在内核组装
服务的编号 就是服务引用
注册是时候 RPC数据是服务名称  RPC代码注册函数ADD_SERVICE
使用服务   RPC代码 制定服务函数,RPC数据 服务函数的参数
驱动角度分析  binder_proc open的时候创建   接收IPC数据的buffer 大小为调用mmap时制定的大小,保存在binder_buffer结构体中  而后binder_buffer结构体注册到binder_proc
驱动通过handle查找到manager的binder_node和binder_proc结构体,然后为要注册的服务,生成binder_node结构体.驱动将binder_node分别注册到server的binder_proc和manager的binder_proc,(驱动会分别将binder_node注册到server的binder_proc和manager的binder_proc结构体之中)然后驱动找到manager的binder_buffer发送IPC数据.


进程 buffer发送ipc数据->驱动,驱动通过handle找到服务和binder_proc,然后把数据通过其中的buffer继续转发


检索的时候 是指向manager 请求指定名称的handle作为返回值
驱动将服务的binder_node结构体注册到客户端的binder_proc


一般server向manager注册的时候才会生成binder_node结构体,但是manager直接访问驱动,生成binder_node.并且该binder_node的编号为0.


发送请求的时候会在驱动中找到 请求的binder_proc结构体,向其中接收的buffer中复制ipc数据.
请求返回的时候会把服务的binder节点注册到请求的binder_proc结构体,将ipc应答数据复制到buffer进去,唤醒客户端传递ipc应答数据.
驱动根据manager返回的服务编号handle查找相对于的binder_node结构体,而后将查找到的binder_node结构体注册到客户端的binder_proc,binder_node结构体用于在服务使用阶段查找server的binder_proc结构体.


服务使用
驱动接收到数据,经过处理,会唤醒一方,然后让一方进入待机状态.
函数分析
        进程使用binder驱动第一步就是打开文件binder_open 会初始化binder_proc结构体,初始化待机队列(wait queue).用来将进程切换到待机状态下.
        binder_proc称为binder驱动的根结构体
mmap将用户空间的特定区域映射到内核空间的特定区域.  用户空间中的进程不能直接访问内核空间,所以只能通过内核空间的特定映射区域来访问内核空间.
cat pid/maps查看链接信息
/home/mec/froyo/build/core/prelink-linux-arm.map
使用binder的进程调用驱动的mmap函数创建与用户空间映射的binder mmap区域, 并且 驱动只在内核空间的数据接收区保存数据,
用户空间的接收区域与内核空间的ipc数据接收区域由binder_mmap函数映射在一起.
在内核中开辟buffer,然后和用户空间的buffer映射起来
用户空间和内核空间的buffer要映射到实际的物理内存上.
binder_update_page_range分配物理页,
binder_insert_free_buffer   free_buffers变量是接收ipc数据的buffer 根据ipc数据的大小,分配不同大小的free_buffer
//binder_write_read是在RPC代码中  BINDER_WRITE_READ命令码在在RPC中
上面的应该是ipc代码???????????
binder_write_read中的write_buffer成员变量 在传递经由驱动的数据时使用,来发送ipc数据或ipc应答数据
中的read_buffer 接收来自驱动的数据  ipc数据以何种形态存在binder_write_read结构体中的write_buffer 和read_buffer
handle RPC代码和RPC数据保存在binder_transaction_data的结构体中  加上binder协议 就是IPC数据
        binder协议在前,后面依次是handle RPC代码和RPC数据   包含在了write_buffer中228图有问题
binder_write_read结构体在传输的过程中(用户空间到内核空间) 使用了copy_from_user
 接收完数据要传递到用户空间处理
将用户空间中的数据拷贝到自身的binder_write_read结构体中
        根据read_size和write_size 变量是发送ipc数据还是接收ipc数据
                接收时将生成相应的read_buffer,并且read_size大于零
interruptible可中断的  exclusive独有的  通过当前进程的binder_proc结构体wait变量将当前任务注册到待机队列中.  然后将task_truct结构体内的state变量又TASK_RUNNING 更改为TASK_INTERRUPTIBLE再调用schedule函数
wake_up即可唤醒
binder_write_read
        中的结构体,一个是接收ipc数据,一个是发送ipc数据.
                服务名称和flat_binder_object(数据,注册和检索时,用于生成或查找binder节点)结构体会被注册到ipc数据的RPC数据中flat_binder_object要么代码节点(BINDER_TYPE_BINDER)要么是引用  传送write_size就大于零
                consumed指ipc数据的个数   get_user获取binder协议
在binder_transaction_data中 协议排在第一位,然后是RPC数据  将指针所指向的内容重新拷贝到内核空间中
接收端和发送端通过binder_transaction结构体收发数据
        target_proc指向了接收端的binder_proc结构体  wait_queue_head_t用来帮助接收端脱离待机状态
通过handle找到binder_node然后找到对于的接收端进程
        binder_proc中有很多Binder_node,且binder_node中的binder_proc指向同一个


向接收端发送数据
        t->from 来自线程此处的thread为binder_get_thread获取的 通过线程查找到所在进程
           t->to_proc 到达进程
t.buffer 为binder驱动程序为该事务分配的一块内存缓冲区然后把事务注册
        struct binder_transaction *transaction;  //每一个事务都关联一个目标Binder实体对象
                一个事务中含有一个小的binder_buffer结构体,来传送ipc数据
/////////////////////////////////////////////
一个copy_from_user完成了从发送进程到接收进程的转换
binder_buffer结构体是小的.
新生成的binder节点会被注册到当前进程的binder_proc中
binder_get_ref_for_node检查binder节点是否存在于binder_proc进程中 若存在,则返回,不然新建,并将binder节点注册其中.并且binder_ref也注册到进程中refs_by_node变量中
        desc首次注册服务时生成,编号用来在manager中管理服务列表中不同的服务.   这些东西都注册到了binder_proc中
wait_queue_head_t  *target_wait;   target_list 接收端的 是todo(数据在这里面找 binder_work(更底层了) 传输数据协议) 添加到wait中执行
wake_up_interruptible函数唤醒
//////////////////////////////////驱动中完成
发送端执行
binder_thread_write函数进入等待  在binder_ioctl层面

        在接收端先查找出来binder_work,   

Container_of第一参数是结构体成员变量的地址,第二个是结构体定义,第三个是第一个参数在结构体中的名称

接收的时候不要调用copy_to_user,应用内核和用户空间都映射到了物理空间上面
BR_TRANSACTION是在接收端的内核进程中加的.
binder_write_read.read_buffer  等于binde协议BR_TRANSACTION + binder_transaction_data
把binder_transaction保存在线程的transaction_stack              查找另一端接收进程


服务检索阶段 的时候返回会创建flat_binder_object
binder_write_read.write_buffer  等于binde协议BC_TRANSACTION + binder_transaction_data
                                                handle(0) RPC代码 RPC数据(服务名称)
  服务端应答
binder_write_read.write_buffer  等于binde协议BC_REPLY + binder_transaction_data
          handle(0) RPC代码 RPC数据(binder_object)服务目录编号 type设为HANDLE
服务使用阶段
binder_write_read.write_buffer  等于binde协议BC_TRANSACTION + binder_transaction_data
          handle(binder_ref中的desc) RPC代码服务函数 RPC数据(服务函数参数)
          handle-> binder_ref->binder_node->binder_proc
          通过handle获取binder_ref(此结构体由manager持有)
        还要向服务客户端注册binder节点,都会生成一个binder_ref,同时desc加1,然后修改handle编号的值
服务目录 通过/system/service程序即可查看  service list查看服务列表
manager采用c语言编写  驱动编写 

猜你喜欢

转载自blog.csdn.net/jiyilanzhou/article/details/51914192
今日推荐