Android系統之Binder子系統

        今天我們來看看 Android 系統的一個子系統Binder。首先先來介紹下 Binder 子系統,它是 Android 系統中用來進程間通信的一種方式。那麼爲什麼要學習下 binder 系統呢?因爲掌握了 binder 子系統,我們就能很好的理解在 Android 系統進程間到底是怎樣進行通訊的,並且基於 binder 實現的各種 client 和 server 之間是怎麼進行一步步通訊的。下來我們就開始來看看 binder 子系統。

        binder 系統的核心是另種通信方式:IPC 和 RPC。IPC 是 src A 直接發給 des B,而 RPC 是 src A 通過遠程函數調用 des B。

        1、IPC 通信的方式有三個要素:

            1. 發送源:A;

            2. 目的:B 向 servicemanger 註冊 led 服務,A 向 servicemanger 查詢 led 服務,得到一個 handle;

            3. 數據本身:char buf[512];

        2、RPC 通信方式是遠程函數調用:

            1. 調用的是哪個函數:sever 的函數編號;

            2. 傳給它什麼參數,返回值。通過 IPC 的 buf 傳輸。

        example:LED 傳輸。IPC 方式是從 A 直接發送給B;而 RPC 方式是 led_open、led_ctl 進行封裝數據,然後發送給 B,在 B 那邊調用 led_open,led_ctl 再次取出數據。


        我們先來大概介紹下 client、servicemanger、server 三個的作用。

        client:

            1. 打開驅動;

            2. 獲取服務:向 servicemanger 查詢服務,獲得一個 handle;

            3. 向 handle 發送數據。


        servicemanger:

            1. 打開驅動;

            2. 告訴驅動,它是 “servicemanger”;

            3. while(1) {

                    讀驅動獲取數據;

                    解析數據;

                    調用:a. 註冊服務:在鏈表中記錄服務名;

                              b. 獲取服務:b.1 在鏈表中查詢有無此服務;b.2 返回 “server進程”的 handle。

             };


        server:

            1. 打開驅動;

            2. 註冊服務:向 servicemanger 發送服務;

            3. while(1) {

                    讀驅動獲取數據;

                    解析數據;

                    調用對應函數。

             };


    它們三個都是基於 binder 驅動進行工作。我們先來看看 service_manger.c 文件,mian 函數大體如下

int main(int argc, char **argv)
{
    struct binder_state *bs;

    bs = binder_open(128*1024);      // 對應上面的第一步。打開驅動
    if (!bs) {
        ALOGE("failed to open binder driver\n");
        return -1;
    }

    if (binder_become_context_manager(bs)) {
        ALOGE("cannot become context manager (%s)\n", strerror(errno));
        return -1;
    }

    selinux_enabled = is_selinux_enabled();
    sehandle = selinux_android_service_context_handle();

    if (selinux_enabled > 0) {
        if (sehandle == NULL) {
            ALOGE("SELinux: Failed to acquire sehandle. Aborting.\n");
            abort();
        }

        if (getcon(&service_manager_context) != 0) {
            ALOGE("SELinux: Failed to acquire service_manager context. Aborting.\n");
            abort();
        }
    }

    union selinux_callback cb;
    cb.func_audit = audit_callback;
    selinux_set_callback(SELINUX_CB_AUDIT, cb);
    cb.func_log = selinux_log_callback;
    selinux_set_callback(SELINUX_CB_LOG, cb);

    svcmgr_handle = BINDER_SERVICE_MANAGER;     // 對應上面的第二步。告訴驅動,它是 ServiceManager
    binder_loop(bs, svcmgr_handler);                  // 對應上面的第三步。 while 循環所做的事情

    return 0;
}

    我們再來看看 binder.c (對應於上面的 server),其中 binder_loop 函數就在此文件中。我們來看看 binder_loop 函數所做的事情,code 如下

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

    bwr.write_size = 0;
    bwr.write_consumed = 0;
    bwr.write_buffer = 0;

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

    for (;;) {
        bwr.read_size = sizeof(readbuf);
        bwr.read_consumed = 0;
        bwr.read_buffer = (uintptr_t) readbuf;

        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);                            // 讀取驅動獲得數據

        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;
        }
    }
}

    我們再來看看 bctest.c 文件(對應於上面的 client),code 如下

