Android Service Manager 的啓動流程

本章學習內容:學習 Service Manager 的啓動流程

前言

Service Manager 是 Binder 進程間通信機制的核心組件之一, 它扮演者 Binder 進程間通信機制上下文管理者的角色, 同時負責管理系統中的 Service 組件, 並且向 Client 組件提供獲取 Service 代理對象的服務.

Service Manager (下文都稱 SM) 運行在一個獨立的進程中, 因此, Service 組件和 Client 組件也需要通過進程間通信機制來和它進行交互, 而採用的進程間通信機制整好也是 Binder 機制. 這樣看來 SM 除了是 Binder 進程間通信機制的上下文管理者外, 還是一個特殊的 Service 組件.

爲了閱讀方便, 將會去掉一些無關緊要的代碼, 只保留關鍵代碼. 不懂 C/C++ 代碼也沒關係, 這裏只看流程. 不做具體的分析, 需要了解具體分析的朋友可以去看老羅的的 Android 系統源碼情景分析 一書. 本文也是參考這本書來學習的. 只不過省去了一些步驟.

一. Service Manager 的啓動

1. servicemanager.rc

SM 是由 init 進程通過解析 init.rc 文件而創建的, 在 Android 7.0 以後, 獨立出了 servicemanager.rc, 位於 /frameworks/native/cmds/servicemanager/servicemanager.rc
腳本如下

service servicemanager /system/bin/servicemanager
  class core animation
  user system
  group system readproc
  critical
  onrestart restart healthd
  onrestart restart zygote
  onrestart restart audioserver
  onrestart restart media
  onrestart restart surfaceflinger
  onrestart restart inputflinger
  onrestart restart drm
  onrestart restart cameraserver
  writepid /dev/cpuset/system-background/tasks

第一行的關鍵字 service 表明 SM 是以服務的形式啓動的, 對應的程序文件和進程名稱分別爲 /system/bin/servicemanagerservicemanager.
所對應的源文件就是當前文件上級目錄下的 service_manager.c 找到這個類中的入口函數 main 函數.

2. servicemanager.c 中的 main 函數

源碼路徑: /frameworks/native/cmds/servicemanager/service_manager.c

int main(int argc, char **argv)
{
    struct binder_state *bs;
    bs = binder_open(128*1024);
    ...
    if (binder_become_context_manager(bs)) {
        ALOGE("cannot become context manager (%s)\n", strerror(errno));
        return -1;
    }
    ...
    ...
    binder_loop(bs, svcmgr_handler);
    return 0;
}

通過上面代碼大概可看出, SM 的啓動過程由 3 個步驟組成,

  • 調用函數 binder_open 打開設備文件 /dev/binder 以及將它映射到本進程的地址空間.
  • 調用函數 binder_become_context_manager 將自己註冊爲所有服務的大管家.
  • 調用函數 binder_loop 來循環等待和處理 Client 進程的通信請求.

下面先看在 binder_open 中都做了什麼事情.

3. binder_open

源碼路徑: /frameworks/native/cmds/servicemanager/binder.c

struct binder_state *binder_open(size_t mapsize)
{
    struct binder_state *bs;
    struct binder_version vers;
    ...
    bs->fd = open("/dev/binder", O_RDWR);
    ...
    ...
    bs->mapsize = mapsize;
    bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
    ...
    return bs;
    ...
}

binder_openservicemanager.main 函數中都出現了一個結構體 binder_state 那麼它究竟是什麼樣呢.

struct binder_state
{
    int fd;
    void *mapped;
    size_t mapsize;
};

SM 打開了設備文件 /dev/binder 之後, 就會將得到的文件描述符保存在一個 binder_state 結構體的成員變量 fb 中. 以便後面可以通過它來和 Binder 驅動程序交互.
那麼一個進程如果要和 Binder 驅動程序交互, 除了要打開設備文件外, 還需要將該設備文件映射到進程的地址空間, 以便 Binder 驅動程序可以爲它分配內核緩衝區來保存進程間通信的數據. 因此 SM 將映射後得到的地址空間大小和氣質地址保存到 binder_state 結構體的成員變量 mapsizemapped 中.

那麼接着回到 binder_open 函數中. 看到調用函數open 打開 /dev/binder 設備文件. 得到文件描述符.保存到 binder_state 結構體的成員變量 fd 中.

