Android進程間通信(三)——系統(tǒng)服務管理進程servicemanager啟動源碼分析

Android進程間通信——Binder通信相關系統(tǒng)進程(二)這篇中我們知道滤灯,手機系統(tǒng)服務需要先注冊到servicemanager中才能被服務端找到和使用纺座,而一個普通應用的運行過程中需要頻繁用到系統(tǒng)服務,比如AMS,WMS等恬叹,這些系統(tǒng)服務都要在系統(tǒng)啟動時就注冊到servicemanager進程中,之后才能被其他進程調(diào)用迂烁。所以進程間通信中最底層和最核心的進程就是servicemanager恭陡,沒有它可以說進程間通信是無法正常工作的,所以我們就先從servicemanager這個進程開始坦报,通過分析源碼了解一步一步了解進Android進程間通信的原理库说。源碼github地址

servicemanager的源碼

servicemanager是一個c語言編寫的程序,其源碼位于系統(tǒng)源碼/frameworks/native/cmds/servicemanager/service_manager.c
首先我們看下main方法做了什么,這里我省略了一些與核心邏輯無關代碼

int main(int argc, char** argv)
{
    char *driver;
    if (argc > 1) {
        driver = argv[1];
    } else {
        driver = "/dev/binder";
    }
    bs = binder_open(driver, 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;
}

binder_open

我們看到片择,在main方法中潜的,調(diào)用了binder_open方法,同時傳入得數(shù)據(jù)是一個driver字符傳表示binder驅(qū)動得路徑字管,另外一個是128*1026也就是128k啰挪。
binder_open的源碼位于service_manager.c同目錄下的binder.c中,注意這個不是真正的binder驅(qū)動代碼嘲叔。

struct binder_state    //binder_open方法的返回數(shù)據(jù)結(jié)構(gòu)體
{
    int fd;
    void *mapped;
    size_t mapsize;
};
//注意其中忽略了核心邏輯無關代碼
struct binder_state *binder_open(size_t mapsize)
{
    struct binder_state *bs;
    bs = malloc(sizeof(*bs));
    bs->fd = open("/dev/binder", O_RDWR);
    bs->mapsize = mapsize;
    bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
    return bs;
}

1.首先為創(chuàng)建了一個binder_state結(jié)構(gòu)體變量bs并分配了內(nèi)存
2.通過open("/dev/binder",O_RDWR)方法打開binder驅(qū)動亡呵,并返回fd文件句柄保存到bs中
open方法會調(diào)用驅(qū)動kernel/drivers/staging/android/binder.c
binder是一種虛擬的字符設備,注冊在/dev/binder中,字符(char)設備是個能夠像字節(jié)流(類似文件)一樣被訪問的設備借跪,由字符設備驅(qū)動程序來實現(xiàn)這種特性政己。字符設備驅(qū)動程序通常至少要實現(xiàn)open,close,read和write系統(tǒng)調(diào)用。大多數(shù)字符設備是一個個只能順序訪問的數(shù)據(jù)通道

// 每個使用Binder的進程都會在使用前打開binder驅(qū)動,驅(qū)動在open的時候會創(chuàng)建結(jié)構(gòu)體binder_proc歇由,并將它的指針記錄到文件結(jié)構(gòu)體private_data中卵牍,以后的驅(qū)動操作就能通過文件結(jié)構(gòu)體來獲取該進程的信息。
  //  驅(qū)動里都是以binder_proc為單元來分配和記錄資源的沦泌,驅(qū)動會給每個使用binder的進程創(chuàng)建一個/proc/binder/proc/pid文件糊昙,用來展示進程相關的binder信息。
//proc/binder/stats和/proc/binder/proc/pid能查看到相關信息
static int binder_open(struct inode *nodp, struct file *filp)
{
    struct binder_proc *proc;//進程信息
    struct binder_device *binder_dev;//
    proc = kzalloc(sizeof(*proc), GFP_KERNEL);
    get_task_struct(current);
    proc->tsk = current;
    INIT_LIST_HEAD(&proc->todo);//注意初始化todo鏈表谢谦,表示要執(zhí)行的任務
    init_waitqueue_head(&proc->wait);//初始化等待隊列释牺,表示掛起任務
    proc->default_priority = task_nice(current);
    binder_dev = container_of(filp->private_data, struct binder_device,
                  miscdev);/、
    proc->context = &binder_dev->context回挽;//context上下文信息没咙,保存有servicemanager 進程相關信息
    binder_stats_created(BINDER_STAT_PROC);
    hlist_add_head(&proc->proc_node, &binder_procs);
    proc->pid = current->group_leader->pid;
    INIT_LIST_HEAD(&proc->delivered_death);
    filp->private_data = proc;
}

3.通過mmap方法給打開的文件句柄fd映射一塊內(nèi)存,對這個映射的內(nèi)存讀寫就可以實現(xiàn)對文件的讀寫
這里是一個重點千劈,也就是通常說binder只需要一次拷貝來實現(xiàn)進程間通信祭刚。
這里需要我們補充一下Linux的背景知識,知道為啥需要mmap墙牌,可以參考這篇博客Android Binder通信一次拷貝你真的理解了嗎涡驮?

我們需要了解以下背景
1、為啥要有跨進程通信機制喜滨?
因為Linux的內(nèi)存模型是進程間內(nèi)存隔離的捉捅,2個進程不能直接通信,也就是進程之間不能直接訪問對方的內(nèi)存虽风, 這肯定是為了安全
2棒口、Android為啥不用Linux原生自帶的進程間通信機制可參考Android Bander設計與實現(xiàn) - 設計篇
為了性能和安全等因素
3、了解什么是內(nèi)核空間和什么是用戶空間
普通應用運行在內(nèi)核空間焰情,系統(tǒng)應用運行在內(nèi)核空間陌凳,普通應用不能直接訪問內(nèi)核空間,可以通過系統(tǒng)調(diào)用訪問內(nèi)核空間


4内舟、傳統(tǒng)跨進程通信方式合敦,或不用mmap的跨進程通信方式

也就是需要進程A先把數(shù)據(jù)拷貝到內(nèi)核空間,然后再從內(nèi)核空間拷貝到進程B 這樣才能實現(xiàn)跨進程通信验游,但是2次拷貝的開銷很大
5充岛、Linux的虛擬內(nèi)存——用戶空間程序使用的內(nèi)存地址都是系統(tǒng)分配的虛擬內(nèi)存地址,虛擬內(nèi)存是對物理內(nèi)存的映射

mmap

這里調(diào)用mmap最終會調(diào)用到binder驅(qū)動中的binder_mmap方法具體流程可參考Android binder中的mmap到binder_mmap調(diào)用流程 在理解binder_mmap函數(shù)前耕蝉,我們要了解一些基本的結(jié)構(gòu)體的作用崔梗。

struct file

代表一個打開的文件描述符

vm_area_struct *vma

表示一個虛擬內(nèi)存,因為實際物理內(nèi)存并不是連續(xù)的垒在,所以程序的虛擬內(nèi)存是一個鏈表
vm_area_struct結(jié)構(gòu)體中幾個重要的變量

struct vm_area_struct {
    unsigned long vm_start;//虛擬內(nèi)存起始位置
    unsigned long vm_end;//虛擬內(nèi)存結(jié)束位置
    struct vm_area_struct* vm_next, * vm_prev;//虛擬內(nèi)存的前后節(jié)點指針
    unsigned long vm_flags;//主要保存VMA標志位
    const struct vm_operations_struct* vm_ops;//VMA操作函數(shù)合集蒜魄,常用于文件映射
    struct file* vm_file;//描述一個被映射的文件
    void* vm_private_data;
};

binder_proc

binder_proc是一個非常重要的結(jié)構(gòu)體,其中保存了進程的很多信息

struct binder_proc {
    // 進程打開設備文件/dev/binder時,Binder驅(qū)動會為它創(chuàng)建一個binder_proc結(jié)構(gòu)體谈为,并將它
    // 保存在全局hash列表中旅挤,proc_node是該hash列表的節(jié)點。
    struct hlist_node proc_node;
    //每個使用了Binder機制的進程都有一個Binder線程池伞鲫,用來處理進程間通信請求粘茄。threads以
    // 線程ID作為key來組織進程的Binder線程池。進程可以調(diào)用ioctl將線程注冊到Binder驅(qū)動中
    // 當沒有足夠的空閑線程處理進程間通信請求時秕脓,驅(qū)動可以要求進程注冊更多的線程到Binder線程
    // 池中
    struct rb_root threads;
    struct rb_root nodes;//組織Binder實體對象柒瓣,它以成員ptr作為key
    struct rb_root refs_by_desc;//進程中的rb_root紅黑樹的根,用于查找handle對應的node  組織Binder引用對象吠架,它以成員desc作為key
    struct rb_root refs_by_node;//組織Binder引用對象芙贫,它以成員node作為key
    int pid;
    struct vm_area_struct *vma;  // 內(nèi)核緩沖區(qū)的用戶空間地址,供應用程序使用
    struct mm_struct *vma_vm_mm;
    struct task_struct *tsk;
    struct files_struct *files;
    struct hlist_node deferred_work_node;
    int deferred_work;
    void *buffer;// 內(nèi)核緩沖區(qū)的內(nèi)核空間地址诵肛,供驅(qū)動程序使用
    ptrdiff_t user_buffer_offset;// vma和buffer之間的差值
    // buffer指向一塊大的內(nèi)核緩沖區(qū)屹培,驅(qū)動程序為方便管理,將它劃分成若干小塊怔檩,這些小塊的內(nèi)核緩
    // 沖區(qū)用binder_buffer描述保存在列表中,按地址從小到大排列蓄诽。buffers指向該列表的頭部薛训。
    struct list_head buffers;
    struct rb_root free_buffers// buffers中的小塊有的正在使用,被保存在此紅黑樹
    struct rb_root allocated_buffers;;// buffers中的空閑小塊被保存在此紅黑樹
    size_t free_async_space;
    struct page **pages;// buffer和vma都是虛擬地址仑氛,它們對應的物理頁面保存在page中乙埃,這是一個數(shù)組,每個元素指向一個物理頁面
    size_t buffer_size;
    uint32_t buffer_free;
    struct list_head todo;//// 當進程接收到一個進程間通信請求時锯岖,Binder驅(qū)動就將該請求封
                            // 裝成一個工作項介袜,并且加入到進程的待處理工作向隊列中,該隊列
                            // 使用成員變量todo來描述出吹。
    wait_queue_head_t wait;// 線程池中空閑Binder線程會睡眠在由該成員所描述的等待隊列中
                            // 當宿主進程的待處理工作項隊列增加新工作項后遇伞,驅(qū)動會喚醒這
                            // 些線程,以便處理新的工作項
    struct binder_stats stats;
    struct list_head delivered_death;
    int max_threads;// 驅(qū)動程序最多可以主動請求進程注冊的線程數(shù)
    int requested_threads;
    int requested_threads_started;
    int ready_threads;// 進程當前的空閑Binder線程數(shù)
    long default_priority;
    struct dentry *debugfs_entry;
    struct binder_context *context;//保存binder的上下文信息
};

binder_buffer

該結(jié)構(gòu)體用來描述一個內(nèi)核緩沖區(qū)捶牢,該緩沖區(qū)用于在進程間傳輸數(shù)據(jù)鸠珠。

struct binder_buffer {
    // 每一個使用Binder機制的進程在Binder驅(qū)動中都有一個內(nèi)核緩沖區(qū)列表,用來保存Binder驅(qū)動
    // 程序為它分配的內(nèi)核緩沖區(qū)秋麸,entry是該列表的一個節(jié)點
    struct list_head entry; /* free and allocated entries by address */
     // 進程使用兩個紅黑樹分別保存使用中以及空閑的內(nèi)核緩沖區(qū)渐排。如果空閑,free=1灸蟆,
    //rb_node就是空閑內(nèi)核緩沖區(qū)紅黑樹中的節(jié)點驯耻,否則是使用中內(nèi)核緩沖區(qū)紅黑樹中的節(jié)點
    struct rb_node rb_node; /* free entry by size or allocated entry */
                /* by address */
    unsigned free:1;
    unsigned allow_user_free:1;
    unsigned async_transaction:1;
    unsigned debug_id:29;
    struct binder_transaction *transaction;
    struct binder_node *target_node;
    size_t data_size;
    size_t offsets_size;
    size_t extra_buffers_size;
    // 保存通信數(shù)據(jù),分兩種類型:普通數(shù)據(jù)、Binder對象可缚。驅(qū)動程序不關心普通數(shù)據(jù)霎迫,但必須知道里面
    // 的Binder對象,因為要根據(jù)它們來維護內(nèi)核中Binder實體對象和Binder引用對象的生命周期城看。
    uint8_t data[0];
};

下面開始看下binder_mmap的流程

static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
{
    int ret;
    struct vm_struct *area;
    struct binder_proc *proc = filp->private_data;//在binder_open驅(qū)動打開時,filp的private_data指向binder_proc的地址filp->private_data = proc;具體可參考kernel/drivers/staging/android/binder.c
    const char *failure_string;
    //內(nèi)核進入這個函數(shù)時女气,就已經(jīng)預先為此次映射分配好了調(diào)用進程在用戶空間的虛擬地址范圍
  //(vma->vm_start,vma->vm_end)
    struct binder_buffer *buffer;//
//  // 有效性檢查:映射的內(nèi)存不能大于4M
    if ((vma->vm_end - vma->vm_start) > SZ_4M)
        vma->vm_end = vma->vm_start + SZ_4M;
    vma->vm_flags = (vma->vm_flags | VM_DONTCOPY) & ~VM_MAYWRITE;
  //為進程所在的內(nèi)核空間申請與用戶空間同樣長度的虛擬地址空間测柠,這段空間用于內(nèi)核來訪問和管理binder內(nèi)存區(qū)域
    area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP);
    //將內(nèi)核空間地址賦值給proc->buffer炼鞠,即保存到進程上下文中,對應內(nèi)核虛擬地址的開始即為binder內(nèi)存的開始地址
    proc->buffer = area->addr;
      // 計算 "內(nèi)核空間地址" 和 "進程虛擬地址" 的偏移
    proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer;
     // 為proc->pages分配內(nèi)存轰胁,用于存放內(nèi)核分配的物理頁的頁描述指針:
    //struct page谒主,每個物理頁對應這樣一個struct page結(jié)構(gòu)
    proc->pages = kzalloc(sizeof(proc->pages[0]) * ((vma->vm_end - vma->vm_start) / PAGE_SIZE), GFP_KERNEL);
        // 內(nèi)核空間的內(nèi)存大小 = 進程虛擬地址區(qū)域(用戶空間)的內(nèi)存大小
    proc->buffer_size = vma->vm_end - vma->vm_start;
    vma->vm_ops = &binder_vm_ops;
      // 將 proc(進程上下文信息) 賦值給vma私有數(shù)據(jù)
    vma->vm_private_data = proc;
// 通過調(diào)用binder_update_page_range()來分配物理頁面。即赃阀,將物理內(nèi)存映射到內(nèi)核空間 以及 用戶空間
    buffer = proc->buffer;
    INIT_LIST_HEAD(&proc->buffers);
        // 將物理內(nèi)存添加到proc->buffers鏈表中進行管理霎肯。
    list_add(&buffer->entry, &proc->buffers);
    buffer->free = 1;
      // 把分配好內(nèi)存插入到對應的表中(空閑內(nèi)存表)
    binder_insert_free_buffer(proc, buffer);
    //異步傳輸大小只有一半
    proc->free_async_space = proc->buffer_size / 2;
    barrier();
    proc->files = get_files_struct(current);
      // 將用戶空間地址信息保存到proc中
    proc->vma = vma;
    proc->vma_vm_mm = vma->vm_mm;
    /*pr_info("binder_mmap: %d %lx-%lx maps %p\n",
         proc->pid, vma->vm_start, vma->vm_end, proc->buffer);*/
    return 0;
}

