/dev/binder 详解

/dev/binder 是 Android 系统中的一个字符设备驱动 ,是 Binder IPC 机制的核心。

🔧 什么是 Binder?

Binder 是 Android 中的进程间通信 (IPC) 机制,类似于 Linux 中的 socket、管道等,但专门为 Android 设计。

📍 /dev/binder 的作用

scss 复制代码
进程 A                          进程 B
│                              │
├─ 打开 /dev/binder            ├─ 打开 /dev/binder
│  (获得文件描述符)             │  (获得文件描述符)
│                              │
├─ mmap() 内存映射              ├─ mmap() 内存映射
│  (共享内存缓冲区)             │  (共享内存缓冲区)
│                              │
└─ ioctl() 系统调用             └─ ioctl() 系统调用
   (发送 Binder 事务)              (接收 Binder 事务)
   
   ↓ Binder 驱动核心 (/dev/binder) ↓
   
   进程间通信 (IPC)

🔄 Binder 通信流程

markdown 复制代码
发送方进程:
  1. 创建 Parcel 对象(序列化数据)
  2. 通过 ioctl() 调用 Binder 驱动
  3. Binder 驱动:
     ├─ 验证权限
     ├─ 进行数据转换
     ├─ 添加到接收方的待处理队列
     └─ 唤醒接收方进程
  
接收方进程:
  1. 从 ioctl() 阻塞中唤醒
  2. 从 Binder 缓冲区读取数据
  3. 反序列化 Parcel 对象
  4. 执行对应的方法
  5. 准备返回值
  6. 发送回复给发送方

🏗️ /dev/binder 的结构

scss 复制代码
/dev/binder (字符设备)
  │
  ├─ 主要功能:
  │  ├─ open()    - 打开设备,获得 Binder 上下文
  │  ├─ mmap()    - 创建共享内存区域
  │  ├─ ioctl()   - 发送/接收 Binder 事务
  │  └─ close()   - 关闭连接
  │
  ├─ 内部数据结构:
  │  ├─ binder_proc (每个进程)
  │  ├─ binder_thread (每个线程)
  │  ├─ binder_node (每个对象)
  │  ├─ binder_ref (对象引用)
  │  └─ binder_transaction (事务)
  │
  └─ 驱动特性:
     ├─ 自动垃圾回收 (引用计数)
     ├─ 死亡通知 (Binder Notification)
     ├─ 权限控制
     └─ 性能优化 (零拷贝)

📝 在 Telephony 中的具体使用

让我查看代码中 Binder 的实际使用:

让我查看 IPhoneSubInfo AIDL 文件:

让我查看一个 AIDL 文件来展示 Binder 的使用:

完美!现在我可以详细解释 /dev/binder 在 Telephony 中的实际使用:

📞 Telephony 中 /dev/binder 的实际使用例子

场景:应用调用 TelephonyManager.getDeviceId()

scss 复制代码
应用进程 (uid: 10001):
  ┌───────────────────────────────────┐
  │ val tm = context.getSystemService │
  │        (Context.TELEPHONY_SERVICE)│
  │ tm.getDeviceId()                  │
  └────────────┬──────────────────────┘
               │
               ├─ 通过 Binder 获取 IPhoneSubInfo 代理
               │  (asInterface())
               │
               ├─ 调用代理的 getDeviceId(callingPackage)
               │
               └─ 发送 Binder 事务到 /dev/binder
                  
                  ↓ /dev/binder (Binder 驱动)
                  
                  ├─ 检查权限
                  ├─ 检查调用者 UID
                  ├─ 序列化数据
                  └─ 转发到目标进程

system_server 进程 (uid: 1000):
  ┌───────────────────────────────────┐
  │ PhoneSubInfoController            │
  │ (IPhoneSubInfo 的实现)             │
  │                                    │
  │ onTransact():                      │
  │   case GET_DEVICE_ID:             │
  │     ├─ 获取 callingPackage        │
  │     ├─ 权限检查                    │
  │     ├─ 调用 Phone.getDeviceId()   │
  │     └─ 返回结果                   │
  └────────┬───────────────────────────┘
           │
           ├─ 序列化返回值
           └─ 发送回复到 /dev/binder
              
              ↓ /dev/binder (返回事务)
              
              转发回应用进程

应用进程:
  ┌───────────────────────────────────┐
  │ 从 Binder 缓冲区读取返回值        │
  │ deviceId = "123456789ABCDEF"      │
  └───────────────────────────────────┘

🔍 AIDL 文件如何变成 Binder 调用

java 复制代码
// IPhoneSubInfo.aidl
interface IPhoneSubInfo {
    String getDeviceIdWithFeature(String callingPackage, String callingFeatureId);
}

