我是Linux系统编程的新手,在阅读时遇到了API和ABI
Linux系统编程。
API的定义:
API定义了接口
一个软件进行通信
与另一个在源级。
ABI的定义:
而API定义了一个源
接口时,ABI定义了
两个之间的低级二进制接口
或者更多的软件
特定的体系结构。它定义了
应用程序如何与
本身,应用程序如何交互
与内核,以及如何一个
应用程序与库交互。
程序如何在源级进行通信?什么是源级别?它是否与源代码有关?或者库的源代码包含在主程序中?
我所知道的唯一区别是API主要由程序员使用,而ABI主要由编译器使用。
您的程序(源代码)可以使用提供适当API的模块进行编译。
您的程序(二进制)可以在提供适当ABI的平台上运行。
API限制了类型定义、函数定义、宏,有时还有库应该公开的全局变量。
ABI限制了一个“平台”应该为您的程序运行提供什么。我喜欢从三个层面来考虑:
处理器级——指令集,调用约定
内核级——系统调用约定,特殊的文件路径约定(例如Linux中的/proc和/sys文件),等等。
操作系统级别——对象格式、运行时库等。
考虑一个名为arm-linux-gnueabi-gcc的交叉编译器。“arm”表示处理器架构,“linux”表示内核,“gnu”表示其目标程序使用gnu的libc作为运行时库,不同于arm-linux-androideabi-gcc使用Android的libc实现。
我经常在api不兼容的更改或abi不兼容的更改的意义上遇到这些术语。
API更改本质上是使用以前版本编译的代码将不再工作。这可能是因为您向函数添加了参数,或者更改了在本地代码之外可访问的内容的名称。任何时候,当您更改一个头文件时,它就会迫使您更改.c/.cpp文件中的某些内容,您就做了一个api更改。
ABI更改是指已经根据版本1编译的代码将不再使用版本2的代码库(通常是库)。这通常比与api不兼容的更改更难跟踪,因为像向类添加虚拟方法这样简单的事情可能与ABI不兼容。
我发现了两种非常有用的资源,用于了解什么是ABI兼容性以及如何保存它:
在KDE项目中使用c++的做和不做的列表
Ulrich Drepper的《如何编写共享库》(glibc的主要作者)
让我举一个具体的例子,说明在Java中ABI和API是如何不同的。
ABI不兼容的更改是如果我将方法a# m()从将字符串作为参数更改为String…论点。这不是ABI兼容的,因为您必须重新编译调用它的代码,但它是API兼容的,因为您可以通过重新编译来解析它,而无需在调用方中更改任何代码。
这里有一个例子。我的Java库有类A
// Version 1.0.0
public class A {
public void m(String string) {
System.out.println(string);
}
}
我有一个类使用这个库
public class Main {
public static void main(String[] args) {
(new A()).m("string");
}
}
现在,库作者编译了他们的类A,我编译了我的类Main,一切都运行得很好。想象一个新版本的a出现了
// Version 2.0.0
public class A {
public void m(String... string) {
System.out.println(string[0]);
}
}
如果我只是使用新编译的类A,并将其与之前编译的类Main一起删除,那么在试图调用该方法时就会得到异常
Exception in thread "main" java.lang.NoSuchMethodError: A.m(Ljava/lang/String;)V
at Main.main(Main.java:5)
如果我重新编译Main,这是固定的,所有工作再次。