binder driver
2015-08-21 16:32
106 查看
一:初识Binder
Binder的用户空间为每个进程维持着一个线程池,线程池处理接收的IPC和本进程的本地消息;Binder通信是同步的。
守护进程Service Manager管理系统的各个服务。Service Manager监听是否有程序向其发送请求,有则响应,无则监听等待。
每个服务须向Service Manager注册,请求服务的客户端创建IBinder 接口用于IPC。所以Binder 机制基于C/S架构。
IPC中handle是本地进程对象,即本地进程的地址,而RPC中handle是远程对象的引用,是一个32位抽象的句柄。对发送者而言,均认为被发送Binder对象的是远端对象的句柄,而远端进程接收到Binder对象后均认为是本地对象地址。Binder驱动负责两种不同名称对象的正确映射。
二: Binder 驱动的原理及实现
Binder采用AIDL(android interface description language)来描述进程间通信的接口。Binder作为一个特殊的字符设备,其设备节点/dev/binder。主要代码实现在:
kernel/drivers/staging/binder.h
kernel/drivers/staging/binder.c
其中
binder_ioctl: 与用户空间进行数据交互,参数cmd区分不同的请求,BINDER_WRITE_READ表示读写数据。
binder_thread_write: 为copy_to_user服务。 get_user从用户空间获取请求并发送 或者 返回用户空间进程产生的结果. binder_thread_write函数调用binder_transaction函数转发请求并返回结果,当收到请求时,binder_transcation函数通过对象handle找到对象所在进程。将请求放到目标进程的队列中,等待目标进程读取。数据的解析工作在binder_parse()中实现。
binder_thread_read: 为copy_from_user 服务。 put_user读取结果。
1) Binder_work
struct binder_work {
struct list_head entry; //双链表存储binder_work队列
enum { //binder工作状态
BINDER_WORK_TRANSACTION = 1,
BINDER_WORK_TRANSACTION_COMPLETE,
BINDER_WORK_NODE,
BINDER_WORK_DEAD_BINDER,
BINDER_WORK_DEAD_BINDER_AND_CLEAR,
BINDER_WORK_CLEAR_DEATH_NOTIFICATION,
} type;
};
2)binder类型
#define B_PACK_CHARS(c1, c2, c3, c4) \
((((c1)<<24)) | (((c2)<<16)) | (((c3)<<8)) | (c4))
#define B_TYPE_LARGE 0x85
enum { //binder类型
//本地对象地址
BINDER_TYPE_BINDER = B_PACK_CHARS('s', 'b', '*', B_TYPE_LARGE),
BINDER_TYPE_WEAK_BINDER = B_PACK_CHARS('w', 'b', '*', B_TYPE_LARGE),
//远程对象引用
BINDER_TYPE_HANDLE = B_PACK_CHARS('s', 'h', '*', B_TYPE_LARGE),
BINDER_TYPE_WEAK_HANDLE = B_PACK_CHARS('w', 'h', '*', B_TYPE_LARGE),
//文件,找到fd对应的文件
BINDER_TYPE_FD = B_PACK_CHARS('f', 'd', '*', B_TYPE_LARGE),
};
3) binder对象-------进程间传递的数据
struct flat_binder_object {
unsigned long type; //binder类型
unsigned long flags; //同步或异步
union {
void *binder;
signed long handle;
};
void *cookie;
};
enum transaction_flags { //flags
TF_ONE_WAY = 0x01,
TF_ROOT_OBJECT = 0x04,
TF_STATUS_CODE = 0x08,
TF_ACCEPT_FDS = 0x10,
};
4) Binder对象传递的实际内容
struct binder_transaction_data {
union {
size_t handle;
void *ptr;
} target;
void *cookie;
unsigned int code;
unsigned int flags;
pid_t sender_pid;
uid_t sender_euid;
size_t data_size;
size_t offsets_size;
union {
struct {
const void *buffer;
const void *offsets;
} ptr; //target->ptr对应对象的数据
uint8_t buf[8]; //target->handle对应对象的数据
} data;
};
5) binder_write_read
struct binder_write_read {
signed long write_size;
signed long write_consumed;
unsigned long write_buffer; //请求线程执行的Binder命令
signed long read_size;
signed long read_consumed;
unsigned long read_buffer; //线程执行后的返回结果
};
//BINDER_WRITE_READ读操作命令协议
enum BinderDriverReturnProtocol {
BR_TRANSACTION = _IOR('r', 2, struct binder_transaction_data), //翻译解析将要被处理的数据
BR_REPLY = _IOR('r', 3, struct binder_transaction_data), //对返回结果数据的操作命令
……
};
//BINDER_WRITE_READ写操作命令协议
enum BinderDriverCommandProtocol {
BC_TRANSACTION = _IOW('c', 0, struct binder_transaction_data),
BC_REPLY = _IOW('c', 1, struct binder_transaction_data),
……
};
6) Binder_thread 存储每一个单独线程的信息
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;
uint32_t return_error2;
wait_queue_head_t wait;
struct binder_stats stats;
};
struct binder_stats {
int br[_IOC_NR(BR_FAILED_REPLY) + 1]; //存储BINDER_WRITE_READ读操作协议
int bc[_IOC_NR(BC_DEAD_BINDER_DONE) + 1]; //存储BINDER_WRITE_READ写操作协议
int obj_created[BINDER_STAT_COUNT]; //对象计数
int obj_deleted[BINDER_STAT_COUNT];
};
enum {
BINDER_LOOPER_STATE_REGISTERED = 0x01,
BINDER_LOOPER_STATE_ENTERED = 0x02,
BINDER_LOOPER_STATE_EXITED = 0x04,
BINDER_LOOPER_STATE_INVALID = 0x08,
BINDER_LOOPER_STATE_WAITING = 0x10,
BINDER_LOOPER_STATE_NEED_RETURN = 0x20
};
7) binder_transcation 用来中转请求和返回结果,保持接收和发送进程的信息。
struct binder_transaction {
int debug_id;
struct binder_work work;
struct binder_thread *from; //接收进程信息
struct binder_transaction *from_parent; //进程父节点信息
struct binder_proc *to_proc;
struct binder_thread *to_thread; //要发送的进程信息
struct binder_transaction *to_parent;
unsigned need_reply:1;
struct binder_buffer *buffer;
unsigned int code;
unsigned int flags;
long priority;
long saved_priority;
uid_t sender_euid; //Linux中包含用户ID(UID 进程由谁创建)和有效用户ID(EUID 发送进程对资源和文件的访问)
};
struct binder_buffer {
struct list_head entry;
struct rb_node rb_node;
unsigned free:1;
unsigned allow_user_free:1;
unsigned async_transaction:1;
unsigned debug_id:29;
struct binder_transaction *transaction; //中转请求和返回结果
struct binder_node *target_node;
size_t data_size;
size_t offsets_size;
uint8_t data[0]; //存储实际数据
};
三:驱动分析。 一般从初始化开始分析
1) binder_init
static int __init binder_init(void)
{
int ret;
binder_deferred_workqueue = create_singlethread_workqueue("binder"); //创建proc节点
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); //注册Misc设备,使用misc来命名主要是表示该文件目前还没归类好
if (binder_debugfs_dir_entry_root) { //创建各文件
debugfs_create_file("state", //proc/binder/state
S_IRUGO,
binder_debugfs_dir_entry_root,
NULL,
&binder_state_fops);
debugfs_create_file("stats", //proc/binder/stats
S_IRUGO,
binder_debugfs_dir_entry_root,
NULL,
&binder_stats_fops);
debugfs_create_file("transactions", //proc/binder/transactions
S_IRUGO,
binder_debugfs_dir_entry_root,
NULL,
&binder_transactions_fops);
debugfs_create_file("transaction_log", //proc/binder/transaction_log
S_IRUGO,
binder_debugfs_dir_entry_root,
&binder_transaction_log,
&binder_transaction_log_fops);
debugfs_create_file("failed_transaction_log", //proc/binder/failed_transcation_log
S_IRUGO,
binder_debugfs_dir_entry_root,
&binder_transaction_log_failed,
&binder_transaction_log_fops);
}
return ret;
}
__init和__exit标记函数,__initdata和__exitdata标记数据。
此宏定义可知标记后的函数与数据其实是放到了特定的(代码或数据)段中。标记为初始化的函数,表明该函数供在初始化期间使用。在模块装载之后,模块装载就会将初始化函数扔掉。这样可以将该函数占用的内存释放出来。
__exit修饰词标记函数只在模块卸载时使用。如果模块被直接编进内核则该函数就不会被调用。如果内核编译时没有包含该模块,则此标记的函数将被简单地丢弃。
_init不属于c的标准在内核代码里,这个表示把这个函数放在.init.text section里,在include/linux/init.h里有定义
#define __init __attribute__ ((__section__ (".init.text")))
这个section的空间是会被回收的,section是和连接有关的概念
binder_init由device_initcall调用,而不是传统的module_init和module_exit。这是因为使binder驱动直接编译进内核,不支持动态编译,同时需要在Kernel中做镜像。可以修改为module_init。
misc_register在init进程中在handle_device_fd(device_fd)函数中调用handle_device_event(&uevent)函数执行uevent_netlink事件在/dev文件下创建binder文件
static struct miscdevice binder_miscdev = { //动态获得主设备号
.minor = MISC_DYNAMIC_MINOR,
.name = "binder", //设备名
.fops = &binder_fops //file_oprations
};
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,
};
2) binder_open
static int binder_open(struct inode *nodp, struct file *filp)
{
struct binder_proc *proc; //保存调用Binder的各个进程和线程的信息,进程线程ID,Binder状态等
binder_debug(BINDER_DEBUG_OPEN_CLOSE, "binder_open: %d:%d\n",
current->group_leader->pid, current->pid);
proc = kzalloc(sizeof(*proc), GFP_KERNEL); //kzalloc效果等同于先是用 kmalloc() 申请空间 , 然后用 memset() 来初始化 ,所有申请的元素都被初始化为 0.
if (proc == NULL)
return -ENOMEM;
get_task_struct(current); //增加引用计数
proc->tsk = current; //保持引用计数
INIT_LIST_HEAD(&proc->todo); //初始化Binder_proc队列
init_waitqueue_head(&proc->wait);
proc->default_priority = task_nice(current);
mutex_lock(&binder_lock);
binder_stats_created(BINDER_STAT_PROC); //增加BINDER_STAT_PROC
hlist_add_head(&proc->proc_node, &binder_procs); //添加到HASH表
proc->pid = current->group_leader->pid; //保持pid,private_data等数据
INIT_LIST_HEAD(&proc->delivered_death);
filp->private_data = proc;
mutex_unlock(&binder_lock);
if (binder_debugfs_dir_entry_proc) { //创建proc/binder/proc/$pid文件
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;
}
步骤:
l 创建并分配一个binder_proc空间来保持binder数据。
l 增加当前进程/线程的引用计数,并保存到binder_proc的tsk。
l 初始化binder_proc队列,包括使用INIT_LIST_HEAD初始化链表头todo,使用init_waitqueue_head初始化等待序列wait,设置默认优先级为当前进程的nice值。
l 增加BINDER_STAT_PROC的对象计数,并通过hlist_add_head把创建的binder_proc对象添加到全局的binder_proc哈希表中,这样一来,任何进程都可以访问其他进程的binder_proc对象了。
l 将当前线程组的 pid赋值给proc的pid字段,即thread_group指向线程组中的第一个线程的task_struct结构,同时把创建的binder_proc对象指针赋值给flip的private_data对象。
l 创建文件/proc/binder/proc/$pid,用来输出当前binder proc对象的状态,文件名以pid命名。Pid不是当前进程/线程的id,而是线程组的pid,即线程组中第一个线程的pid。另外,创建该文件时,也指定了操作该文件的函数接口为binder_read_proc参数即创建的binder_proc对像proc。
3) binder_release
static int binder_release(struct inode *nodp, struct file *filp)
{
struct binder_proc *proc = filp->private_data; //获取进程,线程的pid, 然后删除/proc/binder/proc/$pid文件
debugfs_remove(proc->debugfs_entry);
binder_defer_work(proc, BINDER_DEFERRED_RELEASE); //释放binder_proc对象的数据和分配的空间
return 0;
}
4) binder_flush //关闭一个设备文件描述符复制到时候被调用
static int binder_flush(struct file *filp, fl_owner_t id)
{
struct binder_proc *proc = filp->private_data;
binder_defer_work(proc, BINDER_DEFERRED_FLUSH);
return 0;
}
5) binder_poll
//非阻塞型IO操作的内核驱动实现。当用户程序read数据时,如果驱动程序没有准备好数据,则阻塞该进程,直到数据准备好。称为阻塞型IO操作
static unsigned int binder_poll(struct file *filp, struct poll_table_struct *wait)
{
struct binder_proc *proc = filp->private_data;
struct binder_thread *thread = NULL;
int wait_for_proc_work;
mutex_lock(&binder_lock);
thread = binder_get_thread(proc); //获取当前进程/线程信息
wait_for_proc_work = thread->transaction_stack == NULL && list_empty(&thread->todo) && thread->return_error == BR_OK;
mutex_unlock(&binder_lock);
if (wait_for_proc_work) { //proc_work工作方式 进程?
if (binder_has_proc_work(proc, thread))
return POLLIN;
poll_wait(filp, &proc->wait, wait);
if (binder_has_proc_work(proc, thread))
return POLLIN;
} else { //thread_work工作方式
线程?
if (binder_has_thread_work(thread))
return POLLIN;
poll_wait(filp, &thread->wait, wait);
if (binder_has_thread_work(thread))
return POLLIN;
}
return 0;
}
static int binder_has_proc_work(struct binder_proc *proc,
struct binder_thread *thread)
{
return !list_empty(&proc->todo) ||
(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN);
}
static int binder_has_thread_work(struct binder_thread *thread)
{
return !list_empty(&thread->todo) || thread->return_error != BR_OK ||
(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN);
}
根据检测线程队列是否为空,线程循环状态信息和返回信息确定等待方式,然后调用wait_poll实现poll操作。
static struct binder_thread *binder_get_thread(struct binder_proc *proc)
{
struct binder_thread *thread = NULL;
struct rb_node *parent = NULL;
struct rb_node **p = &proc->threads.rb_node; //取得队列中的线程
while (*p) {
parent = *p;
thread = rb_entry(parent, struct binder_thread, rb_node); //取得将要比较的线程
if (current->pid < thread->pid)
p = &(*p)->rb_left;
else if (current->pid > thread->pid)
p = &(*p)->rb_right;
else
break;
}
if (*p == NULL) { //如果不存在
thread = kzalloc(sizeof(*thread), GFP_KERNEL);
if (thread == NULL)
return NULL;
binder_stats_created(BINDER_STAT_THREAD);
thread->proc = proc;
thread->pid = current->pid;
init_waitqueue_head(&thread->wait); //初始化等待队列
INIT_LIST_HEAD(&thread->todo); //初始化链表表头
rb_link_node(&thread->rb_node, parent, p);
rb_insert_color(&thread->rb_node, &proc->threads);
thread->looper |= BINDER_LOOPER_STATE_NEED_RETURN; //设置线程循环状态
thread->return_error = BR_OK;
thread->return_error2 = BR_OK;
}
return thread;
}
6) binder_mmap
mmap把设备内存(内核空间)映射到用户空间,从而用户程序可以在用户空间操作内核空间的地址;
binder_mmap能映射的最大内存空间为4M,而且不能映射具有写权限的内存区域;
binder_mmap现在内核虚拟映射表上获取一块可以使用的区域,然后分配物理页,再把物理页映射到虚拟映射表上;
由于设备内存的映射在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;
if ((vma->vm_end - vma->vm_start) > SZ_4M) //检测映射内存大小
vma->vm_end = vma->vm_start + SZ_4M;
binder_debug(BINDER_DEBUG_OPEN_CLOSE,
"binder_mmap: %d %lx-%lx (%ld K) vma %lx pagep %lx\n",
proc->pid, vma->vm_start, vma->vm_end,
(vma->vm_end - vma->vm_start) / SZ_1K, vma->vm_flags,
(unsigned long)pgprot_val(vma->vm_page_prot));
if (vma->vm_flags & FORBIDDEN_MMAP_FLAGS) { //检测flags
ret = -EPERM;
failure_string = "bad vm_flags";
goto err_bad_arg;
}
vma->vm_flags = (vma->vm_flags | VM_DONTCOPY) & ~VM_MAYWRITE;
if (proc->buffer) { //判断是否已经mmap过
ret = -EBUSY;
failure_string = "already mapped";
goto err_already_mapped;
}
area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP); //申请虚拟空间
if (area == NULL) {
ret = -ENOMEM;
failure_string = "get_vm_area";
goto err_get_vm_area_failed;
}
proc->buffer = area->addr;
proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer;
#ifdef CONFIG_CPU_CACHE_VIPT
if (cache_is_vipt_aliasing()) {
while (CACHE_COLOUR((vma->vm_start ^ (uint32_t)proc->buffer))) {
printk(KERN_INFO "binder_mmap: %d %lx-%lx maps %p bad alignment\n", proc->pid, vma->vm_start, vma->vm_end,
proc->buffer);
vma->vm_start += PAGE_SIZE;
}
}
#endif
proc->pages = kzalloc(sizeof(proc->pages[0]) * ((vma->vm_end - vma->vm_start) / PAGE_SIZE), GFP_KERNEL);
//分配pages空间
if (proc->pages == NULL) {
ret = -ENOMEM;
failure_string = "alloc page array";
goto err_alloc_pages_failed;
}
proc->buffer_size = vma->vm_end - vma->vm_start;
vma->vm_ops = &binder_vm_ops;
vma->vm_private_data = proc;
//真正开始分配物理空间
if (binder_update_page_range(proc, 1, proc->buffer, proc->buffer + PAGE_SIZE, vma)) {
ret = -ENOMEM;
failure_string = "alloc small buf";
goto err_alloc_small_buf_failed;
}
buffer = proc->buffer;
INIT_LIST_HEAD(&proc->buffers);
list_add(&buffer->entry, &proc->buffers);
buffer->free = 1;
binder_insert_free_buffer(proc, buffer);
proc->free_async_space = proc->buffer_size / 2;
barrier();
proc->files = get_files_struct(current);
proc->vma = vma;
return 0;
err_alloc_small_buf_failed:
kfree(proc->pages);
proc->pages = NULL;
err_alloc_pages_failed:
vfree(proc->buffer);
proc->buffer = NULL;
err_get_vm_area_failed:
err_already_mapped:
err_bad_arg:
printk(KERN_ERR "binder_mmap: %d %lx-%lx %s failed %d\n",
proc->pid, vma->vm_start, vma->vm_end, failure_string, ret);
return ret;
}
binder_update_page_range函数执行步骤:
1:alloc_page 分配页面
2:map_vm_area 为分配的内存做映射关系
3:vm_insert_page 把分配的物理页插入到用户VMA区域
总结binder_mmap顺序
1:检测内存映射条件,即映射内存大小(4MB),flags,是否已经映射过;
2:获取地址空间,并把此空间的地址记录到进程信息buffer中;
3:分配物理页面并记录下来;
4:将buffer插入到进程信息的buffer列表中;
5:调用buffer_update_page_range函数将分配的物理页面和vm空间对应起来;
6:通过binder_insert_free_buffer函数把此进程的buffer插入到进程信息中。
7) binder_ioctl
binder的ioctl命令主要有七个:
#define BINDER_WRITE_READ _IOWR('b', 1, struct binder_write_read) //读写操作
#define BINDER_SET_IDLE_TIMEOUT _IOW('b', 3, int64_t)
#define BINDER_SET_MAX_THREADS _IOW('b', 5, size_t)
#define BINDER_SET_IDLE_PRIORITY _IOW('b', 6, int)
#define BINDER_SET_CONTEXT_MGR _IOW('b', 7, int)
#define BINDER_THREAD_EXIT _IOW('b', 8, int)
#define BINDER_VERSION _IOWR('b', 9, struct binder_version)
BINDER_SET_IDLE_PRIORITY 被service_manager用于设置自己作为context manager,所有为0的handle均被指向这个master节点。
用户程序只是将它想要做到命令传递给驱动,而驱动就是将事情完成,用户程序不需要知道如何做的这件事。
Linxu中为保证用户程序与具体设备驱动中的命令一一对应,定义了命令码的结构
这样一个命令码就转变成一个整数形式的命令码。但不直观。_IOW的宏定义可以在
Ioctl.h (c:\users\lw\desktop\myandroid2.3\myandroid2.3\kernel_imx\include\asm-generic)中找到
(1) BINDER_VERSION
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;
}
(2)BINDER_SET_MAX_THREADS
case BINDER_SET_MAX_THREADS:
if (copy_from_user(&proc->max_threads, ubuf, sizeof(proc->max_threads))) {
ret = -EINVAL;
goto err;
}
(3)BINDER_THREAD_EXIT
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;
(4)BINDER_SET_CONTEXT_MGR
若一个进程/线程能被成功设置成binder_context_mgr_node对象,称该进程/线程为Context Manager.即设置驱动中的全局变量binder_context_mgr_uid为当前进程的uid.并初始化一个Binder_node并赋值给全局变量binder_context_mgr_node。
只有创建binder_context_mgr_node对象的Binder上下文管理进程/线程才有权限重新设置这个对象。
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;
}
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_node节点
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_node节点数据
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;
static struct binder_node *binder_new_node(struct binder_proc *proc,
void __user *ptr,
void __user *cookie)
{
struct rb_node **p = &proc->nodes.rb_node;
struct rb_node *parent = NULL;
struct binder_node *node;
while (*p) { //查找第一个叶节点
parent = *p;
node = rb_entry(parent, struct binder_node, rb_node);
if (ptr < node->ptr)
p = &(*p)->rb_left;
else if (ptr > node->ptr)
p = &(*p)->rb_right;
else
return NULL;
}
//创建binder_node节点
node = kzalloc(sizeof(*node), GFP_KERNEL);
if (node == NULL)
return NULL;
binder_stats_created(BINDER_STAT_NODE);
rb_link_node(&node->rb_node, parent, p); //插入节点
rb_insert_color(&node->rb_node, &proc->nodes);
node->debug_id = ++binder_last_id; //初始化数据
node->proc = proc;
node->ptr = ptr;
node->cookie = cookie;
node->work.type = BINDER_WORK_NODE;
INIT_LIST_HEAD(&node->work.entry); //初始化链表头
INIT_LIST_HEAD(&node->async_todo);
binder_debug(BINDER_DEBUG_INTERNAL_REFS,
"binder: %d:%d node %d u%p c%p created\n",
proc->pid, current->pid, node->debug_id,
node->ptr, node->cookie);
return node;
}
Binder_proc的成员node是binder_node的根节点,这是一颗红黑树,该函数首先根据规则找到第一个叶节点作为新插入的节点的父节点,然后创建binder_node节点并插入。
(5) BINDER_WRITE_READ
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))) { //从用户空间复制数据到binder_write_read中
ret = -EFAULT;
goto err;
}
binder_debug(BINDER_DEBUG_READ_WRITE,
"binder: %d:%d write %ld at lx, read %ld at lx\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;
}
static int binder_free_thread(struct binder_proc *proc, struct binder_thread *thread)
{
struct binder_transaction *t;
struct binder_transaction *send_reply = NULL;
int active_transactions = 0;
rb_erase(&thread->rb_node, &proc->threads); //将当前线程从红杉树上删除
t = thread->transaction_stack; //取得binder_transaction数据
if (t && t->to_thread == thread) //判断要释放的是否是“回复”进程
send_reply = t;
while (t) { //释放所有的binder_transaction
active_transactions++;
binder_debug(BINDER_DEBUG_DEAD_TRANSACTION,
"binder: release %d:%d transaction %d "
"%s, still active\n", proc->pid, thread->pid,
t->debug_id,
(t->to_thread == thread) ? "in" : "out");
if (t->to_thread == thread) {
t->to_proc = NULL;
t->to_thread = NULL;
if (t->buffer) {
t->buffer->transaction = NULL;
t->buffer = NULL;
}
t = t->to_parent;
} else if (t->from == thread) {
t->from = NULL;
t = t->from_parent;
} else
BUG();
}
if (send_reply) //需要发送失败回复
binder_send_failed_reply(send_reply, BR_DEAD_REPLY);
binder_release_work(&thread->todo); //释放binder_work
kfree(thread); //释放binder_thread
binder_stats_deleted(BINDER_STAT_THREAD); //改变binder状态
return active_transactions;
}
static void binder_release_work(struct list_head *list)
{
struct binder_work *w;
while (!list_empty(list)) {
w = list_first_entry(list, struct binder_work, entry);
list_del_init(&w->entry);
switch (w->type) {
case BINDER_WORK_TRANSACTION: {
struct binder_transaction *t;
t = container_of(w, struct binder_transaction, work);
if (t->buffer->target_node && !(t->flags & TF_ONE_WAY))
binder_send_failed_reply(t, BR_DEAD_REPLY);
} break;
case BINDER_WORK_TRANSACTION_COMPLETE: {
kfree(w);
binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE);
} break;
default:
break;
}
}
}
一:初识Binder
Binder的用户空间为每个进程维持着一个线程池,线程池处理接收的IPC和本进程的本地消息;Binder通信是同步的。
守护进程Service Manager管理系统的各个服务。Service Manager监听是否有程序向其发送请求,有则响应,无则监听等待。
每个服务须向Service Manager注册,请求服务的客户端创建IBinder 接口用于IPC。所以Binder 机制基于C/S架构。
IPC中handle是本地进程对象,即本地进程的地址,而RPC中handle是远程对象的引用,是一个32位抽象的句柄。对发送者而言,均认为被发送Binder对象的是远端对象的句柄,而远端进程接收到Binder对象后均认为是本地对象地址。Binder驱动负责两种不同名称对象的正确映射。
二: Binder 驱动的原理及实现
Binder采用AIDL(android interface description language)来描述进程间通信的接口。Binder作为一个特殊的字符设备,其设备节点/dev/binder。主要代码实现在:
kernel/drivers/staging/binder.h
kernel/drivers/staging/binder.c
其中
binder_ioctl: 与用户空间进行数据交互,参数cmd区分不同的请求,BINDER_WRITE_READ表示读写数据。
binder_thread_write: 为copy_to_user服务。 get_user从用户空间获取请求并发送 或者 返回用户空间进程产生的结果. binder_thread_write函数调用binder_transaction函数转发请求并返回结果,当收到请求时,binder_transcation函数通过对象handle找到对象所在进程。将请求放到目标进程的队列中,等待目标进程读取。数据的解析工作在binder_parse()中实现。
binder_thread_read: 为copy_from_user 服务。 put_user读取结果。
1) Binder_work
struct binder_work {
struct list_head entry; //双链表存储binder_work队列
enum { //binder工作状态
BINDER_WORK_TRANSACTION = 1,
BINDER_WORK_TRANSACTION_COMPLETE,
BINDER_WORK_NODE,
BINDER_WORK_DEAD_BINDER,
BINDER_WORK_DEAD_BINDER_AND_CLEAR,
BINDER_WORK_CLEAR_DEATH_NOTIFICATION,
} type;
};
2)binder类型
#define B_PACK_CHARS(c1, c2, c3, c4) \
((((c1)<<24)) | (((c2)<<16)) | (((c3)<<8)) | (c4))
#define B_TYPE_LARGE 0x85
enum { //binder类型
//本地对象地址
BINDER_TYPE_BINDER = B_PACK_CHARS('s', 'b', '*', B_TYPE_LARGE),
BINDER_TYPE_WEAK_BINDER = B_PACK_CHARS('w', 'b', '*', B_TYPE_LARGE),
//远程对象引用
BINDER_TYPE_HANDLE = B_PACK_CHARS('s', 'h', '*', B_TYPE_LARGE),
BINDER_TYPE_WEAK_HANDLE = B_PACK_CHARS('w', 'h', '*', B_TYPE_LARGE),
//文件,找到fd对应的文件
BINDER_TYPE_FD = B_PACK_CHARS('f', 'd', '*', B_TYPE_LARGE),
};
3) binder对象-------进程间传递的数据
struct flat_binder_object {
unsigned long type; //binder类型
unsigned long flags; //同步或异步
union {
void *binder;
signed long handle;
};
void *cookie;
};
enum transaction_flags { //flags
TF_ONE_WAY = 0x01,
TF_ROOT_OBJECT = 0x04,
TF_STATUS_CODE = 0x08,
TF_ACCEPT_FDS = 0x10,
};
4) Binder对象传递的实际内容
struct binder_transaction_data {
union {
size_t handle;
void *ptr;
} target;
void *cookie;
unsigned int code;
unsigned int flags;
pid_t sender_pid;
uid_t sender_euid;
size_t data_size;
size_t offsets_size;
union {
struct {
const void *buffer;
const void *offsets;
} ptr; //target->ptr对应对象的数据
uint8_t buf[8]; //target->handle对应对象的数据
} data;
};
5) binder_write_read
struct binder_write_read {
signed long write_size;
signed long write_consumed;
unsigned long write_buffer; //请求线程执行的Binder命令
signed long read_size;
signed long read_consumed;
unsigned long read_buffer; //线程执行后的返回结果
};
//BINDER_WRITE_READ读操作命令协议
enum BinderDriverReturnProtocol {
BR_TRANSACTION = _IOR('r', 2, struct binder_transaction_data), //翻译解析将要被处理的数据
BR_REPLY = _IOR('r', 3, struct binder_transaction_data), //对返回结果数据的操作命令
……
};
//BINDER_WRITE_READ写操作命令协议
enum BinderDriverCommandProtocol {
BC_TRANSACTION = _IOW('c', 0, struct binder_transaction_data),
BC_REPLY = _IOW('c', 1, struct binder_transaction_data),
……
};
6) Binder_thread 存储每一个单独线程的信息
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;
uint32_t return_error2;
wait_queue_head_t wait;
struct binder_stats stats;
};
struct binder_stats {
int br[_IOC_NR(BR_FAILED_REPLY) + 1]; //存储BINDER_WRITE_READ读操作协议
int bc[_IOC_NR(BC_DEAD_BINDER_DONE) + 1]; //存储BINDER_WRITE_READ写操作协议
int obj_created[BINDER_STAT_COUNT]; //对象计数
int obj_deleted[BINDER_STAT_COUNT];
};
enum {
BINDER_LOOPER_STATE_REGISTERED = 0x01,
BINDER_LOOPER_STATE_ENTERED = 0x02,
BINDER_LOOPER_STATE_EXITED = 0x04,
BINDER_LOOPER_STATE_INVALID = 0x08,
BINDER_LOOPER_STATE_WAITING = 0x10,
BINDER_LOOPER_STATE_NEED_RETURN = 0x20
};
7) binder_transcation 用来中转请求和返回结果,保持接收和发送进程的信息。
struct binder_transaction {
int debug_id;
struct binder_work work;
struct binder_thread *from; //接收进程信息
struct binder_transaction *from_parent; //进程父节点信息
struct binder_proc *to_proc;
struct binder_thread *to_thread; //要发送的进程信息
struct binder_transaction *to_parent;
unsigned need_reply:1;
struct binder_buffer *buffer;
unsigned int code;
unsigned int flags;
long priority;
long saved_priority;
uid_t sender_euid; //Linux中包含用户ID(UID 进程由谁创建)和有效用户ID(EUID 发送进程对资源和文件的访问)
};
struct binder_buffer {
struct list_head entry;
struct rb_node rb_node;
unsigned free:1;
unsigned allow_user_free:1;
unsigned async_transaction:1;
unsigned debug_id:29;
struct binder_transaction *transaction; //中转请求和返回结果
struct binder_node *target_node;
size_t data_size;
size_t offsets_size;
uint8_t data[0]; //存储实际数据
};
三:驱动分析。 一般从初始化开始分析
1) binder_init
static int __init binder_init(void)
{
int ret;
binder_deferred_workqueue = create_singlethread_workqueue("binder"); //创建proc节点
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); //注册Misc设备,使用misc来命名主要是表示该文件目前还没归类好
if (binder_debugfs_dir_entry_root) { //创建各文件
debugfs_create_file("state", //proc/binder/state
S_IRUGO,
binder_debugfs_dir_entry_root,
NULL,
&binder_state_fops);
debugfs_create_file("stats", //proc/binder/stats
S_IRUGO,
binder_debugfs_dir_entry_root,
NULL,
&binder_stats_fops);
debugfs_create_file("transactions", //proc/binder/transactions
S_IRUGO,
binder_debugfs_dir_entry_root,
NULL,
&binder_transactions_fops);
debugfs_create_file("transaction_log", //proc/binder/transaction_log
S_IRUGO,
binder_debugfs_dir_entry_root,
&binder_transaction_log,
&binder_transaction_log_fops);
debugfs_create_file("failed_transaction_log", //proc/binder/failed_transcation_log
S_IRUGO,
binder_debugfs_dir_entry_root,
&binder_transaction_log_failed,
&binder_transaction_log_fops);
}
return ret;
}
__init和__exit标记函数,__initdata和__exitdata标记数据。
此宏定义可知标记后的函数与数据其实是放到了特定的(代码或数据)段中。标记为初始化的函数,表明该函数供在初始化期间使用。在模块装载之后,模块装载就会将初始化函数扔掉。这样可以将该函数占用的内存释放出来。
__exit修饰词标记函数只在模块卸载时使用。如果模块被直接编进内核则该函数就不会被调用。如果内核编译时没有包含该模块,则此标记的函数将被简单地丢弃。
_init不属于c的标准在内核代码里,这个表示把这个函数放在.init.text section里,在include/linux/init.h里有定义
#define __init __attribute__ ((__section__ (".init.text")))
这个section的空间是会被回收的,section是和连接有关的概念
binder_init由device_initcall调用,而不是传统的module_init和module_exit。这是因为使binder驱动直接编译进内核,不支持动态编译,同时需要在Kernel中做镜像。可以修改为module_init。
misc_register在init进程中在handle_device_fd(device_fd)函数中调用handle_device_event(&uevent)函数执行uevent_netlink事件在/dev文件下创建binder文件
static struct miscdevice binder_miscdev = { //动态获得主设备号
.minor = MISC_DYNAMIC_MINOR,
.name = "binder", //设备名
.fops = &binder_fops //file_oprations
};
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,
};
2) binder_open
static int binder_open(struct inode *nodp, struct file *filp)
{
struct binder_proc *proc; //保存调用Binder的各个进程和线程的信息,进程线程ID,Binder状态等
binder_debug(BINDER_DEBUG_OPEN_CLOSE, "binder_open: %d:%d\n",
current->group_leader->pid, current->pid);
proc = kzalloc(sizeof(*proc), GFP_KERNEL); //kzalloc效果等同于先是用 kmalloc() 申请空间 , 然后用 memset() 来初始化 ,所有申请的元素都被初始化为 0.
if (proc == NULL)
return -ENOMEM;
get_task_struct(current); //增加引用计数
proc->tsk = current; //保持引用计数
INIT_LIST_HEAD(&proc->todo); //初始化Binder_proc队列
init_waitqueue_head(&proc->wait);
proc->default_priority = task_nice(current);
mutex_lock(&binder_lock);
binder_stats_created(BINDER_STAT_PROC); //增加BINDER_STAT_PROC
hlist_add_head(&proc->proc_node, &binder_procs); //添加到HASH表
proc->pid = current->group_leader->pid; //保持pid,private_data等数据
INIT_LIST_HEAD(&proc->delivered_death);
filp->private_data = proc;
mutex_unlock(&binder_lock);
if (binder_debugfs_dir_entry_proc) { //创建proc/binder/proc/$pid文件
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;
}
步骤:
l 创建并分配一个binder_proc空间来保持binder数据。
l 增加当前进程/线程的引用计数,并保存到binder_proc的tsk。
l 初始化binder_proc队列,包括使用INIT_LIST_HEAD初始化链表头todo,使用init_waitqueue_head初始化等待序列wait,设置默认优先级为当前进程的nice值。
l 增加BINDER_STAT_PROC的对象计数,并通过hlist_add_head把创建的binder_proc对象添加到全局的binder_proc哈希表中,这样一来,任何进程都可以访问其他进程的binder_proc对象了。
l 将当前线程组的 pid赋值给proc的pid字段,即thread_group指向线程组中的第一个线程的task_struct结构,同时把创建的binder_proc对象指针赋值给flip的private_data对象。
l 创建文件/proc/binder/proc/$pid,用来输出当前binder proc对象的状态,文件名以pid命名。Pid不是当前进程/线程的id,而是线程组的pid,即线程组中第一个线程的pid。另外,创建该文件时,也指定了操作该文件的函数接口为binder_read_proc参数即创建的binder_proc对像proc。
3) binder_release
static int binder_release(struct inode *nodp, struct file *filp)
{
struct binder_proc *proc = filp->private_data; //获取进程,线程的pid, 然后删除/proc/binder/proc/$pid文件
debugfs_remove(proc->debugfs_entry);
binder_defer_work(proc, BINDER_DEFERRED_RELEASE); //释放binder_proc对象的数据和分配的空间
return 0;
}
4) binder_flush //关闭一个设备文件描述符复制到时候被调用
static int binder_flush(struct file *filp, fl_owner_t id)
{
struct binder_proc *proc = filp->private_data;
binder_defer_work(proc, BINDER_DEFERRED_FLUSH);
return 0;
}
5) binder_poll
//非阻塞型IO操作的内核驱动实现。当用户程序read数据时,如果驱动程序没有准备好数据,则阻塞该进程,直到数据准备好。称为阻塞型IO操作
static unsigned int binder_poll(struct file *filp, struct poll_table_struct *wait)
{
struct binder_proc *proc = filp->private_data;
struct binder_thread *thread = NULL;
int wait_for_proc_work;
mutex_lock(&binder_lock);
thread = binder_get_thread(proc); //获取当前进程/线程信息
wait_for_proc_work = thread->transaction_stack == NULL && list_empty(&thread->todo) && thread->return_error == BR_OK;
mutex_unlock(&binder_lock);
if (wait_for_proc_work) { //proc_work工作方式 进程?
if (binder_has_proc_work(proc, thread))
return POLLIN;
poll_wait(filp, &proc->wait, wait);
if (binder_has_proc_work(proc, thread))
return POLLIN;
} else { //thread_work工作方式
线程?
if (binder_has_thread_work(thread))
return POLLIN;
poll_wait(filp, &thread->wait, wait);
if (binder_has_thread_work(thread))
return POLLIN;
}
return 0;
}
static int binder_has_proc_work(struct binder_proc *proc,
struct binder_thread *thread)
{
return !list_empty(&proc->todo) ||
(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN);
}
static int binder_has_thread_work(struct binder_thread *thread)
{
return !list_empty(&thread->todo) || thread->return_error != BR_OK ||
(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN);
}
根据检测线程队列是否为空,线程循环状态信息和返回信息确定等待方式,然后调用wait_poll实现poll操作。
static struct binder_thread *binder_get_thread(struct binder_proc *proc)
{
struct binder_thread *thread = NULL;
struct rb_node *parent = NULL;
struct rb_node **p = &proc->threads.rb_node; //取得队列中的线程
while (*p) {
parent = *p;
thread = rb_entry(parent, struct binder_thread, rb_node); //取得将要比较的线程
if (current->pid < thread->pid)
p = &(*p)->rb_left;
else if (current->pid > thread->pid)
p = &(*p)->rb_right;
else
break;
}
if (*p == NULL) { //如果不存在
thread = kzalloc(sizeof(*thread), GFP_KERNEL);
if (thread == NULL)
return NULL;
binder_stats_created(BINDER_STAT_THREAD);
thread->proc = proc;
thread->pid = current->pid;
init_waitqueue_head(&thread->wait); //初始化等待队列
INIT_LIST_HEAD(&thread->todo); //初始化链表表头
rb_link_node(&thread->rb_node, parent, p);
rb_insert_color(&thread->rb_node, &proc->threads);
thread->looper |= BINDER_LOOPER_STATE_NEED_RETURN; //设置线程循环状态
thread->return_error = BR_OK;
thread->return_error2 = BR_OK;
}
return thread;
}
6) binder_mmap
mmap把设备内存(内核空间)映射到用户空间,从而用户程序可以在用户空间操作内核空间的地址;
binder_mmap能映射的最大内存空间为4M,而且不能映射具有写权限的内存区域;
binder_mmap现在内核虚拟映射表上获取一块可以使用的区域,然后分配物理页,再把物理页映射到虚拟映射表上;
由于设备内存的映射在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;
if ((vma->vm_end - vma->vm_start) > SZ_4M) //检测映射内存大小
vma->vm_end = vma->vm_start + SZ_4M;
binder_debug(BINDER_DEBUG_OPEN_CLOSE,
"binder_mmap: %d %lx-%lx (%ld K) vma %lx pagep %lx\n",
proc->pid, vma->vm_start, vma->vm_end,
(vma->vm_end - vma->vm_start) / SZ_1K, vma->vm_flags,
(unsigned long)pgprot_val(vma->vm_page_prot));
if (vma->vm_flags & FORBIDDEN_MMAP_FLAGS) { //检测flags
ret = -EPERM;
failure_string = "bad vm_flags";
goto err_bad_arg;
}
vma->vm_flags = (vma->vm_flags | VM_DONTCOPY) & ~VM_MAYWRITE;
if (proc->buffer) { //判断是否已经mmap过
ret = -EBUSY;
failure_string = "already mapped";
goto err_already_mapped;
}
area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP); //申请虚拟空间
if (area == NULL) {
ret = -ENOMEM;
failure_string = "get_vm_area";
goto err_get_vm_area_failed;
}
proc->buffer = area->addr;
proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer;
#ifdef CONFIG_CPU_CACHE_VIPT
if (cache_is_vipt_aliasing()) {
while (CACHE_COLOUR((vma->vm_start ^ (uint32_t)proc->buffer))) {
printk(KERN_INFO "binder_mmap: %d %lx-%lx maps %p bad alignment\n", proc->pid, vma->vm_start, vma->vm_end,
proc->buffer);
vma->vm_start += PAGE_SIZE;
}
}
#endif
proc->pages = kzalloc(sizeof(proc->pages[0]) * ((vma->vm_end - vma->vm_start) / PAGE_SIZE), GFP_KERNEL);
//分配pages空间
if (proc->pages == NULL) {
ret = -ENOMEM;
failure_string = "alloc page array";
goto err_alloc_pages_failed;
}
proc->buffer_size = vma->vm_end - vma->vm_start;
vma->vm_ops = &binder_vm_ops;
vma->vm_private_data = proc;
//真正开始分配物理空间
if (binder_update_page_range(proc, 1, proc->buffer, proc->buffer + PAGE_SIZE, vma)) {
ret = -ENOMEM;
failure_string = "alloc small buf";
goto err_alloc_small_buf_failed;
}
buffer = proc->buffer;
INIT_LIST_HEAD(&proc->buffers);
list_add(&buffer->entry, &proc->buffers);
buffer->free = 1;
binder_insert_free_buffer(proc, buffer);
proc->free_async_space = proc->buffer_size / 2;
barrier();
proc->files = get_files_struct(current);
proc->vma = vma;
return 0;
err_alloc_small_buf_failed:
kfree(proc->pages);
proc->pages = NULL;
err_alloc_pages_failed:
vfree(proc->buffer);
proc->buffer = NULL;
err_get_vm_area_failed:
err_already_mapped:
err_bad_arg:
printk(KERN_ERR "binder_mmap: %d %lx-%lx %s failed %d\n",
proc->pid, vma->vm_start, vma->vm_end, failure_string, ret);
return ret;
}
binder_update_page_range函数执行步骤:
1:alloc_page 分配页面
2:map_vm_area 为分配的内存做映射关系
3:vm_insert_page 把分配的物理页插入到用户VMA区域
总结binder_mmap顺序
1:检测内存映射条件,即映射内存大小(4MB),flags,是否已经映射过;
2:获取地址空间,并把此空间的地址记录到进程信息buffer中;
3:分配物理页面并记录下来;
4:将buffer插入到进程信息的buffer列表中;
5:调用buffer_update_page_range函数将分配的物理页面和vm空间对应起来;
6:通过binder_insert_free_buffer函数把此进程的buffer插入到进程信息中。
7) binder_ioctl
binder的ioctl命令主要有七个:
#define BINDER_WRITE_READ _IOWR('b', 1, struct binder_write_read) //读写操作
#define BINDER_SET_IDLE_TIMEOUT _IOW('b', 3, int64_t)
#define BINDER_SET_MAX_THREADS _IOW('b', 5, size_t)
#define BINDER_SET_IDLE_PRIORITY _IOW('b', 6, int)
#define BINDER_SET_CONTEXT_MGR _IOW('b', 7, int)
#define BINDER_THREAD_EXIT _IOW('b', 8, int)
#define BINDER_VERSION _IOWR('b', 9, struct binder_version)
BINDER_SET_IDLE_PRIORITY 被service_manager用于设置自己作为context manager,所有为0的handle均被指向这个master节点。
用户程序只是将它想要做到命令传递给驱动,而驱动就是将事情完成,用户程序不需要知道如何做的这件事。
Linxu中为保证用户程序与具体设备驱动中的命令一一对应,定义了命令码的结构
这样一个命令码就转变成一个整数形式的命令码。但不直观。_IOW的宏定义可以在
Ioctl.h (c:\users\lw\desktop\myandroid2.3\myandroid2.3\kernel_imx\include\asm-generic)中找到
(1) BINDER_VERSION
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;
}
(2)BINDER_SET_MAX_THREADS
case BINDER_SET_MAX_THREADS:
if (copy_from_user(&proc->max_threads, ubuf, sizeof(proc->max_threads))) {
ret = -EINVAL;
goto err;
}
(3)BINDER_THREAD_EXIT
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;
(4)BINDER_SET_CONTEXT_MGR
若一个进程/线程能被成功设置成binder_context_mgr_node对象,称该进程/线程为Context Manager.即设置驱动中的全局变量binder_context_mgr_uid为当前进程的uid.并初始化一个Binder_node并赋值给全局变量binder_context_mgr_node。
只有创建binder_context_mgr_node对象的Binder上下文管理进程/线程才有权限重新设置这个对象。
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;
}
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_node节点
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_node节点数据
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;
static struct binder_node *binder_new_node(struct binder_proc *proc,
void __user *ptr,
void __user *cookie)
{
struct rb_node **p = &proc->nodes.rb_node;
struct rb_node *parent = NULL;
struct binder_node *node;
while (*p) { //查找第一个叶节点
parent = *p;
node = rb_entry(parent, struct binder_node, rb_node);
if (ptr < node->ptr)
p = &(*p)->rb_left;
else if (ptr > node->ptr)
p = &(*p)->rb_right;
else
return NULL;
}
//创建binder_node节点
node = kzalloc(sizeof(*node), GFP_KERNEL);
if (node == NULL)
return NULL;
binder_stats_created(BINDER_STAT_NODE);
rb_link_node(&node->rb_node, parent, p); //插入节点
rb_insert_color(&node->rb_node, &proc->nodes);
node->debug_id = ++binder_last_id; //初始化数据
node->proc = proc;
node->ptr = ptr;
node->cookie = cookie;
node->work.type = BINDER_WORK_NODE;
INIT_LIST_HEAD(&node->work.entry); //初始化链表头
INIT_LIST_HEAD(&node->async_todo);
binder_debug(BINDER_DEBUG_INTERNAL_REFS,
"binder: %d:%d node %d u%p c%p created\n",
proc->pid, current->pid, node->debug_id,
node->ptr, node->cookie);
return node;
}
Binder_proc的成员node是binder_node的根节点,这是一颗红黑树,该函数首先根据规则找到第一个叶节点作为新插入的节点的父节点,然后创建binder_node节点并插入。
(5) BINDER_WRITE_READ
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))) { //从用户空间复制数据到binder_write_read中
ret = -EFAULT;
goto err;
}
binder_debug(BINDER_DEBUG_READ_WRITE,
"binder: %d:%d write %ld at lx, read %ld at lx\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;
}
static int binder_free_thread(struct binder_proc *proc, struct binder_thread *thread)
{
struct binder_transaction *t;
struct binder_transaction *send_reply = NULL;
int active_transactions = 0;
rb_erase(&thread->rb_node, &proc->threads); //将当前线程从红杉树上删除
t = thread->transaction_stack; //取得binder_transaction数据
if (t && t->to_thread == thread) //判断要释放的是否是“回复”进程
send_reply = t;
while (t) { //释放所有的binder_transaction
active_transactions++;
binder_debug(BINDER_DEBUG_DEAD_TRANSACTION,
"binder: release %d:%d transaction %d "
"%s, still active\n", proc->pid, thread->pid,
t->debug_id,
(t->to_thread == thread) ? "in" : "out");
if (t->to_thread == thread) {
t->to_proc = NULL;
t->to_thread = NULL;
if (t->buffer) {
t->buffer->transaction = NULL;
t->buffer = NULL;
}
t = t->to_parent;
} else if (t->from == thread) {
t->from = NULL;
t = t->from_parent;
} else
BUG();
}
if (send_reply) //需要发送失败回复
binder_send_failed_reply(send_reply, BR_DEAD_REPLY);
binder_release_work(&thread->todo); //释放binder_work
kfree(thread); //释放binder_thread
binder_stats_deleted(BINDER_STAT_THREAD); //改变binder状态
return active_transactions;
}
static void binder_release_work(struct list_head *list)
{
struct binder_work *w;
while (!list_empty(list)) {
w = list_first_entry(list, struct binder_work, entry);
list_del_init(&w->entry);
switch (w->type) {
case BINDER_WORK_TRANSACTION: {
struct binder_transaction *t;
t = container_of(w, struct binder_transaction, work);
if (t->buffer->target_node && !(t->flags & TF_ONE_WAY))
binder_send_failed_reply(t, BR_DEAD_REPLY);
} break;
case BINDER_WORK_TRANSACTION_COMPLETE: {
kfree(w);
binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE);
} break;
default:
break;
}
}
}
相关文章推荐
- 【网络编程】之三、socket网络编程
- createrepo
- PackageManager.java中的方法resolveContentProvider的参数说明有误。
- Eclipse无提示的解决办法
- EditPlus行首行尾批量添加字符
- Performance Log
- python-相对导入与绝对导入
- RESTClient
- poj 3468 Simple Problem with Integers
- 【网络编程】之二、socket API学习
- 【网络编程】之一、初识WinSocket
- kettel job脚本执行定时任务步骤
- Android Orientation Sensor(方向传感器)详解与应用
- 详解用户登录流程——Volley 请求
- hdu 5407 CRB and Candies 2015多校联合训练赛#10 找规律 素数筛法
- vs2013 c# 中调用 c 编写的dll出错的可能错误
- 电赛总结(四)——波形发生芯片总结之AD9851
- 缓存知识 (未完,待续....)
- HDOJ 5416 CRB and Tree DFS
- Eclipse汉化后怎么改回英文版(可切换中英文)