Android Binder 機制之 ServiceManager 模塊
ServiceManager 啟動源碼分析
以 Android 9.0 代碼為例介紹
Init 拉起 ServiceManager 進程
init 進程通過 init.rc 腳本拉起 Native 層的 ServiceManager 進程
- init.rc
// system/core/rootdir/init.rc
on late-init
...
trigger post-fs # late_init 事件觸發 post_fs 事件
...
on post-fs
load_system_props
start logd
start servicemanager # post-fs 事件中啟動三個服務管理進程
start hwservicemanager
start vndservicemanager
- init.cpp
// system/core/init/init.cpp:740
int main(int argc, char** argv) {
std::string bootmode = GetProperty("ro.bootmode", "");
if (bootmode == "charger") {
am.QueueEventTrigger("charger");
} else {
am.QueueEventTrigger("late-init"); // 當前的 bootmode 不是 charger 時,將觸發 late-init 事件
}
...
am.ExecuteOneCommand(); // 執行該事件里設定好的命令,即拉起 servicemanager 進程
}
ServiceManager 啟動
ServiceManager 啟動后完成兩件事
- 打開 binder 驅動
- 將自己注冊到 binder 驅動中,設置自己為守護進程,等待服務端和客戶端的調用
然后 ServiceManager 進程陷入循環,等待請求
// frameworks/native/cmds/servicemanager/service_manager.c
int main(int argc, char** argv){
...
driver = "/dev/binder";
bs = binder_open(driver, 128*1024); // 打開 binder 驅動
...
if (binder_become_context_manager(bs)) { // 將當前進程設置為守護進程
...
}
...
binder_loop(bs, svcmgr_handler); // 在 svcmgr_handler() 函數中循環等待 client 發過來的請求
...
}
1. ServiceManager 打開 binder 驅動
在 binder_open(),完成兩件事
- 獲取 binder 驅動的句柄文件
- 和 binder 內核完成共享內存步驟
binder_open() 打開驅動
system_manager 會在 main.cpp 中,調用 open 系統調用
// frameworks/native/cmds/servicemanager/binder.c
struct binder_state *binder_open(const char* driver, size_t mapsize){
struct binder_state *bs; // 記錄 binder 的結構體
...
bs->fd = open(driver, O_RDWR | O_CLOEXEC); // 打開文件節點的文件描述符
...
// 寫 BINDER_VERSION 到 Binder 內核
if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) ||
(vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION)) {
fprintf(stderr,
"binder: kernel driver version (%d) differs from user space version (%d)\n",
vers.protocol_version, BINDER_CURRENT_PROTOCOL_VERSION);
goto fail_open;
}
...
bs->mapsize = mapsize; // 分配的內存映射的空間大小
bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0); // 分配的內存的空間地址
}
binder_open() 內核處理
上層通過系統調用接口 open() 調用到 binder 內核中的 binder_open() 函數,調用過程如下:
- 應用進程調用
open()系統調用,請求并打開指定文件 - 系統調用轉為內核中的
do_sys_open()函數調用 - 該函數在
VFS(Virtual File System)層查找并打開指定的文件設備,并創建一個新的 file 結構體實例 - VFS 設置 file 結構體的各個字段,包含與設備文件相關聯的操作函數(read/write/ioctl)
do_sys_open()函數返回一個文件描述符,該文件描述符與新創建的 file 結構體關聯- 如果請求的 Binder 設備文件,VFS 會調用
binder_open函數,并將 file 結構體作為參數傳遞過去。
Binder 內核在 binder_open() 函數中做了 4 件事,分別是
- 創建
binder_proc結構體,存儲于 Binder 相關的進程信息,每個使用 Binder 的進程都有一份獨立的binder_proc實例 - 初始化數據結構,包括初始化 todo 隊列和等待隊列,這些隊列用于管理 Binder 事務和進程間通信
- 記錄
binder_proc,將創建的binder_proc添加到內核的binder_procs列表中,這樣系統就可以在需要時訪問和管理所有的 Binder 進程 - 分配文件描述符,
binder_open()會為應用進程分配一個文件描述符,以便應用進程可以通過該描述符與 Binder 驅動進行通信
// device/renesas/kernel/drivers/android/binder.c
static int binder_open(struct inode *nodp, struct file *filp)
{
struct binder_proc *proc;
...
proc = kzalloc(sizeof(*proc), GFP_KERNEL); // 初始化 binder_proc
...
INIT_LIST_HEAD(&proc->todo); // 初始化 todo 隊列
...
INIT_LIST_HEAD(&proc->waiting_threads); // 初始化 等待隊列
...
filp->private_data = proc; // 將 binder_proc 記錄到 file 實例中
....
hlist_add_head(&proc->proc_node, &binder_procs); // 將 binder_proc 記錄到 binder_procs 列表中
...
}
binder_mmap() 內核的內存映射處理
mmap 系統調用最終會到 binder_mmap 函數中
binder_mmap 完成以下幾個工作:
- 內存映射:binder_mmap 會在內核虛擬地址空間中申請一塊與用戶虛擬地址內存相同大小的內存
- 物理分配內存:它會申請一塊頁面大小的物理內存
- 映射同步:將同一塊物理內存分別映射到內核虛擬地址空間和用戶虛擬內存空間,實現用戶空間的 Buffer 和內核空間的 Buffer 同步操作
// device/renesas/kernel/drivers/android/binder.c
static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
{
...
// 獲取對應進程的信息
struct binder_proc *proc = filp->private_data;
...
// 設置了一塊內存的操作接口,打開關閉和缺頁等
// 并且將該塊內存和指定進程綁定
vma->vm_ops = &binder_vm_ops;
vma->vm_private_data = proc;
...
ret = binder_alloc_mmap_handler(&proc->alloc, vma);
...
}
// device/renesas/kernel/drivers/android/binder_alloc.c
int binder_alloc_mmap_handler(struct binder_alloc *alloc,
struct vm_area_struct *vma)
{
...
// 在內核虛擬地址空間中申請一塊與用戶虛擬內存大小相同的內存
// 將該內存與當前進程綁定
alloc->pages = kzalloc(sizeof(alloc->pages[0]) *
((vma->vm_end - vma->vm_start) / PAGE_SIZE),
GFP_KERNEL);
alloc->buffer_size = vma->vm_end - vma->vm_start; // 記錄虛擬內存空間大小
// 申請一塊頁面大小的物理內存
buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
buffer->user_data = alloc->buffer; // 物理地址地址和用戶虛擬地址和內核虛擬地址綁定
list_add(&buffer->entry, &alloc->buffers);
buffer->free = 1;
binder_insert_free_buffer(alloc, buffer);
alloc->free_async_space = alloc->buffer_size / 2;
binder_alloc_set_vma(alloc, vma);
mmgrab(alloc->vma_vm_mm);
...
}
需要注意的是:以上代碼是早期版本內核的 mmap 處理步驟,等到 4.19 版本之后,內存不再申請一塊內存地址了,而 binder_mmap 也不再做映射了,等到進程通信數據傳遞的時候,才會完成用戶空間虛擬地址到物理地址的映射,然后再把數據拷貝到這塊物理內存中完成數據傳遞。
架構圖
如上,ServiceManager 的 binder 驅動已打開,并且已經做好 ServiceManager 和 Binder 內核的共享內存申請
架構圖如下:
// todo
2. ServiceManager 注冊自己為守護進程
在 Native 層
// frameworks/native/cmds/servicemanager/binder.c
int binder_become_context_manager(struct binder_state *bs)
{
// 通過 ioctl 將 binder 描述符傳入,并且通過指令 BINDER_SET_CONTEXT_MGR 設置為守護進程
return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}
在 Binder 內核中完成三件事
- 在 Binder 驅動層創建 binder_node 結構體對象
- 將當前的 binder_proc 加入到 binder_node 的 node->proc
- 創建 binder_node 的 async_todo 和 binder_work 兩個隊列
// device/renesas/kernel/drivers/android/binder.c
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
switch (cmd) {
...
case BINDER_SET_CONTEXT_MGR:
ret = binder_ioctl_set_ctx_mgr(filp, NULL);
if (ret)
goto err;
break;
...
}
}
static int binder_ioctl_set_ctx_mgr(struct file *filp,
struct flat_binder_object *fbo)
{
...
// 確保守護進程只注冊一次
if (context->binder_context_mgr_node) {
pr_err("BINDER_SET_CONTEXT_MGR already set\n");
ret = -EBUSY;
goto out;
}
...
if (uid_valid(context->binder_context_mgr_uid)) {
// 檢查 uid 有效
} else {
context->binder_context_mgr_uid = curr_euid; // 無效的話將當前線程 euid 設置為 service_manager
}
...
new_node = binder_new_node(proc, fbo); // 創建 service_manager 實體
...
new_node->local_weak_refs++; // 強弱引用 +1
new_node->local_strong_refs++;
...
}
static struct binder_node *binder_new_node(struct binder_proc *proc,
struct flat_binder_object *fp) {
// 分配內核空間
struct binder_node *new_node = kzalloc(sizeof(*node), GFP_KERNEL);
node = binder_init_node_ilocked(proc, new_node, fp); // 處理 binder_node
...
}
static struct binder_node *binder_init_node_ilocked(
struct binder_proc *proc,
struct binder_node *new_node,
struct flat_binder_object *fp)
{
...
node = new_node;
binder_stats_created(BINDER_STAT_NODE);
node->tmp_refs++;
rb_link_node(&node->rb_node, parent, p); // 將節點放入到紅黑數樹中
rb_insert_color(&node->rb_node, &proc->nodes);
node->debug_id = atomic_inc_return(&binder_last_id);
node->proc = proc; // 初始化 binder_node
node->ptr = ptr;
node->cookie = cookie;
node->work.type = BINDER_WORK_NODE; // 設置 binder_work 的type
...
}
3. ServiceManager 進入循環等待
在 native 層,ServiceManager 發送 looper 指令告訴內核
// 發送 BC_ENTER_LOOPER 指令到 Binder 內核
// frameworks/native/cmds/servicemanager/binder.c
void binder_loop(struct binder_state *bs, binder_handler func)
{
bwr.write_size = 0;
bwr.write_consumed = 0;
bwr.write_buffer = 0;
uint32_t readbuf[32];
readbuf[0] = BC_ENTER_LOOPER;
binder_write(bs, readbuf, sizeof(uint32_t)); // 寫一次 ioctl
for (;;) {
bwr.read_size = sizeof(readbuf);
bwr.read_consumed = 0;
bwr.read_buffer = (uintptr_t) readbuf;
// 有消息處理,沒消息中斷等待消息
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr); // 再寫,只讀不寫
...
// 消息處理,func
res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
}
}
// 將 BC_ENTER_LOOPER 寫入到 binder 內核中
int binder_write(struct binder_state *bs, void *data, size_t len)
{
struct binder_write_read bwr;
int res;
bwr.write_size = len; // 只寫
bwr.write_consumed = 0;
bwr.write_buffer = (uintptr_t) data;
bwr.read_size = 0; // 不讀
bwr.read_consumed = 0;
bwr.read_buffer = 0;
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
if (res < 0) {
fprintf(stderr,"binder_write: ioctl failed (%s)\n",
strerror(errno));
}
return res;
}
// frameworks/native/cmds/servicemanager/service_manager.c
int svcmgr_handler(struct binder_state *bs,
struct binder_transaction_data *txn,
struct binder_io *msg,
struct binder_io *reply)
{
...
// 根據傳入的請求類型,做處理
// 獲取/檢查服務,添加服務,列出服務列表
switch(txn->code) {
case SVC_MGR_GET_SERVICE:
case SVC_MGR_CHECK_SERVICE:
...
case SVC_MGR_ADD_SERVICE:
...
case SVC_MGR_LIST_SERVICES:
...
}
}
binder 內核收到 BINDER_WRITE_READ 請求處理
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
...
switch (cmd) {
case BINDER_WRITE_READ:
ret = binder_ioctl_write_read(filp, cmd, arg, thread);
if (ret)
goto err;
break;
}
...
}
static int binder_ioctl_write_read(struct file *filp,
unsigned int cmd, unsigned long arg,
struct binder_thread *thread)
{
...
// 將傳入的參數,從用戶空間拷貝到內核空間
void __user *ubuf = (void __user *)arg;
if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
ret = -EFAULT;
goto out;
}
...
// 有寫入的數據,即用戶進程向內核進程寫數據,對應 binder_loop() 的第一次 ioctl
if (bwr.write_size > 0) {
ret = binder_thread_write(proc, thread,
bwr.write_buffer,
bwr.write_size,
&bwr.write_consumed);
trace_binder_write_done(ret);
if (ret < 0) {
bwr.read_consumed = 0;
if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
ret = -EFAULT;
goto out;
}
}
// 如果有需要讀取的數據,即內核向用戶寫數據,對應 binder_loop() 的 for 循環中的 ioctl
if (bwr.read_size > 0) {
ret = binder_thread_read(proc, thread, bwr.read_buffer,
bwr.read_size,
&bwr.read_consumed,
filp->f_flags & O_NONBLOCK);
trace_binder_read_done(ret);
binder_inner_proc_lock(proc);
if (!binder_worklist_empty_ilocked(&proc->todo))
binder_wakeup_proc_ilocked(proc);
binder_inner_proc_unlock(proc);
if (ret < 0) {
if (copy_to_user(ubuf, &bwr, sizeof(bwr))) // 讀取完拷貝到用戶空間
ret = -EFAULT;
goto out;
}
}
...
}
// 第一次 ioctl 寫 BC_ENTER_LOOPER 處理邏輯
static int binder_thread_write(struct binder_proc *proc,
struct binder_thread *thread,
binder_uintptr_t binder_buffer, size_t size,
binder_size_t *consumed)
{
while (ptr < end && thread->return_error.cmd == BR_OK) {
int ret;
// 從 ptr 中獲取數據,即 BC_ENTER_LOOPER
if (get_user(cmd, (uint32_t __user *)ptr))
return -EFAULT;
...
switch (cmd) {
...
// 將指定線程設置為 loop 狀態,該線程是從 device 文件中獲取。
case BC_ENTER_LOOPER:
binder_debug(BINDER_DEBUG_THREADS,
"%d:%d BC_ENTER_LOOPER\n",
proc->pid, thread->pid);
if (thread->looper & BINDER_LOOPER_STATE_REGISTERED) {
thread->looper |= BINDER_LOOPER_STATE_INVALID;
binder_user_error("%d:%d ERROR: BC_ENTER_LOOPER called after BC_REGISTER_LOOPER\n",
proc->pid, thread->pid);
}
thread->looper |= BINDER_LOOPER_STATE_ENTERED; // 設置該線程的狀態為 BINDER_LOOPER_STATE_ENTERED
break;
...
}
}
...
}
// for 循環中只讀不寫的處理
static int binder_thread_read(struct binder_proc *proc,
struct binder_thread *thread,
binder_uintptr_t binder_buffer, size_t size,
binder_size_t *consumed, int non_block)
{
...
// for 循環中的 read_consumed 值,是 0
if (*consumed == 0) {
if (put_user(BR_NOOP, (uint32_t __user *)ptr)) // 將 BR_NOOP 設置到 ptr 中
return -EFAULT;
ptr += sizeof(uint32_t);
}
...
// binder 線程是否能工作,此時肯定是 true
binder_inner_proc_lock(proc);
wait_for_proc_work = binder_available_for_proc_work_ilocked(thread);
binder_inner_proc_unlock(proc);
...
// filp->f_flags 是 O_RDWR | O_CLOEXEC( 02000000 | 02)
// non_block = filp->f_flags & O_NONBLOCK(00004000 & 02000002 = 0)
if (non_block) {
if (!binder_has_work(thread, wait_for_proc_work))
ret = -EAGAIN;
} else {
ret = binder_wait_for_work(thread, wait_for_proc_work); // 將傳入的線程阻塞在此處
}
}
static int binder_wait_for_work(struct binder_thread *thread,
bool do_proc_work)
{
...
for (;;) {
prepare_to_wait(&thread->wait, &wait, TASK_INTERRUPTIBLE);
if (binder_has_work_ilocked(thread, do_proc_work))
break;
/*
* 調度函數,從運行隊列的鏈表中找到一個進程,隨后 CPU 分配給這個進程。
* 如果當前進程因不能獲得必須的資源而要被阻塞時,需要使用調度函數
* 1. 先將當前進程插入到適當的等待隊列
* 2. 將當前進程狀態修改為 TASK_INTERRUPTIBLE 或者 TASK_UNINTERRUPTIBLE
* 3. 調用 schedule()
* 4. 檢查資源是否可能,如果不可用轉到步驟 2
* 5. 如果資源可用就從等待隊列中刪除當前進程
*/
schedule();
...
}
...
}
至此,ServiceManager 啟動,到打開 Binder 內核,注冊自己為守護進程,然后自己進入 loop 循環,而 Binder 內核也進入中斷等待請求到來的邏輯分析完畢
疑問:
- binder 內核處進程阻塞后, BR_NOOP 是什么時候被通知到 ServiceManager 中的

浙公網安備 33010602011771號