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

前言

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

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

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

Binder通信機制

Binder是由ClientServer宇攻,ServiceManager萍摊,Binder驅動程序組成。

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函數(shù)朴则,同時重啟了下面幾個模塊

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

接下來在init.rc里面調(diào)用啟動servicemanager

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

通過class_start core就啟動了servicemanager芥被。

接下里就來看看service_manager.cmain函數(shù)

//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加載完內(nèi)核的時候,init函數(shù)會執(zhí)行device_initcall席镀。從而執(zhí)行binder_init函數(shù)匹中,初始化binder驅動程序。再來看看binder_init()函數(shù)

//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單一內(nèi)核進程

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

misc_register 它傳的參數(shù)是一個結構體如下

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

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

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

binder_open()

binder_init初始化之后屎篱,接下來service_manager.cmain函數(shù)會調(diào)用binder_open服赎。先來看看binder_open()函數(shù)。

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進程內(nèi)的用來用戶請求處理的線程重虑,線程最大數(shù)由max_threads決定
  2. nodes 保存binder_proc進程內(nèi)的binder的實體
  3. refs_by_descrefs_by_node 保存binder_proc進程內(nèi)的binder的引用
  4. tsk 保存binder_proc進程的地址
  5. todo 待處理的事務鏈表
  6. wait 等待處理的鏈表
  7. default_priority 默認處理的事務優(yōu)先級

get_task_struct Linux內(nèi)核方法,源碼如下

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

最終調(diào)用atomic_add函數(shù)秦士,通過原子加的形式實現(xiàn)當前進程引用的計數(shù)

接下來就是對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)會自動調(diào)用misc_open函數(shù),而在misc_open函數(shù)曹傀,會遍歷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函數(shù)卖毁。再來看看binder_mmap函數(shù)

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結構揖曾,內(nèi)存映射信息放在vma參數(shù)中落萎。其中vmavm_area_struct結構體,它是給進程使用的一塊連續(xù)的虛擬地址空間炭剪,而vm_struct是個內(nèi)核使用的一塊連續(xù)的虛擬地址空間练链。在同一個物理頁面中,一方映射到進程虛擬地址空間奴拦,一方面映射到內(nèi)核虛擬地址空間媒鼓,這樣,進程和內(nèi)核之間就可以減少一次內(nèi)存拷貝了错妖。接下來該函數(shù)就是處理內(nèi)存映射和管理的詳細步驟绿鸣。

binder_ioctl()

再來看看binder_ioctl函數(shù)

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ā)數(shù)據(jù)

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市暂氯,隨后出現(xiàn)的幾起案子潮模,更是在濱河造成了極大的恐慌,老刑警劉巖痴施,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件擎厢,死亡現(xiàn)場離奇詭異,居然都是意外死亡辣吃,警方通過查閱死者的電腦和手機动遭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來神得,“玉大人厘惦,你說我怎么就攤上這事×ú荆” “怎么了绵估?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長卡骂。 經(jīng)常有香客問我,道長形入,這世上最難降的妖魔是什么全跨? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮亿遂,結果婚禮上浓若,老公的妹妹穿的比我還像新娘。我一直安慰自己蛇数,他們只是感情好挪钓,可當我...
    茶點故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著耳舅,像睡著了一般碌上。 火紅的嫁衣襯著肌膚如雪倚评。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天馏予,我揣著相機與錄音天梧,去河邊找鬼。 笑死霞丧,一個胖子當著我的面吹牛呢岗,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蛹尝,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼后豫,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了突那?” 一聲冷哼從身側響起挫酿,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎陨收,沒想到半個月后掰茶,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡赫编,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年蛤奢,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片饵骨。...
    茶點故事閱讀 40,615評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡翘悉,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出居触,到底是詐尸還是另有隱情妖混,我是刑警寧澤,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布轮洋,位于F島的核電站制市,受9級特大地震影響,放射性物質發(fā)生泄漏弊予。R本人自食惡果不足惜祥楣,卻給世界環(huán)境...
    茶點故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望汉柒。 院中可真熱鬧误褪,春花似錦、人聲如沸碾褂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽正塌。三九已至嘀略,卻和暖如春恤溶,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背屎鳍。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工宏娄, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人逮壁。 一個月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓孵坚,卻偏偏與公主長得像,于是被迫代替她去往敵國和親窥淆。 傳聞我的和親對象是個殘疾皇子卖宠,可洞房花燭夜當晚...
    茶點故事閱讀 45,630評論 2 359

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