binder_update_page_range

......
    for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {
        int ret;

        page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE];

        BUG_ON(*page);
        *page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO);
        if (*page == NULL) {
            pr_err("%d: binder_alloc_buf failed for page at %p\n",
                proc->pid, page_addr);
            goto err_alloc_page_failed;
        }
        //內(nèi)核空間的虛擬內(nèi)存映射到物理內(nèi)存
        ret = map_kernel_range_noflush((unsigned long)page_addr,
                    PAGE_SIZE, PAGE_KERNEL, page);
        flush_cache_vmap((unsigned long)page_addr,
                (unsigned long)page_addr + PAGE_SIZE);
        if (ret != 1) {
            pr_err("%d: binder_alloc_buf failed to map page at %p in kernel\n",
                   proc->pid, page_addr);
            goto err_map_kernel_failed;
        }
        user_page_addr =
            (uintptr_t)page_addr + proc->user_buffer_offset;
            //用戶空間的虛擬內(nèi)存映射到物理內(nèi)存
        ret = vm_insert_page(vma, user_page_addr, page[0]);
        if (ret) {
            pr_err("%d: binder_alloc_buf failed to map page at %lx in userspace\n",
                   proc->pid, user_page_addr);
            goto err_vm_insert_page_failed;
        }
        /* vm_insert_page does not seem to increment the refcount */
    }
