Android Binder IPC

The Binder communicates between processes using a small custom kernel module. The Binder IPC is used instead of standard Linux IPC facilities (name pipe, socket, signal and so on) so that we can efficiently model our IPC operations as “thread migration”. That is, an IPC between processes looks as if the thread instigating the IPC has hopped over to the destination process to execute the code there, and then hopped back with the result.

 

The Binder IPC mechanism itself, however,is not actually implemented using thread migration. Instead, the Binder’suser-space code maintains a pool of available threads in each process, which are used to process incoming IPCs and execute local events in that process. The kernel module emulates a thread migration model by propagating threadpriorities across processes as IPCs are dispatched and ensuring that, if an IPCrecourses back into an originating process, the IPC is handled by its originating thread.

 

In addition to IPC itself, the Binder’skernel module is also responsible for tracking object references across processes. This involves mapping from remote object references in one process to the real object in its host process, and making sure that objects are not destroyed as long as other processes hold references on them.

 

The rest of this document will describe in detail how Binder IPC works.


0.    Get Started

When a user-space process wants to participate in Binder IPC (either to send an IPC to another process or to receiving an incoming IPC), the first thing it must do is open the device supplied by the Binder kernel module. This associates a file descriptor with all threads of the process, which is used by the kernel module to identify the initiators and recipients of Binder IPCs.

 

It is through this specified descriptor that all interaction with the IPC mechanism will happen, through a small set of ioctl() commands. The main commands are:

BINDER_WRITE_READ sends zero or more Binder operations, and blocks waiting to receive incoming operations and return with a result. (This is the same as doing a normal write() followed by a read() on the file descriptor, just a little more efficient.)

BINDER_SET_IDLE_TIMEOUT sets the time period threads will remain idle (waiting for a new in coming transaction) before they time out.

BINDER_SET_MAX_THREADS sets the maximum number of threads that the driver is allowed to create for that process’s thread pool.

BINDER_SET_IDLE_PRIORITY sets the priority of threads in idle.

BINDER_SET_CONTEXT_MGR sets the current process as the service manager (service manager will be explained later).

BINDER_THREAD_EXIT destroys one thread.

BINDER_VERSION gets the version info of the Binder IPC driver.

 

The core functionality of the Binder IPC driver is encapsulated in the BINDER_WRITE_READ operation. The ioctl’s data isthis structure:

 

46 struct binder_write_read{

 47 signed long write_size;

 48 signed long write_consumed;

 49 unsigned long write_buffer;

 50 signed long read_size;

 51 signed long read_consumed;

 52 unsigned long read_buffer;

 53 };       

 

Upon calling the driver, the “write_buffer”contains a series of commands for it to perform, and upon return the“read_buffer” is filled in with a series of responses for the thread to execute. The “write_size” and “read_size” tell the total size of the“write_buffer” and “read_buffer” respectively. And the “write_consumed” and“read_consumed” indicates the buffer size parsed by the Binder IPC driver. In general the write buffer will consist of zero or more book-keeping commands(usually incrementing/decrementing object references) and ending with a command requiring a response (such as sending an IPC transaction or attempt to acquire a strong reference on a remote object). Likewise, the receive buffer will be filled with a series of book-keeping commands and end with the result for the last written command, or a command to perform a new nested operation.

 

A process can send the following list ofthe commands to the Binder IPC driver:

 

154 enum BinderDriverCommandProtocol {

155  BC_TRANSACTION = _IOW_BAD('c', 0, struct binder_transaction_data),

156  BC_REPLY = _IOW_BAD('c', 1, structbinder_transaction_data),

158 BC_ACQUIRE_RESULT = _IOW_BAD('c', 2, int),

160 BC_FREE_BUFFER = _IOW_BAD('c', 3, int),

162 BC_INCREFS = _IOW_BAD('c', 4, int),

163 BC_ACQUIRE = _IOW_BAD('c', 5, int),

164 BC_RELEASE = _IOW_BAD('c', 6, int),

165 BC_DECREFS = _IOW_BAD('c', 7, int),

167 BC_INCREFS_DONE = _IOW_BAD('c', 8, struct binder_ptr_cookie),

168 BC_ACQUIRE_DONE = _IOW_BAD('c', 9, struct binder_ptr_cookie),

170 BC_ATTEMPT_ACQUIRE = _IOW_BAD('c', 10, struct binder_pri_desc),

172  BC_REGISTER_LOOPER= _IO('c', 11),

174  BC_ENTER_LOOPER= _IO('c', 12),

175  BC_EXIT_LOOPER = _IO('c', 13),

177 BC_REQUEST_DEATH_NOTIFICATION =_IOW_BAD('c', 14, struct binder_ptr_cookie),

179  BC_CLEAR_DEATH_NOTIFICATION = _IOW_BAD('c',15, struct binder_ptr_cookie),

181 BC_DEAD_BINDER_DONE = _IOW_BAD('c', 16, void *),

183 };

 

