Binder IPC机制

来源:互联网 发布:qq飞车抽奖辅助软件 编辑:程序博客网 时间:2024/05/18 00:52
 

< Binder Process Model | Binder Kit | pidgen >

________________________________________

http://www.angryredplanet.com/~hackbod/openbinder/docs/html/BinderProcessModel.html

Binder采用一个定制的内核小模块在进程间进行通信。这是用来代替标准的Linux的IPC设施,使我们能够有效地为IPC操作模型化为"thread migration"。也就是说,在进程间的IPC看来,如果线程触发的IPC已经跳上了到目标进程,执行该代码,然后跳下,结果跳回来。

 

Binder IPC机制本身不是真正实现使用线程迁移。相反,Binder的用户空间代码在每一个进程维护一个可用的线程,它是用来处理一些传入的IPC,并在这一过程中执行本地事件。内核模块通过产生一些跨进程线程优先级的模拟线程迁移模型,并确保IPC被派遣,如果一个IPC的递归回原进程中,IPC是由其原线程处理。

 

除了本身的IPC,Binder的内核模块还负责跟踪对象引用跨进程负责。这涉及到从一个进程中远程对象的引用,以在其宿主进程的真正对象映射,并确保对象不会被破坏,只要其他进程持有的他们的引用。

 

本文件的其余部分将详细描述Binder IPC如何运作。这些细节都没有接触到应用开发,使他们能够安全地忽略。

 

Getting Started

当一个用户空间线程想要加入Binder IPC(传一个IPC到另一个进程或接收一个IPC),首先必须做的就是打开的Binder内核模块提供的驱动程序。该线程领取一个文件描述符,这在内核模块用来标识IPC的发起者和接收者。

正是通过这个文件描述,所有与IPC机制将发生相互作用,通过一系列的ioctl()命令小集。主要的命令是:

?BINDER_WRITE_READ发送零个或多个Binder操作,然后阻塞等待接收传入的操作和结果。 (这与对一个文件描述符先做write()然后read()操作一样,只是效率高点)

?BINDER_SET_WAKEUP_TIME设置在其中一个用户空间的事件是发生在预定时间调用进程。

?BINDER_SET_IDLE_TIMEOUT设置线程将保持空闲的时间(等待一个新的transaction)超时。

?BINDER_SET_REPLY_TIMEOUT设置时间线程将阻止等待答复,直到它们超时。

?BINDER_SET_MAX_THREADS设置线程的最大数目,该驱动程序允许创建该进程的线程池。

关键是BINDER_WRITE_READ命令,这是所有IPC的业务基础。在进入有关的细节去,但是,应该指出的driver希望用户代码以维护一个线程池以等待传入的 transactions。您需要确保始终有一个可用的线程(线程到你想的最大数目),使IPC均可被处理。当在须处理在本地进程的新的异步事件(from SHandler) 的时候,该driver还须注意唤醒线程池。BINDER_WRITE_READ

如上所述,该驱动程序的核心功能是封装在BINDER_WRITE_READ运作。 ioctl的数据是这样的结构:

struct binder_write_read

{

    ssize_t     write_size;

    const void* write_buffer;

    ssize_t     read_size;

    void*       read_buffer;

};

当调用驱动程序时,write_buffer包含一系列的命令来执行它,并在read_buffer充满一系列线程执行应答后返回。一般来说,写缓冲区将包括零个或多个book-keeping命令(通常用递增/递减对象引用),用一个命令方式结束,并需要应答(如发送的IPC transaction或试图获得对远程对象的强引用回应) 。同样,接收缓冲区将填充一系列book-keeping命令,或者是last written,或者是new nested命令操作来结束。

这里是可以由一个进程发送到驱动程序的命令列表,对如下数据缓冲区中的每个命令作了描述:

enum BinderDriverCommandProtocol {

    bcNOOP = 0,

        No parameters! 

 

    bcTRANSACTION,

    bcREPLY,

 

        binder_transaction_data: the sent command.

 

 