....

binder_mmap中關鍵的代碼
1.area = get_vm_area(size) 這個是分配了一塊內(nèi)存空間
2.proc->buffer = area->addr proc->buffer是內(nèi)核空間的數(shù)據(jù)緩沖區(qū),也就是把剛剛分配的內(nèi)存空間地址給了內(nèi)核空間的buffer

  1. proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer;計算 "內(nèi)核空間地址" 和 "用戶空間地址" 的偏移

所以我們可以得出榛斯,如果用戶空間想要訪問剛剛分配的內(nèi)核空間內(nèi)存buffer,那么只需要通過proc的buffer指針+user_buffer_offset就可以訪問到vma->vm_start=proc->user_buffer_offset+proc_buffer观游。


總結(jié)
binder_mmap給當前進程分配了一塊物理內(nèi)存,并將這塊物理內(nèi)存的應用保存到proc進程的上下文中驮俗。

到目前為止懂缕,終于走完了main方法中的binder_open方法。servicermanager進程打開了binder驅(qū)動王凑,并在進程中創(chuàng)建了一塊內(nèi)存搪柑。

binder_become_context_manager

在open_binder方法執(zhí)行完成后,開始將該進程servicemanager注冊為上下文管理器索烹,也就是在binder驅(qū)動中工碾,記錄servicemanager的進程信息,之后這個進程就會管理系統(tǒng)服務百姓,其他系統(tǒng)服務都要在這個進程中注冊后才能使用

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