// 编译后生成的 Java 代码(简化版):
public class IPhoneSubInfo {
    // 服务端(PhoneSubInfoController)
    public abstract class Stub extends Binder implements IPhoneSubInfo {
        @Override
        public boolean onTransact(int code, Parcel data, Parcel reply, int flags) {
            // 接收 Binder 事务
            switch (code) {
                case TRANSACTION_getDeviceIdWithFeature: {
                    String callingPackage = data.readString();
                    String callingFeatureId = data.readString();
                    String result = this.getDeviceIdWithFeature(callingPackage, callingFeatureId);
                    reply.writeString(result);  // 序列化返回值
                    return true;
                }
            }
            return false;
        }
    }
    
    // 客户端(应用进程)
    public static class Proxy implements IPhoneSubInfo {
        private IBinder mRemote;
        
        @Override
        public String getDeviceIdWithFeature(String callingPackage, String callingFeatureId) {
            Parcel _data = Parcel.obtain();
            Parcel _reply = Parcel.obtain();
            try {
                _data.writeString(callingPackage);
                _data.writeString(callingFeatureId);
                
                // 发送 Binder 事务
                mRemote.transact(TRANSACTION_getDeviceIdWithFeature, _data, _reply, 0);
                
                // 接收返回值
                String _result = _reply.readString();
                return _result;
            } finally {
                _reply.recycle();
                _data.recycle();
            }
        }
    }
}

🎯 /dev/binder 中的关键概念

1. 文件描述符 (FD)

bash 复制代码
# 在进程中查看 Binder 连接
$ adb shell "cat /proc/[pid]/fd" | grep binder

# 输出示例:
# lrwx------ root root 11 /dev/binder

2. mmap() - 内存映射

java 复制代码
// 每个进程通过 mmap 获得共享缓冲区
private class BinderConnection {
    private static final int BINDER_VM_SIZE = (1 * 1024 * 1024) - PAGE_SIZE;
    
    // 驱动程序会创建一块共享内存
    // 用于 Binder 数据交换
    void setupBinderBuffer() {
        // 大约 1MB 的缓冲区用于 Binder 通信
        mmap(0, BINDER_VM_SIZE, 
             PROT_READ, MAP_PRIVATE, 
             mBinderFd, 0);
    }
}

3. ioctl() - 系统调用

c 复制代码
// Binder 驱动支持的主要 ioctl 命令
#define BINDER_WRITE_READ  _IOWR('b', 1, struct binder_write_read)
#define BINDER_SET_MAX_THREADS  _IOW('b', 5, __u32)
#define BINDER_SET_CONTEXT_MGR  _IOW('b', 7, __s32)
#define BINDER_THREAD_EXIT  _IOW('b', 8, __s32)
#define BINDER_VERSION  _IOWR('b', 9, struct binder_version)

🔐 /dev/binder 的权限和安全

bash 复制代码
# /dev/binder 的权限
$ ls -la /dev/binder
crw-rw-rw- 1 root root 10, 48 11-15 10:30 /dev/binder

# 所有进程都可以打开,但 Binder 驱动会检查:
# 1. 调用者的 UID/GID
# 2. 权限声明(manifest 权限)
# 3. AppOps 设置
# 4. SELinux 策略

📊 Binder 与其他 IPC 的对比

特性 Binder Socket Pipe Shared Memory
效率 很高 很高
易用性 很高
权限检查 ✅ 内置 ❌ 无 ❌ 无 ❌ 无
同步支持 ✅ 是 ✅ 是 ❌ 否 ❌ 否
垃圾回收 ✅ 自动 ❌ 手动 ❌ 手动 ❌ 手动
Android专用 ✅ 是 ❌ 否 ❌ 否 ❌ 否

📌 总结

/dev/binder 是:

  1. 字符设备驱动 - Linux 内核模块
  2. IPC 机制 - 进程间通信的核心
  3. AIDL 基础 - AIDL 编译后的代码使用 Binder
  4. 权限检查点 - 系统安全的重要组成部分
  5. 高效通信 - 零拷贝技术,性能优化

在 Telephony 中,所有的 AIDL 接口(如 IPhoneSubInfoITelephony 等)都是通过 /dev/binder 进行通信的。

相关推荐
Gopher_HBo5 小时前
Go进阶之recover
后端
程序员布吉岛6 小时前
写了 10 年 MyBatis,一直以为“去 XML”=写注解,直到看到了这个项目
后端
却尘6 小时前
一篇小白也能看懂的 Go 字符串拼接 & Builder & cap 全家桶
后端·go
茶杯梦轩6 小时前
从零起步学习Redis || 第七章:Redis持久化方案的实现及底层原理解析(RDB快照与AOF日志)
redis·后端
QZQ541886 小时前
重构即时IM项目13:优化消息通路(下)
后端
柠檬味拥抱6 小时前
揭秘Cookie操纵:深入解析模拟登录与维持会话技巧
后端
不想打工的码农6 小时前
MyBatis-Plus多数据源实战:被DBA追着改配置后,我肝出这份避坑指南(附动态切换源码)
java·后端
ZeroTaboo6 小时前
rmx:给 Windows 换一个能用的删除
前端·后端
Coder_Boy_6 小时前
Deeplearning4j+ Spring Boot 电商用户复购预测案例
java·人工智能·spring boot·后端·spring