當使用進程函數 open 打開設備文件/dev/binder時, Binder 驅動中的函數 binder_open 就會被調用, 會爲當前進程創建一個 binder_proc 結構體, 用來描述當前進程的 Binder 進程間通信狀態.

從傳入的參數 mapsize 得知大小爲 128K. 接着調用函數 mmap 將設備文件 /dev/binder 映射到進程的地址空間, 請求映射的地址空間大小爲 128K, 即請求 Binder 驅動程序爲進程分配 128K 大小的內核緩衝區. 映射後得到的地址空間的起始地址和大小分別保存在一個 binder_state 結構體 bs 的成員變量 mapsizemapped 中. 最後將這個結構體返回給調用者, 即 main 函數.

4. binder_become_context_manager 註冊爲所有服務的大管家

調用 binder_become_context_manager() 註冊爲所有服務的大管家, 也可以理解爲 SM 要成爲 Binder 進程間通信機制的上下文管理者, 就必須通過 IO 控制命令 BINDER_SET_CONTEXT_MGR 將自己註冊到 Binder 驅動程序中. 進入 binder_become_context_manager() 函數.
源碼路徑: /frameworks/native/cmds/servicemanager/binder.c

int binder_become_context_manager(struct binder_state *bs)
{
    return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}

看到調用 ioctl 函數, 就知道又要進入到驅動層的 binder_ioctl() 函數. 也就是在這個函數內處理這個 IO 控制命令的.

由於與 SM 對應的 Binder 本地對象是一個虛擬的對象, 並且它的地址等於 0, 因此, 函數 binder_become_context_manager 就將 IO 控制命令 BINDER_SET_CONTEXT_MGR 的參數設置爲 0. 表示 SM 對應的句柄爲 0.

5. binder_ioctl 處理 IO 控制命令 BINDER_SET_CONTEXT_MGR

源碼路徑: kernel 3.18下的 /drivers/staging/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);
          if (ret)
              goto err;
          break;
       ...
      }
  ...
}

switch 中如果命中了這個 IO 控制命令, 又調用了 binder_ioctl_set_ctx_mgr 函數.

6. binder_ioctl_set_ctx_mgr 具體的處理 IO 控制命令 BINDER_SET_CONTEXT_MGR

源碼路徑: kernel 3.18下的 /drivers/staging/android/binder.c

static int binder_ioctl_set_ctx_mgr(struct file *filp)  
{
    int ret = 0;
    struct binder_proc *proc = filp->private_data;
        ...
    if (binder_context_mgr_node != NULL) {
        pr_err("BINDER_SET_CONTEXT_MGR already set\n");
        ret = -EBUSY;
        goto out;
    }
    if (uid_valid(binder_context_mgr_uid)) {
        if (!uid_eq(binder_context_mgr_uid, curr_euid)) {
            pr_err("BINDER_SET_CONTEXT_MGR bad uid %d != %d\n", from_kuid(&init_user_ns, curr_euid),from_kuid(&init_user_ns,binder_context_mgr_uid));
            ret = -EPERM;
            goto out;
        }
    } else {
        binder_context_mgr_uid = curr_euid;
    }
    binder_context_mgr_node = binder_new_node(proc, 0, 0);
    if (binder_context_mgr_node == NULL) {
        ret = -ENOMEM;
        goto out;
    }
        ...
    return ret;
}

先獲得前面 Binder 驅動程序爲 SM 進程創建的一個 binder_proc 結構體並保存在變量 proc 中.

binder_context_mgr_node 是一個全局變量. 聲明爲 static struct binder_node *binder_context_mgr_node; 用來描述 Binder 進程間通信機制的上下文管理者對應的一個 Binder 實體對象. 如果它不等於 null 成立, 說明前面已經有組件將自己註冊爲上下文管理者了, 由於 Binder 驅動程序不允許重複註冊上下文管理者, 所以直接就返回了錯誤.

接着判斷用戶 ID 是否有效, 當前這裏是無效的. 直接執行 else 中內容. 將當前進程的有效用戶 ID 賦值給全局變量 binder_context_mgr_uid.