The most interesting commands are BC_TRANSACTION and BC_REPLY, which initiate an IPC transaction and return are ply for a transaction, respectively.

 

To initiate an IPC transaction, you will essentially perform a BINDER_READ_WRITE ioctl with the write buffer containing BC_TRANSACTION command followed by a binder_transaction_data structure.

 

77 struct binder_transaction_data {

 79 union {

 80 size_t handle;

 81  void*ptr;

 82  } target;

 83  void*cookie;

 84 unsigned int code;

 85

 86 unsigned int flags;

 87 pid_t sender_pid;

 88 uid_t sender_euid;

 89 size_t data_size;

 90 size_t offsets_size;

 91

 92 union {

 93 struct {

 95 const void *buffer;

 97 const void *offsets;

 98  }ptr;

 99 uint8_t buf[8];

100 } data;

101 };

 

In this structure “target” is the handle of the binder object that should receive the transaction (the relationship between target.handle and target.ptr is explained later), “code” tells the object what to do when it receives the transaction, “priority” is the thread priority to run the IPC at, and there is a “data” buffer containing the transaction data,as well as an (optional) additional offsets buffer of meta-data.

 

The “offsets” meta-data will consist ofzero or more binder objects. The structure of a binder object is as follows:

 

12 struct binder_object

 13 {

 14    uint32_t type;

 15    uint32_t flags;

 16    void *pointer;

 17     void *cookie;

 18}; 

The types of binder objects are listed:

 

20 enum {          

 21  BINDER_TYPE_BINDER = B_PACK_CHARS('s', 'b', '*',B_TYPE_LARGE),

 22 BINDER_TYPE_WEAK_BINDER = B_PACK_CHARS('w', 'b', '*', B_TYPE_LARGE),

 23  BINDER_TYPE_HANDLE = B_PACK_CHARS('s', 'h', '*',B_TYPE_LARGE),

 24 BINDER_TYPE_WEAK_HANDLE = B_PACK_CHARS('w', 'h', '*', B_TYPE_LARGE),

 25 BINDER_TYPE_FD = B_PACK_CHARS('f', 'd', '*', B_TYPE_LARGE),

 26};          

 

Given the target handle, the Binder IPC driver determines which process the object lives in and dispatches this transaction to one of the waiting threads in its thread pool (spawning a new thread if needed and the maximum number of threads is set by the BINDER_SET_MAX_THREADS ioctl command). That thread is waiting in a BINDER_WRITE_READ ioctl to the driver, and so returns with its read buffer filled in with the commands it needs to execute. These commands are very similar to the write commands, for the most part corresponding to write operations on the other side.

 

119 enum BinderDriverReturnProtocol {

120 BR_ERROR = _IOR_BAD('r', 0, int),

122  BR_OK = _IO('r', 1),

124  BR_TRANSACTION = _IOR_BAD('r', 2, structbinder_transaction_data),

125  BR_REPLY = _IOR_BAD('r', 3, structbinder_transaction_data),

127 BR_ACQUIRE_RESULT = _IOR_BAD('r', 4, int),

129 BR_DEAD_REPLY = _IO('r', 5),

131 BR_TRANSACTION_COMPLETE = _IO('r', 6),

133 BR_INCREFS = _IOR_BAD('r', 7, struct binder_ptr_cookie),

134 BR_ACQUIRE = _IOR_BAD('r', 8, struct binder_ptr_cookie),

135 BR_RELEASE = _IOR_BAD('r', 9, struct binder_ptr_cookie),

136 BR_DECREFS = _IOR_BAD('r', 10, struct binder_ptr_cookie),

138 BR_ATTEMPT_ACQUIRE = _IOR_BAD('r', 11, struct binder_pri_ptr_cookie),

140 BR_NOOP = _IO('r', 12),

142 BR_SPAWN_LOOPER = _IO('r', 13),

144  BR_FINISHED = _IO('r', 14),

146 BR_DEAD_BINDER = _IOR_BAD('r', 15, void *),

148 BR_CLEAR_DEATH_NOTIFICATION_DONE = _IOR_BAD('r', 16, void *),

150 BR_FAILED_REPLY = _IO('r', 17),

152 };

 

