细读《深入理解 Android 内核设计思想》(三)Binder 机制 [上]

本文详细介绍了Android系统中Binder机制的必备知识,包括设备驱动、文件描述符和页框的概念。接着,深入讲解了Binder概述,包括其作为进程间通信(IPC)的关键角色。文章进一步剖析了binder驱动的核心函数,如binder_open、binder_mmap和binder_ioctl,以及Service Manager的启动、注册与查询功能。通过对Service Manager的分析,展示了其在Binder通信中作为服务注册和查询的枢纽作用。
摘要由CSDN通过智能技术生成

对冗余挑拣重点,对重点深入补充,输出结构清晰的精简版

1.必备知识
设备驱动
文件描述符
页框
2.Binder 概述
3.binder 驱动
binder_open
binder_mmap
binder_ioctl
4.Service Manager
启动
注册与查询
5.最后

必备知识

设备驱动

Linux 把所有的硬件访问都抽象为对文件的读写、设置,这一"抽象"的具体实现就是驱动程序。驱动程序充当硬件和软件之间的枢纽,提供了一套标准化的调用,并将这些调用映射为实际硬件设备相关的操作,对应用程序来说隐藏了设备工作的细节。

Linux 设备分为三类,分别是字符设备、块设备和网络设备:

1.字符设备: 能够像字节流(类似文件)一样被访问的设备。对字符设备进行读/写操作时,实际硬件的 I/O 操作一般也紧接着发生。字符设备驱动程序通常都会实现 open、close、read 和 write 系统调用,比如触摸屏、键盘、串口、LCD、LED 等。
2.块设备: 指通过传输数据块(一般为 512 或 1k)来访问的设备,比如硬盘、SD卡、U盘、光盘等。
3.网络设备: 能够和其他主机交换数据的设备,比如网卡设备、蓝牙设备等。

通过 cat /proc/devices 命令可以查看字符设备和块设备:

Character devices:
  1 mem
  4 ttyS
 10 misc
   ...
Block devices:
  1 ramdisk
  7 loop
  8 sd
   ...

可以看到属于字符设备的 misc 杂项设备,设备号为 10。通过 ls /dev -l 命令可以查看具体的注册设备:

crw-rw-rw- 1 root   root    10,  61 2020-03-16 16:52 ashmem
crw-rw-rw- 1 root   root    10,  58 2020-03-16 16:52 binder
...

其中 Ashmem、Binder 的设备号是 10,都属于 misc 杂项设备,10 是 主设备号,61、58 叫做 从设备号,有了主、从设备号,就可以唯一标识一个设备。

文件描述符

Linux 中一切都可以看作文件,包括普通文件、链接文件、Socket 以及设备驱动等,对其进行相关操作时,都可能会创建对应的文件描述符。文件描述符(file descriptor)是内核为了高效管理已被打开的文件所创建的索引,用于指代被打开的文件,对文件所有 I/O 操作相关的系统调用都需要通过文件描述符。

文件描述符与文件是什么关系呢?下图 Linux 中的三张表可以体现:

  • 进程级别的文件描述符表:内核为每个进程维护一个文件描述符表,该表记录了文件描述符的相关信息,包括文件描述符、指向打开文件表中记录的指针。

  • 系统级别的打开文件表:内核对所有打开文件维护的一个进程共享的打开文件描述表,表中存储了处于打开状态文件的相关信息,包括文件类型、访问权限、文件操作函数(file_operations)等。

  • 系统级别的 i-node 表:i-node 结构体记录了文件相关的信息,包括文件长度,文件所在设备,文件物理位置,创建、修改和更新时间等,“ls -i” 命令可以查看文件 i-node 节点

文件描述符是一种系统资源,可以通过以下命令来查看文件描述符的上限:

#查看所有进程允许打开的最大 fd 数量
126|generic_x86:/ # cat /proc/sys/fs/file-max
174139

#查看所有进程已经打开的 fd 数量以及允许的最大数量
generic_x86:/ # cat /proc/sys/fs/file-nr
11040   0       174139

#查看单个进程允许打开的最大 fd 数量.
generic_x86:/ # ulimit -n
32768

也可以查看某进程当前已使用的 fd :

#查看某进程(进程 id 为 15077)已经打开的 fd
generic_x86:/ # ls -l /proc/15077/fd/
total 0
lrwx------ 1 u0_a136 u0_a136 64 2020-04-15 23:04 0 -> /dev/null
lrwx------ 1 u0_a136 u0_a136 64 2020-04-15 23:04 1 -> /dev/null
lrwx------ 1 u0_a136 u0_a136 64 2020-04-15 23:04 35 -> /dev/binder
lrwx------ 1 u0_a136 u0_a136 64 2020-04-09 01:01 44 -> socket:[780404]
lrwx------ 1 u0_a136 u0_a136 64 2020-04-15 23:04 55 -> /dev/ashmem
lrwx------ 1 u0_a136 u0_a136 64 2020-04-15 23:04 60 -> /dev/ashmem
...

上面这个进程是一个 Android 应用进程,所以能看到 ashmem、binder 等 Android 特有设备文件相关的 fd 。再来看一个实际打开磁盘文件的例子:

     File file = new File(getCacheDir(), "testFdFile");
     FileOutputStream out = new FileOutputStream(file);