全局變量 binder_context_mgr_uid 用來描述註冊了 Binder 進程間通信機制的上下文管理者進程的有效用戶 ID. 如果值不爲-1, 即判斷 if 內的判斷成立, 也說明前面已經有註冊過上下文管理者了, 接着需要進一步檢查當前進程的有效用戶 ID 是否全局變量 binder_context_mgr_uid 的值. 如果不等於, 就直接返回錯誤.
注意: Binder 驅動程序允許同一個進程重複使用 IO 控制命令 BINDER_SET_CONTEXT_MGR 來註冊上下文管理者.

通過前面的合法性檢查後, 調用函數 binder_new_node 爲 SM 創建一個 Binder 實體對象並保存在全局變量 binder_context_mgr_node 中. 最後再對 binder_context_mgr_node 進行非空校驗. 現在進入到 binder_new_node 函數中. 看在創建 SM 的 Binder 實體對象的時候都做了一些什麼.

7. binder_new_node 創建 SM 的 Binder 實體對象

源碼路徑: kernel 3.18下的 /drivers/staging/android/binder.c

static struct binder_node *binder_new_node(struct binder_proc *proc, binder_uintptr_t ptr, binder_uintptr_t cookie)
{
    struct rb_node **p = &proc->nodes.rb_node;
    struct rb_node *parent = NULL;
    struct binder_node *node;
        ...
        ...
    node = kzalloc(sizeof(*node), GFP_KERNEL);
    if (node == NULL)
        return NULL;
    binder_stats_created(BINDER_STAT_NODE);
    rb_link_node(&node->rb_node, parent, p);
    rb_insert_color(&node->rb_node, &proc->nodes);
    node->debug_id = ++binder_last_id;
    node->proc = proc;
    node->ptr = ptr;
    node->cookie = cookie;
    node->work.type = BINDER_WORK_NODE;
    INIT_LIST_HEAD(&node->work.entry);
    INIT_LIST_HEAD(&node->async_todo);
        ...
    return node;
}

調用 kzalloc 函數創建一個新的 Binder 實體對象, 同時將其加入到宿主進程的成員變量 nodes 所描述的一個紅黑樹中. 最後初始化這個新創建的 Binder 實體對象. 至此, SM 就成功的將自己註冊爲 Binder進程間通信機制的上下文管理者了. 接着返回到用戶空間, 也就是第2步 main 函數中, 調用了 binder_loop 函數來循環等待和處理 Client 進程的通信請求, 即等待和處理 Service 組件的註冊請求, 以及其他代理對象的獲取請求.

8. binder_loop 循環等待處理請求

源碼路徑: /frameworks/native/cmds/servicemanager/binder.c

01 void binder_loop(struct binder_state *bs, binder_handler func)
02 {
03     int res;
04     struct binder_write_read bwr;
05     uint32_t readbuf[32];

06     bwr.write_size = 0;
07     bwr.write_consumed = 0;
08     bwr.write_buffer = 0;

09     readbuf[0] = BC_ENTER_LOOPER;
10     binder_write(bs, readbuf, sizeof(uint32_t));

11     for (;;) {
12         bwr.read_size = sizeof(readbuf);
13         bwr.read_consumed = 0;
14         bwr.read_buffer = (uintptr_t) readbuf;

15         res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
           ...
16         res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
           ...
        }
    }
}

由於 SM 需要在系統運行期間爲 Service 組件和 Client 組件提供服務, 因此它就需要通過一個無線循環來等待和處理 Service 和 Client 組件的進程間通信請求.

函數的第一個參數 *bs指向前面在第 5 步 binder_open 中創建的一個 binder_state 結構體.
函數的第二個參數 func 指向 SM 中的函數 svcmgr_handler, 它是用來處理 Service 組件和 Client 組件進程間通信請求的.

一個線程要通過協議 BC_ENTER_LOOPER 或者 BC_REGISTER_LOOPER 將自己註冊爲 Binder 線程, 以便 Binder 驅動程序可以將進程間通信請求分發給它處理. 由於 SM 進程的主線程是主動成爲一個 Binder 線程的, 因此,它就需要使用 BC_ENTER_LOOPER 協議代碼將自己註冊到 Binder 驅動程序中.

在第 9行處首先將 BC_ENTER_LOOPER 協議代碼寫入到緩衝區 readbuf 中. 接着第 10 行調用函數 binder_write 將它發送到 Binder 驅動程序中.

9. binder_write 準備註冊到 Binder 驅動程序.

源碼路徑: /frameworks/native/cmds/servicemanager/binder.c

