久久精品人人爽,华人av在线,亚洲性视频网站,欧美专区一二三

Android系統中Binder子系統有什么用

189次閱讀
沒有評論

共計 20348 個字符,預計需要花費 51 分鐘才能閱讀完成。

這篇文章主要介紹了 Android 系統中 Binder 子系統有什么用,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓丸趣 TV 小編帶著大家一起了解一下。

 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- 
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;
 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 的內核態完成的。

感謝你能夠認真閱讀完這篇文章,希望丸趣 TV 小編分享的“Android 系統中 Binder 子系統有什么用”這篇文章對大家有幫助,同時也希望大家多多支持丸趣 TV,關注丸趣 TV 行業資訊頻道,更多相關知識等著你來學習!

正文完
 
丸趣
版權聲明:本站原創文章,由 丸趣 2023-08-25發表,共計20348字。
轉載說明:除特殊說明外本站除技術相關以外文章皆由網絡搜集發布,轉載請注明出處。
評論(沒有評論)
主站蜘蛛池模板: 易门县| 筠连县| 镇平县| 荆州市| 广州市| 高雄市| 绥化市| 东光县| 壤塘县| 景宁| 格尔木市| 广安市| 榆中县| 衡阳县| 大宁县| 始兴县| 马鞍山市| 玉树县| 武强县| 任丘市| 大化| 会东县| 平远县| 辽宁省| 莒南县| 日喀则市| 扶余县| 晋中市| 左云县| 泽库县| 稻城县| 澎湖县| 凤庆县| 东乡族自治县| 中江县| 龙川县| 兴文县| 石家庄市| 关岭| 什邡市| 凤庆县|