由于简书对文章有最大长度限制,这部分内容拆解为三篇,分别为:
Binder驱动之设备控制binder_ioctl
-- 一
Binder驱动之设备控制binder_ioctl
-- 二
Binder驱动之设备控制binder_ioctl
-- 三
概述
ioctl
是Linux中常见的系统调用,它用于对底层设备的一些特性进行控制的用户态接口,应用程序在调用ioctl
进行设备控制时,最后会调用到设备注册struct file_operations
结构体对象时的unlocked_ioctl
或者compat_ioctl
两个钩子上,具体是调用哪个钩子判断标准如下:
-
compat_ioctl
: 32位的应用运行在64位的内核上,这个钩子被调用。 -
unlocked_ioctl
: 64位的应用运行在64位的内核或者32位的应用运行在32位的内核上,则调用这个钩子。
Binder做为Android中进程间高效通信的核心组件,其底层是以misc设备驱动的形式实现的,但它本身并没有实现read
,write
操作,所有的控制都是通过ioctl
操作来实现。在Binder驱动的struct file_operations
定义中可见,它的compat_ioctl
和unlocked_ioctl
两个钩子的的实现都是对应到binder_ioctl
上的。
static const struct file_operations binder_fops = {
.owner = THIS_MODULE,
.poll = binder_poll,
.unlocked_ioctl = binder_ioctl,
.compat_ioctl = binder_ioctl,
.mmap = binder_mmap,
.open = binder_open,
.flush = binder_flush,
.release = binder_release,
};
接下来,我们来先看看binder_ioctl
的实现有关的结构体。
相关数据结构
2.1 struct binder_thread
struct binder_thread {
struct binder_proc *proc; /*该thread相关联的binder_proc*/
struct rb_node rb_node; /*用于链入proc的threads红黑树*/
int pid;
int looper; /* 状态标识位,用于表示当前线程所处的状态,具体包括以下几种状态:
* enum {
* BINDER_LOOPER_STATE_REGISTERED = 0x01, /*进程的非主线程进入Binder循环状态*/
* BINDER_LOOPER_STATE_ENTERED = 0x02, /*进程的主线程进入Binder循环状态*/
* BINDER_LOOPER_STATE_EXITED = 0x04, /*线程退出Binder循环状态*/
* BINDER_LOOPER_STATE_INVALID = 0x08, /*线程处在一个无效的状态,表示出错*/
* BINDER_LOOPER_STATE_WAITING = 0x10, /*线程的todo队列为空,进入等待请求的状态*/
* BINDER_LOOPER_STATE_NEED_RETURN = 0x20 /*线程是否需要返回数据给进程的用户态*/
* };
*/
struct binder_transaction *transaction_stack; /*该线程的事务栈。通过struct binder_transaction的
* from_parent和to_parent分别链入客户端和服务端线程的
* transaction_stack事务栈中(即本字段)。详见2.7 */
struct list_head todo; /*binder_work队列,管理本线程所有待处理的binder_work*/
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 。发送reply时发生错误,该错误码用于返回给发送进程 */
wait_queue_head_t wait; /*binder线程空闲时,用于等待队列相关的结构,是Linux内核的一个数据结构*/
struct binder_stats stats; /*统计有关的结构*/
};
2.2 struct binder_proc
中的相关成员
struct binder_proc{
...
struct rb_root threads; /*管理thread的红黑树根节点,以线程的id为序*/
struct rb_root nodes; /*管理binder_node的红黑树根节点,以binder_node中的ptr大小为序*/
struct rb_root refs_by_desc; /*管理binder_ref的红黑树根节点,以binder_ref中的desc大小为序*/
struct rb_root refs_by_node; /*管理binder_ref的红黑树根节点,以binder_ref对应的binder_node的地址为序*/
...
int max_threads; /*最大线程数*/
...
};
2.3 用于用户态向内核态传输数据的结构体 —— struct binder_write_read
struct binder_write_read {
/* process ----> kernel */
binder_size_t write_size; /* bytes to write, 进程用户态地址空间传递到内核数据的大小*/
binder_size_t write_consumed; /* bytes consumed by driver 进程用户态地址空间传递到内核数据中已经被内核态处理的大小*/
binder_uintptr_t write_buffer; /*进程用户态地址空间传递到内核数据的起始地址*/
/* kernel ----> process */
binder_size_t read_size; /* bytes to read, 总共可供给驱动写入的字节数,read_buffer可供内核使用的大小*/
binder_size_t read_consumed; /* bytes consumed by driver, 内核Binder驱动发送给用户态进程的字节数*/
binder_uintptr_t read_buffer; /*内核驱动发送给进程数据buffer的起始地址*/
};
2.4 Binder C/S通信架构中,C端的驱动层表示 —— struct binder_ref
struct binder_ref {
/* Lookups needed: */
/* node + proc => ref (transaction) */
/* desc + proc => ref (transaction, inc/dec ref) */
/* node => refs + procs (proc exit) */
int debug_id; /*每个binder_ref的唯一标识符,主要用于debug*/
struct rb_node rb_node_desc; /*用来链入proc->refs_by_desc红黑树中,该红黑树以desc域为序组织的*/
struct rb_node rb_node_node; /*用来链入proc->refs_by_node红黑树中, 该红黑树以该binder_ref所对应的binder_node的地址为序组织的*/
struct hlist_node node_entry; /*用来链入binder_node的refs哈希链表中。*/
struct binder_proc *proc; /*指向该binder_ref中所属的binder_proc*/
struct binder_node *node; /*指向该binder_ref所对应(引用)的binder_node*/
uint32_t desc; /*binder_ref的描述符。用来返回给进程用户态地址空间,标识所对应的binder_ref*/
int strong; /*强引用计数*/
int weak; /*弱引用计数*/
struct binder_ref_death *death; /*Binder“死亡讣告”相关的一个结构体,详见:2.8*/
};
2.5 Binder C/S通信架构中,S端的驱动层表示 —— struct binder_node
struct binder_node {
int debug_id;
struct binder_work work;
union {
struct rb_node rb_node; /*用来链入proc的nodes红黑树,该红黑树以binder_node的ptr的大小为序*/
struct hlist_node dead_node;
};
struct binder_proc *proc;
struct hlist_head refs; /*所有引用这个binder_node的binder_ref通过它的node_entry加入这个哈希链表中,
* 这样binder_node通过查看这个哈希链表就知道有哪些binder_ref在引用它*/
int internal_strong_refs; /*binder_ref的强引用计数,即有多少个binder_ref强引用这个binder_node*/
int local_weak_refs; /*BBinder弱引用计数*/
int local_strong_refs; /*binder_buffer.target_node及BBinder的强引用计数*/
binder_uintptr_t ptr; /*对应BBinder基类RefBase中mRef成员的的地址,它是一个引用计数器,类型为weakref_impl*/
binder_uintptr_t cookie; /*对应BBinder的地址*/**
unsigned has_strong_ref:1; /*标识是否已经增加了用户态对应binder service(BBinder)对象的强引用计数*/
unsigned pending_strong_ref:1; /*标识是否有未处理的BR_ACQUIRE命令,在执行BR_ACQUIRE请求命令前设为1,在BC_ACQUIRE_DONE中设为0*/
unsigned has_weak_ref:1; /*标识是否已经增加了用户态对应binder service(BBinder)对象的弱引用计数*/
unsigned pending_weak_ref:1; /*标识是否有未处理的BR_INCREFS命令,在执行BR_INCREFS请求命令前设为1,在BC_INCREFS_DONE中设为0*/
unsigned has_async_transaction:1; /*标识是否有异步事务要处理。异步事务的含义是:客户端发送了带有TF_ONE_WAY标识的请求。*/
unsigned accept_fds:1; /*是否接受文件描述符*/
unsigned min_priority:8;
struct list_head async_todo; /*异步事务待处理链表*/
};
2.6 用于Binder用户态向内核驱动传输事务数据 —— struct binder_transaction_data
struct binder_transaction_data {
/* The first two are only used for bcTRANSACTION and brTRANSACTION,
* identifying the target and contents of the transaction.
*/
union {
/* target descriptor of command transaction */
__u32 handle;
/* target descriptor of return transaction */
binder_uintptr_t ptr;
} target;
binder_uintptr_t cookie; /* target object cookie */
__u32 code; /* transaction command */
/* General information about the transaction. */
__u32 flags; /*标志位,如:TF_ONE_WAY*/
pid_t sender_pid; /*发送者进程id*/
uid_t sender_euid; /*发送者有效用户id*/
binder_size_t data_size; /* number of bytes of data */
binder_size_t offsets_size; /* number of bytes of offsets */
/* If this transaction is inline, the data immediately
* follows here; otherwise, it ends with a pointer to
* the data buffer.
*/
union { /*存放事务的数据部分,如果是inline,则数据直接放在buf数组中;
* 如果不是,则放在ptr结构体中的buffer和offsets的指针中。一般情况都是通过ptr结构体*/
struct {
/* transaction data */
binder_uintptr_t buffer;
/* offsets from buffer to flat_binder_object structs */
binder_uintptr_t offsets;
} ptr;
__u8 buf[8]; /*inline数据直接放在这个buf中, 在4.0.9的内核中,这个字段没有看见使用的地方*/
} data;
};
2.7 用于Binder内核态驱动表示Binder通信事务数据结构 —— struct binder_transaction
struct binder_transaction {
int debug_id;
struct binder_work work; /*用于链入线程/进程todo队列的成员*/
struct binder_thread *from; /*事务发起线程thread的地址,如果是binder server回复给client,该域为NULL*/
struct binder_transaction ***from_parent**; /* 用于链入事务发起线程的事务栈中,
* 加入的时机是binder_transaction_data传入驱动并被binder_transaction处理的时候*/
struct binder_proc *to_proc; /*目标线程的proc地址*/
struct binder_thread *to_thread; /*事务目标线程thread的地址*/
struct binder_transaction *to_parent; /* 用于链入目标线程的事务栈中,
* 加入的时机是目标线程在调用binder_thread_read处理thread->todo队列
* 类型为BINDER_WORK_TRANACTION的binder_work时*/
unsigned need_reply:1;
/* unsigned is_dead:1; */ /* not used at the moment */
struct binder_buffer *buffer; /*存储数据的地方*/
unsigned int code; /*一个binder调用所对应的代号*/
unsigned int flags;
long priority; /*请求/回复 线程的优先级*/
long saved_priority; /*存储线程优先级备份,当需要修改一个线程的优先级时,先将它当前值放在该变量中,以便于稍后恢复。*/
kuid_t sender_euid;
};
进程用户态传输进来的struct binder_transaction_data
到内核态后,会转化成相应的struct binder_transaction
。该数据结构主要用于承载Binder请求和回复通信中的数据。
2.8 用于注册Binder service死亡通知的数据结构 —— binder_ref_death
struct binder_ref_death {
struct binder_work work; /*binder service死亡时,通过该work的entry域链入thread或者proc的todo队列*/
binder_uintptr_t cookie; /*binder service死亡时,要通知的BpBinder对象的地址*/
};
3. 设备驱动控制 --- binder_ioctl
Binder驱动没有提供read/write操作,所有数据传输、控制都是通过binder_ioctl
进行,因此该部分是Binder驱动的核心内容,承载了Binder数据传输部分的主要业务。
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret;
struct binder_proc *proc = filp->private_data;
struct binder_thread *thread;
/*读取命令的大小*/
unsigned int size = _IOC_SIZE(cmd);
void __user *ubuf = (void __user *)arg;
/*pr_info("binder_ioctl: %d:%d %x %lx\n",
proc->pid, current->pid, cmd, arg);*/
trace_binder_ioctl(cmd, arg);
/* 如果binder_stop_on_user_error < 2 则直接返回0;
* 否则,调用_wait_event_interruptible进入可中断的挂起状态,接着让出处理器,
* 直到被wake_up且条件(binder_stop_on_user_error < 2)为真时才返回
*/
ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
if (ret)
goto err_unlocked;
/*获取binder_main_lock锁*/
binder_lock(__func__);
/*在proc->threads红黑树中查找thread,该红黑树以pid为序,具体详见3.1*/
thread = **binder_get_thread**(proc);
if (thread == NULL) {
ret = -ENOMEM;
goto err;
}
/*根据不同的命令,调用不同的处理函数进行处理*/
switch (cmd) {
case BINDER_WRITE_READ:
/*读写命令,数据传输,binder IPC通信的核心逻辑,详见3.2*/
ret = **binder_ioctl_write_read**(filp, cmd, arg, thread);
if (ret)
goto err;
break;
case BINDER_SET_MAX_THREADS:
/*设置最大线程数,直接将值设置到proc结构的max_threads域中。*/
if (copy_from_user(&**proc->max_threads**, ubuf, sizeof(proc->max_threads))) {
ret = -EINVAL;
goto err;
}
break;
case BINDER_SET_CONTEXT_MGR:
/*设置Context manager,即将自己设置为ServiceManager,详见3.3*/
ret = binder_ioctl_set_ctx_mgr(filp);
if (ret)
goto err;
break;
case BINDER_THREAD_EXIT:
/*binder线程退出命令,释放相关资源,详见3.4*/
binder_debug(BINDER_DEBUG_THREADS, "%d:%d exit\n",
proc->pid, thread->pid);
binder_free_thread(proc, thread);
thread = NULL;
break;
case BINDER_VERSION: {
/*获取binder驱动版本号,在kernel4.4版本中,32位该值为7,64位版本该值为8*/
struct binder_version __user *ver = ubuf;
if (size != sizeof(struct binder_version)) {
ret = -EINVAL;
goto err;
}
/*将版本号信息写入用户态地址空间struct binder_version的protocol_version中*/
if (put_user(BINDER_CURRENT_PROTOCOL_VERSION,
&ver->protocol_version)) {
ret = -EINVAL;
goto err;
}
break;
}
default:
ret = -EINVAL;
goto err;
}
ret = 0;
err:
if (thread)
thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN;
binder_unlock(__func__); /*释放binder_main_lock锁*/
wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
if (ret && ret != -ERESTARTSYS)
pr_info("%d:%d ioctl %x %lx returned %d\n", proc->pid, current->pid, cmd, arg, ret);
err_unlocked:
trace_binder_ioctl_done(ret);
return ret;
}
-
_IOC_SIZE读取参数命令的大小。在32位的体系架构中,参数
cmd
由四个域组成:- 读写属性域(
direction: read/write
), 区分是读取命令,还是写入命令。bit30~bit31,占2bit。可用宏_IOC_DIR
读取。 - 数据大小域(
size : size of argument
), 表示ioctl
中arg
变量所指内存区域占用内存大小。bit16~bit29,占14bit。可用宏_IOC_SIZE
读取 。 - 魔数域 (
type:usually related to the major number
),又叫“幻数”区,用来区分不同的设备驱动,当传入的值与自身的值不一致时则不进行进一步处理,是用于防止误使用的一种状态标识位。一般用字母A~Z
或者a~z
表示。bit8~bit15,占8bit。可用宏_IOC_TYPE
读取。 - 序号数或者基数(
command
):用于区分各种命令。bit0~bit7,占8bit。可用宏_IOC_NR
读取。
- 读写属性域(
-
binder_stop_on_user_error
, 该变量是一个全局静态变量, 它的值通过模块参数stop_on_user_error
控制,当系统出现问题时,可以通过将该值设置为一个大于或等于2的值,来暂停binder,来进行debug。模块参数的设置可以在模块插入时以参数传递的形式设定,如insmod xxx.ko arg=xxxx
形式;如果权限设置允许的话,也可以通过sysfs来动态设置(如echo 3 > /sys/module/binder/parameters/stop_no_user_error
)。相关代码如下:
static int binder_stop_on_user_error;
/*定义模块参数`stop_on_user_error`的设置函数*/
static int binder_set_stop_on_user_error(const char *val,
struct kernel_param *kp)
{
int ret;
ret = param_set_int(val, kp);/*将sysfs中/sys/module/binder/parameters/stop_on_user_error读入binder_stop_on_user_error*/
if (binder_stop_on_user_error < 2)
wake_up(&binder_user_error_wait);
return ret;
}
module_param_call(stop_on_user_error/*模块参数名字,所在路径为:/sys/module/binder/parameters/stop_on_user_error*/,
binder_set_stop_on_user_error /*模块参数`stop_on_user_error`的set函数*/,
param_get_int/*模块参数`stop_on_user_error`的读取函数*/,
&binder_stop_on_user_error/*模块参数对应的变量地址*/,
S_IWUSR | S_IRUGO /*在sysfs中的权限设置*/);
-
module_param_call
该宏用于定义一个内核模块参数,它的定义为module_param_call(name, set, get, arg, perm)
,其中:-
name
:内核模块参数的名字,也是在sysfs中显示的名字; -
set
:是该内核模块参数设定的回调函数,当在插入模式时传递参数或者通过sysfs设定内核模块参数时,该函数会被调用; -
get
: 是该内核模块参数读取的回调函数; -
arg
:内核模块参数的地址; -
perm
:该内核模块参数的权限设置,可以在sysfs中看到。
-
对于基本的数据类型的读取和设定回调函数,内核已经预先做了定义,一般形式为:param_get_xxx
和param_set_xxx
,xxx
是int
, short
等。可以参考一下这篇博客,Linux内核模块的编写方法和技巧。
- 额外提一下sysfs,它是Linux2.6开始提供的一种虚拟文件系统,设计该文件系统的目的是把原本在procfs关于设备的部分独立出来,以“设备层次结构架构(device tree)”的形式呈现。它可以把设备和驱动程序的信息从内核输出到用户空间,也可以对设备和驱动程序做设置。具体详见sysfs简介。
3.1 查找thread --- binder_get_thread
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; /*获取红黑树根节点*/
/*查找pid等于当前线程id的thread,该红黑树以pid大小为序组织*/
while (*p) {
parent = *p;
thread = rb_entry(parent, struct binder_thread, rb_node);
/*current->pid 是当前运行线程的id,不是进程的id*/
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;
/*更新thread创建统计计数*/
binder_stats_created(BINDER_STAT_THREAD);
/*初始化相关数据成员*/
thread->proc = proc;
thread->pid = current->pid; /*获取线程id*/
init_waitqueue_head(&thread->wait); /*初始化等待队列*/
INIT_LIST_HEAD(&thread->todo); /*初始化待处理队列*/
rb_link_node(&thread->rb_node, parent, p); /*加入到proc的threads红黑树中*/
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;
}
网友评论