    bcACQUIRE_RESULT,

 

        int32:  0 if the last brATTEMPT_ACQUIRE was not successful.

        Else you have acquired a primary reference on the object.

 

 

    bcFREE_BUFFER,

 

        void *: ptr to transaction data received on a read

 

 

    bcINCREFS,

    bcACQUIRE,

    bcRELEASE,

    bcDECREFS,

 

        int32:  descriptor

 

 

    bcATTEMPT_ACQUIRE,

 

        int32:  priority

        int32:  descriptor

 

 

    bcRESUME_THREAD,

 

        int32:  thread ID

 

 

    bcSET_THREAD_ENTRY,

 

        void *: thread entry function for new threads created to handle tasks

        void *: argument passed to those threads

 

 

    bcREGISTER_LOOPER,

 

        No parameters.

        Register a spawned looper thread with the device.  This must be

        called by the function that is supplied in bcSET_THREAD_ENTRY as

        part of its initialization with the binder.

 

 

    bcENTER_LOOPER,

    bcEXIT_LOOPER,

 

        No parameters.

        These two commands are sent as an application-level thread

        enters and exits the binder loop, respectively.  They are

        used so the binder can have an accurate count of the number

        of looping threads it has available.

 

 

    bcCATCH_ROOT_OBJECTS,

 

        No parameters.

        Call this to have your team start catching root objects

        published by other teams that are spawned outside of the binder.

        When this happens, you will receive a brTRANSACTION with the

        tfRootObject flag set.  (Note that this is distinct from receiving

        normal root objects, which are a brREPLY.)

 

 

    bcKILL_TEAM

 

        No parameters.

        Simulate death of a kernel team.  For debugging only.

 

};

最有趣的命令,这里有bcTRANSACTION和bcREPLY,分别是发起的IPC  transaction,答复transaction。这些命令是以下数据结构:

enum transaction_flags {

    tfInline = 0x01,            // not yet implemented

    tfRootObject = 0x04,        // contents are the component's root object

    tfStatusCode = 0x08         // contents are a 32-bit status code

};

 

struct binder_transaction_data

{

    // The first two are only used for bcTRANSACTION and brTRANSACTION,

    // identifying the target and contents of the transaction.

    union {

        size_t  handle;     // target descriptor of command transaction

        void    *ptr;       // target descriptor of return transaction

    } target;

    uint32  code;           // transaction command

 

    // General information about the transaction.

    uint32  flags;

    int32   priority;       // requested/current thread priority

    size_t  data_size;      // number of bytes of data

    size_t  offsets_size;   // number of bytes of object offsets

 

    // If this transaction is inline, the data immediately

    // follows here; otherwise, it ends with a pointer to

    // the data buffer.

    union {

        struct {

            const void  *buffer;    // transaction data

            const void  *offsets;   // binder object offsets

        } ptr;

        uint8   buf[8];

    } data;

};

    因此,要启动一个IPC的transaction,您将主要执行一个BINDER_READ_WRITE的ioctl, 执行abinder_transaction_data后,写入含bcTRANSACTION,缓冲区 。此结构的目标是接收transaction对象的handle(我们将在以后讨论handle),代码应当提供对象,当它接收的transaction,应当做什么,优先级-----线程优先运行IPC的级别,和一个数据缓冲区-----包含transaction 数据,以及一个(可选)的元数据的额外offsetsbuffer。

出于目标handle,驱动程序确定对象在哪个的进程,dispatches 这个 transaction给线程池一个等待线程,(如果需要则产生一个新的线程)。该线程正在等待一个BINDER_WRITE_READ的ioctl()的驱动程序,所以通过填充执行命令在缓冲区返回它的read buffer。这些命令与写命令非常相似,另一方面的大部分对应于相应的写操作:

enum BinderDriverReturnProtocol {

    brERROR = -1,

 

        int32: error code

 

 

    brOK = 0,

    brTIMEOUT,

    brWAKEUP,

        No parameters! 

 

    brTRANSACTION,

    brREPLY,

 

