以下内容主要总结自书籍,博客和Android Developers
系统调用,API和ABI
系统调用
由于系统有限的资源有可能被多个不同的应用程序间时访问,因此,如果不加以保护,那么各个应用程序难免产生冲突,并且让应用程序管理资源是极其不安全的。所以现代操作系统都将可能产生冲突的系统资源给保护起来,阻止应用程序直接访问。所有的这些操作都必须交由操作系统来进行,必须进入操作系统内核才能访问这些资源。
系统调用是应用程序(运行库也是应用程序的一部分)与操作系统内核之间的接口。它本身并非内核函数,但它是由内核函数实现,进入内核后,不同的系统调用会找到各自对应的内核函数,这些内核函数被称为系统调用的“服务例程”。比如系统调用getpid实际调用了服务例程为sys_getpid(),或者说系统调用getpid是服务例程sys_getpid()的“封装例程”。
API
系统凋用实际上是非常依赖于硬件结构的一种接口,它受到硬件的严格限制,比如寄存器的数目、调用时的参数传递、中断号、堆找切换等(ABI),都与硬件密切相关。如果硬件结构稍微发生改变,大量的应用程序能就会出现问题。
为了尽量隔离硬件结构的不同而导致的程序兼容性问题,操作系统设计者在系统调用之上增加了一层API层。应用程序不会直接使用系统调用,而是通过API。而API内部可能会封装系统调用(取决于该API的功能)。
这样可以让内核随版本自由地改变系统调用接口,只要让API 层不改变,用户程序就可以完全无碍地运行在新的系统上。
ABI
有了API之后,同样的代码可以在支持相应API的任何系统中编译。但这并不意味着在一个系统中生成的目标代码可以在其它系统中运行。不同的系统可能有不一样的硬件结构,调用约定和指令集等,这些差异导致了目标代码的差异,决定了它们的目标代码是彼此不兼容的。
我们把符号修饰标准、变量内存布局、函数调用方式等这些跟可执行代码二进制兼容性相关的内容称为ABI (Application Binary Interface)。
影响ABI的因素非常多,硬件、编程语言、编译器、链接器、操作系统等都会影响ABI。 我们可以从C语言的角度来看一个编程语言是如何影响ABI的:
• 内置类型(如int、float、char等)的大小和在存储器中的放置方式(大端、小端、对齐方式等)。
• 组合类型(如struct、union、数组等)的存储方式和内存分布。
• 外部符号(external-linkage)与用户定义的符号之间的命名方式和解析方式,如函数名 fimc在C语言的目标文件中是否被解析成外部符号_func。
• 函数调用方式,比如参数入栈顺序、返回值如何保持等。
• 堆栈的分布方式,比如参数和局部变量在堆栈里的位置,参数传递方法等。
• 寄存器使用约定,函数调用时哪些寄存器可以修改,哪些须要保存,等等。
C++对ABI的影响又增加了很多额外的内容:
• 继承类体系的内存分布,如基类,虚基类在继承类中的位置等。
• 指向成员函数的指针(pointer-to-member)的内存分布,如何通过指向成员函数的指针來凋用成员函数,如何传递this指针。
• 如何调用虚函数,viable的内容和分布形式,vtable指针在object中的位置等。
• template如何实例化。
• 外部符号的修饰。
• 全局对象的构造和析构。
• 异常的产生和捕获机制。
• 标准库的细节问题,RTH如何实现等。
• 内嵌函数访问细节。
Android ABI
不同的 Android 设备使用不同的 CPU,而不同的 CPU 支持不同的指令集。CPU 与指令集的每种组合都有专属的应用二进制接口 (ABI)。ABI 包含以下信息:
- 可使用的 CPU 指令集(和扩展指令集)。
- 运行时内存存储和加载的字节顺序。Android 始终是 little-endian。
- 在应用和系统之间传递数据的规范(包括对齐限制),以及系统调用函数时如何使用堆栈和寄存器。
- 可执行二进制文件(例如程序和共享库)的格式,以及它们支持的内容类型。Android 始终使用 ELF。
- 如何重整 C++ 名称。
ABI名字和指令集
可以看到,armeabi,Thumb-2,x86-64,AArch64指的是指令集。
armeabi-v7a
此 ABI 适用于基于 32 位 ARM 的 CPU。
arm64-v8a
此 ABI 适用于基于 ARMv8-A 的 CPU,支持 64 位 AArch64 架构。
x86
此 ABI 适用于支持通常称为“x86”、“i386”或“IA-32”的指令集的 CPU。
x86_64
此 ABI 适用于支持通常称为“x86-64”的指令集的 CPU。
编译生成不同的ABI目标代码
gradle:
android {
defaultConfig {
ndk {
abiFilters 'arm64-v8a', 'x86_64'
}
}
}
Application.mk
APP_ABI := arm64-v8a # Target only arm64-v8a
APP_ABI := all # Target all ABIs, including those that are deprecated.
APP_ABI := armeabi-v7a x86_64 # Target only armeabi-v7a and x86_64.
在源代码中使用 #ifdef 及以下各项确定 ABI 最为方便:
__arm__ armeabi
__arm__ armeabi-v7
__aarch64__ arm64-v8a
__i386__ x86
__x86_64__ x86_64
请注意:32 位 X86 称为 i386,而不是 x86,这可能与您预想的有所不同!