看起來代碼很簡單渊额,只調(diào)用了一個ioctl,傳入fd瓣戚,BINDER_SET_CONTEXT_MGR命令端圈,和一個0

ioctl 是設備驅(qū)動程序中設備控制接口函數(shù),一個字符設備驅(qū)動通常會實現(xiàn)設備打開子库、關閉舱权、讀、寫等功能仑嗅,在一些需要細分的情境下宴倍,如果需要擴展新的功能张症,通常以增設 ioctl() 命令的方式實現(xiàn)。
接收參數(shù)
fd 文件描述符
cmd 交互協(xié)議鸵贬,設備驅(qū)動將根據(jù) cmd 執(zhí)行對應操作
… 可變參數(shù) arg俗他,依賴 cmd 指定長度以及類型

這個ioctl最終會調(diào)用到binder驅(qū)動中的binder_ioctl

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    int ret;
    struct binder_proc *proc = filp->private_data;//獲取進程信息
    struct binder_thread *thread;//進程的線程
    unsigned int size = _IOC_SIZE(cmd);
    void __user *ubuf = (void __user *)arg;//用戶空間的數(shù)據(jù)指針
    thread = binder_get_thread(proc);//從proc信息中獲取線程
    switch (cmd) {
    case BINDER_WRITE_READ://binder讀寫數(shù)據(jù)  arg是數(shù)據(jù)指針  對于smgr binder_write_read  是 arg  cmd為      filp為驅(qū)動的句柄,thread為進程中獲取的線程
        ret = binder_ioctl_write_read(filp, cmd, arg, thread);
        if (ret)
            goto err;
        break;
    case BINDER_SET_MAX_THREADS:
        if (copy_from_user(&proc->max_threads, ubuf, sizeof(proc->max_threads))) {
            ret = -EINVAL;
            goto err;
        }
        break;
    case BINDER_SET_CONTEXT_MGR://用戶空間ioctl傳過來命令,BINDER_SET_CONTEXT_MGR
        ret = binder_ioctl_set_ctx_mgr(filp);
        if (ret)
            goto err;
        break;
    case BINDER_THREAD_EXIT:
        binder_debug(BINDER_DEBUG_THREADS, "%d:%d exit\n",
                 proc->pid, thread->pid);
        binder_free_thread(proc, thread);
        thread = NULL;
        break;
    case BINDER_VERSION: {
        struct binder_version __user *ver = ubuf;

        if (size != sizeof(struct binder_version)) {
            ret = -EINVAL;
            goto err;
        }
        if (put_user(BINDER_CURRENT_PROTOCOL_VERSION,
                 &ver->protocol_version)) {
            ret = -EINVAL;
            goto err;
        }
        break;
    }
}

