목차
들어가기에 앞서, 본 글은 [운영체제 구조 :: System call(시스템 콜)과 API/ABI] 포스팅 도중 Stackoverflow의 게시글 중 ABI의 이해를 돕고 안정적인 ABI 에 대한 코드에 대한 리뷰이다.
출처 및 이전글 확인
2022.07.29 - [OS :: 운영체제] - 운영체제 구조 :: System call(시스템 콜)과 API/ABI
공유 라이브러리와 ABI 관계
먼저 공유 라이브러리에 대해서 알아야하는데 이러한 개념이 생기게 된 배경에는 printf 와 같은 사용빈도가 높은 함수들을 매번 프로그램을 실행시킬때마다 매번 컴파일을 하고 실행을 하게되면 효율적이지 않기 때문에 미리 탑재를 해두고 컴파일을 할 시점에 라이브러리를 로드를 하는 방식으로 작동되는 라이브러리를 공유 라이브러리라고 한다.
따라서 이러한 라이브러리들이 매번 컴파일 되는 것을 막기 위해서는 로드를 할 당시에 안정적으로 유지된 ABI가 존재해야 중단이 되지 않고 작동이 된다. 그럼 안정적인 ABI는 무슨의미 일까?
안정적인 ABI
반환 유형 및 인자의 순서 등을 선언하는 함수 인터페이스, 데이터 유형 또는
데이터 구조의 정의, 정의된 상수 등을 변경하지 않는 ABI
쉽게 말해 들어오는 프로그램들을 버전이나 종류에 상관없이 제자리에 할당해줘야 한다 라고 생각하면 쉽다.
그럼 Stackoverflow의 포스팅 중에 ABI의 안정성이 훼손된 사례를 모방하여 표현한 코드가 있는데(실제로 훼손되었다기 보다는 예시를 들은것에 가깝다) 이해에 도움이 되었어서 포스팅을 해보았다.
<mylib.c>
#ifndef MYLIB_H
#define MYLIB_H
typedef struct {
int old_field;
} mylib_mystruct;
mylib_mystruct* mylib_init(int old_field);
#endif
먼저 mylib.h 에 mylib_mystruct 이라는 별칭을 가진 구조체를 만들어서 mylib_init 구조체에 int 형식의 old_field 변수를 저장하게 하였다.
<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_init에 old_field라는 입력인자를 myobject에 할당을 해주고 저장을 하였고 myobject를 리턴값으로 가지게 하였다.
<main.c>
#include <assert.h>
#include <stdlib.h>
#include "mylib.h"
int main(void) {
mylib_mystruct *myobject = mylib_init(1);
assert(myobject->old_field == 1);
free(myobject);
return EXIT_SUCCESS;
}
main 문에는 myobject에 <mylib.c>에서 선언한 mylib_init에 정수 1을 부여하고
assert() 함수를 이용하여 (assert()함수 포스팅예정)
myobject의 old_field에 1이 할당되어있을시에는 pass, 1이 할당되지 않을시에는 assert error로 알림이 오게된다.
pass의 경우에는 myobject의 할당을 해제하고 EXIT_SUCCESS 을 출력하도록 한다.
컴파일을 하게되면
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
정상적으로 컴파일이 된다.
이제부터 라이브러리가 수정되었다는 상황을 연출하여 version 2 라이브러리를 대신 위에 삽입하게 되면
---- version2 수정내용 ----
typedef struct {
int new_field;
int old_field;
} mylib_mystruct;
구조체에 기존의 int형식의 old_field 뿐만 아니라 int형식의 new_field를 기존의 값보다 먼저 삽입하게 된다. 따라서 main 문의 assert에 의하여 오류를 출력하게된다.
(int로 저장되는 첫 구조체가 old_field 이어야하는데 new_field 에 저장되기 때문이다.)
따라서 여기서 ABI의 기능중 하나인 클래스가 객체가 배치되는 방식을 관리하는 기능이 제대로 작동하지 않으면
(라이브러리의 구조체에 추가적인 인수가 들어갔을 시에 이를 감지하여 제대로된 위치에 배치하지 못하면)
이를 안정적인 ABI 라고 하지 않는다. 이의 반대의 경우에는 안정적인 ABI라고 할수 있다는 뜻이다.
모든 변화를 예상하고 상정한 ABI가 존재하면 좋지만 ABI는 운영체제를 개발한 소수의 전문가가 관리하며 API를 개발하는 개발자들이 정해진 ABI에 맞추어 개발을 하는 것이 대부분이다.
따라서 우리는 위의 API를 ABI에 맞게 디버깅을 해야한다.
Semantic vs Programmatic
이러한 문제를 해결하는 코드의 스타일에 따라서 Semantic API(의미론적인 API), Programmatic API(프로그래밍적인 API) 로 나뉘는데 먼저 Semantic API라는 뜻은 코드 내에 의미를 내포하고 있는것을 중점으로 한 코드임을 뜻하고 태그나 변수 명 등 프로그래밍 방식이 코드를 읽었을때 무엇을 목적으로하고 내포하는지 나타내고 있는 코드스타일을 뜻하고 Programmatic API은 프로그램의 원활한 작동을 중점에 두고 짠 코드 스타일을 뜻한다.
예로 들어서 Programmatic 방식으로
myobject->old_field = old_field;
아래와 같은 방식으로 수정하게 되면
myobject->old_field = old_field + 1;
지금 라이브러리가 가진 문제는 해결이 가능하다. 즉 프로그램 API와 ABI 가 원활하게 작동하지만 의미론적인 의미는 내재하지 않고 있다.
'CS > Operating System' 카테고리의 다른 글
운영체제 구조 :: 운영체제의 생성과 부팅 (0) | 2022.08.03 |
---|---|
운영체제 구조 :: 기술 구조 (2) | 2022.08.02 |
운영체제 구조 :: System call(시스템 콜)과 API/ABI (0) | 2022.07.29 |
운영체제 구조 :: 사용자와의 인터페이스 (0) | 2022.07.27 |
운영체제 구조 :: 서비스 (0) | 2022.07.25 |