int main(int argc, char **argv)
{
    int fd;
    struct binder_state *bs;
    uint32_t svcmgr = BINDER_SERVICE_MANAGER;
    uint32_t handle;

    bs = binder_open(128*1024);
    if (!bs) {
        fprintf(stderr, "failed to open binder driver\n");
        return -1;
    }

    argc--;
    argv++;
    while (argc > 0) {
        if (!strcmp(argv[0],"alt")) {
            handle = svcmgr_lookup(bs, svcmgr, "alt_svc_mgr");
            if (!handle) {
                fprintf(stderr,"cannot find alt_svc_mgr\n");
                return -1;
            }
            svcmgr = handle;
            fprintf(stderr,"svcmgr is via %x\n", handle);
        } else if (!strcmp(argv[0],"lookup")) {
            if (argc < 2) {
                fprintf(stderr,"argument required\n");
                return -1;
            }
            handle = svcmgr_lookup(bs, svcmgr, argv[1]);          // 獲取服務
            fprintf(stderr,"lookup(%s) = %x\n", argv[1], handle);
            argc--;
            argv++;
        } else if (!strcmp(argv[0],"publish")) {
            if (argc < 2) {
                fprintf(stderr,"argument required\n");
                return -1;
            }
            svcmgr_publish(bs, svcmgr, argv[1], &token);          // 註冊服務
            argc--;
            argv++;
        } else {
            fprintf(stderr,"unknown command %s\n", argv[0]);
            return -1;
        }
        argc--;
        argv++;
    }
    return 0;
}

    先來看看 svcmgr_lookup 函數是怎麼來獲取服務的,code 如下

uint32_t svcmgr_lookup(struct binder_state *bs, uint32_t target, const char *name)
{
    uint32_t handle;
    unsigned iodata[512/4];
    struct binder_io msg, reply;

    // 構造 binder_io
    bio_init(&msg, iodata, sizeof(iodata), 4);
    bio_put_uint32(&msg, 0);  // strict mode header
    bio_put_string16_x(&msg, SVC_MGR_NAME);
    bio_put_string16_x(&msg, name);

    if (binder_call(bs, &msg, &reply, target, SVC_MGR_CHECK_SERVICE))    // 獲取服務
        return 0;

    handle = bio_get_ref(&reply);

    if (handle)
        binder_acquire(bs, handle);

    binder_done(bs, &msg, &reply);

    return handle;
}

    我們看到其中核心函數是 binder_call 函數。再來看看 svcmgr_publish 函數是怎麼來註冊服務的,code 如下

int svcmgr_publish(struct binder_state *bs, uint32_t target, const char *name, void *ptr)
{
    int status;
    unsigned iodata[512/4];
    struct binder_io msg, reply;

    bio_init(&msg, iodata, sizeof(iodata), 4);
    bio_put_uint32(&msg, 0);  // strict mode header
    bio_put_string16_x(&msg, SVC_MGR_NAME);
    bio_put_string16_x(&msg, name);
    bio_put_obj(&msg, ptr);

    if (binder_call(bs, &msg, &reply, target, SVC_MGR_ADD_SERVICE))    // 註冊服務
        return -1;

    status = bio_get_uint32(&reply);

    binder_done(bs, &msg, &reply);

    return status;
}

    其中核心函數還是 binder_call 函數。binder_call 函數的參數作用分別是:1、遠程調用;2、向誰發送數據;3、調用那個函數;4、提供什麼參數;5、返回值。

    那麼 binder_call 函數中的參數作用如下:

        1、bs 是一個結構體, 代表遠程調用;

        2、msg 中含有服務的名字;

        3、reply 中含有servicemanager回覆的數據, 表示提供服務的進程;

        4、target 代表是的 0,表示servicemanager, (if (target == 0));

        5、SVC_MGR_CHECK_SERVICE 表示要調用servicemanager中的"getservice函數"。

    下來我們具體來看看 binder_call 的實現