在匹配到BINDER_SET_CONTEXT_MGR項后阔逼,驅(qū)動調(diào)用了binder_ioctl_set_ctx_mgr方法

static int binder_ioctl_set_ctx_mgr(struct file *filp)
{
    int ret = 0;
    struct binder_proc *proc = filp->private_data;//從filp中讀取進程信息
    struct binder_context *context = proc->context;// binder_context 保存binder上下文管理者的信息兆衅。通過binder context可以找到service manager對應的bind node。
    kuid_t curr_euid = current_euid();
    if (context->binder_context_mgr_node) {//已經(jīng)注冊了MGR
        pr_err("BINDER_SET_CONTEXT_MGR already set\n");
        ret = -EBUSY;
        goto out;
    }
    ret = security_binder_set_context_mgr(proc->tsk);
    if (ret < 0)
        goto out;
    if (uid_valid(context->binder_context_mgr_uid)) {
        if (!uid_eq(context->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,
                     context->binder_context_mgr_uid));
            ret = -EPERM;
            goto out;
        }
    } else {
        context->binder_context_mgr_uid = curr_euid;
    }
    //創(chuàng)建一個binder_node
    context->binder_context_mgr_node = binder_new_node(proc, 0, 0);
    context->binder_context_mgr_node->local_weak_refs++;
    context->binder_context_mgr_node->local_strong_refs++;
    context->binder_context_mgr_node->has_strong_ref = 1;
    context->binder_context_mgr_node->has_weak_ref = 1;