01 int binder_write(struct binder_state *bs, void *data, size_t len)
02 {
03     struct binder_write_read bwr;
04     int res;
 
05     bwr.write_size = len;
06     bwr.write_consumed = 0;
07     bwr.write_buffer = (uintptr_t) data;
08     bwr.read_size = 0;
09     bwr.read_consumed = 0;
10     bwr.read_buffer = 0;
11     res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
12     if (res < 0) {
13         fprintf(stderr,"binder_write: ioctl failed (%s)\n",strerror(errno));
14     }
15     return res;
  }

由於 BC_ENTER_LOOPER協議是通過 IO 控制命令 BINDER_WRITE_READ 發送到 Binder 驅動程序中的, 所以第3行先定義一個 binder_write_read 的結構體 bwr,

第 7 行將參數 data 所指向的一塊緩衝區作爲它的輸入緩衝區. 接着 8,9,10 行將結構體 bwr 的輸出緩衝區設置爲空. 這樣的話當線程將自己註冊到 Binder 驅動程序之後, 就會馬上返回到用戶空間. 而不會在 Binder 驅動程序中等待 Client 進程的通信請求.

由於參數 ata 所指向的一塊緩衝區的內容以及被設置爲 BC_ENTER_LOOPER 協議代碼, 因此接下來在 11 行就直接調用函數 ioctl 將當前線程註冊到 Binder 驅動程序中了.

10. binder_ioctl, 在 Binder 驅動層處理 IO 控制命令 BINDER_WRITE_READ

源碼路徑: kernel 3.18下的 /drivers/staging/android/binder.c

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);
        ...
        break;
    }
    ...
    return ret;
}

因爲在上一步發送來的 IO 控制命令爲 BINDER_WRITE_READ, 所以直接進入到這個 case 中. 緊接着又調用了 binder_ioctl_write_read 函數. 繼續進入

11. binder_ioctl_write_read

源碼路徑: kernel 3.18下的 /drivers/staging/android/binder.c

static int binder_ioctl_write_read(struct file *filp, unsigned int cmd, unsigned long arg, struct binder_thread *thread)
{
    ...
    ...

    if (bwr.write_size > 0) {
        ret = binder_thread_write(proc, thread, bwr.write_buffer, bwr.write_size, &bwr.write_consumed);
        ...
    }
    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);
         ...
    }
   ...
    return ret;
}

第一個判斷 bwr.write_size > 0 條件是成立的. 命中 if 後又調用了 binder_thread_write函數.
第二個判斷這時候是不成立的, 所以 binder_thread_read 函數. 這個後面會被執行. 先看binder_thread_write

12. binder_thread_write 處理 BC_ENTER_LOOPER 協議

源碼路徑: kernel 3.18下的 /drivers/staging/android/binder.c

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 == BR_OK) {
        ...
        switch (cmd) {
        ...
        case BC_ENTER_LOOPER:
            ...
            thread->looper |= BINDER_LOOPER_STATE_ENTERED;
            break;
        ...
        } 
        ...
}

這個函數太長了 ,所以只截取了一部分. 在 case 內主要就是將目標線程 thread 的狀態設置爲 BINDER_LOOPER_STATE_ENTERED, 表明該線程是一個 Binder 線程, 可以處理進程間通信請求.

函數 binder_thread_write 處理完 BC_ENTER_LOOPER 協議之後, 就返回到函數 binder_ioctl 中, 後者接着又返回到用戶空間中. 即 SM 進程的函數 binder_write 中. 也就是第9步. 最後返回到第8步 binder_loop 函數中.

爲了方便查看, 在這裏貼上第 8 步的代碼.

01 void binder_loop(struct binder_state *bs, binder_handler func)
02 {
03     int res;
04     struct binder_write_read bwr;
05     uint32_t readbuf[32];

06     bwr.write_size = 0;
07     bwr.write_consumed = 0;
08     bwr.write_buffer = 0;

09     readbuf[0] = BC_ENTER_LOOPER;
10     binder_write(bs, readbuf, sizeof(uint32_t));

11     for (;;) {
12         bwr.read_size = sizeof(readbuf);
13         bwr.read_consumed = 0;
14         bwr.read_buffer = (uintptr_t) readbuf;

15         res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
           ...
16         res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
           ...
        }
    }