int binder_call(struct binder_state *bs,
                struct binder_io *msg, struct binder_io *reply,
                uint32_t target, uint32_t code)
{
    int res;
    struct binder_write_read bwr;
    struct {
        uint32_t cmd;
        struct binder_transaction_data txn;
    } __attribute__((packed)) writebuf;
    unsigned readbuf[32];

    if (msg->flags & BIO_F_OVERFLOW) {
        fprintf(stderr,"binder: txn buffer overflow\n");
        goto fail;
    }

    // 構造參數
    writebuf.cmd = BC_TRANSACTION;
    writebuf.txn.target.handle = target;
    writebuf.txn.code = code;
    writebuf.txn.flags = 0;
    writebuf.txn.data_size = msg->data - msg->data0;
    writebuf.txn.offsets_size = ((char*) msg->offs) - ((char*) msg->offs0);
    writebuf.txn.data.ptr.buffer = (uintptr_t)msg->data0;
    writebuf.txn.data.ptr.offsets = (uintptr_t)msg->offs0;

    bwr.write_size = sizeof(writebuf);
    bwr.write_consumed = 0;
    bwr.write_buffer = (uintptr_t) &writebuf;

    hexdump(msg->data0, msg->data - msg->data0);
    for (;;) {
        bwr.read_size = sizeof(readbuf);
        bwr.read_consumed = 0;
        bwr.read_buffer = (uintptr_t) readbuf;

        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);    // 調用 ioctl 發數據

        if (res < 0) {
            fprintf(stderr,"binder: ioctl failed (%s)\n", strerror(errno));
            goto fail;
        }

        res = binder_parse(bs, reply, (uintptr_t) readbuf, bwr.read_consumed, 0);
        if (res == 0) return 0;
        if (res < 0) goto fail;
    }

fail:
    memset(reply, 0, sizeof(*reply));
    reply->flags |= BIO_F_IOERROR;
    return -1;
}

    我們看到在 writebuf 中構造參數,構造參數放在 buf 中,用 binder_io 來描述。先把 binder_io 轉換爲 binder_write_read;在 ioctl 中調用它來發送數據;最後在 binder_parse 函數將 binder_write_read 轉換爲 binder_io。

    下來我們再來看看 IPC 是怎麼進行數據交互的。我們前面說了,IPC 傳輸方式有三個要素:

        1. 源(自己)

        2. 目的:用 handle 表示“服務”,即向實現該“服務”的進程發送數據;handle 是“服務”的引用。

        3. 數據。

    handle 是進程 A 對進程 B 提供的服務 S 的引用。

    下來我們來解釋下上面那句話中的一些關鍵詞:

        引用,code 如下

struct binder_ref {
    /* Lookups needed: */
    /*   node + proc => ref (transaction) */
    /*   desc + proc => ref (transaction, inc/dec ref) */
    /*   node => refs + procs (proc exit) */
    int debug_id;
    struct rb_node rb_node_desc;
    struct rb_node rb_node_node;
    struct hlist_node node_entry;
    struct binder_proc *proc;
    struct binder_node *node;
    uint32_t desc;
    int strong;
    int weak;
    struct binder_ref_death *death;
};

        我們看到 binder_ref 結構體中有個 binder_node 結構體,這個 binder_node 便指的是服務 S。code 如下

struct binder_node {
    int debug_id;
    struct binder_work work;
    union {
        struct rb_node rb_node;
        struct hlist_node dead_node;
    };
    struct binder_proc *proc;
    struct hlist_head refs;
    int internal_strong_refs;
    int local_weak_refs;
    int local_strong_refs;
    void __user *ptr;
    void __user *cookie;
    unsigned has_strong_ref:1;
    unsigned pending_strong_ref:1;
    unsigned has_weak_ref:1;
    unsigned pending_weak_ref:1;
    unsigned has_async_transaction:1;
    unsigned accept_fds:1;
    unsigned min_priority:8;
    struct list_head async_todo;
};

        在 binder_node 結構體中有個 binder_proc 結構體,這個 binder_proc 便指的是進程 B。code 如下

