我是linux系统编程的新手,我在阅读 Linux System Programming 时遇到了API和ABI .
Definition of API :
API定义了一个软件在源级别与另一个软件进行通信的接口 .
Definition of ABI :
API定义源接口,而ABI定义特定体系结构上两个或多个软件之间的低级二进制接口 . 它定义了应用程序如何与自身交互,应用程序如何与内核交互以及应用程序如何与库交互 .
程序如何在源级别进行通信?什么是源级别?它无论如何都与源代码有关?或者库的源代码包含在主程序中?
我所知道的唯一区别是API主要由程序员使用,而ABI主要由编译器使用 .
7 回答
这是我的外行解释:
api - 想想
include
文件 . 他们提供编程接口abi - 想想内核模块 . 当你在某个内核上运行它时,它们必须同意如何在没有包含文件的情况下进行通信,即作为低级二进制接口
API是人类使用的 . 我们编写源代码 . 当我们编写程序并想要使用某些库函数时,我们编写如下代码:
我们需要知道有一个方法
livenMyHills()
,它采用一个长整数参数 . 因此,作为一个编程接口,我们通常不会看到很多二进制级别的动作 .您的程序(源代码)可以使用提供正确 API 的模块进行编译 .
您的程序(二进制)可以在提供正确 ABI 的平台上运行 .
API限制类型定义,函数定义,宏,有时是库应公开的全局变量 .
ABI限制“平台”应该为您运行的程序提供什么 . 我想在3个级别考虑它:
处理器级别 - 指令集,调用约定
内核级别 - 系统调用约定,特殊文件路径约定(例如Linux中的
/proc
和/sys
文件)等 .操作系统级别 - 对象格式,运行时库等 .
考虑一个名为
arm-linux-gnueabi-gcc
的交叉编译器 . "arm"表示处理器体系结构,"linux"表示内核,"gnu"表示其目标程序使用GNU的libc作为运行时库,不同于使用Android的libc实现的arm-linux-androideabi-gcc
.API:应用程序接口
这是您从应用程序/库中公开的一组公共类型/变量/函数 .
在C / C中,这是您在应用程序附带的头文件中公开的内容 .
ABI:应用程序二进制接口
这是编译器构建应用程序的方式 .
它定义的东西(但不限于):
如何将参数传递给函数(寄存器/堆栈) .
谁清除堆栈中的参数(调用者/被调用者) .
返回值的返回值 .
例外如何传播 .
我主要是在API不兼容的更改或ABI不兼容的更改的意义上遇到这些术语 .
API更改本质上是使用以前版本编译的代码将不再起作用的地方 . 这可能是因为您向函数添加了参数,或者更改了本地代码之外可访问的名称 . 每次更改 Headers 时,它都会强制您更改.c / .cpp文件中的某些内容,您已经进行了API更改 .
ABI更改是已针对版本1编译的代码将不再适用于代码库的版本2(通常是库) . 与API不兼容的更改相比,这通常更难以跟踪,因为向类添加虚拟方法这样简单的操作可能与ABI不兼容 .
我发现了两个非常有用的资源,用于确定ABI的兼容性以及如何保留它:
The list of Do's and Dont's with C++ for the KDE project
Ulrich Drepper's How to Write Shared Libraries.pdf(glibc的主要作者)
( A pplication B inary I nterface)与操作系统结合的特定硬件平台的规范 . 它超越了API( A pplication P rogram I nterface),它定义了从应用程序到操作系统的调用 . ABI定义了API以及特定CPU系列的机器语言 . API不能确保运行时兼容性,但ABI确实如此,因为它定义了机器语言或运行时格式 .
Courtesy
让我举一个具体的例子,说明ABI和API在Java方面有何不同 .
ABI不兼容的更改是,如果我将方法A#m()更改为将
String
作为参数更改为String...
参数 . 这不是ABI兼容的,因为您必须重新编译调用它的代码,但它与API兼容,因为您可以通过重新编译来解决它,而无需在调用者中进行任何代码更改 .这是拼写的例子 . 我的Java库有A类
我有一个使用这个库的类
现在,图书馆作者编写了他们的A类,我编写了我的类Main,它们都运行良好 . 想象一下A的新版本
如果我只是采用新编译的A类并将其与之前的一起删除编译类Main,我在尝试调用方法时遇到异常
如果我重新编译Main,这是固定的,一切都在重新运行 .