分享

binder驱动-交互时的传输实现(一)

 mandrave 2012-01-03

目录:

一、binder初始化

二、binder_open()、binder_mmap()

三、binder通讯实现

3.1场景概念

3.2 Transaction request

3.3 transaction async request

3.4 receive request

3.5 receive async requet

3.6 transaction reply

3.7 receive reply

3.8关于发送请求时的一点优化

四、BINDER_WRITE_READ其他功能

五、其他ioctl功能实现

 

Android中重要的IPC:binder,一直是我想研究的部分之一。偶然看到一篇universus的大师级binder理论文章:http://blog.csdn.net/universus/archive/2011/02/27/6211589.aspx(Android Binder设计与实现 – 设计篇),恰巧有点时间,就将binder驱动代码好生读了下。

 

一、       binder初始化

binder驱动不像其他linux驱动那样有特定的应该设备与之对应,唯一和硬件沾点边

边的是它需要对内存操作,不过只是使用内存存储数据罢了。和其他驱动一样,binder驱动采用了标准的linux驱动架构,所以才将其纳入驱动之列。

       驱动都有一个系统启动时initcall的初始化函数,当然binder驱动也有,那就是binder_init()函数。这个函数其实也没做太多的事情:

1. 新建工作队列:binder_deferred_workqueue和工作者内核线程:binder来处理一些可以稍后执行的工作;

2. 为binder驱动注册一个misc设备,设备节点/dev/binder;

3. 创建proc下的相关目录和文件:

proc/binder、proc/binder/proc,

proc/state、proc/stats、proc/transactions、proc/transaction_log、proc/failed_transaction_log。

这些接口文件对应的proc读取函数分别是:binder_read_proc_state()、binder_read_proc_stats()、binder_read_proc_transactions()、binder_read_proc_transaction_log()、binder_read_proc_transaction_log()。

另外在binder.c文件中看到的以print_binder_开头的函数都是和这些属性接口文件有关,如下。所以如果需要对这几个文件进行详细研究的,这些函数就不容错过了。本文的重点不在这块,所以略过。

       print_binder_transaction()

       print_binder_buffer()

       print_binder_work()

       print_binder_thread()

       print_binder_node()

       print_binder_ref()

       print_binder_proc ()

print_binder_stats()

       print_binder_proc_stats()

       binder_read_proc_state()

       binder_read_proc_stats()

binder_read_proc_transactions()

binder_read_proc_proc()

print_binder_transaction_log_entry()

binder_read_proc_transaction_log()

 

二、       binder_open()、binder_mmap()

binder驱动实现了自己的mmap函数,正如universus阐述的那样:它不是为了在物理

介质和用户空间做映射,而是用来创建数据接收的缓存空间。Binder数据只在用户空间和内核空间拷贝一次的秘密也就在于binder驱动对接收缓冲区的管理,关于这部分的讨论放在另外一篇文章:binder驱动-接收缓存区管理,本文不关心这一部分。

       通常上层应用程序在使用的binder的时候,都会有如下调用:

       fd = open(“/dev/binder”, O_RDWR);

mmap(NULL, MAP_SIZE, PROT_READ, MAP_PRIVATE, fd, 0);

但是,如果某应用程序只进行异步请求,那么我们可以不用给队进程分配和管理接收缓存区了。另外binder_mmap()只允许最大分配4MB的虚拟地址空间,而且对于应用程序,只拥有对该内存的读权限。

 

下面来看一看binder_open()的实现,函数定义如下:

static int binder_open(struct inode *nodp, struct file *filp){

       struct binder_proc *proc;

       /* 一个进程打开binder设备节点,就会有一个binder_proc生成来记录这个进程的一些信息。*/

       …

       proc = kzalloc(sizeof(*proc), GFP_KERNEL);// 分配binder_proc的内存空间

       …

       get_task_struct(current); // 增加当前进程的引用计数

       proc->tsk = current;            // 记录当前task_struct的地址

       INIT_LIST_HEAD(&proc->todo);       // 进程全局todo任务链表

       init_waitqueue_head(&proc->wait); // 初始化proc的等待队列头

       proc->default_priority = task_nice(current); // 取得当前进程的nice值保存

       …

       mutex_lock(&binder_lock);

       binder_stats_created(BINDER_STAT_PROC);

       // 在全局统计计数数据结构binder_stats中记录下创建了一个binder_proc

       hlist_add_head(&proc->proc_node, &binder_procs);

       // 将代表当前进程的binder_proc结构体加入全局链表binder_procs中

       proc->pid = current->group_leader->pid;

       /* 如果当前task是进程,那么取得当前进程的pid;如果当前task是线程,那么此处取得创建该线程的进程的pid : current->tgid tgid = thread group id*/

       INIT_LIST_HEAD(&proc->delivered_death);// 和binder死亡通知有关后续单独讨论

       filp->private_data = proc;  // binder_proc保存在file结构体的私有数据中

       mutex_unlock(&binder_lock);

      

       if (binder_proc_dir_entry_proc) {

              char strbuf[11];

              snprintf(strbuf, sizeof(strbuf), "%u", proc->pid);

              /* 以proc->pid作为名字在proc/binder/proc中创建一个入口文件,以方便上层查看该进程的所有binder通讯 */

              remove_proc_entry(strbuf, binder_proc_dir_entry_proc);

              create_proc_read_entry(strbuf, S_IRUGO,

                                   binder_proc_dir_entry_proc,

                                   binder_read_proc_proc, proc);

       }

       return 0;

}

从上面可以看出,binder_open()对binder_proc的以下域做了初始化:

struct binder_proc {

       struct hlist_node proc_node; // 链接入全局链表binder_procs的节点

       …

int pid;

       struct task_struct *tsk;

       …

       struct list_head todo;           // 进程的全局任务队列

       wait_queue_head_t wait;             // 进程空闲进程等待任务的等待队列

       struct list_head delivered_death; // 和binder死亡通知有关,后续单独讨论这块

                                                                 // binder驱动-订阅实体死亡通知

       …

       long default_priority;    // 记录当前task的默认nice值

       …

}

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多