struct binder_proc {
    struct hlist_node proc_node;
    struct rb_root threads;
    struct rb_root nodes;
    struct rb_root refs_by_desc;
    struct rb_root refs_by_node;
    int pid;
    struct vm_area_struct *vma;
    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;
    ptrdiff_t user_buffer_offset;

    struct list_head buffers;
    struct rb_root free_buffers;
    struct rb_root allocated_buffers;
    size_t free_async_space;

    struct page **pages;
    size_t buffer_size;
    uint32_t buffer_free;
    struct list_head todo;
    wait_queue_head_t wait;
    struct binder_stats stats;
    struct list_head delivered_death;
    int max_threads;
    int requested_threads;
    int requested_threads_started;
    int ready_threads;
    long default_priority;
    struct dentry *debugfs_entry;
};

        在 binder_proc 結構體中有個 threads 結構體,這個 threads 便指的是多線程。code 如下

struct binder_thread {
    struct binder_proc *proc;
    struct rb_node rb_node;
    int pid;
    int looper;
    struct binder_transaction *transaction_stack;
    struct list_head todo;
    uint32_t return_error; /* Write failed, return error code in read buf */
    uint32_t return_error2; /* Write failed, return error code in read */
        /* buffer. Used when sending a reply to a dead process that */
        /* we are also waiting on */
    wait_queue_head_t wait;
    struct binder_stats stats;
};

    現在我們就知道多線程是怎麼來進行信息的傳輸了。

    server 傳入一個 flat_binder_object 給驅動:

        1. 在內核態驅動裏爲每個服務創建 binder_node。binder_node.proc = server 進程

        2. service_manger 在驅動中創建 binder_ref,引用 binder_node 。binder_ref.desc = 1,2,3... ;在用戶態創建服務鏈表(name,handle),handle 指的是前面的 binder_ref.desc

        3. client 向 service_manger 查詢服務,傳 name

        4. service_manger 返回 handle 給驅動程序

        5. 驅動程序在 service_manger 的 binder_ref 紅黑樹中根據 handle 找到 binder_ref,再根據 binder_ref.node 找到 binder_node,最後給 client 創建新的 binder_ref(它的 desc 從 1 開始)。驅動返回 desc 給 client,它即爲 handle

        6. client:驅動根據 handle 找到 binder_ref,根據 binder_ref 找到 binder_node,最後根據 binder_node 找到 server 進程。


    下來我們來看看數據傳輸過程(進程切換)

        client 到 server ,是先寫後讀:

            1. client 構造數據,調用 ioctl 發數據;

            2. 驅動里根據 handle 找到 server 進程;

            3. 把數據放入進程的 binder_proc.todo;

            4. 休眠;

            5. 被喚醒;

            6. 從 todo 鏈表中取出數據,返回用戶空間。

        server端,先讀後寫:

            1. 讀數據休眠;

            2. 被喚醒;

            3. 從 todo 鏈表中取出數據,返回用戶空間;

            4. 處理數據;

            5. 把結果寫給 client,也就是放入 client 的 binder_proc.todo 鏈表,喚醒 client。


        那麼一般情況下,數據是如何進行復制的呢?一般方法,需要 2 次複製。

            1. client 構造數據;

            2. 驅動:copy_from_user

            3. server:3.1 驅動,copy_to_user

                             3.2 用戶態處理

        binder複製數據的方法是隻需 1 次複製。

            1. server 進行 mmap 映射,用戶態可以直接訪問驅動中的某塊內存。

            2. client 構造數據,驅動:copy_from_user

            3. server 可以在用戶態直接使用數據。


        但是值得注意的是:在 binder 方法中,從 test_client 到 test_server 端有個數據需複製 2 次。在 ioctl 時,binder_write_read 結構體先 copy_from_user 到某個內存局部變量,然後再 copy_to_user 到 test_server 端。別的數據都是從 test_cliet 端 copy_from_user 到內核內存,然後 test_server 端直接通過 mmap 可以訪問到內核內存,不用經過 copy_to_user 複製。因此 binder 系統在進行通信時效率能提高一倍。


        接下來我們來看看服務註冊過程,我們先來看看 binder 的驅動框架。我們在 binder_init 函數中看到它是使用 misc_register 來註冊的,說明它是 misc 設備驅動。通過註冊 binder_miscdev 結構體以達到調用 binder_fops 結構體,在 binder_fops 結構體中就含有 binder 驅動各種操作的入口函數。具體代碼如下

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;
}

        binder_miscdev 代碼如下