The receiving thread will come back with a BR_TRANSACTION command at the end of its buffer. This command uses the same binder_transaction_data structure that was used to send the data, basically containing the same information that was send but now available in the local process space.

 

The recipient, in user space will then hand this transaction over to the target object for it to execute and return its result. Upon getting the result, a new write buffer is created containing the BC_REPLY reply command with a binder_transaction_data structure containing there sulting data. This is returned with a BINDER_WRITE_READ ioctl() on the driver, sending the reply back to the original process and leaving the thread waiting for the next transaction to perform.

 

The original thread finally returns back from its own BINDER_WRITE_READ with a BR_REPLY command containing the reply data.

 

Note that the original thread may also receive BR_TRANSACTION        commands while it is waiting for a reply. This represents a recursion across processes the receiving thread making a call on to an object back in the original process. It is the responsibility of the drive to keep track of all active transactions, so it can dispatch transactions to the correct thread when recursion happens.

 

A simple transaction progress between two processes can be illustrated as follows:


1.    What’s the service manager?

Usually, one process which provides binder objects maintains several services. That’s the reason that kind of process is sometimes called “service provider”. The service manger works as a special service provider and is known by all other processes- “target.handle” 0 is reserved for the service manager. The source code of the service manger is under the frameworks/base/cmds/servicemanager directory.

 

(1)   Service provider: A process provides services which can be accessed by another process. One service is identified by one binder object.

(2)   Service manager: Service manager is actually a special service provider, which collects all services provided by other processes. That’s to say, in order to let its service visible, one process should register its service to the service manager. And only one service manager is allowed in Binder IPC mechanism.

(3)   Any process, including the service provider itself, can access services provided by one service provider.Of course, the process should query the service manager to get the specified service.

 

   One process issuing the “BINDER_SET_CONTEXT_MGR” will notify the kerneldriver the current process is the service manager.

    The interaction between processes andservice manger is as follows.


 The process B is one service provider and registers one service to the service manager. The process A queries the service of process B and then communicates with process B directly.

 

2.  What Binder IPC driver does?

The Binder IPC driver works as the bridge between two processes in IPC communication. The source code is located at kernel/drivers/staging/android/ directory. The driver creates a miscellaneous device (major number is 10) - /dev/binder.

The relationship between the major structures is illustrated as follows.


From the structures, you can find that processes and binder objects participated in IPC communication are traced. The “binder_proc” structure is used to represent one process. Once one process opens the binder device (/dev/binder), one“binder_proc” structure will be created and the basic properties (pid,priority, threads number and so on) will be recorded. One thread in a process’sthreads pool is represented by the “binder_thread” structure. All threads in a process share the same “binder_proc” structure.

The“binder_node” structure describes one binder object provided by one process and the “binder_ref” structure indicates the reference to one binder object. If one process specifies one “BINDER_TYPE_BINDER” or “BINDER_TYPE_WEAK_BINDER” type object in a transaction, one “binder_node” structure will be created by saving the value pointed by the “pointer” in the object in the “ptr” field. And if one process specifies one “BINDER_TYPE_HANDLE” or “BINDER_TYPE_WEAK_HANDLE” type object in a transaction, one “binder_ref” structure will be created in local process by saving the value pointed by the “pointer” in the object in the“desc” field.

The“binder_transaction” is used to describe the transaction between two processes.The current handling transaction of a thread is recorded as the“transaction_stack” in the “binder_thread” structure.

The following figure illustrates how the “binder_node” and “binder_ref” structures are created during transactions between processes and service manager (different processes are distinguished by different colors).


Process B is aservice provider and process A tries to create a transaction to process B. Thecubics in different colors represent the “binder_node” in different process:there is one node in service manager (service manager node) and one node inprocess B. The cycles in different colors show the “binder_ref” in differentprocess: there are two references in process A (one is for service manager nodeand the other is for node in process B) and only one reference in servicemanager to the node in process B. In order to communicate with process B,process A should always provide the target (BINDER_TYPE_HANDLE – 1) for furthertransactions.

 

3.    Services in Android

There are totally about 50 services provided in Android system. You can use the“service list” command to check all services. The services related with Audio system are media.audio_policy and media.audio_fligner. Those services are created by the mediaserver. The source code is under the “frameworks/base/media/mediaserver”directory.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值