API와 ABI의 차이점


194

나는 리눅스 시스템 프로그래밍을 처음 접했고 리눅스 시스템 프로그래밍 을 읽는 동안 API와 ABI를 접했다 .

API의 정의 :

API는 한 소프트웨어가 소스 레벨에서 다른 소프트웨어와 통신하는 인터페이스를 정의합니다.

ABI의 정의 :

API가 소스 인터페이스를 정의하는 반면 ABI는 특정 아키텍처에서 둘 이상의 소프트웨어 간 하위 수준 이진 인터페이스를 정의합니다. 응용 프로그램이 자신과 상호 작용하는 방법, 응용 프로그램이 커널과 상호 작용하는 방법 및 응용 프로그램이 라이브러리와 상호 작용하는 방법을 정의합니다.

프로그램은 어떻게 소스 레벨에서 통신 할 수 있습니까? 소스 레벨은 무엇입니까? 어쨌든 소스 코드와 관련이 있습니까? 또는 라이브러리의 소스가 기본 프로그램에 포함됩니까?

내가 아는 유일한 차이점은 API는 주로 프로그래머가 사용하고 ABI는 대부분 컴파일러에서 사용한다는 것입니다.


2
소스 레벨에서 함수 정의를 노출시키는 파일 포함과 같은 것을 의미합니다
Anycorn

답변:


49

API는 인간이 사용하는 것입니다. 우리는 소스 코드를 작성합니다. 프로그램을 작성하고 라이브러리 함수를 사용하려면 다음과 같은 코드를 작성하십시오.

 long howManyDecibels = 123L;
 int ok = livenMyHills( howManyDecibels);

livenMyHills()긴 정수 매개 변수를 취하는 method가 있다는 것을 알아야했습니다 . 따라서 프로그래밍 인터페이스는 모두 소스 코드로 표현됩니다. 컴파일러는 이것을 특정 운영 체제에서이 언어의 구현에 맞는 실행 가능한 명령으로 바꿉니다. 이 경우 오디오 장치에서 일부 저수준 작동이 발생합니다. 따라서 특정 하드웨어에서는 특정 비트와 바이트가 필요합니다. 따라서 런타임에는 일반적으로 보지 않는 이진 수준 작업이 많이 진행됩니다.


310

API : 응용 프로그램 인터페이스

애플리케이션 / 라이브러리에서 공개하는 공개 유형 / 변수 / 함수 세트입니다.

C / C ++에서는 응용 프로그램과 함께 제공되는 헤더 파일에 표시됩니다.

ABI : 응용 프로그램 이진 인터페이스

이것이 컴파일러가 애플리케이션을 빌드하는 방법입니다.
그것은 것들을 정의하지만 (이에 국한되지는 않음) :

  • 매개 변수가 함수 (레지스터 / 스택)에 전달되는 방법
  • 누가 스택 (호출자 / 계단)에서 매개 변수를 정리합니다.
  • 반환 값이 반환되는 위치
  • 예외 전파 방법

17
이것은 아마도 내가 본 ABI가 무엇인지에 대한 가장 간결한 설명 일 것입니다 . j!
TerryP

3
이 답변이 간결한 것인지 자세한 것인지 결정해야합니다 . :)
jrok

1
@ jrok : 간결하고 세부적인 내용은 상호 배타적이지 않습니다.
Martin York

@LokiAstari, 그래서 ABI도 실제로 API가 아닌가?
Pacerier

4
@Pacerier : 둘 다 인터페이스입니다. 그러나 서로 다른 추상화 수준에 있습니다. API는 응용 프로그램 개발자 수준에 있습니다. ABI는 컴파일러 수준에 있습니다 (어플리케이션 개발자가 절대 사용하지 않는 곳).
Martin York

47

나는 대부분 API와 호환되지 않는 변경 또는 ABI와 호환되지 않는 변경이라는 의미에서 이러한 용어를 접하게됩니다.

API 변경은 본질적으로 이전 버전으로 컴파일 된 코드가 더 이상 작동하지 않는 곳입니다. 함수에 인수를 추가하거나 로컬 코드 외부에서 액세스 할 수있는 이름을 변경했기 때문에 발생할 수 있습니다. 헤더를 변경할 때마다 .c / .cpp 파일에서 무언가를 변경해야 할 때마다 API를 변경했습니다.

ABI 변경은 버전 1에 대해 이미 컴파일 된 코드가 더 이상 코드베이스의 버전 2 (보통 라이브러리)에서 더 이상 작동하지 않는 곳입니다. 클래스에 가상 메소드를 추가하는 것만 큼 간단한 것이 ABI와 호환되지 않을 수 있기 때문에 이는 API 비 호환 변경보다 추적하기가 일반적으로 더 까다 롭습니다.

ABI 호환성이 무엇인지, 보존하는 방법을 알아내는 데 유용한 두 가지 유용한 리소스를 찾았습니다.