static struct miscdevice binder_miscdev = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "binder",
    .fops = &binder_fops
};

        binder_fops 代碼如下

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

        在 service_manger 中,打開 binder driver,緊接着 ioctl,最後再 mmap。代碼如下

struct binder_state *binder_open(size_t mapsize)
{
    struct binder_state *bs;
    struct binder_version vers;

    bs = malloc(sizeof(*bs));
    if (!bs) {
        errno = ENOMEM;
        return NULL;
    }

    bs->fd = open("/dev/binder", O_RDWR);
    if (bs->fd < 0) {
        fprintf(stderr,"binder: cannot open device (%s)\n",
                strerror(errno));
        goto fail_open;
    }

    if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) ||
        (vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION)) {
        fprintf(stderr, "binder: driver version differs from user space\n");
        goto fail_open;
    }

    bs->mapsize = mapsize;
    bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
    if (bs->mapped == MAP_FAILED) {
        fprintf(stderr,"binder: cannot map device (%s)\n",
                strerror(errno));
        goto fail_map;
    }

    return bs;

fail_map:
    close(bs->fd);
fail_open:
    free(bs);
    return NULL;
}

        做完這些操作後,service_manger 便會進入到 binder_loop 循環中。在 binder_loop 函數中,readbuf 中存儲的是 BC_ENTER_LOOPER,接着 ioctl BINDER_WRITE_READ,再進行 binder_parse 解析。代碼如下

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

    bwr.write_size = 0;
    bwr.write_consumed = 0;
    bwr.write_buffer = 0;

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

    for (;;) {
        bwr.read_size = sizeof(readbuf);
        bwr.read_consumed = 0;
        bwr.read_buffer = (uintptr_t) readbuf;

        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);                            // 讀取驅動獲得數據

        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;
        }
    }
}

        binder_write 中傳入了 BC_ENTER_LOOPER,看看它做的是那些事情,代碼如下

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;
}

        我們看到它先是構造了 binder_write_read 結構體,再通過 binder_ioctl 函數發送了  BINDER_WRITE_READ 指令。我們再去 binder_ioctl 函數中看看 BINDER_WRITE_READ 操作做了哪些事情。代碼如下

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;

    /*printk(KERN_INFO "binder_ioctl: %d:%d %x %lx\n",
            proc->pid, current->pid, cmd, arg);*/

    ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
    if (ret)
        return ret;

    binder_lock(__func__);
    thread = binder_get_thread(proc);
    if (thread == NULL) {
        ret = -ENOMEM;
        goto err;
    }

    switch (cmd) {
    case BINDER_WRITE_READ: {
        struct binder_write_read bwr;
        if (size != sizeof(struct binder_write_read)) {
            ret = -EINVAL;
            goto err;
        }
        if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
            ret = -EFAULT;
            goto err;
        }
        binder_debug(BINDER_DEBUG_READ_WRITE,
                 "binder: %d:%d write %ld at %08lx, read %ld at %08lx\n",
                 proc->pid, thread->pid, bwr.write_size, bwr.write_buffer,
                 bwr.read_size, bwr.read_buffer);

        if (bwr.write_size > 0) {
            ret = binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed);
            if (ret < 0) {
                bwr.read_consumed = 0;
                if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
                    ret = -EFAULT;
                goto err;
            }
        }
        if (bwr.read_size > 0) {
            ret = binder_thread_read(proc, thread, (void __user *)bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK);
            if (!list_empty(&proc->todo))
                wake_up_interruptible(&proc->wait);
            if (ret < 0) {
                if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
                    ret = -EFAULT;
                goto err;
            }
        }
        binder_debug(BINDER_DEBUG_READ_WRITE,
                 "binder: %d:%d wrote %ld of %ld, read return %ld of %ld\n",
                 proc->pid, thread->pid, bwr.write_consumed, bwr.write_size,
                 bwr.read_consumed, bwr.read_size);
        if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
            ret = -EFAULT;
            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:
        if (binder_context_mgr_node != NULL) {
            printk(KERN_ERR "binder: BINDER_SET_CONTEXT_MGR already set\n");
            ret = -EBUSY;
            goto err;
        }
        ret = security_binder_set_context_mgr(proc->tsk);
        if (ret < 0)
            goto err;
        if (binder_context_mgr_uid != -1) {
            if (binder_context_mgr_uid != current->cred->euid) {
                printk(KERN_ERR "binder: BINDER_SET_"
                       "CONTEXT_MGR bad uid %d != %d\n",
                       current->cred->euid,
                       binder_context_mgr_uid);
                ret = -EPERM;
                goto err;
            }
        } else
            binder_context_mgr_uid = current->cred->euid;
        binder_context_mgr_node = binder_new_node(proc, NULL, NULL);
        if (binder_context_mgr_node == NULL) {
            ret = -ENOMEM;
            goto err;
        }
        binder_context_mgr_node->local_weak_refs++;
        binder_context_mgr_node->local_strong_refs++;
        binder_context_mgr_node->has_strong_ref = 1;
        binder_context_mgr_node->has_weak_ref = 1;
        break;
    case BINDER_THREAD_EXIT:
        binder_debug(BINDER_DEBUG_THREADS, "binder: %d:%d exit\n",
                 proc->pid, thread->pid);
        binder_free_thread(proc, thread);
        thread = NULL;
        break;
    case BINDER_VERSION:
        if (size != sizeof(struct binder_version)) {
            ret = -EINVAL;
            goto err;
        }
        if (put_user(BINDER_CURRENT_PROTOCOL_VERSION, &((struct binder_version *)ubuf)->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)
        printk(KERN_INFO "binder: %d:%d ioctl %x %lx returned %d\n", proc->pid, current->pid, cmd, arg, ret);
    return ret;
}

        我們看到先是構造了一個 binder_write_read 結構體,然後利用 copy_from_user 函數將用戶態的數據拷貝至內核(驅動)中。如果有需要給線程中寫入數據,便利用 binder_thread_write 寫進線程中,同理,讀操作也是如此。最後再將 binder_write_read 結構體寫回到用戶層。對於所有的讀操作,數據頭都是 BR_NOOP。那麼對於這種數據頭的處理,binder_parse 函數是直接 break,做休眠處理。

        對於 test_server 先是 binder_open,也就是 打開 binder driver,緊接着 ioctl,最後再 mmap 那一套。然後 while 循環,如果我們傳入的是 lookup,他便會調用 svcmgr_lookup 獲取服務;如果是 publish,它便會調用 svcmgr_publish 註冊服務。

        一般情況是 test_server 先通過 binder_thread_write 函數發送 BC_TRANSACTION,接着便是調用 binder_thread_read 函數來得到一個 BR_NOOP,等待休眠。然後 service_manger 通過 binder_thread_read 獲得 BR_TRANSACTION,再通過 binder_thread_write 發送一個 BC_REPLY,最後 test_server 通過 binder_thread_read 獲得 BR_REPLY。

        我們重點來講下 binder_thread_write 函數的 BC_TRANSACTION:

            1. 構造數據:

                a. 構造 binder_io;

                b. 轉爲 binder_transaction_data;

                c. 放入 binder_write_read 結構體中。

            2. 通過 ioctl 發送數據;

            3. 進去驅動。binder_ioctl 把數據放入 service_manger 進程的 todo 鏈表,並喚醒他。

                a. 根據 handle 找到目的進程 service_manger(之前 mmap 映射的空間);

                b. 把數據 copy_from_user,放入 mmap 的空間;

                c. 處理 offset 數據,flat_binder_object: 構造 binder_node 給 test_server,構造 binder_ref 給 service_manger,增加引用計數。

                d. 喚醒目的進程。

        後面就一直是處於 test_server 和 service_manger 進程的 binder_thread_write 和 binder_thread_read 的來回作用中。

        在這其中所涉及的 cmd 中,只有 BC_TRANSACTION,BR_TRANSACTION,BC_REPLY 和 BR_REPLY 是涉及兩進程的,其他所有的 cmd 只是 APP 和驅動的交互,用於改變/報告狀態。

        我們來總結服務的註冊過程和獲取過程。

        服務註冊過程如下:

            1. 構造數據,包括 name = “hello” 和 flat_binder_node 結構體;

            2. 發送 ioctl;

            3. 根據 handle = 0 找到 service_manger 進程,再把數據放到 service_manger 的 todo 鏈表中;

            4. 構造結構體。binder_node 給源進程,binder_ref 給目的進程;

            5. 喚醒 service_manger;

            6. 調用 ADD_SERVICE 函數;

            7. 在 svclist 中創建一項(主要是 name =“hello”和 handle);

            8. binder_ref 引用服務,此時的 node 便指向 binder_node。

        上面的 1 和 2 是在 test_server 的用戶態完成的,3 4 5 是在 test_server 的內核態完成的;6 7 是在 service_manger 的用戶態完成的,8 是在 service_manger 的內核態完成的。

        服務獲取過程如下:

            1. 構造數據(name = “hello”);

            2. 通過 ioctl 發送數據給 service_manger,handle = 0;

            3. 根據 handle = 0,找到 service_manger,把數據放入他的 todo 鏈表;

            4. 喚醒 service_manger;

            5. service_manger 內核態返回數據;

            6. service_manger 用戶態取出數據,得到 hello 服務;

            7. 在 svclist 鏈表里根據 hello 服務名 找到一項,得到 handle = 1;

            8. 用 ioctl 把 handle 發給驅動;

            9. service_manger 在內核態的 refs_by_desc 樹中,根據 handle = 1 找到 binder_ref,進而找到 hello 服務的 binder_node;

            10. 爲 test_client 創建 binder_ref,把handle = 1 放入 test_cient 的 todo 鏈表;

            11. 喚醒 tes_client;

            12. test_client 內核態返回 handle = 1;

            13. test_client 用戶態得到 handle = 1,進而 binder_ref.desc = 1,它中的 node 便對應於前面的 hello 服務。

        上面的 1 2 13 是在 test_client 的用戶態完成的,3 4 12 是在 test_client 的內核態完成的;6 7 8 是在 service_manger 的用戶態完成的,5 9 10 11 是在 service_manger 的內核態完成的。

        下面我們來看看服務使用過程,跟註冊和獲取過程類似

            1. 獲得 “hello”服務,handle = 1;

            2. 構造數據,code 是指調用哪個函數,構造參數;

            3. 通過 ioctl 發送數據(先寫後讀);

            4. binder_ioctl,根據 handle 找到目的進程;即 test_server;

            5. 把數據放入 test_server 的 todo 鏈表;

            6. 喚醒 test_server,然後再 binder_thread_read 中休眠;

            7. test_server 內核態被喚醒,返回數據到 test_server 用戶態;

            8. test_server 用戶態取出數據,根據 code 和 參數 調用函數;

            9. 用返回值構造數據;

            10. 通過 ioctl 回覆 REPLY;

            11. test_server 內核態找出要回復的進程,即 test_client;

            12. 把數據放入 test_client 的 todo 鏈表;

            13. 喚醒 test_client;

            14. 內核態被喚醒,把數據犯規給用戶空間;

            15. test_client 用戶態取出返回值,至此使用過程完成。

        上面的 1 2 3 15 是在 test_client 的用戶態完成的,4 5 6 14 是在 test_client 的內核態完成的;8 9 10 是在 test_server 的用戶態完成的,7 11 12 13 是在 test_server 的內核態完成的。

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