执行上面代码后会申请一个对应的 fd:

# ls -l /proc/{pid}/fd/
...
l-wx------ u0_a55   u0_a55  2020-04-16 00:24 995 -> /data/data/com.example.test/cache/testFdFile
...

实际开发中,可能会遇到 fd 资源超过上限导致的 “Too many open files” 之类的问题,一般都是因为没有及时释放掉 fd,比如上面代码中 FileOutputStream 没有关闭,若循环执行超过单个进程允许打开的最大 fd 数量,程序就会出现异常。

页框

页框(Page Frame)是指一块实际的物理内存块,页是指程序的一块内存数据单元。内存数据一定是存储在实际的物理内存上,即页必然对应于一个页框,页数据实际是存储在页框上的。

页框和页一样大,都是内核对内存的分块单位。一个页框可以映射给多个页,也就是说一块实际的物理存储空间可以映射给多个进程的多个虚拟内存空间,这也是 mmap 机制依赖的基础规则。

Binder 概述

不同进程处于不同的内存空间,具有不同的虚拟地址映射规则,所以不能直接通信。 Binder 是 Android 中使用最广泛的 IPC 机制,正因为有了 Binder,Android 系统中形形色色的进程与组件才能真正统一成有机的整体。Binder 通信机制与 TCP/IP 有共通之处,其组成元素可以这样来类比:

  • binder 驱动 -> 路由器
  • Service Manager -> DNS
  • Binder Client -> 客户端
  • Binder Server -> 服务器

Binder 的本质目标就是客户端要与服务器通信,但由于是不同的进程,必须通过 binder 驱动(路由器)把请求正确投递到对方进程中,所以通信的进程需要持有一个唯一的 Binder 标志(IP 地址)。

而 Binder 标志可能是会动态更新的 “IP 地址”,对通信进程来说获取难度较大且可读性差,这就需要一个 Service Manager(DNS)来解决这个问题。但 Service Manager 自身也是一个 Binder Server(服务器),怎么找到它的 "IP 地址"呢?Binder 机制对此做了特别规定:Service Manager 在 Binder 通信过程中的唯一标志永远是 0。

binder 驱动

binder 驱动运行在内核态,向上层提供 /dev/binder 设备节点,并不对应真实的硬件设备。binder 驱动的注册逻辑在 Binder.c 中:

//drivers/staging/android/Binder.c
static init __init binder_init(void){
   
    ...
    ret = misc_register(&binder_miscdev); //注册为 misc 驱动
}

binder_miscdev 即 Binder 设备描述如下:

static struct miscdevice binder_miscdev = {
   
    .minor = MISC_DYNAMIC_MINOR, //自动分配次设备号
    .name = "binder", //驱动名称
    .fops = &binder_fops //binder 驱动支持的文件操作
}

binder_fops 为 Binder 设备支持的操作函数,如下:

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,
};

与 Ashmem 设备类似,最关键的是 binder_open()、binder_mmap()、binder_ioctl(),下面分别介绍这三个函数。

binder_open

用户应用程序通过 Binder 通信时,需先调用 binder_open() 方法打开 binder 驱动,binder_open() 中主要做了两个工作,对应的分为两部分来看:

//binder.c
static int 
一本以情景方式对Android的源代码进行深入分析的书。内容广泛,以对Framework层的分析为主,兼顾Native层和Application层;分析深入,每一部分源代码的分析都力求透彻;针对性强,注重实际应用开发需求,书中所涵盖的知识点都是Android应用开发者和系统开发者需要重点掌握的。共10章,第1章介绍了阅读本书所需要做的准备工作,主要包括对Android系统架构和源码阅读方法的介绍;第2章通过对Android系统中的MediaScanner进行分析,详细讲解了Android中十分重要的JNI技术;第3章分析了init进程,揭示了通过解析init.rc来启动Zygote以及属性服务的工作原理;第4章分析了Zygote、SystemServer等进程的工作机制,同时还讨论了Android的启动速度、虚拟机HeapSize的大小调整、Watchdog工作原理等问题;第5章讲解了Android系统中常用的类,包括sp、wp、RefBase、Thread等类,同步类,以及Java中的Handler类和Looper类,掌握这些类的知识后方能在后续的代码分析中做到游刃有余;第6章以MediaServer为切入点,对Android中极为重要的Binder进行了较为全面的分析,深刻揭示了其本质。第7章对Audio系统进行了深入的分析,尤其是AudioTrack、AudioFlinger和AudioPolicyService等的工作原理。第8章深入讲解了Surface系统的实现原理,分析了Surface与Activity之间以及Surface与SurfaceFlinger之间的关系、SurfaceFlinger的工作原理、Surface系统中的帧数据传输以及LayerBuffer的工作流程。第9章对Vold和Rild的原理和机制进行了深入的分析,同时还探讨了Phone设计优化的问题;第10章分析了多媒体系统中MediaScanner的工作原理。适合有一定基础的Android应用开发工程师和系统工程师阅读。通过对本书的学习,大家将能更深刻地理解Android系统,从而自如应对实际开发中遇到的难题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值