4
상호 배타성을 지적한 +1 예를 들어, assert 키워드에 대한 Java의 소개는 API와 호환되지 않지만 ABI와 호환되는 변경 docs.oracle.com/javase/7/docs/technotes/guides/language/… 입니다.
Pacerier

리소스 섹션에 tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html의 "3.6. 호환되지 않는 라이브러리"를 추가 하면 ABI 변경을 유발할 수있는 사항이 나열됩니다.
데 미룬

20

이것은 내 평신도 설명입니다.

  • API- include파일을 생각 하십시오. 프로그래밍 인터페이스를 제공합니다.
  • ABI-커널 모듈을 생각하십시오. 일부 커널에서 실행할 때 포함 파일없이 통신하는 방법 (예 : 저수준 이진 인터페이스)에 동의해야합니다.

13

Linux 공유 라이브러리 최소 실행 가능 API 대 ABI 예제

이 답변은 다른 답변에서 발췌되었습니다 . 응용 프로그램 이진 인터페이스 (ABI) 란 무엇입니까? 그러나 나는 이것이 이것에도 직접 대답하고 질문은 중복되지 않는다고 생각했습니다.

공유 라이브러리와 관련하여 "안정적인 ABI를 갖는"의 가장 중요한 의미는 라이브러리가 변경된 후 프로그램을 다시 컴파일 할 필요가 없다는 것입니다.

아래 예에서 볼 수 있듯이 API가 변경되지 않아도 ABI를 수정하여 프로그램을 중단 할 수 있습니다.

main.c

#include <assert.h>
#include <stdlib.h>

#include "mylib.h"

int main(void) {
    mylib_mystrict *myobject = mylib_init(1);
    assert(myobject->old_field == 1);
    free(myobject);
    return EXIT_SUCCESS;
}

mylib.c

#include <stdlib.h>

#include "mylib.h"

mylib_mystruct* mylib_init(int old_field) {
    mylib_mystruct *myobject;
    myobject = malloc(sizeof(mylib_mystruct));
    myobject->old_field = old_field;
    return myobject;
}

mylib.h

#ifndef MYLIB_H
#define MYLIB_H

typedef struct {
    int old_field;
} mylib_mystruct;

mylib_mystruct* mylib_init(int old_field);

#endif

다음을 사용하여 컴파일 및 실행

cc='gcc -pedantic-errors -std=c89 -Wall -Wextra'
$cc -fPIC -c -o mylib.o mylib.c
$cc -L . -shared -o libmylib.so mylib.o
$cc -L . -o main.out main.c -lmylib
LD_LIBRARY_PATH=. ./main.out

이제 라이브러리 v2에 대해 mylib_mystrict이라는 새로운 필드를 추가하려고한다고 가정합니다 new_field.

다음 old_field과 같이 필드를 추가 한 경우 :

typedef struct {
    int new_field;
    int old_field;
} mylib_mystruct;

라이브러리를 다시 빌드했지만하지 않으면 main.out어설 션이 실패합니다!

그 이유는 다음과 같습니다.

myobject->old_field == 1

int구조체의 첫 번째 에 액세스하려고하는 어셈블리를 생성했으며 현재 new_field는 예상이 아닙니다 old_field.

따라서이 변경으로 인해 ABI가 중단되었습니다.

경우, 그러나, 우리는 추가 new_field한 후 old_field:

typedef struct {
    int old_field;
    int new_field;
} mylib_mystruct;

그런 다음 이전에 생성 된 어셈블리는 여전히 첫 번째 int구조체에 액세스 하고 프로그램은 여전히 ​​작동합니다 .ABI를 안정적으로 유지했기 때문입니다.

다음은 GitHub에 대한이 예제완전 자동화 된 버전입니다 .

이 ABI를 안정적으로 유지하는 또 다른 방법 mylib_mystruct불투명 구조체 로 취급 하고 메서드 도우미를 통해서만 해당 필드에 액세스하는 것입니다. 따라서 ABI를 안정적으로 유지할 수 있지만 더 많은 함수 호출을 수행하면 성능 오버 헤드가 발생합니다.

API 대 ABI

이전 예제에서 new_fieldbefore 를 추가 old_field하면 API가 아닌 ABI 만 중단 했다는 점에 주목하는 것이 흥미 롭습니다 .

이것이 의미하는 바는 main.c프로그램을 라이브러리에 대해 다시 컴파일 했다면 상관없이 작동했을 것입니다.

그러나 예를 들어 함수 서명을 변경 한 경우 API를 중단했을 수도 있습니다.

mylib_mystruct* mylib_init(int old_field, int new_field);

이 경우 main.c컴파일을 완전히 중지합니다.

시맨틱 API vs 프로그래밍 API vs ABI

또한 시맨틱 변경이라는 세 번째 유형으로 API 변경을 분류 할 수 있습니다.

예를 들어 수정 한 경우

myobject->old_field = old_field;

에:

myobject->old_field = old_field + 1;

그러면 API 나 ABI가 모두 깨지지 않았지만 main.c여전히 깨 졌습니다 !

이는 프로그램 적으로 눈에 띄는 측면이 아니라 함수가 수행해야하는 기능에 대한 "인간 설명"을 변경했기 때문입니다.

방금 소프트웨어의 공식적인 검증이 "의미 론적 API"를보다 "프로그램 적으로 검증 가능한 API"로 옮긴다 는 철학적 통찰력을 얻었습니다 .

시맨틱 API vs 프로그래밍 API

또한 시맨틱 변경이라는 세 번째 유형으로 API 변경을 분류 할 수 있습니다.

시맨틱 API는 일반적으로 API가 수행해야하는 것에 대한 자연어 설명이며 일반적으로 API 문서에 포함됩니다.

따라서 프로그램 빌드 자체를 중단하지 않고 시맨틱 API를 중단 할 수 있습니다.

예를 들어 수정 한 경우

myobject->old_field = old_field;

에:

myobject->old_field = old_field + 1;

그러면 프로그래밍 API 나 ABI가 모두 깨지지 않았지만 main.c의미 API가 깨졌습니다.

프로그래밍 방식으로 계약 API를 확인하는 두 가지 방법이 있습니다.

  • 코너 케이스를 테스트하십시오. 쉬운 일이지만 항상 놓칠 수 있습니다.
  • 공식적인 검증 . 수행하기는 더 어렵지만 수학적 정확성을 입증하여 문서와 테스트를 "인간"/ 기계 검증 가능한 방식으로 통합합니다! 공식적인 설명에 버그가없는 한 ;-)

우분투 18.10, GCC 8.2.0에서 테스트되었습니다.


2
귀하의 답변은 API와 ABI의 차이점을 이해하는 데 도움이 될 정도로 상세했습니다. 감사합니다!
Rakshith Ravi

9

( pplication B inary I nterface) 스펙은 운영 체제와 함께, 특정 하드웨어 플랫폼. 그것은 API (넘어 한 단계입니다 pplication P rogram I 운영 시스템에 응용 프로그램에서 호출을 정의 nterface). ABI는 API와 특정 CPU 제품군의 기계 언어를 정의합니다. API는 런타임 호환성을 보장하지 않지만 ABI는 기계 언어 또는 런타임 형식을 정의하므로이를 보장합니다.

여기에 이미지 설명을 입력하십시오

예의


9

Java에서 ABI와 API가 어떻게 다른지 구체적인 예를 들어 보겠습니다.

ABI 호환되지 않는 변경은 String인수 A 인수에서 String...인수 A # m ()을 변경하는 경우 입니다. 이다 ABI 호환되지 않습니다 당신이를 호출 코드를 다시 컴파일해야하기 때문에,하지만 호환 API입니다 당신이 호출자의 코드를 변경하지 않고 다시 컴파일하여 해결할 수있다.

다음은 철자가 예제입니다. 클래스 A의 Java 라이브러리가 있습니다.

// 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을 다시 컴파일하면 문제가 해결되고 모두 다시 작동합니다.


6

적절한 API 를 제공하는 모듈로 프로그램 (소스 코드)을 컴파일 할 수 있습니다 .

귀하의 프로그램 (이진)은 적절한 ABI 를 제공하는 플랫폼에서 실행될 수 있습니다 .

API는 유형 정의, 함수 정의, 매크로, 때로는 라이브러리가 노출해야하는 전역 변수를 제한합니다.

ABI는 프로그램 실행을 위해 "플랫폼"이 제공하는 것을 제한합니다. 나는 그것을 3 단계로 고려하고 싶다 :

  • 프로세서 레벨-명령어 세트, 호출 규칙

  • 커널 수준-시스템 호출 규칙, 특수 파일 경로 규칙 (예 : Linux 의 /proc/sys파일) 등

  • OS 레벨-오브젝트 형식, 런타임 라이브러리 등

이라는 크로스 컴파일러를 고려하십시오 arm-linux-gnueabi-gcc. "arm"은 프로세서 아키텍처를 나타내고 "linux"는 커널을 나타내고 "gnu"는 대상 프로그램이 GNU의 libc를 런타임 라이브러리 arm-linux-androideabi-gcc로 사용하고 Android의 libc 구현을 사용하는 것과 다릅니다 .


1
이것은 그들 사이의 차이점에 대한 매우 간결한 설명이며 매우 독특한 관점입니다.
사주 크

1

API- 개발자 가 라이브러리, OS, 소스 코드의 핵심 호출과 같은 비 프로젝트 기능을 사용 하는 데 사용할 수 Application Programming Interface있는 컴파일 시간 인터페이스입니다.

ABI[소개] -Application Binary InterfaceA는 런타임 머신 코드 에서 컴포넌트 간 통신을 위해 실행하는 동안 프로그램에서 사용 인터페이스 입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.