4. Binder驱动与Android框架的接口
Binder驱动作为Android系统中进程间通信(IPC)的核心枢纽,其与上层框架的接口设计直接决定了系统的稳定性与扩展性。本章深入剖析Binder设备节点的创建与访问机制、驱动层提供的ioctl接口体系,以及驱动与ServiceManager的交互流程,为多平台移植适配奠定基础。
4.1 Binder设备节点:/dev/binder 的创建与访问
在Android系统中,Binder驱动通过字符设备节点 /dev/binder 暴露给用户空间。该节点的创建是驱动初始化的关键步骤,其访问权限控制着系统服务的通信能力。
4.1.1 设备节点创建流程
Binder设备节点的创建发生在驱动模块加载阶段,由内核的 misc_register() 机制完成。以下是核心代码路径:
// drivers/android/binder.c
static int __init binder_init(void)
{
int ret;
// 创建binder设备节点
binder_deferred_workqueue = create_singlethread_workqueue("binder");
if (!binder_deferred_workqueue)
return -ENOMEM;
// 注册misc设备,自动创建/dev/binder
ret = misc_register(&binder_miscdev);
if (ret < 0) {
destroy_workqueue(binder_deferred_workqueue);
return ret;
}
return 0;
}
static struct miscdevice binder_miscdev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "binder",
.fops = &binder_fops
};
设备节点的关键属性如下表所示:
| 属性 | 值 | 说明 |
|---|---|---|
| 设备路径 | /dev/binder |
标准字符设备节点 |
| 主设备号 | 10 (MISC_MAJOR) | misc设备共用主设备号 |
| 次设备号 | 动态分配 | 由内核自动分配 |
| 文件操作 | binder_fops |
包含open/release/ioctl/mmap等 |
| 默认权限 | 0666 | 允许所有进程访问 |
4.1.2 用户空间访问控制
在Android框架中,/dev/binder 的访问权限通过SELinux策略严格控制。以下是典型的SELinux规则:
# external/sepolicy/binder.te
type binder_device, dev_type;
allow servicemanager binder_device:chr_file { open read write ioctl };
allow system_app binder_device:chr_file { open read write ioctl };
allow untrusted_app binder_device:chr_file { open read write ioctl };
mknod /dev/binder c 10 231 && chmod 0666 /dev/binder。
4.2 Binder驱动ioctl接口详解
Binder驱动通过ioctl系统调用提供丰富的控制接口,用户空间通过 ioctl(fd, cmd, arg) 与驱动交互。所有ioctl命令定义在 linux/binder.h 头文件中。
4.2.1 ioctl命令分类
Binder驱动定义了以下核心ioctl命令,按功能分为三大类:
| 命令宏 | 命令码 | 方向 | 功能描述 |
|---|---|---|---|
BINDER_WRITE_READ |
_IOWR('b', 1, struct binder_write_read) |
双向 | 核心读写操作,发送/接收Binder事务 |
BINDER_SET_IDLE_TIMEOUT |
_IOW('b', 3, __s64) |
写入 | 设置空闲超时时间(已废弃) |
BINDER_SET_MAX_THREADS |
_IOW('b', 5, __u32) |
写入 | 设置线程池最大线程数 |
BINDER_SET_IDLE_PRIORITY |
_IOW('b', 6, __s32) |
写入 | 设置空闲优先级(已废弃) |
BINDER_SET_CONTEXT_MGR |
_IOW('b', 7, __s32) |
写入 | 注册当前进程为Context Manager(ServiceManager) |
BINDER_THREAD_EXIT |
_IOW('b', 8, __s32) |
写入 | 通知驱动Binder线程退出 |
BINDER_VERSION |
_IOWR('b', 9, struct binder_version) |
双向 | 获取驱动版本信息 |
BINDER_GET_NODE_DEBUG_INFO |
_IOWR('b', 11, struct binder_node_debug_info) |
双向 | 获取Binder节点调试信息 |
BINDER_GET_NODE_INFO_FOR_REF |
_IOWR('b', 12, struct binder_node_info_for_ref) |
双向 | 根据引用获取节点信息 |
BINDER_SET_CONTEXT_MGR_EXT |
_IOW('b', 13, struct flat_binder_object) |
写入 | 扩展版Context Manager注册 |
BINDER_FREEZE |
_IOW('b', 14, struct binder_freeze_info) |
写入 | 冻结/解冻Binder节点 |
BINDER_GET_FROZEN_INFO |
_IOWR('b', 15, struct binder_frozen_status_info) |
双向 | 获取冻结状态信息 |
4.2.2 核心ioctl:BINDER_WRITE_READ
BINDER_WRITE_READ 是Binder驱动最核心的ioctl命令,它封装了所有事务的发送与接收。其数据结构如下:
struct binder_write_read {
// 写入缓冲区
struct binder_transaction_data *write_buffer;
unsigned long write_size; // 写入数据大小
unsigned long write_consumed; // 已消耗的写入数据
// 读取缓冲区
struct binder_transaction_data *read_buffer;
unsigned long read_size; // 读取缓冲区大小
unsigned long read_consumed; // 已读取的数据量
};
用户空间通过填充 binder_write_read 结构体,将事务数据写入驱动,同时从驱动读取待处理的事务响应。驱动内部通过 binder_ioctl_write_read() 函数处理该命令:
static long binder_ioctl_write_read(struct file *filp,
unsigned int cmd, unsigned long arg,
struct binder_thread *thread)
{
struct binder_proc *proc = filp->private_data;
struct binder_write_read bwr;
int ret;
// 从用户空间拷贝bwr结构
if (copy_from_user(&bwr, (void __user *)arg, sizeof(bwr)))
return -EFAULT;
// 处理写入数据
if (bwr.write_size > 0) {
ret = binder_thread_write(proc, thread,
bwr.write_buffer,
bwr.write_size,
&bwr.write_consumed);
if (ret < 0 && bwr.read_consumed == 0)
return ret;
}
// 处理读取数据
if (bwr.read_size > 0) {
ret = binder_thread_read(proc, thread,
bwr.read_buffer,
bwr.read_size,
&bwr.read_consumed,
filp->f_flags & O_NONBLOCK);
}
// 将结果写回用户空间
if (copy_to_user((void __user *)arg, &bwr, sizeof(bwr)))
return -EFAULT;
return 0;
}
4.2.3 ioctl调用流程
以下SVG图展示了Binder ioctl的完整调用链路:
4.3 Binder驱动与ServiceManager的交互
ServiceManager是Android系统中所有Binder服务的注册中心,它作为第一个启动的Binder服务,拥有特殊的Context Manager身份。Binder驱动为ServiceManager提供了特权接口,使其能够管理全局服务注册表。
4.3.1 ServiceManager注册机制
ServiceManager通过 BINDER_SET_CONTEXT_MGR ioctl命令向驱动注册自身为Context Manager。该操作只能执行一次,后续尝试会返回错误。
// frameworks/native/cmds/servicemanager/service_manager.c
int main(int argc, char **argv)
{
struct binder_state *bs;
// 打开binder设备
bs = binder_open("/dev/binder", 128*1024);
if (!bs) {
ALOGE("failed to open binder device\n");
return -1;
}
// 注册为Context Manager
if (binder_become_context_manager(bs)) {
ALOGE("cannot become context manager (%s)\n", strerror(errno));
return -1;
}
// 进入事件循环
binder_loop(bs, svcmgr_handler);
return 0;
}
int binder_become_context_manager(struct binder_state *bs)
{
return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}
4.3.2 驱动侧Context Manager处理
当驱动收到 BINDER_SET_CONTEXT_MGR 命令时,执行以下关键逻辑:
static long binder_ioctl_set_ctx_mgr(struct file *filp,
struct binder_thread *thread)
{
struct binder_proc *proc = filp->private_data;
struct binder_context *context = proc->context;
struct binder_node *new_node;
struct binder_node *context_mgr_node;
int ret = 0;
// 检查是否已存在Context Manager
mutex_lock(&context->context_mgr_node_lock);
context_mgr_node = context->binder_context_mgr_node;
if (context_mgr_node) {
// 已存在,返回错误
ret = -EBUSY;
goto out;
}
// 创建新的Binder节点表示ServiceManager
new_node = binder_new_node(proc, NULL);
if (!new_node) {
ret = -ENOMEM;
goto out;
}
// 设置节点为Context Manager
new_node->local_weak_refs++;
new_node->local_strong_refs++;
new_node->has_strong_ref = 1;
new_node->has_weak_ref = 1;
context->binder_context_mgr_node = new_node;
out:
mutex_unlock(&context->binder_context_mgr_node_lock);
return ret;
}
4.3.3 服务注册与查询流程
ServiceManager维护一个服务名称到Binder引用的映射表。服务注册和查询的交互流程如下:
4.3.4 服务注册数据结构
ServiceManager内部使用红黑树或哈希表维护服务注册信息,核心数据结构如下:
// frameworks/native/cmds/servicemanager/service_manager.c
struct svcinfo {
struct svcinfo *next; // 链表指针
struct svcinfo *prev; // 前驱指针
unsigned int handle; // Binder引用句柄
struct binder_death death; // 死亡通知
int allow_isolated; // 是否允许隔离进程访问
size_t len; // 服务名称长度
uint16_t name[0]; // 服务名称(变长数组)
};
BINDER_SET_CONTEXT_MGR命令在驱动中正确实现- 驱动能够识别并特殊处理Context Manager节点
- 服务注册和查询的事务格式与Android框架兼容
4.3.5 关键交互场景对比
| 场景 | 发起方 | Binder事务码 | 驱动处理 | ServiceManager处理 |
|---|---|---|---|---|
| 服务注册 | 服务提供者 | SVC_MGR_ADD_SERVICE |
转发到Context Manager节点 | 存储名称→句柄映射 |
| 服务查询 | 服务使用者 | SVC_MGR_GET_SERVICE |
转发到Context Manager节点 | 查找并返回句柄 |
| 服务检查 | 服务使用者 | SVC_MGR_CHECK_SERVICE |
转发到Context Manager节点 | 查找并返回句柄(不等待) |
| 服务列表 | 调试工具 | SVC_MGR_LIST_SERVICES |
转发到Context Manager节点 | 返回所有已注册服务名称 |
4.4 本章小结
Binder驱动与Android框架的接口设计体现了清晰的分层架构:
- 设备节点层:
/dev/binder作为用户空间与内核空间的桥梁,通过misc设备机制创建,权限由SELinux控制 - ioctl接口层: 提供12个标准命令,其中
BINDER_WRITE_READ是核心数据通道,BINDER_SET_CONTEXT_MGR是特权注册接口 - ServiceManager交互层: 驱动为Context Manager提供特殊节点标识,所有服务注册/查询都通过驱动中转,形成星型拓扑结构
在多平台移植时,这三层接口必须完整实现,任何一层的缺失或行为不一致都会导致Android框架无法正常运行。