zoukankan      html  css  js  c++  java
  • 9.2 Binder系统_驱动情景分析_服务注册过程

    1. 几个重要结构体的引入
    给test_server添加一个goodbye服务, 由此引入以下概念:

    进程间通信其实质也是需要三要素:源、目的、数据,源是自己,目的用handle表示;通讯的过程是源向实现进程的“服务”发数据,handle是对“服务”的引用,在不同的进程里面handle不一样,即使多个进程对同一个进程的“服务”发数据,这些多个进程里面的handle可以也不一样

    总结:handle是进程A对进程B提供的服务S的引用,进程A调用binder_call把handle传递给驱动,驱动根据handle找到服务S,驱动内部会有个binder_ref结构体来描述服务S的引用,这个结构体里面有个desc,驱动程序对比一系列的binder_ref结构体里的desc,找到与handle对应的binder_ref,而对应的服务用binder_node来表示,binder_ref结构体里的node指向binder_node,根据binder_node结构体里的binder_proc可以找到进程B,可能存在多个进程A1、A2等同时向进程B请求服务,B在binder_node结构体中有个rb_root thread线程树来挂载这些线程,线程用结构体binder_thread来保存

    binder_ref
    binder_node
    binder_proc
    binder_thread
    binder_buffer

    (1)server出入一个flat_binder_object给驱动,在内核态驱动程序里根据flat_binder_object结构体为每个服务创建binder_node

     binder_node.proc = server进程

    (2)在内核态驱动程序中 service_manager创建binder_ref,其引用binder_node

     其binder_ref.desc的值取一系列的binder_ref最大值加1,desc的值从1开始往上加

     在用户态会创建服务链表,并未服务链表添加一项,含有name和handle,handle就等于binder_ref结构体里的desc

    (3)client向service_manager查询服务,向其传入name

    (4)service_manager返回handle给驱动程序

    (5)驱动程序在service_manager的一些列binder_ref中根据handle找到binder_ref,在根据binder_ref.node找到binder_node,最后给client创建新的binder_ref,其执行binder_node,且binder_ref的desc值从1开始,其client中一系列的binder_ref中desc的最大值,驱动返回desc给client,它即为handle

    (因此service_manager里面有一系列的binder_ref,其desc值描述的每个服务是由注册服务的顺序决定的,client里面也有一系列的binder_ref,其desc值描述的每个服务是由获取服务的顺序决定的)

    (6)client发数据给handle的时候,驱动根据handle找到binder_ref,在找到binder_node,在找到server进程

    数据传输过程:

    client                                                                           =>                      server

    client端步骤:

    (1)client构造数据,调用ioctl发数据          

    (2)驱动里根据handle找到server进程          

    (3)把进程放入进程的binder_proc.todo链表中去,  并唤醒server       

    (4)休眠                       

    (5)被唤醒 

    (6)从todo链表中取出数据,返回用户空间

    server端步骤:                        

     (1)读数据,无数据时休眠

    (2)被唤醒              

     (3)从todo链表中取出数据,返回用户空间

    (4)处理

    (5)把数据写入client进程的binder_proc.todo链表,唤醒client

    交互过程数据如何复制:

    一般方法需要2次复制:

    client构造数据,驱动copy_from_user;找到server后copy_to_user,用户态处理

    binder的方法:

    server程序mmap,server用户态可以直接范问驱动中某块内存;client构造数据,驱动copy_from_user数据后把数据保存到mmap分配的驱动中那块内存,这样server可以在用户态直接操作,此复制操作涉及的结构体是binder_buffer,一次复制仅指的是数据buffer,而binder_write_read结构体还是要复制两次来传递

    2. IPC数据交互过程
    进程间通过ioctl来传递数据,数据组织为binder_write_read结构体,其中write_buffer结构体用来保存输入的数据,当我们读数据的时候也是读到一个binder_write_read结构体,其中的read_buffer结构体用来保存读到的数据,read_buffer和write_buffer也是个结构体,其前面放着四字节表示数据类型,后面存放着数据本身。

     binder驱动在:drivers/staging/android/binder.c中,应用程序中ioctl被调用时,驱动中binder_ioctl被调用,更具cmd来执行,并且调用根据条件执行binder_thread_write或binder_thread_read:

    eg:binder_thread_write函数中binder_proc表示进程(结构体里的task_strcut有进程名字和ID),binder_thread表示线程,函数中的cmd表示消息类型

    在我们前面写的C语言binder例子中,client向server写数据的时候,数据类型是BC_TRANSACTION,读数据的时候数据类型是BR_TRANSACTION,因此我们在分析binder_thread_write或binder_thread_read的时候仅关注这两个数据类型的分支就可以了

    3. 服务注册过程(用到的数据是在驱动中打印出来的,因为应用程序会缓存消息,如果在应用程序中打印,消息的顺序可以能不准)

    可以阅读下面文章以了解BC_XXX, BR_XXX
    Android Bander设计与实现
    http://blog.csdn.net/universus/article/details/6211589

    binder驱动框架(binder驱动是调用misc_register来注册,misc_register是misc驱动提供)

    (1)misc设备驱动:file_operations中只有.open = misc_open;调用register_chrdev注册

    (2)binder驱动本身:构造miscdevice结构体,里面有个file_operations;调用misc_register来注册,把miscdevice结构体放入链表

    进程A向进程B发送数据,A发送的数据类型是BC_TRANSACTION,B接受的消息类型是BR_TRANSACTION,A发送的和B接受的是一个消息;B处理完数据后,会回给A的消息类型是BC_REPLY,A接收到的是BR_REPLY类型,在binder中,进程与进程间通信设计的消息类型就只有上述四种,其他消息类型都是进程与驱动之间通信,用来改变和设置某些状态。


    ./service_manager &

    service_manager进程在open的时候调用binder_open,其会创建一个binder_proc指向service_manager进程

    接着会有些ioctl,在第一次ioctl中,在binder_ioctl中会创建biner_thread,并且设置它
    service_manager (1362, 1362), binder_thread_write : BC_ENTER_LOOPER//在在binder_ioctl中会根据BC_ENTER_LOOPER来设置biner_thread的成员looper来改变状态
    service_manager (1362, 1362), binder_thread_read : BR_NOOP  //service_manager发起一个读操作,没数据的时候休眠,读所有的读操作,数据头部都是BR_NOOP,读出的数据是binder_write_read结构体,里面的readbuf数据格式为:BR_NOOP|cmd+数据|cmd+数据.......

    ./test_server &
    test_server (1363, 1363), binder_thread_write : BC_TRANSACTION //test_server发起一个写操作,数据里面肯定有服务的名称,数据会放入service_manager的todo链表,并且唤醒service_manager,(todo链表在binder_proc结构体中),server在svcmgr_publish函数中组织数据并调用ioctl传输数据,数据放在iodata这个buf中,通过binder_io结构体来管理数据格式,iodata数据buf格式如下:

    (1)在bio_init中把iodata前面16字节空出来,binder_io的offs指向iodata起始地址,data指向iodata起始地址+16字节;

    (2)bio_put_uint32(&msg,0)设置data区域四字节00 00 00 00

    (3)bio_put_string16_x(&msg,SVC_MGR_NAME)//SVC_MGR_NAME宏定义为"android.os.IServiceManager",先放字符串长度,用四字节表示,接着存放字符,每个字符用两字节表示

    (4)bio_put_string16_x(&msg,name)//name在这里是字符串hello,在iodate中接着上面的字符串存放,先放长度

    (5)bio_put_obj(&msg,ptr);//分配一个flat_binder_object结构体,设置这个结构体,并且把结构体的数据保存到iodata中,并且在iodata前面空出来的16个字节中用四字节保存flat_binder_object结构体的偏移,总共可以保存4个flat_binder_object结构体

    (6)接着调用binder_call,其调用ioctl传输数据,数据类型是BC_TRANSACTION,在binder_call中组织binder_write_read结构体,其中数据保存在结构体里面的binder_transaction_data结构体中

    其中data_size是iodata中除去前面16字节剩下数据域的长度,offsets_size是前面16字节中保存有flat_binder_object结构体的长度,data.ptr.buffer保存iodata+16字节的位置,data.ptr.offsets保存iodata起始地址

    (7)进入驱动binder_ioctl把数据放入service_manager进程的todo链表,并唤醒service_manager;把数据放入service_manager之前,驱动需要根据handle找到目的进程即service_manager;接着把数据copy_from_user(数据的复制过程是在binder_thread_write中,在调用其之前还有个copy_from_user,其只会复制头部)放入mmap指向的空间,在binder_thread_write中根据数据类型调用binder_transaction函数,对于BC_TRANSACTION其中会根据handle找到binder_ref结构体,根据结构体里的node->proc找到目的进程service_manager,然后把数据放到目的进程空间,并把flat_binder_object数据处理后放入todo链表,接着唤醒service_manager;

    (8)flat_binder_object结构体的第一个数据是type,表示当前传输的结构体是实体(BINDER_TYPE_BINDER)还是引用(BINDER_TYPE_HANDLE),只有server的提供者可以传输实体,server_manager和client只能是引用,server_manager接受数据后会修改flat_binder_object结构体的数据,就是下面对flat_binder_object数据的处理过程,可以见service_manager (1362, 1362), binder_thread_read : BR_TRANSACTION的收到的数据;第二个数据flag可以不用关心;第三个数据是根据第一个数据来的,当第一个数据是实体的时候,第三个数据是binder,是一个指针,可以保存服务的执行函数,在总执行函数中更handle执行不同的服务函数;如果第一个参数是引用,第三个参数是handle,即binder_refs->desc参数,第四个参数没用到。并且会给server创建binder_node节点,给server_manager创建binder_ref

    (9)处理flat_binder_object的过程就是针对该对象的个数(服务数)创建binder_node

    binder: 1363:1363 BC_TRANSACTION 2 -> 1362 - node 1, data beca6a5c-beca6a4c size 96-4

    test_server (1363, 1363), binder_transaction , print data :
    0000: 00 . 00 . 00 . 00 . 1a . 00 . 00 . 00 . 61 a 00 . 6e n 00 . 64 d 00 . 72 r 00 .
    0016: 6f o 00 . 69 i 00 . 64 d 00 . 2e . 00 . 6f o 00 . 73 s 00 . 2e . 00 . 49 I 00 .
    0032: 53 S 00 . 65 e 00 . 72 r 00 . 76 v 00 . 69 i 00 . 63 c 00 . 65 e 00 . 4d M 00 .
    0048: 61 a 00 . 6e n 00 . 61 a 00 . 67 g 00 . 65 e 00 . 72 r 00 . 00 . 00 . 00 . 00 .
    0064: 05 . 00 . 00 . 00 . 68 h 00 . 65 e 00 . 6c l 00 . 6c l 00 . 6f o 00 . 00 . 00 .
    0080: 85 . 2a * 62 b 73 s 7f . 01 . 00 . 00 . e0 . 88 . 00 . 00 . 00 . 00 . 00 . 00 .
    test_server (1363, 1363), binder_thread_read : BR_NOOP


    service_manager (1362, 1362), binder_thread_read : BR_TRANSACTION  //service_manager被唤醒后读到BR_TRANSACTION类型的数据,数据里面含有服务的名称“hello”,service_manager调用ioctl去读数据,没数据的时候休眠,被唤醒后从进程或者线程的todo链表中取出数据,把数据传给用户空间的时候把数据类型标记为BR_TRANSACTION  ,并且根据binder_transaction来构造binder_transaction_data,然后返回到server_manager,server_manager去解析数据,从数据中取出服务名字,并从flat_binder_object中取出handle,server注册的第一个服务器handle为1,第二个为2

    service_manager (1362, 1362), binder_thread_read , print data :
    0000: 00 . 00 . 00 . 00 . 1a . 00 . 00 . 00 . 61 a 00 . 6e n 00 . 64 d 00 . 72 r 00 .
    0016: 6f o 00 . 69 i 00 . 64 d 00 . 2e . 00 . 6f o 00 . 73 s 00 . 2e . 00 . 49 I 00 .
    0032: 53 S 00 . 65 e 00 . 72 r 00 . 76 v 00 . 69 i 00 . 63 c 00 . 65 e 00 . 4d M 00 .
    0048: 61 a 00 . 6e n 00 . 61 a 00 . 67 g 00 . 65 e 00 . 72 r 00 . 00 . 00 . 00 . 00 .
    0064: 05 . 00 . 00 . 00 . 68 h 00 . 65 e 00 . 6c l 00 . 6c l 00 . 6f o 00 . 00 . 00 .
    0080: 85 . 2a * 68 h 73 s 7f . 01 . 00 . 00 . 01 . 00 . 00 . 00 . 00 . 00 . 00 . 00 .
    test_server (1363, 1363), binder_thread_read : BR_INCREFS
    test_server (1363, 1363), binder_thread_read : BR_ACQUIRE
    test_server (1363, 1363), binder_thread_read : BR_TRANSACTION_COMPLETE
    service_manager (1362, 1362), binder_thread_write : BC_ACQUIRE
    test_server (1363, 1363), binder_thread_read : BR_NOOP
    service_manager (1362, 1362), binder_thread_write : BC_REQUEST_DEATH_NOTIFICATION
    service_manager (1362, 1362), binder_thread_write : BC_FREE_BUFFER


    service_manager (1362, 1362), binder_thread_write : BC_REPLY    //service_manager处理完数据后,发送一个BC_REPLY类型的数据,表示其处理完了
    binder: 1362:1362 BC_REPLY 5 -> 1363:1363, data bed9fa4c-bed9fa3c size 4-0
    service_manager (1362, 1362), binder_transaction , print data :
    0000: 00 . 00 . 00 . 00 .
    test_server (1363, 1363), binder_thread_read : BR_REPLY    //test_server读到BR_REPLY类型数据
    test_server (1363, 1363), binder_thread_read , print data :
    0000: 00 . 00 . 00 . 00 .
    service_manager (1362, 1362), binder_thread_read : BR_NOOP
    test_server (1363, 1363), binder_thread_write : BC_FREE_BUFFER
    service_manager (1362, 1362), binder_thread_read : BR_TRANSACTION_COMPLETE
    service_manager (1362, 1362), binder_thread_read : BR_NOOP
    test_server (1363, 1363), binder_thread_write : BC_ENTER_LOOPER
    test_server (1363, 1363), binder_thread_read : BR_NOOP
    svcmgr: add_service('hello'), handle = 1

    数据分析见图片:Binder系统_驱动情景分析2_服务注册过程.jpg



  • 相关阅读:
    react fake double , bind click and dblclick on the same element
    Microbit MicroPython 介绍
    树莓派Raspberry Pi微改款,Model B 3+规格探析
    用Micro:bit做剪刀、石头、布游戏
    用Micro:bit做交通信号灯
    树莓派 Raspberry Pi 与 micro:bit起手式
    Microbit蓝芽配对
    micro:bit 软件生态系统介绍
    Micro:bit 硬件架构介绍
    Ruby 学习笔记7
  • 原文地址:https://www.cnblogs.com/liusiluandzhangkun/p/9141049.html
Copyright © 2011-2022 走看看