Dynamic Library Programming
이 포스트는 Apple Documentation Archive 중 Dynamic Library Programming 에서 Dynamic Library Design Guidelines 위주로 정리한 포스트입니다.
Mac OSX의 Dynamic Library 에 대한 포스트입니다.
Introduction
앱은 필요한 많은 기능을 구현하기 위하여 라이브러리를 사용합니다.
그러나 라이브러리에 연결하면 대용량 실행 파일이 생성되고 메모리가 낭비됩니다.
앱의 파일 크기 및 메모리 설치 공간을 줄이는 한 가지 방법은 앱 시작 시 로드되는 코드 크기를 줄이는 것입니다.
What Are Dynamic Libraries?
정적 링커를 사용하여 앱을 라이브러리와 연결하면 앱에서 사용하는 코드가 생성 된 실행 파일에 복사됩니다.
정적 링커는 컴파일 된 소스 코드(object code) 및 라이브러리 코드를 런타임에 메모리에 완전히 로드되는 하나의 실행 파일로 수집합니다. 앱 실행 파일의 일부가 되는 종류의 라이브러리를 정적 라이브러리라고 합니다.
정적 라이브러리는 객체 파일의 모음 또는 아카이브입니다.
Note: Static libraries 는 static archive libraries, static linked shred libraries 로 알려져 있습니다.
앱이 시작되면 앱과 연결된 정적 라이브러리의 코드가 포함 된 앱의 코드가 앱의 주소 공간에 로드됩니다. 많은 정적 라이브러리를 앱에 연결하면 큰 앱 실행 파일이 생성됩니다. 그림 1은 정적 라이브러리에서 구현 된 기능을 사용하는 앱의 메모리 사용량을 보여줍니다. 실행 파일이 큰 응용 프로그램은 시작 시간이 느리고 메모리 공간이 부족합니다.
더 나은 방법은 앱이 실제로 시작시 또는 런타임에 주소 공간에 코드를 로드하는 것입니다. 이러한 유연성을 제공하는 라이브러리 유형을 동적 라이브러리라고 합니다. 동적 라이브러리는 클라이언트 앱에 정적으로 연결되지 않습니다. 실행 파일의 일부가되지 않습니다. 대신, 앱이 시작되거나 실행될 때 동적 라이브러리를 앱에 로드하고 링크 할 수 있습니다.
Note: Dynamic libraries 는 dynamic shared libraries, dynamic linked libraries 로 알려져 있습니다.
그림 2는 동적 라이브러리로 일부 기능을 구현하여 시작 후 앱이 사용하는 메모리를 줄이는 방법을 보여줍니다.
동적 라이브러리를 사용하면 앱 개발자가 앱을 다시 컴파일하지 않고도 클라이언트 앱의 기능을 개선하고 확장 할 수 있습니다.
또 다른 이점은 라이브러리가 로드 될 때 초기화되고 클라이언트 앱이 정상적으로 종료 될 때 종료 작업을 수행 할 수 있다는 것입니다.
정적 라이브러리에는 이 기능이 없습니다.
동적 라이브러리를 개발할 때 개발자가 명심해야 할 한 가지 문제는 라이브러리가 업데이트 될 때 클라이언트 앱과의 호환성을 유지하는 것입니다.
How Dynamic Libraries Are Used
1. 앱이 시작되면 OS X 커널은 앱의 코드와 데이터를 새 프로세스의 주소 공간에 로드합니다.
커널은 또한 동적 로더 (/usr/ lib/dyld)를 프로세스에로드하고 제어를 전달합니다.
2. 동적 로더는 앱의 종속 라이브러리를 로드합니다.
3. 정적 링커는 앱이 링크 될 때 각 종속 라이브러리의 파일 이름을 기록합니다. (이 파일 이름은 동적 라이브러리의 설치 이름입니다.)
4. 동적 로더는 앱의 종속 라이브러리 설치 이름을 사용하여 파일 시스템에서 해당 라이브러리를 찾습니다.
5. 시작시 동적 로더가 앱의 모든 종속 라이브러리를 찾지 못하거나 라이브러리가 앱과 호환되지 않는 경우 실행 프로세스가 중단됩니다.
6. 동적 로더는 시작 프로세스 중에 앱이 실제로 사용하는 정의되지 않은 외부 기호 만 분석합니다.
앱에서 사용할 때까지 다른 심볼은 확인되지 않은 상태로 유지됩니다.
(앱이 실행될 때 동적 로더가 진행하는 프로세스에 대한 자세한 내용은 “Executing Mach-O Files” in Mach-O Programming Topics.을 참조하십시오.)
7. 시작시 동적 라이브러리를 자동으로 로드 할뿐만 아니라 동적 로더는 앱 요청시 런타임에 동적 라이브러리를 로드합니다.
즉, 앱이 실행될 때 동적 라이브러리를 로드 할 필요가 없는 경우 개발자는 앱의 객체 파일을 동적 라이브러리와 연결하지 않고 대신 앱의 일부에만 동적 라이브러리를 로드하도록 선택할 수 있습니다. 이런 방식으로 동적 라이브러리를 사용하면 시작 프로세스 속도가 빨라집니다.
런타임에로드 된 동적 라이브러리를 동적로드 라이브러리라고합니다. 런타임에 라이브러리를 로드하기 위해 앱은 실행중인 플랫폼의 동적 로더와 상호 작용하는 함수를 사용할 수 있습니다.
Note : 클라이언트와 동적 라이브러리의 대상 아키텍처는 동일해야합니다. 그렇지 않으면 동적 로더가 라이브러리를 로드하지 않습니다.
자세한 내용은 ABI 를 참조하세요!
플랫폼마다 동적 로더를 다르게 구현합니다. 동적 코드 로딩 인터페이스가 있을 수 있습니다.
예를 들어 Jorge Acereda와 Peter O'Gorman은 UNIX에서 Linux로 앱을 쉽게 포팅하기 위해 동적 로더 호환성 (DLC) 기능을 개발했습니다.
DLC 기능은 /usr/include/dlfcn.h에 선언되어 있습니다.
- dlopen(3) OS X Developer Tools Manual Page
- dlsym(3) OS X Developer Tools Manual Page
- dladdr(3) OS X Developer Tools Manual Page
- dlclose(3) OS X Developer Tools Manual Page
- dlerror(3) OS X Developer Tools Manual Page
Dynamic Library Design Guidelines
동적 라이브러리는 공통 기능을 그룹화하는 것 외에도 앱의 시작 시간을 줄이는 데 도움이됩니다. 그러나 잘못 설계된 동적 라이브러리는 클라이언트 성능을 저하시킬 수 있습니다.
라이브러리의 기능에 작고 효과적인 인터페이스를 개발하면 다른 라이브러리 나 앱에서 쉽게 사용할 수 있습니다.
Designing an Optimal Dynamic Library
최적의 동적 라이브러리의 속성들입니다.
Focused
라이브러리는 작은 단위의 중점을 두어야 합니다.
(이미지 처리도 할 수 있고 네트워크 통신도 할 수 있고 여러 기능을 포함하면 안된다는 것)
Easy to use
interface 가 간단해야 합니다.
(Capsulization 해야 합니다)
Capsulization: ATM 를 예를 들 수 있다.
ATM 는 내부적으로 보안, Transaction, 타 은행 연계 등 복잡한 단계를 수행하지만
사용자에게는 단순히 출금의 Interface 만 노출한다.
Capsulization 하지 않으면 내부의 복잡성이 높아지고 사용자의 지식 수준이 높아지게 된다.
Easy to maintain
라이브러리 개발자의 내부 inteface와 Client 가 사용하는 inteface를 명확하게 분리함으로써
클라이언트에 미치는 영향을 최소화하면서 라이브러리의 내부 작업을 크게 변경할 수 있습니다.
Managing Client Compatibility With Dependent Libraries
동적 라이브러리를 디자인 할 때 지속적인 유지 관리를 고려해야 해야 합니다.
새로운 기능을 구현하거나 문제를 해결하기 위해 라이브러리를 변경되었을 때 기존에 사용하는 클라이언트에 대해 고려해야 합니다.
동적 라이브러리의 클라이언트는 두 가지 방식으로 사용할 수 있습니다.
1. 종속 라이브러리 (dependent library)
2. 런타임 로드 라이브러리 (runtime-loaded library)
종속 라이브러리 (dependent library)
종속 라이브러리는 클라이언트가로드 프로세스의 일부로 로드되는 동일한 프로세스에 로드됩니다.
예를 들어 앱이 시작되면 기본 기능이 실행되기 전에 종속 라이브러리가 시작 프로세스의 일부로 로드됩니다.
동적 라이브러리가 실행중인 프로세스에 로드되면 제어가 라이브러리를 연 루틴으로 전달되기 전에 종속 라이브러리가 프로세스에 로드됩니다.
클라이언트는 항상 종속 라이브러리와 호환되어야합니다. 그렇지 않으면 앱이 시작되지 않거나 라이브러리가 로드되지 않습니다.
런타임 로드 라이브러리 (runtime-loaded library)
런타임 로드 라이브러리는 클라이언트가 dlopen(3) OS X Developer Tools Manual Page 함수으로 여는 동적 라이브러리입니다.
클라이언트가 로드 될 때 동적 로더는 라이브러리를 열지 않습니다. 클라이언트는 내보내는 심볼을 사용하려고 할 때 런타임로드 라이브러리를 엽니다.
클라이언트는 런타임로드 라이브러리의 심볼에 대한 정의되지 않은 외부 참조가 없습니다.
클라이언트는 dlsym(3) OS X Developer Tools Manual Page 함수를 호출하여 런타임로드 라이브러리에서 필요한 모든 심볼의 주소를 얻습니다.
Defining Client Compatibility
클라이언트가 연결된 라이브러리보다 이전 또는 이후 버전의 종속 라이브러리를 사용할 수있는 상태를 Client Compatibility 이라고합니다.
minor: 클라이언트에게 알려지지 않은 심볼 추가가 포함됩니다.
major: 심볼 제거, 심볼 size 또는 visibility 변경, 함수 변경 등이 포함됩니다.
라이브러리가 클라이언트에게 노출되는 모든 심볼은 ABI (app binary interface)를 구성합니다.
ABI 에 대한 자세한 설명은 여기에서 확인할 수 있다.
왼쪽 그림 설명
Draw 1.0 는 Client 1.0 과 완벽하게 호환된다.
Draw 1.1 draw_polygon() 함수가 추가되어도 버전 호환성은 유지된다.
오른쪽 그림 설명
Draw 2.0 에서 draw_polygon() 삭제되었기 때문에 버전 호환성이 유지되지 않는다.
참고 : 라이브러리의 헤더 파일에는 라이브러리의 클라이언트가 실제로 사용해야하는 기호 만 포함되어야합니다. 클라이언트가 지정한 기호 이외의 기호를 사용하는 경우 새 버전 또는 이전 버전의 라이브러리와 제품의 호환성이 제한됩니다.
Specifying Your Library’s Interface
클라이언트가 라이브러리를 사용하면서 아래 3가지는 앱의 성능에 영향을줍니다.
Ease of use
이해할 수있는 pulbic symbol는 정의 된 모든 symbol를 내보내는 것보다 사용하기가 훨씬 쉽습니다.
Ease of maintenance
클라이언트 진입 점이 거의 없기 때문에 소수의 pubic symbol 모음과 적절한 private symbol 모음이 있는 라이브러리는 유지 관리가 훨씬 쉽습니다.
Performance
라이브러리에 노출시키는 Symbol이 적을수록 동적 로더가 더 빨리 로드합니다.
Deciding What Symbols to Export
1. 클라이언트에게 노출되는 symbols 수를 최대한 줄여야 합니다.
- 라이브러리를 쉽게 사용하고 유지 관리 할 수 있습니다.
2. 전역 변수는 절대 export 하지 않아야 합니다.
- 전역 변수에 대한 제어되지 않은 액세스를 제공하면 클라이언트가 해당 변수에 부적절한 값을 할당하여 발생하는 문제에 라이브러리가 열려있게됩니다.
3. export 함수로 구현 된 기능이 필요한 경우 내부 함수를 변경하거나 Wrapper 함수 추가하는 것을 고려해야 합니다.
참조: