Android系統(tǒng)之Binder通信機制

前言

Linunx進程中使用的通信方式有:socket(套接字通信),named(命令管道),pipe(管道),message queque(報文隊列),signal(信號),share memory(共享內存)。

Java進程中使用的通信方式有:socket,named,pipe等。

Android進程中使用的通信方式主要是Binder通信,下面就來看看Binder通信機制

Binder通信機制

Binder是由Client,Server,ServiceManagerBinder驅動程序組成。

ServiceManager

其中ServiceManagerBinder機制的守護進程,同時也是一個特殊的Service。它在init.rc里面就開始啟動了,其中Android7.0servicemanager的啟動代碼拆分到servicemanager.rc里面,代碼如下:

//frameworks/native/master/./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
    onrestart restart keystore
    onrestart restart gatekeeperd
    writepid /dev/cpuset/system-background/tasks
    shutdown critical

從上面代碼可知啟動了servicemanager進程,從而執(zhí)行service_manager.cmain函數,同時重啟了下面幾個模塊

  • healthd 監(jiān)聽電池的狀態(tài)和信息,同時傳遞給BatteryService,從而展示電池相關的信息。
  • zygotezygotekill的時候,servicemanager會在這里嘗試重新喚醒它
  • audioserver ,media 音視頻相關的服務
  • surfaceflinger 繪制應用程序的用戶界面的服務
  • inputflinger 系統(tǒng)輸入事件服務
  • drm 數字版權管理服務
  • cameraserver 相機服務
  • keystore 應用簽名文件
  • gatekeeperd 系統(tǒng)的圖案/密碼認證

接下來在init.rc里面調用啟動servicemanager

//init.rc
//....
  class_start core
//....

通過class_start core就啟動了servicemanager。

接下里就來看看service_manager.cmain函數

//frameworks/native/master/./cmds/servicemanager/service_manager.c
int main()
{
    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;
}

主要做了下面幾種

  1. binder_open打開Binder設備
  2. binder_become_context_manager 通知Binder驅動程序自己是Binder上下文管理者
  3. binder_loop 進入一個無窮循環(huán),充當Server的角色,等待Client的請求

Binder驅動程序初始化

Binder驅動程序源碼位于/drivers/staging/android/binder.c,在源碼中可以看到這行代碼

device_initcall(binder_init);

binder_init()

Linux加載完內核的時候,init函數會執(zhí)行device_initcall。從而執(zhí)行binder_init函數,初始化binder驅動程序。再來看看binder_init()函數

//android/kernel/msm/android-7.1.2_r0.33/./drivers/staging/android/ binder.c
static int __init binder_init(void)
{
    int ret;
    binder_deferred_workqueue = create_singlethread_workqueue("binder");
    if (!binder_deferred_workqueue)
        return -ENOMEM;
    binder_debugfs_dir_entry_root = debugfs_create_dir("binder", NULL);
    if (binder_debugfs_dir_entry_root)
        binder_debugfs_dir_entry_proc = debugfs_create_dir("proc",
                         binder_debugfs_dir_entry_root);
    ret = misc_register(&binder_miscdev);
    if (binder_debugfs_dir_entry_root) {
        debugfs_create_file("state",
                    S_IRUGO,
                    binder_debugfs_dir_entry_root,
                    NULL,
                    &binder_state_fops);
        debugfs_create_file("stats",
                    S_IRUGO,
                    binder_debugfs_dir_entry_root,
                    NULL,
                    &binder_stats_fops);
        debugfs_create_file("transactions",
                    S_IRUGO,
                    binder_debugfs_dir_entry_root,
                    NULL,
                    &binder_transactions_fops);
        debugfs_create_file("transaction_log",
                    S_IRUGO,
                    binder_debugfs_dir_entry_root,
                    &binder_transaction_log,
                    &binder_transaction_log_fops);
        debugfs_create_file("failed_transaction_log",
                    S_IRUGO,
                    binder_debugfs_dir_entry_root,
                    &binder_transaction_log_failed,
                    &binder_transaction_log_fops);
    }
    return ret;
}

create_singlethread_workqueue 創(chuàng)建了一個binderworker_thread單一內核進程

debugfs_create_dir debugfs是一種內核調試的虛擬文件系統(tǒng),內核開發(fā)者通過debugfs和用戶空間交換數據,它是在Linux運行的時候建立。這里就是創(chuàng)建debugfs相應的文件

misc_register 它傳的參數是一個結構體如下

static struct miscdevice binder_miscdev = {
    .minor = MISC_DYNAMIC_MINOR, //次設備號動態(tài)分配
    .name = "binder",//設備號
    .fops = &binder_fops// 設備的文件操作系統(tǒng)
};

通過misc_register函數為binder驅動注冊一個misc設備

接下來就是創(chuàng)建procstate目錄下的一些文件

binder_open()

binder_init初始化之后,接下來service_manager.cmain函數會調用binder_open。先來看看binder_open()函數。

static int binder_open(struct inode *nodp, struct file *filp)
{
    struct binder_proc *proc;
    binder_debug(BINDER_DEBUG_OPEN_CLOSE, "binder_open: %d:%d\n",
             current->group_leader->pid, current->pid);
    proc = kzalloc(sizeof(*proc), GFP_KERNEL);
    if (proc == NULL)
        return -ENOMEM;
    get_task_struct(current);
    proc->tsk = current;
    INIT_LIST_HEAD(&proc->todo);
    init_waitqueue_head(&proc->wait);
    proc->default_priority = task_nice(current);
    binder_lock(__func__);
    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;
    binder_unlock(__func__);
    if (binder_debugfs_dir_entry_proc) {
        char strbuf[11];
        snprintf(strbuf, sizeof(strbuf), "%u", proc->pid);
        proc->debugfs_entry = debugfs_create_file(strbuf, S_IRUGO,
            binder_debugfs_dir_entry_proc, proc, &binder_proc_fops);
    }
    return 0;
}

binder_proc 它是保存打開/dev/binder設備的進程的結構體,保存的信息如下

struct binder_proc {
    //...
    struct rb_root threads;
    struct rb_root nodes;
    struct rb_root refs_by_desc;
    struct rb_root refs_by_node;
    struct task_struct *tsk;
    struct list_head todo;
    wait_queue_head_t wait;
    int max_threads;
    long default_priority;
      //...
};
  1. threads 保存binder_proc進程內的用來用戶請求處理的線程,線程最大數由max_threads決定
  2. nodes 保存binder_proc進程內的binder的實體
  3. refs_by_descrefs_by_node 保存binder_proc進程內的binder的引用
  4. tsk 保存binder_proc進程的地址
  5. todo 待處理的事務鏈表
  6. wait 等待處理的鏈表
  7. default_priority 默認處理的事務優(yōu)先級

get_task_struct Linux內核方法,源碼如下

#define get_task_struct(tsk) do{ atomic_inc&((tsk) ->[usage] } while(0)

最終調用atomic_add函數,通過原子加的形式實現當前進程引用的計數

接下來就是對binder_proc鏈表的初始化以及其他進程相關信息進行初始化賦值。

binder_mmap()

binder_open里面有一句代碼是

static int binder_open(struct inode *nodp, struct file *filp)
{
//....
  filp->private_data = proc;
//....
}

前面說到binder_init里面會把binder驅動注冊一個misc設備。進去misc.c會看到

//kernel/common/drivers/char/misc.c
/ * The structure passed is linked into the kernel and may not be
 *  destroyed until it has been unregistered. By default, an open()
 *  syscall to the device sets file->private_data to point to the
 *  structure. Drivers don't need open in fops for this.
 */

int misc_register(struct miscdevice * misc)
{
//....
list_add(&misc->list, &misc_list);
 out:
    mutex_unlock(&misc_mtx);
    return err;
}

把設備保存在misc_list里面,在上面的注釋可知,它是在file->private_data具有指針指向的時候系統(tǒng)會自動調用misc_open函數,而在misc_open函數,會遍歷file->private_data里面所有保存的設備的fops。

static int misc_open(struct inode * inode, struct file * file)
{
//...
list_for_each_entry(c, &misc_list, list) {
        if (c->minor == minor) {
            new_fops = fops_get(c->fops);
            break;
        }
    }
//...
}

這里fops是傳入的binder_miscdev結構體里面的fopsfops對應的是binder_fops結構體

static const struct file_operations binder_fops = {
    .owner = THIS_MODULE,
    .poll = binder_poll,
    .unlocked_ioctl = binder_ioctl,
    .compat_ioctl = binder_ioctl,
    .mmap = binder_mmap,
    .open = binder_open,
    .flush = binder_flush,
    .release = binder_release,
};

從這里可以找出初始化binder_mmap函數。再來看看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;
    const char *failure_string;
    struct binder_buffer *buffer;
//....
}

通過filp->private_data得到在打開設備文件 /dev/binder創(chuàng)建的struct binder_proc結構,內存映射信息放在vma參數中。其中vmavm_area_struct結構體,它是給進程使用的一塊連續(xù)的虛擬地址空間,而vm_struct是個內核使用的一塊連續(xù)的虛擬地址空間。在同一個物理頁面中,一方映射到進程虛擬地址空間,一方面映射到內核虛擬地址空間,這樣,進程和內核之間就可以減少一次內存拷貝了。接下來該函數就是處理內存映射和管理的詳細步驟。

binder_ioctl()

再來看看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;
    /*pr_info("binder_ioctl: %d:%d %x %lx\n",
            proc->pid, current->pid, cmd, arg);*/
    trace_binder_ioctl(cmd, arg);
    ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
    if (ret)
        goto err_unlocked;
    binder_lock(__func__);
    thread = binder_get_thread(proc);
    if (thread == NULL) {
        ret = -ENOMEM;
        goto err;
    }
    switch (cmd) {
    case BINDER_WRITE_READ:
        ret = binder_ioctl_write_read(filp, cmd, arg, thread);
        if (ret)
            goto err;
        break;
    case BINDER_SET_MAX_THREADS:
        if (copy_from_user_preempt_disabled(&proc->max_threads, ubuf, sizeof(proc->max_threads))) {
            ret = -EINVAL;
            goto err;
        }
        break;
    case 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_preempt_disabled(BINDER_CURRENT_PROTOCOL_VERSION, &ver->protocol_version)) {
            ret = -EINVAL;
            goto err;
        }
        break;
    }
    default:
        ret = -EINVAL;
        goto err;
    }
    ret = 0;
err:
    if (thread)
        thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN;
    binder_unlock(__func__);
    wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
    if (ret && ret != -ERESTARTSYS)
        pr_info("%d:%d ioctl %x %lx returned %d\n", proc->pid, current->pid, cmd, arg, ret);
err_unlocked:
    trace_binder_ioctl_done(ret);
    return ret;
}

它主要是負責在兩個進程間進行收發(fā)數據

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容