직전 글의 정적 라이브러리는 응용 프로그램이 사용할 수 있는 많은 관련 함수들을 만드는데 관련된 여러 이슈들을 다뤘다.

그러나 정적 라이브러리는 여전히 몇 가지 중대한 단점이 있다. 정적 라이브러리들은 다른 모든 소프트웨어처럼 관리해야 하고 주기적으로 갱신해야 한다. 만일 응용 프로그래머들이 가장 최근의 라이브러리 버전을 사용하고 싶으면, 이들은 어떤 방식으로든 라이브러리가 변경되었다는 것을 인지해야만 하며, 그 후에 명시적으로 자신들의 프로그램과 갱신된 라이브러리들을 재링크(Re-link) 해야 한다.

또 다른 이슈는 거의 모든 프로그램이 printf와 scanf와 같은 표준 I/O 함수들을 사용한다는 점이다. 런타임에 이 함수들을 위한 코드는 각각 실행되는 프로세스의 텍스트 세그먼트 내에서 복제된다. 50~100개의 프로세스가 돌고 있는 전형적인 시스템에서 이것은 부족한 메모리 시스템 자원의 상당한 낭비가 있다.

정적 라이브러리는 갱신 추적이 어렵고, printf와 같은 시스템에 필수적인 공용 함수를 포함할 경우
메모리의 심각한 낭비가 야기될 수 있다. 

 

공유 라이브러리들은 정적 라이브러리의 단점들을 극복하는 현대의 혁신이라고 한다. 공유 라이브러리는 런타임에 임의의 메모리 주소에서 로드되고 메모리에서 프로그램으로 연결될 수 있는 목적 모듈이다. 이 과정은 동적 링킹이라고 알려져 있으며 동적 링커라고 하는 프로그램에 의해 수행된다.

공유 라이브러리는 또한 공유 객체라고 불리며, Unix 시스템에서 일반적으로 .so 확장자로 나타낸다. 마이크로소프트 운영체제는 많은 공유 라이브러리를 사용하며, 이들은 DLL(Dynamic Link Libraries)라고 부른다.

공유 라이브러리들은 두 가지 다른 방법으로 공유된다. 첫째는 어떤 주어진 파일 시스템에서, 특정 라이브러리에 대해 정확히 한 개의 .so 파일을 갖고 이 .so 파일 내의 코드와 데이터는 이 라이브러리를 참조하는 모든 실행 가능한 목적 파일들에 의해 공유된다. 이는 앞서 참조하는 실행 파일들 내에 라이브러리의 코드와 데이터가 복사되는 정적 라이브러리의 방식과는 정반대이다. 둘째 메모리에 있는 .text 섹션은 서로 다른 실행 중의 프로세스들에 의해 공유될 수 있다. 이 부분은 이후 가상 메모리를 배우면서 다시 살펴보도록 하자.

아래 그림은 예제 프로그램에 의해서 동적 링크 과정을 요약한 것이다. 

벡터 관련 루틴들의 경우 공유 라이브러리 libvector.so를 만들기 위해서 컴파일러 드라이버를 링커로 다음과 같은 특별한 명령어를 사용해서 호출한다. -shared 플래그는 링커가 공유 목적파일을 생성하도록 지시한다.

unix> gcc -shared -o libvector.so addvec.c multvec.c

 

일단 라이브러리를 만들었다면, 이를 다음과 같이 링크할 수 있다.

unix> gcc -o program main.c ./libvector.so

 

이는 실행 가능 목적파일 program을 런타임에 libvector.so와 링크될 수 있는 형태로 생성한다. 기본 아이디어는 링킹의 일부가 실행가능 파일이 생성될 때 정적으로 수행되도록 하고, 프로그램이 로드가 완료된 이후 링킹 작업을 동적으로 완료하는 것이다.

기본 링킹만이 수행이 되는 시점에서, 코드와 데이터 섹션 중 실제로 program 실행파일로 복사된 것은 아무것도 없다는 것이 중요하다. 대신 링커는 일부 재배치와 심볼 테이블 정보를 복사하며, 이들은 libvector.so 내부의 코드와 데이터에 대한 참조가 런타임에 해결되도록 해준다.

로더가 실행 파일 program을 로드하고 실행하면, 실행 가능 목적파일의 로딩 과정에 따라 부분적으로 링크된 실행파일 program을 로딩한다. 다음으로 로더는 program이 .interp 섹션을 포함하는 것을 감지하고 제어를 응용프로그램으로 넘기는 대신 로더는 동적 링커를 로드하고 동적 링커를 실행한다.

동적 링커는 링킹 작업을 다음과 같은 재배치를 수행해서 완료한다.

  • libc.so 의 텍스트와 데이터를 일부 메모리 세그먼트에 재배치한다.
  • 다른 메모리 세그먼트로 libvector.so의 텍스트와 데이터를 재배치한다.
  • libc.so와 libvector.so에서 정의된 심볼로 program의 참조를 재배치한다.

즉, libc.so와 libvector.so의 텍스트와 데이터를 일부 메모리 세그먼트에 재배치하고, 이를 가지고 로더를 거쳐 심볼 참조를 해결하고자 하는 program의 unresolved symbol들을 해결한다.

마지막으로 동적 링커는 제어를 응용 프로그램으로 넘겨준다. 이 지점에서 공유 라이브러리의 위치는 고정되며 프로그램을 실행하는 동안에 바뀌지 않는다.