抛开合法性问题不提,相对于GLibC,Bionic 也可以算是非常轻量级的了,而且对于Android所要达成的目标,Bionic 也更为有效。Bionic 中去掉的下列这些特性的原因或是认为没有必要或是认为太过复杂。
由于系统用是使用得非常频繁的,所以 Boinic 想要通过尽量减少对它们的封装的方式,降低因使用系统调用而引起的开销。系统调用的 stub 函数是在bionic/libc/SYSCALLS.TXT 的帮助下自动生成的,甚至有些系统调用都不会被导出。
在 Boinic 没有导出的系统调用中,有一部分是用来处理UNIX System V 进程间通信(IPC,Inter-Process-Communication)和共享内存的。这是由于 Android 的设计使然,去掉这种形式的 IPC,而改用Android自己的IPC。
一方面,Bionic 本身就支持 Pthread (即不需要一个单独的libpthread.so)。
但是另一方面,Bionic 对 Pthread 的支持也不是很全面,不少特性已经不再支持了,其中最值得注意的就是现在已经不能使用的pthread cancel()这个用来杀线程的函数了。
对 mutex 的支持也被阉割掉了,取而代之的是更为高效的内核快速 mutex(futex(2)系统调用),此外较为高级的IPC对象(比如rwlocks)也已经不复存在了。
尽管确实是支持 C++的(而且事实上Android 的大多数代码也是用C++写的),但是异常处理已经不支持了。与之类似,STL (Standard Template Library,标准模板库)也已经不再包含在其中了一一尽管也不是严格地不让使用 STL,不过得要手工添加一些代码才行(部分代码可以在external/stlport 项目中找到)。
Bionic 原生只需要ASCII–尽管通过 libutils.so还是可以使用Unicode的。
去掉这些东西还是很有道理的,特别是考虑到 Android 中大部分代码是跑在虚拟机里的,而使用虚拟机本身就意味着不需要 App
的开发者使用这些功能。 例如,虚拟机有自己的线程管理和 Unicode 支持 (通过ICU)。不过,去掉这些特性还是会给原生代码的开发者带来一些麻烦,特别是那些想要把一些库从 Linux 移植到 Androd 上来的程序员。
Bionic 也在标准 LibC 中新增了一些特性,这些都是为 Android 而特别优化的,这些新增的特性包括:
系统属性是 Android 特有的特性,它支持系统或应用在一个简明的由“键/值”构成的存储空间中记录各种配置和操作参数。它类似于 Java 中“属性”的概念(而且事实上它是可以通过 Java 的 System.properties 访问的)。Android 极度依赖于这一机制,系统属性是在一段共享内存中实现的,系统中的所有进程都能以只读权限访问它,但只有 imit 进程才有对它进行设置(写)的权限。
与传统的 UNIX系统中依赖于 passwd 和 group 文件进行权限管理的方式不同,Android 中选择使用硬编码写死的id,并模拟getpwnam(3)及其相关函数的方法进行权限管理。我们只要考虑一下 Android 的安全模型,就会觉得这一做法也不无道理:
Android 中每个应用都会被分配到它自己的UID 和GID (从10000 开始),而且这些ID会被映射为方便人来阅读的格式 ,例如:app_uxXX (或者从JellyBean 版起,这一格式改为:uX_aYYY)。
此外Android 把数字较小的UID/GID (1000-9999)保留了下来,供它自己的子系统使用。
Bionic 中整合进了“名称-IP”的DNS 解析代码(在传统 Linux中,这部分代码是放在 libresolv.so 中的)。
Bionic 中使用的代码更为安全(源端口和Query ID都是随机的,这样做能有效地防范“生日攻击”),并且引入了一个新奇的特性一一每个进程都可以有自己的 DNS 解析服务器列表。
这使得特定的应用可以通过重新设置net.dns.pid系统属性的方式,抓取 DNS 解析的结果或者重定向解析 DNS 请求的服务器。
DNS 配置本身也是存储在系统属性中的(net.dns#)。
因为已经把原来 libresolvso 做的事都给包圆了,Android 中就去掉了传统 UNIX 系统中通常是放在/etc 目录下的 protocols 和 services 这两个文件。
Android 会运行在许多不同种类的设备上 (平板电脑、手机、机顶盒、跑步机等),底层硬件在适用范围和支持方面都有极大的不同。
为了解决这个问题,Android 定义了一个硬件抽象层(HAL,Hardware Abstraction Layer),其目标是通过定义一个转换层,使对各种不同类型硬件的操作趋于标准化。
硬件厂商可以在内核态中随心所欲地使用它们自己生产的设备的驱动程序但是必须提供一个接口库 (shim),以Android (特别是 Dalvik) 预期的方式与操作系统对接。
硬件抽象层上定义了从 Android 的角度看来,摄像头、GPS、传感器以及其他的硬件组件应该是个什么样子。
这并不会妨碍厂商扩展或修改硬件的功能,只需要厂商把接口库放到/system/lib/hw 目录里去就行了,然后 HAL(硬件抽象层)(也就是 libhardwareso) 会自动加载它们。打印某台Android设备使用的HAL接口库,例如:
Linux 内核得益于它开源和免费授权的本质,为 Android 提供了一个极为出色的基底。
尽管它现在已经和 Linus Torvalds 最初的版本差了不止十万八千里,但是 Linux 内核仍在以不可思议的速度演进着一每个月,甚至每一周都会增加新的特性。Android 所能拥有的功能在很大程度上会受到内核的影响。
较为有名的例子当数 zRAM 和对 64 位的支持。后者有助于解释为什么表Lollipop版的Android 系统会有两个最低版本,因为 Linux 内核是从3.7 版开始才支持 ARM64(AArch64)的。
谷歌在选择内核版本时也会受到一些限制,因为它只能从http://www.kernel.org 网站中选择那些被标为长期支持 (long term)的内核版本。
与其他 Linux 内核相比,Android 内核被编译得稍有差异。因为配置文件生成时同时使用了Android 的基础配置和默认内核发布版本中的建议配置模板。
如 source.android.com 网站中 kernel
前文中已经提到过,Android 在内核中引入了一些它特有的特性(Androidism)。
其中的一些被放在内核的 core 中,用条件编译语句#ifdef 加了一层保护,而剩下的则被放在了drivers/staging/android 目录中。
这些Android 特有的特性包括:
这是一种允许进程间共享内存的机制。应用可以打开一个字符设备节点 (/dev/ashmem)并创建一段内存空间,然后再把它映射 (map)到进程内存中去。这需要把工作限定在非全局可写(no world-writable)的目录中,并进行 System V进程间通信。
Binder 是 Android 中所有进程间通信的关键,它源自于 BeOS。Binder 表现为一个所有进程都能够打开的字符设备节点 (/dev/binder)。
Android 中的各种服务都注册在 Binder 这里,客户端可以在 servicemanager 的帮助下连上相应的服务。Binder 提供了一个有效的高级进程间通信机制。
提供基于内核的 ring buffer,用以高速记录日志。Android 的日志并不是记录在文件中的,而是由一个字符设备节点 (/dev/log)来承载的。
Android Lollipop 版中专门为此新增了一个用户态守护进程logd。
ION 内存管理器是在冰激凌三明治版中被引入的,其功能是向内核驱动和用户态下的类似模块(通过/dev/ion) 提供高效的内存分配服务。ION 替换掉了旧的Android 特有的组件 PMEM,ION 的设计目标是为各种不同的 SoC 架构中的内存管理提供一个统一的标准。
“SoC” 是 “System on Chip” 的缩写,翻译为中文是 “芯片上的系统”。它指的是一种将计算机或电子系统的所有核心功能集成到单个芯片上的设计。一个SoC通常包括处理器核心(CPU)、内存、输入输出接口、电源管理等各种组件,使得整个系统可以在一个芯片上完成。
在移动设备、嵌入式系统和其他一些电子产品中,SoC是一种常见的设计方式,因为它可以实现更高的集成度、更小的体积和更低的功耗。不同的SoC可以针对不同的应用场景和需求进行优化。
这个组件改进自 Linux 原有的OOM(Out-Of-Memory)进程终止器一一它会在内存不足时,杀掉一些进程,以释放内存空间。
不过 Android 中的这个组件是使用启发式的方法来寻找要被杀掉的进程的,而 Linux 原有的进程终止器是以一种更具确定性的方式控制杀进程的行为的,而且还允许定义内存压力等级。Android Lollipop 版中为此专门增加了一个用户态守护进程 Imkd。
“lmkd” 是指 “Low Memory Killer Daemon”,即低内存杀手守护进程。在 Android> 系统中,lmkd 是为了改进 Linux 原有的 Out-Of-Memory (OOM) 进程终止器而引入的。
Android 的低内存杀手(lmkd)是一个用户态守护进程,其任务是在内存不足时通过启发式方法选择并终止一些进程,以释放内存空间,从而维护系统的稳定性。相比于Linux 原有的 OOM 进程终止器,Android 的低内存杀手采用了一种更灵活的方式来选择要终止的进程,并允许定义不同的内存压力等级。
该组件在 Android Lollipop版本中引入,目的是提高系统在低内存情况下的性能和稳定性。低内存杀手会根据系统的内存状态动态地选择适当的进程进行终止,以应对不同的内存压力。
这是一种保存内核崩溃时输出的诊断用数据(程转储和最近的日志)的机制。不过较新版本的Android 系统已经弃用了这一特性,转而采用 Linux 内核中自有的一个功能了。
这是最新引入的 Android 特有的特性,引入它是为了快速同步基元(primitive),它主要是用在 Android 图形栈(特别是 surfaceflinger)中的。
使得用户态程序在用户态空间里就能访问 GPIO 寄存器,并在间隔一段时间之后自动设置GPIO 寄存器的值。
它的主要客户端是设备中的振动器(vibrator)功能框架(通过硬件抽象层)可以把一个数值(单位为毫秒)写入/sys/classtimedoutput/vibrator/enable 中,这样就启动了振动器,并让振动器振动了这个值规定的时间之后停下来。
什么是GPIO?
GPIO 是 General Purpose
Input/Output(通用输入/输出)的缩写,它是一种用于在计算机系统中进行数字输入和输出的通用接口。GPIO
可以通过软件配置为输入或输出,并且可以用于与外部电路、传感器、执行器等进行通信。GPIO 寄存器是设备上的通用输入/输出寄存器,用于控制和读取与设备连接的 GPIO 引脚的状态。通过操作 GPIO
寄存器,可以实现对设备上的硬件进行控制,例如振动器。在嵌入式系统和嵌入式软件开发中,程序可以通过设置 GPIO 寄存器的值来控制连接到系统的各种外围设备。在这种情况下,振动器的控制是通过访问
GPIO 寄存器进行的,通过设置寄存器的值来启动或停止振动器。GPIO 提供了一种通用的、灵活的方式,使得用户态程序能够在用户态空间中直接访问设备的硬件接口,从而实现对硬件设备的控制和通信。
最初这是一个单独的用来控制电源管理并阻止内核进入休眠状态的Android 特有的特性。不过 wakelocks 现在已经逐步被并入到内核自己的 wakeup source机制中去了。
《最强Android书:架构大剖析》