out:
    return ret;
}

我們看下binder_context結(jié)構(gòu)體

//binder_context是全局唯一的嗜浮,其中包含了一個binder_context_mgr_node也就是節(jié)點信息羡亩,可以通過這個節(jié)點找到對應的進程
struct binder_context {
    struct binder_node *binder_context_mgr_node;
    kuid_t binder_context_mgr_uid;
    const char *name;
};

到目前為止servicemanager已經(jīng)在binder驅(qū)動中注冊為一個MGR節(jié)點,也就是成為了系統(tǒng)的服務管家進程危融。其他進程都可以通過proc->context->binder_node來獲取到servicemanager來將自己注冊畏铆。

binder_loop

binder_loop(bs, svcmgr_handler)

從字面意思上我們看到,servicemanager進程開啟了一個循環(huán)吉殃,成為了一個守護進程辞居,開始了作為系統(tǒng)服務管家的工作。我們看到接收的參數(shù)為一個binder_state結(jié)構(gòu)蛋勺,和一個svcmgr_handler瓦灶,這個是一個回調(diào)函數(shù),用于處理其他進程發(fā)送給servicemanager進程的命令

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://注意倚搬,這個是注冊服務命令,也就是AMS通過跨進程調(diào)用最終調(diào)用到servicemanager的方法乾蛤,也是我們經(jīng)過大量的源碼分析后最ServerManager.addService的終點
        s = bio_get_string16(msg, &len);
        if (s == NULL) {
            return -1;
        }
        handle = bio_get_ref(msg);
        allow_isolated = bio_get_uint32(msg) ? 1 : 0;
        if (do_add_service(bs, s, len, handle, txn->sender_euid,
            allow_isolated, txn->sender_pid))
            return -1;
        break;
    case SVC_MGR_LIST_SERVICES: {
    return 0;
}

binder_loop命令調(diào)用了驅(qū)動的binder_loop命令,先看下相關的結(jié)構(gòu)體binder_write_read 這個結(jié)構(gòu)體的作用其實就是對binder通信數(shù)據(jù)的封裝