進入 11 行的死循環了, 在循環中, 不斷的使用 IO 控制命令 BINDER_WRITE_READ 來檢查 Binder 驅動程序是否有新的進程間通信請求需要它來處理. 如果有, 就交給函數 binder_parse 來處理. 否則當前線程就會在 Binder 驅動程序中休眠等待, 直到有新的進程間通信請求到來爲止. 所以接着又會進入到第 10 步, 接着又進入到第 11 步.

在 for 循環中, 每一次通過 IO 控制命令 BINDER_WRITE_READ 進入到 Binder 驅動程序時, 所傳遞的 binder_write_read 結構體中的輸入緩衝區長度均爲 0, 而輸出緩衝區, 也就是 read_size的長度等於緩衝區 readbuf 的大小, 即 128 個字節. 因此, 在第 11 步時, 會進入到第二個 if 判斷內.執行 binder_thread_read 函數來檢查 SM 進程是否有新的進程間通信請求需要處理, 休眠等待也是在此.

由於 15 行, 調用 ioct 函數會後會再此走到第 10 步, 接着進入到第 11 步執行 binder_ioctl_write_read 函數中的第二個 if 執行 binder_thread_read , 所以直接進入到 binder_thread_read 去查看, 省去了跳到 10,11 步.

12. binder_thread_read 檢查是否有請求需要處理

源碼路徑: kernel 3.18下的 /drivers/staging/android/binder.c

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)
{
   ...
    int wait_for_proc_work;
   ...
retry:
    //分析 1
    wait_for_proc_work = thread->transaction_stack == NULL && list_empty(&thread->todo);
    ...
    //分析 2
    thread->looper |= BINDER_LOOPER_STATE_WAITING;
    if (wait_for_proc_work)
      proc->ready_threads++;
    ...
    //分析 3
    if (wait_for_proc_work) {
        ...
        if (non_block) {
          ...
        } else
            ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread));
    } else {
        ...
    }
    ...
    //分析 4
    if (wait_for_proc_work)
        proc->ready_threads--;
    thread->looper &= ~BINDER_LOOPER_STATE_WAITING;

   ...
     ...
    return 0;
}

如果一個線程的的事務堆棧 transaction_stack 不等於 NULL, 表示它正在等待其他線程完成另外一個事務.

如果一個線程的 todo 隊列不等於 NULL, 表示該線程有未處理的工作項.

一個線程只有在其事務堆棧 transaction_stack 爲 NULL, 並且 todo 隊列爲 NULL 時, 纔可以去處理其所屬進程todo 隊列中的待處理工作項. 否則就要處理其事務堆棧 transaction_stack 中的事物或者 todo 隊列中的待處理工作項.

在分析 1 處檢查這兩個是否都爲 NULL, 如果兩個都成立則 wait_for_proc_work 的值爲 1. 表示接下來要檢查它所屬進程的todo 隊列中是否有未處理的工作項, 否則爲 0, 則表示接下來要優先處理自己的事務或者工作項了.. 本流程此次 wait_for_proc_work 的值爲 1.

分析 2 處將當前線程的狀態設置爲 BINDER_LOOPER_STATE_WAITING 表示該線程正處於空閒狀態. 然後判斷 wait_for_proc_work 值是否爲 1, 如果是說明當前線程所屬的進程又多了一個空閒的 Binder 線程. 就將該進程的空閒 Binder 線程數 ready_threads 加 1. 接下來如果 Binder 驅動程序發現當前線程有新的工作項要處理時, 在分析 4 處會將狀態位 BINDER_LOOPER_STATE_WAITING清空. 並且根據變量 wait_for_proc_work 的值是否爲 1 來決定是否要減少它所屬進程的空閒 Binder 線程數.

分析 3 因爲本次流程 wait_for_proc_work 值爲 1. 所以直接命中 if. non_block 表示當前線程是否是以非阻塞模式打開設備文件 /dev/binder. 本次流程爲阻塞, 所以這裏直接進入 else, 執行 wait_event_freezable_exclusive 函數來睡眠等待直到其所屬的進程有新的未處理工作項爲止.

binder_has_proc_work 就是檢查是否有未處理工作項的函數

到這裏 SM 的啓動就分析完了, 接下來就是 SM 的獲取. 假設 SM 中沒有待處理的工作項, 因此它就睡眠在 Binder 驅動函數 binder_thread_read 中. 也就是上面的分析 3 處. 等待其他進程的 Service組件或者 Client 組件向它發出進程間通信請求.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章