        binder_transaction_data: the received command.

 

 

    brACQUIRE_RESULT,

 

        int32: 0 if the last bcATTEMPT_ACQUIRE was not successful.

        Else the remote object has acquired a primary reference.

 

 

    brDEAD_REPLY,

 

        The target of the last transaction (either a bcTRANSACTION or

        a bcATTEMPT_ACQUIRE) is no longer with us.  No parameters.

 

 

    brTRANSACTION_COMPLETE,

 

        No parameters... always refers to the last transaction requested

        (including replies).  Note that this will be sent even for asynchronous

        transactions.

 

 

    brINCREFS,

    brACQUIRE,

    brRELEASE,

    brDECREFS,

 

        void *: ptr to binder

 

 

    brATTEMPT_ACQUIRE,

 

        int32:  priority

        void *: ptr to binder

 

 

    brEVENT_OCCURRED,

 

        This is returned when the bcSET_NEXT_EVENT_TIME has elapsed.

        At this point the next event time is set to B_INFINITE_TIMEOUT,

        so you must send another bcSET_NEXT_EVENT_TIME command if you

        have another event pending.

 

 

    brFINISHED

};

    接下来我们的例子中,线程将在其缓冲区的末尾接收brTRANSACTION命令。此命令使用相同的binder_transaction_datastructure被用来发送数据,基本上包含相同的信息已发送,但现在在本地进程的可用空间。

    在用户空间的接收者将传送这个transaction至目标对象,执行并返回其结果。根据结果,一个新的写缓冲区创建载有binder_transaction_data结构bcREPLY答复包,包含结果数据的命令。这是一个driver返回BINDER_WRITE_READ的ioctl(),发送的答复回到原来的进程,并离开线程等待下一个 transaction 执行等。

原来的线程最终返回自己的BINDER_WRITE_READ----一个包含回复数据的brREPLY命令。

请注意,当等待答复时,原来的线程也可能会收到brTRANSACTION命令。这是一个递归过程,在接收线程调用一个对象回到原来的进程。这是driver的责任,保持跟踪所有active transactions,因此它可以dispatch transactions向正确的线程递归时发生。

Object Mapping and Referencing

    对driver的重要职责之一是从一个进程的对象执行映射到另一个。这是两个关键的沟通机制(targetting和referencing objects), 和能力模型(只允许一个特定的进程来操作远程对象----并已显示告知)。

    有两种不同的对象的引用形式:作为一个进程的内存空间中的地址,或作为一个抽象的32位handle。这些表是互斥的:一个进程在一个本地的对象进程的所有引用采用地址的形式,而对另一个进程中的所有引用对象总是采用句柄的形式。

    例如,请注意binder_transaction_data的目标领域。当发送一个transaction,这包含对目标对象的handle(因为你总是发送transactions到其他进程)。这项transactions的接收者则认为这个对象是本地地址空间的指向。该驱动程序维护的进程之间的指针和handles的映射 ,以便它可以执行这个解释翻译操作。

    我们还必须能够通过transactions发送对象的引用。这是通过把对象的引用(指针无论是本地或远程处理)到该transactions.的缓冲区。driver必须翻译这个引用,对应于接收进程相应的引用,这样,就像我们做的transaction目标。 

    为了做引用翻译,driver 需要知道这些transaction 数据出现的引用在哪里。这就是额外的缓冲区偏移指向,它具有一系列的数据缓冲区索引,描述对象出现在哪里。该驱动程序可以重写缓冲区数据,翻译每一个从发送过程的对象引用至接收过程中的正确的引用。 

    请注意该驱动程序不知道任何特定的Binder对象,直到该对象是通过驱动程序发送到另一个进程。在这一点上,驱动程序添加对象的地址映射表,并要求其所属的进程就此进行引用。当没有其他进程知道对象,它就从映射表中删除,其过程是告诉释放driver的引用。这就避免了维护对象(相对重要的)driver state,只要仅在本地进程中使用的驱动程序的状态。

原创粉丝点击