struct binder_write_read {
    binder_size_t       write_size; /* bytes to write */寫數(shù)據(jù)大小
    binder_size_t       write_consumed; /* bytes consumed by driver */已經(jīng)讀取的寫入數(shù)據(jù)
    binder_uintptr_t    write_buffer;//寫入數(shù)據(jù)的buffer地址
    binder_size_t       read_size;  /* bytes to read *///讀數(shù)據(jù)大小
    binder_size_t       read_consumed;  /* bytes consumed by driver *已經(jīng)讀取了多少數(shù)據(jù)
    binder_uintptr_t    read_buffer;//讀取數(shù)據(jù)的buffer
};

調(diào)用binder_looper

void binder_loop(struct binder_state *bs, binder_handler func)
{
    int res;
    struct binder_write_read bwr;//內(nèi)核中創(chuàng)建一個binder_write_read結(jié)構(gòu)體指針
    uint32_t readbuf[32];//分配讀取buffer內(nèi)存

、////設置寫入數(shù)據(jù)為0捅僵,代表沒有寫入數(shù)據(jù)
    bwr.write_size = 0;
    bwr.write_consumed = 0;
    bwr.write_buffer = 0;

    readbuf[0] = BC_ENTER_LOOPER;
    binder_write(bs, readbuf, sizeof(uint32_t));//設置servicemanager進程中的thread標志位為BINDER_LOOPER_STATE_ENTERED

    for (;;) {//無限循環(huán)
        bwr.read_size = sizeof(readbuf);
        bwr.read_consumed = 0;
        bwr.read_buffer = (uintptr_t) readbuf;
         //掛起家卖,等待喚醒
        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);//用戶空間發(fā)送bwr數(shù)據(jù)到內(nèi)核空間
        if (res < 0) {
            ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
            break;
        }
        res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
        if (res == 0) {
            ALOGE("binder_loop: unexpected reply?!\n");
            break;
        }
        if (res < 0) {
            ALOGE("binder_loop: io error %d %s\n", res, strerror(errno));
            break;
        }
    }
}

關鍵的是進入無限循環(huán)后,bwr的read_buffer內(nèi)容是 readbuf ,read_size不等于0庙楚,通過通過ioctl發(fā)送BINDER_WRITE_REA命令給驅(qū)動層上荡,其實驅(qū)動層接收到這個命令后,會根據(jù)bwr中的write_size和read_size來判斷是讀數(shù)據(jù)還是寫數(shù)據(jù)馒闷,write_size不等0表示進程寫入數(shù)據(jù)到binder驅(qū)動中,read_siez不等于0表示驅(qū)動讀取數(shù)據(jù)到進程下面我們看ioctl的驅(qū)動層實現(xiàn)binder_ioctrl做了什么

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    int ret;
    struct binder_proc *proc = filp->private_data;//獲取進程信息
    struct binder_thread *thread;//進程的線程
    unsigned int size = _IOC_SIZE(cmd);
    void __user *ubuf = (void __user *)arg;//用戶空間的數(shù)據(jù)指針
    thread = binder_get_thread(proc);//從proc信息中根據(jù)pid查找線程

    switch (cmd) {
    case BINDER_WRITE_READ://binder讀寫數(shù)據(jù)  arg是數(shù)據(jù)指針,filp為驅(qū)動的句柄,thread為進程中獲取的線程
        ret = binder_ioctl_write_read(filp, cmd, arg, thread);
        if (ret)
            goto err;
        break;
}

binder_ioctl_write_read

static int binder_ioctl_write_read(struct file *filp,
                unsigned int cmd, unsigned long arg,
                struct binder_thread *thread)
{   int ret = 0;
    struct binder_proc *proc = filp->private_data;//
    unsigned int size = _IOC_SIZE(cmd);
    void __user *ubuf = (void __user *)arg;
    struct binder_write_read bwr;//內(nèi)核空間數(shù)據(jù)封裝
    if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {//從用戶空間拷貝數(shù)據(jù)到內(nèi)核空間酪捡,第一次拷貝數(shù)據(jù)
        ret = -EFAULT;
        goto out;
    }

這里我們發(fā)現(xiàn)了一次拷貝,我們知道binder_write_read就是一個對讀寫數(shù)據(jù)進程封裝的結(jié)構(gòu)體纳账,


這樣用戶空間的數(shù)據(jù)封裝結(jié)構(gòu)體內(nèi)容被拷貝到了內(nèi)核空間逛薇,繼續(xù)看代碼

if (bwr.write_size > 0) {//bwr是從用戶空間拷貝過來的數(shù)據(jù)封裝,如果發(fā)現(xiàn)數(shù)據(jù)封裝中寫入數(shù)據(jù)大于0疏虫,則進入binder_thread_write方法永罚,發(fā)送數(shù)據(jù)
        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;
        }
    }
    if (bwr.read_size > 0) {//如果發(fā)現(xiàn)啤呼,讀取數(shù)據(jù)大于0,則進入binder_thread_read來接收數(shù)據(jù)
        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);
        if (!list_empty(&proc->todo))
            wake_up_interruptible(&proc->wait);
        if (ret < 0) {
            if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
                ret = -EFAULT;
            goto out;
        }
    }

由于我們知道呢袱,bwr結(jié)構(gòu)體中封裝的數(shù)據(jù)read_size是大于0官扣,write_size是小于0的,所以進入binder_thread_read方法

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)
{
    void __user *buffer = (void __user *)(uintptr_t)binder_buffer;//讀取數(shù)據(jù)buffer指針
    void __user *ptr = buffer + *consumed;//未讀取數(shù)據(jù)位置 
    void __user *end = buffer + size;//讀取數(shù)據(jù)的結(jié)束位置
    int wait_for_proc_work;//進程是否掛起標志
    wait_for_proc_work = thread->transaction_stack == NULL &&list_empty(&thread->todo);//wait_for_proc_work目前為1羞福,表示沒有要處理的任務
    if (wait_for_proc_work)//ready_threads為1,進程多了一個空閑線程
        proc->ready_threads++;
    if (wait_for_proc_work) {
        binder_set_nice(proc->default_priority);;//把當前線程的優(yōu)先級設置為它所屬進程的優(yōu)先級
        if (non_block) {//非阻塞要立刻返回處理結(jié)果
            if (!binder_has_proc_work(proc, thread))
                ret = -EAGAIN;
        } else
            ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread));
    } else {
        if (non_block) {//非阻塞要立刻返回處理結(jié)果
            if (!binder_has_thread_work(thread))
                ret = -EAGAIN;
        } else
            ret = wait_event_freezable(thread->wait, binder_has_thread_work(thread));////睡眠等待直到線程有新的未處理項為止 然后發(fā)送端進程就進入了休眠惕蹄。目的是為了等待目標進程處理完請求后給予它響應。
    }
//線程被喚醒了
if (wait_for_proc_work)
        proc->ready_threads--;
    thread->looper &= ~BINDER_LOOPER_STATE_WAITING;

    if (ret)
        return ret;

    while (1) {
        uint32_t cmd;
        struct binder_transaction_data tr;
        struct binder_work *w;
        struct binder_transaction *t = NULL;
.......

從代碼可以看到servicemanager進程啟動時 thread->transaction_stack 肯定未空并且thread->todo也為空治专,所以wait_for_proc_work為真proc->ready_threads增加1卖陵,之后根據(jù)線程同步的還是異步的來確定時直接返回還是掛起,binder通信默認時同步的看靠,所以最終會執(zhí)行到wait_event_freezable_exclusive掛起線程赶促,等待接收喚醒。至此挟炬,servicemanager進程終于啟動完成了鸥滨,并且進入了休眠,等待后面其他進程來喚醒了

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末谤祖,一起剝皮案震驚了整個濱河市婿滓,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌粥喜,老刑警劉巖凸主,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異额湘,居然都是意外死亡卿吐,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門锋华,熙熙樓的掌柜王于貴愁眉苦臉地迎上來嗡官,“玉大人,你說我怎么就攤上這事毯焕⊙苄龋” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵纳猫,是天一觀的道長婆咸。 經(jīng)常有香客問我,道長芜辕,這世上最難降的妖魔是什么尚骄? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮物遇,結(jié)果婚禮上乖仇,老公的妹妹穿的比我還像新娘憾儒。我一直安慰自己,他們只是感情好乃沙,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布起趾。 她就那樣靜靜地躺著,像睡著了一般警儒。 火紅的嫁衣襯著肌膚如雪训裆。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天蜀铲,我揣著相機與錄音边琉,去河邊找鬼。 笑死记劝,一個胖子當著我的面吹牛变姨,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播厌丑,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼定欧,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了怒竿?” 一聲冷哼從身側(cè)響起砍鸠,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎耕驰,沒想到半個月后爷辱,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡朦肘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年饭弓,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片媒抠。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡示启,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出领舰,到底是詐尸還是另有隱情,我是刑警寧澤迟螺,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布冲秽,位于F島的核電站,受9級特大地震影響矩父,放射性物質(zhì)發(fā)生泄漏锉桑。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一窍株、第九天 我趴在偏房一處隱蔽的房頂上張望民轴。 院中可真熱鬧攻柠,春花似錦、人聲如沸后裸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽微驶。三九已至浪谴,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間因苹,已是汗流浹背苟耻。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留扶檐,地道東北人凶杖。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像款筑,于是被迫代替她去往敵國和親智蝠。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容