问题
Android系统上的C语言基础库Bionic里都有啥?(此篇分析基础为Android 7.1.1系统源码)
1 概述
Bionic 提供C 语言基础库(简称 C 库)的功能,它是一个专为嵌入式系统设计的轻量级标准库实现。Bionic的源码和头文件在 bionic/目录中。
相对Linux操作系统中的 C 库实现glibc,Bionic 的体积和内存占用量更小。Bionic 支持标准C和 C++库的绝大部分功能,支持数学库及 NPTL 线程库。其中还有自己实现的 Linker 及 Loader,用于动态库的创建和加载。
Bionic 提供的 C++的支持是一种最轻量级的支持,只支持了 C++语法,没有支持 STL(标准模板库)。Bionic 也有对标准 C 库进行了扩展,加入了 Android 独有的一些功能。例如,Android 特定的log 机制的底层支持、Android 中特定的 Property 系统支持。
2 Bionic 各部分的结构
Pedro@x86:bionic$ tree -L 1
.
├── Android.mk
├── benchmarks
├── build
├── CleanSpec.mk
├── CPPLINT.cfg
├── libc
├── libdl
├── libm
├── libstdc++
├── linker
├── README.md
├── tests
└── tools
Bionic目录当中的各个子目录中的内容如下所示。
- libc:C 库,生成动态库 libc.so 和静态库 libc.a
- libm:数学库,生成动态 libm.so 静态 libm.a
- libdl:动态库使用工具,生成动态库 libdlso
- libstdc++:C++库,生成动态库 libstdc++.so 和静态库libstdc++.a
- libthread_db:线程库,生成动态库 libthread_db.so和静态lbthread_db.a
libc实际是C库的实现,目录中包含了 stdio、string、stdlib 和 binonic 等几个源代码的子目录,提供对各个部分 C 语言的实现支持。
libm 和libdl也是标准的名称。这两个库提供了标准的C语言运行环境。
libstdc++是 C++支持库,是一个Android 中的特定库。实际上,C++语言的支持库大部分是由编译器实现的。libstdc++是一个非常小的库,提供了 new 操作符等。
libthread_db 是一个基于 pthread 线程库的线程管理库,为Android 系统中的特殊实现。
3 Bionic 的头文件
对于 Android 中大多数的开发,不需要关心 Bionic 的实现。但是可能需要直接使用Bionic 提供的功能,需要了解下 Bionic 的头文件。Bionic 的各个子目录中,一般都包含名称为 include 的子目录,这个子目录实际上就是这个库对应的头文件提供的支持。作为C 库功能的实现,Bionic 的头文件包括几个部分:C 语言标准头文件、UNIX 标准头文件、Linux内核中的头文件、Bionic特殊的头文件。
arch-XXX | 架构相关,包含汇编代码 |
bionic | 由C实现的功能,与架构无关 |
dns | dns相关操作 |
include | 头文件 |
kernel | Linux内核头文件 |
malloc_debug | 内存泄漏调试相关 |
private | 私有头文件 |
stdio | stdio函数实现 |
stdlib | stdlib函数实现 |
tools | 工具 |
tzcode | 时区相关代码 |
zoneinfo | 时区信息 |
3.1 主要头文件
libc/include 目录中的各个文件为 C语言主要的头文件,例如 stdio.h、stdlib.h等。它们都是C语言标准的头文件。
libc/include/sys 目录中的各个文件是 Linux 系统以sys 为开头的头文件。例如:time.h、ioctl.h 等。
//在代码中,一般通过如下所示:
#include <sys/time.h>
其中的一部分文件是经过 Android 系统重写的,可能和标准的 Linux 系统的文件略有区别,但是它们定义的功能基本相同。
3.2 Linux特定头文件
在 Linux 中进行基于 C 语言的开发时,除了可以引用标准的头文件之外,有时还可以引用 Linux的头文件。Bionic 中的libc/kernel为 Linux 内核头文件目录。这个目录是对 Linux 源代码中各个部分头文件的重新组织。其结构如下所示:
dockdroid@zj-x86:kernel$ tree -L 2
.
├── common
│ └── scsi
├── README.TXT
├── tools
│ ├── clean_header.py
│ ├── cpp.py
│ ├── defaults.py
│ ├── generate_uapi_headers.sh
│ ├── kernel.py
│ ├── update_all.py
│ └── utils.py
└── uapi
├── asm-arm
├── asm-arm64
├── asm-generic
├── asm-mips
├── asm-x86
├── drm
├── linux
├── misc
├── mtd
├── rdma
├── scsi
├── sound
├── video
└── xen
在Android 的本地开发过程中,如果使用<linux/xxx.h>等方式包含头文件,引用的是这里的头文件。
#include <linux/input.h>
此时,实际上引用的是 Bionic中 libc/kernel/common/linux目录中的input.h。
在一个 Android 系统中,由于内核和用户空间的内容是分别构建的。不同 kermel 版本中的头文件会略有不同。因此真正系统的内核头文件和 Bionic 中具有的内核头文件有可能具有不匹配的问题。
在严格意义上,应该使用系统中真正的Linux内核头文件替换Bionic原本提供的Linux内核头文件。并且内核中定义的宏也应该是相同的,否则也存在着一定的风险。
由于各个版本 Linux 内核的功能不会改变,在实际的操作过程中,即使这里直接使用风险也并不大。
4 属性系统的支持
Android 的C库除了提供标准的功能之外,还提供了 Android 的属性系统的底层支持属性的本质是一块共享内存。其中 libc/include/sys/ 目录中的 system_properties.h 和 _system_properties.h 是属性机制的头文件,libc/bionic/目录中的 system_properties.c 是其源代码文件,两个宏用于定义属性名称和属性值的最大字符数:
//Android 中属性名称最大32字节,属性值92字节。
#define PROP_NAME_MAX 32
#define PROP_VALUE_MAX 92
_system_properties.h 属性相关的头文件中定义了重要的结构体:
struct prop_msg
{
unsigned cmd;
char name[PROP_NAME_MAX];
char value[PROP_VALUE_MAX];
};
_system_properties.h 中关于默认的属性文件的定义如下所示:
#define PROP_PATH_RAMDISK_DEFAULT "/default.prop"
#define PROP_PATH_SYSTEM_BUILD "/system/build.prop"
#define PROP_PATH_VENDOR_BUILD "/vendor/build.prop"
#define PROP_PATH_LOCAL_OVERRIDE "/data/local.prop"
#define PROP_PATH_FACTORY "/factory/factory.prop"
几个扩展名为 prop 的属性文件是系统的默认属性定义文件,可以通过增加这几个文件的内容,定义系统的默认属性。Bionic 中实际上只是提供了属性机制最底层的支持,属性机制还需要依托 Android 其他部分才能使用(再往上有libcutil、libutil库的封装,以及init进程中的属性服务)。