수안이의 컴퓨터 연구실

  • Mainpage
  • About Me
  • Tags
  • Metapage
  • Notice
  • Location
  • Keywords
  • Guestbook
  • Admin
  • Write an Article
  • Total | 1694961
  • Today | 706
  • Yesterday | 606

2 Articles, Search for '공유 라이브러리'

  1. 2007/05/17 시간 중심의 코드 성능 향상을 위한 팁
  2. 2007/05/10 공유 라이브러리 이해하기
Programming/MFC2007/05/17 17:38

시간 중심의 코드 성능 향상을 위한 팁

Visual C++ 개념: C/C++ 프로그램 빌드

시간 중심의 코드 성능 향상을 위한 팁
빠른 코드를 작성하려면 해당 응용 프로그램의 모든 측면을 이해하고 시스템과 상호 작용하는 방법을 이해해야 합니다. 이 항목에서는 시간 중심의 코드 부분 성능을 향상시킬 수 있는 코딩 기법을 설명합니다.

요컨대, 시간 중심의 코드 성능을 향상시키려면 다음 사항을 알고 있어야 합니다.

프로그램에서 속도가 빨라야 하는 부분
코드의 크기와 속도
새로운 기능 사용에 따른 비용
작업 수행에 필요한 최소 작업
코드의 성능에 대한 정보는 성능 모니터(perfmon.exe)를 사용하여 얻을 수 있습니다.

캐시 적종 및 페이지 폴트
정렬 및 검색
MFC 및 클래스 라이브러리
공유 라이브러리
힙
스레드
작은 작업 집합
캐시 누락 및 페이지 폴트

프로그램 명령어 및 데이터를 찾아 보조 저장 장치로 이동하는 페이지 폴트뿐만 아니라 내부 및 외부 캐시에 대한 누락된 캐시 적중은 프로그램 성능을 저하시킵니다.

CPU 캐시 적중은 10–20 클럭 주기가 소요될 수 있습니다. 외장 캐시 적중은 20–40 클럭 주기가 소요될 수 있습니다. 프로세서가 초 당 5억개의 명령어를 처리하고 페이지 폴트 1회 당 2밀리초의 시간이 걸린다고 가정할 때 페이지 폴트는 100만 클럭 주기가 소요될 수 있습니다. 따라서 누락된 캐시 적중 및 페이지 폴트 횟수를 줄이는 코드를 작성하면 프로그램 실행 성능이 향상됩니다.

프로그램의 속도가 느린 이유 중 하나는 필요 이상으로 페이지 폴트가 많거나 캐시가 누락되기 때문입니다. 이러한 문제를 방지하려면 참조 집약성이 좋은 데이터 구조를 사용하는 것이 중요합니다. 참조 집약성이란 관련된 항목을 함께 두어 참조하기 쉽도록 하는 것을 의미합니다. 보기 좋은 데이터 구조라도 참조 집약성이 미약하면 속도가 느릴 수 있고 그 반대인 경우도 있습니다. 두 가지 예를 들겠습니다.

항목을 검색하거나 목록의 끝까지 이동할 때 건너 뛴 각 연결마다 캐시가 누락되거나 페이지 폴트가 발생할 수 있기 때문에 동적으로 할당된 연결 리스트는 프로그램 성능을 저하시킬 수 있습니다. 간단한 배열을 기초로 목록을 구현하면 캐싱은 더 좋아지고 페이지 폴트는 줄어들기 때문에 실제로 속도는 더 빨라질 수 있습니다. 배열의 크기가 커지기는 더 어려울 것이라는 사실을 감안하더라도 속도는 여전히 더 빠를 것입니다.
동적으로 할당된 연결 리스트를 사용하는 해시 테이블은 성능을 저하시킬 수 있습니다. 확장되는 경우 동적으로 할당된 연결 리스트를 사용하여 내용을 저장하는 해시 테이블의 성능은 그보다 상당히 더 떨어질 것입니다. 실제로 최종 분석에서는 상황에 따라 배열을 통한 단순한 선행 검색이 실제로 더 빠를 수 있습니다. "폐쇄형 해시"라는 배열 기반 해시 테이블은 종종 간과되긴 하지만, 성능이 뛰어난 구현 방법입니다.
정렬 및 검색
정렬은 일반적인 많은 작업에 비해 시간이 많이 소모됩니다. 불필요한 속도 저하를 방지하는 가장 좋은 방법은, 작업을 빨리 수행해야 하는 시간에는 정렬을 수행하지 않는 것입니다. 다음과 같은 방법을 사용할 수 있습니다.

작업을 빨리 수행해야 하는 시간이 지난 후에 정렬을 수행합니다.
작업을 빨리 수행해야 하는 시간 이전에 미리 데이터를 정렬합니다.
반드시 정렬해야 하는 데이터 부분만 정렬합니다.
경우에 따라서는 목록을 정렬된 순서로 빌드할 수 있습니다. 정렬된 순서로 데이터를 삽입해야 할 경우 참조 집약성이 떨어지는 더 복잡한 데이터 구조를 사용하게 되면 캐시 누락과 페이지 폴트가 발생할 수 있으므로 주의해야 합니다. 모든 경우에 적용할 수 있는 방법은 없습니다. 여러 방법을 사용해 보고 차이점을 평가해 보십시오.

다음은 정렬에 관한 몇 가지 일반적인 팁입니다.

스톡 정렬을 사용하여 버그를 최소화합니다.
정렬의 복잡성을 줄이기 위한 사전 작업을 하면 도움이 됩니다. 데이터를 한 번 전달하여 비교를 단순화하고 정렬을 O(n log n)에서 O(n)으로 줄이면 결과가 확실하게 미리 나타납니다.
실행하려고 하는 데이터와 정렬 알고리즘의 참조 집약성을 고려합니다.
검색은 정렬보다 사용할 수 있는 방법이 적습니다. 검색에서 시간이 중요한 경우에는 이진 검색이나 해시 테이블 조회가 가장 좋은 방법이지만 정렬의 경우와 마찬가지로 집약성을 염두에 두어야 합니다. 작은 배열을 통한 선형 검색은 페이지 폴트나 캐시 누락을 초래하는 포인터가 많은 데이터 구조를 통한 이진 검색보다 더 빠를 수 있습니다.

MFC 및 클래스 라이브러리
MFC(Microsoft Foundation Classes)를 사용하면 코드를 작성하는 일이 매우 간단해질 수 있습니다. 시간 중심 코드를 작성할 때는 일부 클래스에 내재하는 오버헤드를 주의해야 합니다. 시간 중심 코드에서 사용하는 MFC 코드를 검토하여 성능 요구 사항에 맞는지 확인하십시오. 다음은 반드시 알고 있어야 하는 MFC 클래스 및 함수 목록입니다.

CString MFC는 C 런타임 라이브러리를 호출하여 CString에 메모리를 동적으로 할당합니다. 일반적으로 Cstring은 동적으로 할당된 다른 문자열만큼 효율적이며, 동적으로 할당된 모든 문자열과 마찬가지로 동적 할당 및 해제에 따르는 오버헤드를 지닙니다. 스택에서의 간단한 char 배열을 사용하면 같은 작업을 더 빨리 수행할 수 있습니다. 상수 문자열을 저장하려면 Cstring을 사용하지 말고 const char *를 대신 사용합니다. CString 개체를 사용하여 수행하는 모든 작업에는 약간의 오버헤드가 따릅니다. 런타임 라이브러리인 문자열 함수를 사용하는 것이 더 빠를 수 있습니다.
CArray CArray는 일반 배열과는 달리 융통성을 제공하지만 프로그램에 융통성이 필요하지 않을 수도 있습니다. 배열의 구체적인 한계를 알면 대신 전역 고정 배열을 사용할 수 있습니다. Carray를 사용하는 경우에는 CArray::SetSize를 사용하여 크기를 설정하고, 재할당이 필요할 경우 Carray를 몇 개 요소만큼씩 증가시킬지를 지정하십시오. 이 값을 지정하지 않고 요소를 추가하면 배열이 자주 재할당되고 복사될 수 있는데, 이는 비효율적이며 메모리가 단편화될 수 있습니다. 또한 항목을 배열에 삽입하면 Carray는 다음 항목을 메모리로 이동시켜 배열의 크기가 커질 수 있습니다. 이로 인해 캐시 누락과 페이지 폴트가 발생할 수 있습니다. MFC에서 사용하는 코드를 검토하면 시나리오와 관려된 더 구체적인 코드를 작성하여 성능을 향상시킬 수 있습니다. 예를 들어, Carray는 템플릿이므로 특정 형식에 Carray 특수화를 제공할 수 있습니다.
CList CList는 이중 연결 리스트이므로 리스트의 맨 위, 맨 아래 및 알려진 위치(POSITION)에서 요소를 삽입하는 작업의 속도는 매우 빠릅니다. 요소를 값 또는 인덱스로 조회하려면 순차 검색이 필요하지만 리스트가 길면 오래 걸릴 수 있습니다. 코드에 이중 연결 리스트가 필요하지 않은 경우에는 Clist 사용을 다시 고려할 수 있습니다. 단일 연결 리스트를 사용하면 모든 작업에 대한 추가 포인터와 해당 포인터에 대한 메모리를 업데이트하는 오버헤드가 줄어듭니다. 추가로 사용되는 메모리 양은 얼마 안되지만 캐시 누락이나 페이지 폴트의 또 다른 원인이 됩니다.
IsKindOf 이 함수는 많은 호출을 생성하고 서로 다른 데이터 영역의 많은 메모리에 액세스할 수 있으므로 참조 집약성이 떨어지게 됩니다. 디버그 빌드(예: ASSERT 호출)에는 유용하지만 릴리스 빌드에서는 사용하지 않는 것이 좋습니다.
PreTranslateMessage 창의 특정 트리에 여러 키보드 액셀러레이터 키가 필요하거나 메시지 펌프에 메시지 처리를 삽입해야 할 때 PreTranslateMessage를 사용합니다. PreTranslateMessage는 MFC 디스패치 메시지를 변경합니다. PreTranslateMessage를 재정의하는 경우에는 필요한 수준에서만 재정의하십시오. 예를 들어, 특정 뷰의 하위 뷰로 전달되는 메시지에만 관심이 있다면 CMainFrame::PreTranslateMessage를 재정의할 필요가 없습니다. 대신 뷰 클래스의 PreTranslateMessage를 재정의하십시오.
창으로 보낸 메시지를 처리하려면 PreTranslateMessage를 사용하여 정상적인 디스패치 경로를 우회하지 마십시오. 정상적인 디스패치 경로를 우회하려면 창 프로시저와 MFC 메시지 맵을 사용하십시오.

OnIdle WM_KEYDOWN과 WM_KEYUP 이벤트 사이처럼 예상치 않은 시간에 유휴 이벤트가 발생할 수 있습니다. 코드를 트리거할 때는 타이머를 사용하는 것이 더 효율적인 방법일 수 있습니다. 거짓 메시지를 작성하거나 OnIdle 재정의에서 항상 TRUE를 반환하여 OnIdle이 강제로 반복 호출되지 않도록 하십시오. 이렇게 하면 스레드가 대기할 수 없게 됩니다. 이 경우에도 타이머나 별도의 스레드를 사용하는 것이 더 효율적일 수 있습니다.
공유 라이브러리
코드를 다시 사용하는 것이 바람직합니다. 그러나 다른 사람이 작성한 코드를 사용하려는 경우, 성능이 중요한 작업에 어떤 영향을 미치는지 정확히 알아야 합니다. 가장 좋은 방법은 소스 코드를 단계별로 실행하거나 PView 또는 성능 모니터 등의 도구를 사용하여 측정하는 것입니다.

힙
여러 개의 힙을 사용할 때는 신중해야 합니다. HeapCreate 및 HeapAlloc으로 만들어진 힙을 사용하여 관련된 할당 집합들을 관리한 다음 삭제할 수 있습니다. 너무 많은 메모리를 커밋하지 않도록 하십시오. 여러 힙을 사용하는 경우 처음에 커밋되는 메모리 양에 특히 주의하십시오.

여러 힙을 사용하는 대신 코드와 기본 힙 사이의 인터페이스에 대해 도우미 함수를 사용할 수 있습니다. 도우미 함수를 사용하면 응용 프로그램의 성능을 향상시킬 수 있는 사용자 지정 할당 전략을 쉽게 작성할 수 있습니다. 예를 들어, 작은 할당을 자주 수행하는 경우 기본 힙의 한 부분으로 할당을 지역화할 수 있습니다. 큰 블록의 메모리를 할당한 다음 도우미 함수를 사용하여 그 블록에서 다시 할당할 수 있습니다. 이 방법을 사용하면 기본 힙에서 할당이 이루어지므로 사용되지 않은 메모리를 지니는 추가 힙이 없어집니다.

그러나, 기본 힙을 사용하면 참조 집약성이 떨어지는 경우도 있습니다. 프로세스 뷰어, Spy++ 또는 성능 모니터를 사용하여 힙 간에 이동하는 개체의 영향을 측정하십시오.

힙을 측정하면 힙에 대한 모든 할당을 설명할 수 있습니다. C 런타임 디버그 힙 루틴을 검사점으로 사용하고 힙을 덤프합니다. Microsoft Excel 등의 스프레드시트 프로그램으로 결과를 읽어 오고 피벗 테이블을 사용하여 결과를 볼 수 있습니다. 할당의 전체 수, 크기 및 분포를 기록하고 작업 집합 크기와 비교하십시오. 관련 크기의 개체 클러스터링도 검토하십시오.

성능 카운터를 사용하여 메모리 사용을 모니터링할 수도 있습니다.

스레드
백그라운드 작업의 경우 이벤트 유휴 상태를 효율적으로 처리하는 것이 스레드를 사용하는 것보다 더 빠를 수 있습니다. 단일 스레드 프로그램에서는 참조 집약성을 이해하기가 더 쉽습니다.

가장 좋은 방법은 차단한 운영 체제 알림이 백그라운드 작업의 루트에 있는 경우에만 스레드를 사용하는 것입니다. 이러한 경우 이벤트에 대한 주 스레드를 차단하는 것이 비실용적이기 때문에 스레드를 사용하는 것이 가장 좋은 해결 방법입니다.

또한 스레드를 사용할 때는 통신 문제도 있습니다. 메시지 목록을 사용하거나 공유 메모리를 할당하여 사용하여 스레드 사이의 통신 연결을 관리해야 합니다. 통신 연결을 관리하려면 일반적으로 경쟁 상태와 교착 상태 문제를 방지하기 위해 동기화가 필요합니다. 이러한 복잡성 때문에 버그와 성능 문제가 발생하기 쉽습니다.

자세한 내용은 유휴 루프 처리 및 다중 스레딩을 참조하십시오.

작은 작업 집합
작업 집합의 크기가 작아질수록 참조 집약성은 더 좋아지고, 페이지 폴트는 줄어들며, 캐시 적중은 더 높아집니다. 프로세스 작업 집합은 운영 체제에서 참조 집약성을 측정하기 위해 직접 제공하는 가장 가까운 메트릭입니다.

작업 집합의 상한 및 하한을 설정하려면 SetProcessWorkingSetSize를 사용합니다.
작업 집합의 상한 및 하한을 구하려면 GetProcessWorkingSetSize를 사용합니다.
작업 집합의 크기를 보려면 Spy++를 사용합니다.

출처 : http://msdn.microsoft.com/library/kor/d ··· code.asp
"MFC" 카테고리의 다른 글
  • 쓰레드 풀 (Thread Pooling) 작성 (1)2007/05/17
  • 시간 중심의 코드 성능 향상을 위한 팁 (0)2007/05/17
  • 상태바위에 ProgressBar 올리기 #2 (0)2007/05/17
  • 상태바위에 ProgressBar 올리기 #1 (0)2007/05/17
  • 다이얼로그상의 특정 컨트롤의 색상 변경 (0)2007/05/17
2007/05/17 17:38 2007/05/17 17:38
Posted by webdizen
Tags MFC, 검색, 공유 라이브러리, 쓰레드, 정렬, 캐시 누락, 캐시 적종, 코드 성능 향상, 페이지 폴트, 힙
No Trackback No Comment

Trackback URL : http://www.webdizen.net/blog/trackback/2983

Leave your greetings.

[로그인][오픈아이디란?]

Unix & Linux/Develop2007/05/10 09:17

공유 라이브러리 이해하기

공유 라이브러리 이해하기
난이도 : 초급


Peter Seebach
프리랜스 작가
2005년 1 월 11 일

공유 라이브러리는 애플리케이션들이 사용하는 라이브러리에 업그레이드를 적용할 때 버전 숫자를 사용한다. 동시에 오래된 애플리케이션과의 호환성도 유지한다. 이 글에서는 일반적인 리눅스 시스템에 /usr/lib로의 심볼릭 링크가 많은 이유를 설명하겠다.
공유 라이브러리들은 현대적인 유닉스 시스템상에서 공간과 리소스를 효율적으로 사용하기 위한 기본 컴포넌트들이다. SUSE 9.1 시스템 상의 C 라이브러리는 약 1.3 MB를 차지하고 있다. /usr/bin(나의 경우는2,569 이다!)에서 모든 프로그램을 위한 라이브러리는 2 기가 바이트 공간을 차지한다.

물론 이 숫자는 과장된 것이다. 정적으로 링크 된 프로그램들은 자신들이 사용하고 있는 라리브러리의 일부하고만 결합한다. 그럼에도 불구하고, 이 모든 중복된 printf() 카피들과 묶인 공간들은 시스템을 팽창시킬 것이다.

공유 라이브러리를 사용하면 디스크 공간 뿐 아니라 메모리를 절약할 수 있다. 커널은 메모리에 한 카피의 공유 메모리를 보유하면서 이를 다중의 애플리케이션들과 공유한다. 따라서 디스크 상에 한 카피의 printf()를 가지고 있을 뿐만 아니라 메모리에도 한 개를 갖고 있는 것이다. 이것은 퍼포먼스에 뚜렷한 영향력을 갖고 있다.

이 글에서 공유 라이브러리에 사용되는 기본 기술을 검토하고 공유 라이브러리 버저닝(versioning)을 활용하여 과거의 단순한 공유 라이브러리 구현 때문에 겪었던 호환성 악몽을 어떻게 극복하는지 알게 될 것이다. 우선 공유 라이브러리가 어떻게 작동하는지 보자.

공유 라이브러리 작동 방법
개념은 이해하기 쉽다. 당신은 라이브러리를 갖고 있고 그 라이브러리를 공유한다. 하지만 프로그램이 printf()를 호출하려 할 때 실제로 어떤 일이 벌어지겠는가? 실제 작동 방식은 약간 더 복잡하다.

동적 링크 시스템 보다 정적 링크 시스템에서 프로세스가 더 간단하다. 정적 링크 시스템에서, 생성된 코드는 함수에 대한 레퍼런스를 처리한다. 링커는 이 레퍼런스를 함수를 로딩했던 실제 주소로 교체한다. 따라서 결과 바이너리 코드는 올바른 주소를 적재적소에 갖게 되는 것이다. 그런 다음, 코드가 실행되면 관련 주소로 점프(jump)한다. 프로그램의 어떤 지점에서 실제로 레퍼런스 되는 객체들에서만 링크할 수 있기 때문에 관리가 간단하다.

하지만 대부분의 공유 라이브러리들은 동적으로 링크 된다. 이것이 의미하는 바는 크다. 함수가 호출될 때 실제로 어떤 주소에 그 함수가 있게 될지 미리 예견할 수 없다. (BSD/OS에 있는 것 처럼 정적으로 링크 된 공유 라이브러리 스키마가 있었지만 이 글에서는 다루지 않겠다.)

동적 링커(dynamic linker)는 링크 된 각 함수에 대해 상당량의 작업을 수행할 수 있기 때문에 대부분의 링커들은 게으르다. 이들은 함수가 호출될 때 그 작업을 실제로 끝낸다. C 라이브러리에는 천 개 이상의 외부에 보여지는 심볼들과, 거의 삼천 개 이상의 로컬 심볼 들이 있다. 많은 시간을 절약할 수 있다.

작동 마법은 Procedure Linkage Table (PLT)라고 하는 데이터 청크이다. 이것은 프로그램이 호출하는 모든 함수를 나열하고 있는 프로그램의 테이블이다. 프로그램이 시작되면 PLT는 각 함수용 코드를 포함하여 함수를 로딩했던 주소에 대한 런타임 링커를 쿼리한다. 그런 다음 테이블의 모든 엔트리를 채우고 그곳으로 옮겨간다. 각 함수가 호출될 때 PLT의 엔트리는 로딩된 함수로 단순히 직접 점프한다.

하지만 여분의 인다이렉션 레이어를 남겨둔다는 것을 기억해야 한다. 각 함수 호출은 점프를 통해 테이블로 바뀐다.

호환성은 관계만을 위한 것은 아니다!
링크하는 것으로 완료한 라이브러리는 이것이 호출하는 코드와 호환되도록 해야 한다. 정적으로 링크 된 실행파일이 있다면 변경될 것이 없다. 동적 링크라면 보장 못한다.

새로운 버전의 라이브러리가 나왔다면? 특히 새로운 버전이 기존 함수의 호출 순서를 변경했다면?

버전 숫자가 구원자다. (공유 라이브러리는 버전을 갖게 될 것이므로). 프로그램이 라이브러리로 링크 되면 버전 숫자를 갖게 된다. 동적 링커는 매칭(matching) 버전 숫자를 검사할 수 있다. 라이브러리가 변경되면 버전 숫자는 맞지 않을 것이고 프로그램은 새로운 버전의 라이브러리로 링크 되지 않을 것이다.

하지만 동적 링크의 강력한 장점들 중 하나는 버그를 픽스하는데 있다. 라이브러리에서 버그를 픽스하고 그 픽스를 활용하기 위해 많은 프로그램들을 재컴파일 할 필요가 없다면 참 좋은 것이다. 가끔은 새 버전으로 링크해야 할 때도 있다.

불행히도 새로운 버전으로 링크해야 하는 경우도 있고, 어떤 경우는 오래된 버전을 구수해야 한다. 해결책은 있다. 두 종류의 버전 숫자이다:

메이저 넘버(major number)는 라이브러리 버전들 간 잠재적 비호환성을 나타낸다.
마이너 넘버(minor number)는 버그 픽스들만을 나타낸다.
대부분의 경우 같은 메이저 넘버와 더 높은 마이너 넘버를 가진 라이브러리를 로딩하는 것이 안전하다; 높은 메이저 넘버를 가진 라이브러리를 로딩하는 것은 안전하지 못하다.

사용자들(프로그래머들)이 라이브러리 넘버와 업데이트를 트래킹하지 않도록 하려면 시스템은 많은 심볼릭 링크들이 있어야 한다. 일반적일 패턴은 다음과 같다.

libexample.so

는 다음으로 링크 된다.

libexample.so.N

이 시스템에서 N은 가장 높은 메이저 버전 숫자이다.

지원되는 모든 메이저 버전 넘버들은,

libexample.so.N

다음으로 링크 된다.

libexample.so.N.M

M은 가장 큰 마이너 버전 넘버이다.

따라서 -lexample을 링커에 지정하면 최근 버전에 대한 심볼릭 링크인 libexample.so를 찾는다. 한편, 기존 프로그램이 로딩되면 libexample.so.N을 로딩할 것이다. N은 원래 링크 되었던 버전이다.

디버그 하기 위해서는 우선 컴파일 방법을 알아야 한다!
공유 라이브러리로 문제들을 디버깅하기 위해서는 이들이 어떻게 컴파일 되는지를 알아두는 것도 유용하다.

전통적인 정적 라이브러리에서, 생성된 코드는 일반적으로 .a로 끝나는 이름을 가진 라이브러리 파일로 바인딩 되고 링커로 전달된다. 동적 라이브러리에서, 라이브러리 파일의 이름은 일반적으로 .so로 끝난다. 파일 구조는 약간 다르다.

일반적인 정적 라이브러리는 ar 유틸리티에서 만들어진 포맷에 있다. 이는 기본적으로 매우 단순한 아카이브 프로그램으로서 tar 와 비슷하지만 더 단순하다. 반면 공유 라이브러리들은 일반적으로 보다 복잡한 파일 포맷으로 저장된다.

현대적인 리눅스 시스템에서, 이는 일반적으로 ELF 바이너리 포맷((Executable and Linkable Format)을 의미한다. ELF에서, 각 파일은 하나의 ELF 헤더, 0 또는 세그먼트 그리고 0 또는 몇몇 섹션 등으로 구성된다. 세그먼트는 파일의 런타임 실행에 필요한 정보를 포함하고 있고, 섹션에는 링크와 재배치를 위한 중요한 데이터가 포함된다. 전체 파일에서 각 바이트는 한번에 단 한 섹션을 차지한다. 하지만 섹션에 들어가지 않은 고아 바이트가 있을 수 있다. 일반적으로 유닉스 실행파일에서 한 개 이상의 섹션들은 하나의 세그먼트에 둘러 쌓인다.

ELF 포맷은 애플리케이션과 라이브러리를 위한 스팩을 갖고 있다. 이 라이브러리 포맷은 객체 모듈의 아카이브 보다 훨씬 더 복잡하다.

링커는 심볼에 대한 레퍼런스들을 통해 소팅하면서 그들이 어떤 라이브러리들을 발견했는지를 기록한다. 정적 라이브러리에서 온 심볼들은 최종 실행파일에 추가된다; 공유 라이브러리에서 온 심볼들은 PLT에 놓여지고 PLT에 대한 레퍼런스들이 만들어진다. 일단 이 작업들이 수행되면 결과 실행 파일들은 런타임 시 로딩 될 라이브러리에서 검색할 심볼 리스트를 갖게 된다.

런타임 시, 애플리케이션은 동적 링커를 로딩한다. 사실, 동적 링커 자체는 공유 라이브러리들과 같은 종류의 버저닝을 사용한다. 예를 들어, SUSE Linux 9.1에서 /lib/ld-linux.so.2파일은 /lib/ld-linux.so.2.3.3에 대한 심볼릭 링크이다. 반면 /lib/ld-linux.so.1을 찾는 프로그램은 새 버전을 사용하려고 하지 않는다.

동적 링커는 재미있는 작업을 수행해야 한다. 프로그램이 원래 어떤 라이브러리(그리고 어떤 버전)에 링크 되었는지를 찾아서 이들을 로딩한다. 라이브러리 로딩은 다음으로 구성된다:

찾기(시스템 상의 여러 디렉토리들 중 하나에 있을 수 있다)
프로그램의 어드레스 공간으로 매핑하기
라이브러리에 필요한 제로 메모리 블록 할당하기
라이브러리의 심볼 테이블 어태치하기
이 프로세스의 디버깅은 쉬운 일이 아니다. 여러 유형의 문제들을 경험하게 된다. 예를 들어, 동적 링커가 기존 라이브러리를 찾지 못하면 프로그램 로딩에 실패하게 된다. 원하는 모든 라이브러리를 찾았지만 심볼을 찾지 못하면 이 역시 실패한다. (하지만 그 심볼로 레퍼런스를 시도할 때까지 작동하지 않을 수도 있다)—이것은 일반적으로 드문 경우이고, 심볼이 없다면 초기 링크 때 공지된다.

동적 링커 검색 경로 변경하기
프로그램을 링크할 때, 런타임 시 검색할 추가 경로를 지정할 수 있다. gcc에서 문법은 -Wl,-R/path이다. 프로그램이 이미 링크 되었다면 환경 변수 LD_LIBRARY_PATH를 설정하여 이 작동을 변경할 수 있다. 일반적으로 이것은 애플리케이션이 시스템 디폴트의 일부가 아닌 경로를 검색하려고 할 때에만 필요하다. 대부분의 리눅스 시스템에서는 드문 경우이다. 이론상으로는 Mozilla 사용자들은 이 경로 세트로 컴파일 된 바이너리를 배포했지만 실행 파일을 시작하기 전에 라이브러리 경로를 적절히 설정하는 래퍼 스크립트를 배포하는 것을 더 선호한다.

라이브러리 경로 설정은 두 애플리케이션들이 비 호환 버전의 라이브러리를 요구하는 드문 경우에 대안을 제공할 수 있다. 특별한 버전의 라이브러리를 사용하여 디렉토리에서 한 개의 애플리케이션 검색을 갖는데 래퍼 스크립트가 사용될 수 있다. 최상의 솔루션이라고는 볼 수 없지만 어떤 경우에는 이보다 더 나은 것이 없다.

많은 프로그램에 경로를 추가해야 하는 급박한 경우라면 시스템의 디폴트 검색 경로를 변경할 수 있다. 동적 링커는 /etc/ld.so.conf를 통해 제어된다. 여기에는 기본적으로 검색할 디렉토리 리스트가 포함되어 있다. LD_LIBRARY_PATH에서 지정된 모든 경로는 ld.so.conf에 나열된 경로에 앞서 검색된다. 따라서 사용자들은 이들 설정을 오버라이드 할 수 있다.

대부분의 사용자들은 시스템 디폴트 라이브러리 검색 경로를 변경할 이유가 없다; 일반적으로 환경 변수는 검색 경로를 변경하는데 더 알맞다. 툴킷의 라이브러리들과 링크하는 것 또는 새로운 버전의 라이브러리에 대해 프로그램을 테스트 하는 경우가 그 예이다.

ldd 사용하기
공유 라이브러리 문제를 해결하는 한 가지 유용한 툴은 ldd이다. 이 이름은 list dynamic dependencies의 앞 글자를 딴 것이다. 이 프로그램은 주어진 실행 파일 또는 공유 라이브러리를 찾아서 로딩해야 하는 공유 라이브러리가 무엇이고 어떤 버전이 사용되는 지를 파악한다. 아웃풋은 다음과 같다:

Listing 1. /bin/sh


$ ldd /bin/sh
linux-gate.so.1 => (0xffffe000)
libreadline.so.4 => /lib/libreadline.so.4 (0x40036000)
libhistory.so.4 => /lib/libhistory.so.4 (0x40062000)
libncurses.so.5 => /lib/libncurses.so.5 (0x40069000)
libdl.so.2 => /lib/libdl.so.2 (0x400af000)
libc.so.6 => /lib/tls/libc.so.6 (0x400b2000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)



“단순한” 프로그램이 많은 라이브러리들을 사용한다는 사실은 놀랍다. libhistory는 libncurses를 위한 하나의 호출이다. 이를 찾아내려면 또 다른 ldd명령어를 실행한다:

Listing 2. libhistory


$ ldd /lib/libhistory.so.4
linux-gate.so.1 => (0xffffe000)
libncurses.so.5 => /lib/libncurses.so.5 (0x40026000)
libc.so.6 => /lib/tls/libc.so.6 (0x4006b000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x80000000)



어떤 경우, 애플리케이션은 여분의 라이브러리 경로를 지정해야 한다. 예를 들어, 첫 번째 몇 줄을 Mozilla 바이너리에서 ldd 를 실행하면 다음과 같다:

Listing 3. 검색 경로에 없는 아이템을 위한 ldd 결과


$ ldd /opt/mozilla/lib/mozilla-bin
linux-gate.so.1 => (0xffffe000)
libmozjs.so => not found
libplds4.so => not found
libplc4.so => not found
libnspr4.so => not found
libpthread.so.0 => /lib/tls/libpthread.so.0 (0x40037000)



왜 이들 라이브러리를 찾지 않는가? 왜냐하면 일반적인 라이브러리용 검색 경로에 없기 때문이다. 사실 /opt/mozilla/lib에 있기 때문에 이 디렉토리를 LD_LIBRARY_PATH에 추가하는 것이 한 가지 방법이 된다.

또 다른 옵션은 경로를 .으로 설정하고 이 디렉토리에서 ldd를 실행하는 것이다. 비록 이는 약간 위험하다. 현재 디렉토리를 라이브러리 경로에 둔다는 것은 실행 파일 경로에 두는 것에 버금가는 위험한 일이기 때문이다.

이 경우 이들이 속해있는 디렉토리를 시스템 검색 경로에 추가하는 것은 좋은 생각이 아니다. Mozilla만 이들 라이브러리를 필요로 한다.

Mozilla 링크하기
Mozilla와 관련하여 단지 몇 줄의 라이브러리 밖에 못 봤다고 생각한다면 다음을 보기 바란다. Mozilla를 시작하는데 왜 많은 시간이 걸리는지 알게 될 것이다.

Listing 4. mozilla-bin


linux-gate.so.1 => (0xffffe000)
libmozjs.so => ./libmozjs.so (0x40018000)
libplds4.so => ./libplds4.so (0x40099000)
libplc4.so => ./libplc4.so (0x4009d000)
libnspr4.so => ./libnspr4.so (0x400a2000)
libpthread.so.0 => /lib/tls/libpthread.so.0 (0x400f5000)
libdl.so.2 => /lib/libdl.so.2 (0x40105000)
libgtk-x11-2.0.so.0 => /opt/gnome/lib/libgtk-x11-2.0.so.0 (0x40108000)
libgdk-x11-2.0.so.0 => /opt/gnome/lib/libgdk-x11-2.0.so.0 (0x40358000)
libatk-1.0.so.0 => /opt/gnome/lib/libatk-1.0.so.0 (0x403c5000)
libgdk_pixbuf-2.0.so.0 => /opt/gnome/lib/libgdk_pixbuf-2.0.so.0 (0x403df000)
libpangoxft-1.0.so.0 => /opt/gnome/lib/libpangoxft-1.0.so.0 (0x403f1000)
libpangox-1.0.so.0 => /opt/gnome/lib/libpangox-1.0.so.0 (0x40412000)
libpango-1.0.so.0 => /opt/gnome/lib/libpango-1.0.so.0 (0x4041f000)
libgobject-2.0.so.0 => /opt/gnome/lib/libgobject-2.0.so.0 (0x40451000)
libgmodule-2.0.so.0 => /opt/gnome/lib/libgmodule-2.0.so.0 (0x40487000)
libglib-2.0.so.0 => /opt/gnome/lib/libglib-2.0.so.0 (0x4048b000)
libm.so.6 => /lib/tls/libm.so.6 (0x404f7000)
libstdc++.so.5 => /usr/lib/libstdc++.so.5 (0x40519000)
libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x405d5000)
libc.so.6 => /lib/tls/libc.so.6 (0x405dd000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
libX11.so.6 => /usr/X11R6/lib/libX11.so.6 (0x406f3000)
libXrandr.so.2 => /usr/X11R6/lib/libXrandr.so.2 (0x407ef000)
libXi.so.6 => /usr/X11R6/lib/libXi.so.6 (0x407f3000)
libXext.so.6 => /usr/X11R6/lib/libXext.so.6 (0x407fb000)
libXft.so.2 => /usr/X11R6/lib/libXft.so.2 (0x4080a000)
libXrender.so.1 => /usr/X11R6/lib/libXrender.so.1 (0x4081e000)
libfontconfig.so.1 => /usr/lib/libfontconfig.so.1 (0x40826000)
libfreetype.so.6 => /usr/lib/libfreetype.so.6 (0x40850000)
libexpat.so.0 => /usr/lib/libexpat.so.0 (0x408b9000)



공유 라이브러리에 대해 더 자세히 배우기
리눅스에서 동적 링크에 대해 더 자세히 알고 싶다면 여러 방법들이 있다. GNU 컴파일러와 링커 툴 체인 문서화는 잘 되어있다. 하지만 핵심적인 부분은 info포맷으로 저장되어 있고 표준 man 페이지에는 언급되지 않았다.

ld.so의 매뉴얼 페이지에는 동적 링커의 작동을 변경하는 변수 리스트와, 과거에 사용되었던 동적 링커의 다양한 버전들에 대한 설명이 되어 있다.

대부분의 리눅스 문서에는 리눅스 시스템상에서는 일반적으로 모든 공유 라이브러리들은 동적으로 링크 되는 것으로 간주하고 있다. 정적으로 링크된 공유 라이브러리를 만드는 작업은 중요한데, 대부분의 사용자들은 여기에서 어떤 것도 얻지 못한다. 이 기능을 지원하는 시스템에서 퍼포먼스 차이는 확연히 드러난다.

프리패키지(pre-packaged) 시스템을 사용하고 있다면 그렇게 많은 공유 라이브러리 버전이 필요하지 않을 것이다. 링크 되었던 라이브러리를 가진 시스템이기 때문이다. 반면 많은 업데이트와 소스 구현을 해야 한다면 많은 버전의 공유 라이브러리가 생긴다.

언제나 강조하지만 직접 해보는 것이 중요하다. 시스템상의 거의 모든 것들은 같은 몇 개의 공유 라이브러리들을 참조하기 때문에 시스템의 핵심 라이브러리들 중 하나를 고장내면 시스템 복구 툴을 가지고 고생을 해야 한다.

http://www-128.ibm.com/developerworks/k ··· ibs.html
"Develop" 카테고리의 다른 글
  • 세마포어의 사용 (0)2007/05/14
  • cvs를 이용한 프로젝트 관리 (2)2007/05/14
  • 공유 라이브러리 이해하기 (0)2007/05/10
  • 컴포넌트를 안전하게 호출하기 (0)2007/05/10
  • GDB를 이용한 Linux 소프트웨어의 디버깅 (0)2007/05/04
2007/05/10 09:17 2007/05/10 09:17
Posted by webdizen
Tags 공유 라이브러리
No Trackback No Comment

Trackback URL : http://www.webdizen.net/blog/trackback/2910

Leave your greetings.

[로그인][오픈아이디란?]

«Prev  1  Next»

RSS HanRSS
Blog Image
webdizen
이곳은 컴퓨터에 대해 연구하고, 공유하고, 소통하기 위한 연구실입니다. 개인적으로는 OLAP, Data Mining, Semantic Web, Data Modeling에 대해서 연구하고 있습니다.

Categories

전체 (3009)
Webdizen (141)
Life (6)
Diary (16)
Blog (9)
IDEA (2)
Travel (10)
Book (16)
Photo (7)
Movie (8)
Music (14)
Leisure Sports (10)
Funny (6)
Hardware (121)
Software (120)
Windows (5)
Unix & Linux (120)
Installation (5)
Kernel (10)
System (34)
Develop (22)
X-Window (0)
Applicaton (31)
Security (4)
Framework (2)
Hadoop (2)
Programming (804)
Algorithm & Data Structure (1)
Assembly (38)
UNIX/Linux C (95)
C++ (128)
STL (4)
Java (38)
Win32 API (92)
ATL/COM (44)
MFC (151)
.NET (26)
WCF/WPF (4)
C# (28)
Network Programming (17)
Database Programming (12)
OpenGL / DirectX (13)
Multimedia Programming (0)
Game Programming (21)
Parallel Distributed Progra... (0)
Reverse Engineering (0)
Debugging (9)
Python (1)
Ruby (1)
Ruby on Rails (1)
QT (4)
GTK (0)
JSP (0)
PHP (6)
ASP.NET (6)
ASP (2)
Development (28)
Useful Library (2)
Data Modeling (0)
Database (105)
Oracle (4)
MSSQL (41)
MySQL (2)
Data Warehouse (2)
Data Mining (4)
Network (66)
Web (79)
DHTML (4)
XHTML (1)
Javascript (1)
CSS (1)
AJAX (9)
XML (11)
Flex (1)
Silverlight (3)
Security (91)
DoS (1)
Kernel (10)
Scanning (3)
Sniffing (0)
Spoofing (4)
Overflow (28)
Web (11)
Shell (10)
Format String (14)
Window (2)
Embedded (70)
Multimedia (27)
Mobile (14)
Graphic (24)
Management (633)
Knowledge (581)
Hadoop (0)

Notice

  • 메타 블로그 사이트에 등록
  • 새해 맞이 블로그의 변화
  • 블로그 명칭 변경
  • 도메인(www.webdizen.net) 구...
  • TEXTCUBE 1.6.1로 업그레이드...

Tags

  • Mobile
  • firefox2.0
  • 블루투스
  • Architecture
  • Secure
  • 한서관
  • 아바이 벌꿀 소주
  • pcap
  • Testing
  • VS.NET
  • 디스크 포멧
  • LG
  • English
  • 강원대병원
  • 성공
  • Ruby on the Rails
  • 웹 2.0
  • Graphics
  • Symbol
  • DB 래퍼 클래스

Recent Articles

  • 트위터(Twitter)의 시작!.
  • 청년 리더의 조건.
  • 애플의 타블렛 PC - 아이패드....
  • 미래의 인터페이스 - 육감 기....
  • 기초발성법 동영상 강좌.

Recent Comments

  • 경청... 너무나 중요한데.......
    webdizen 14:59
  • 학교 과제물중 쓰레드에 대하....
    장진혁 03/17
  • 관리자만 볼 수 있는 댓글입....
    비밀방문자 03/12
  • 상대방의 이야기를 열심히 경....
    DoNuts 03/03
  • 좋은글 잘 보고 갑니다..
    Und_hacker 01/08

Recent Trackbacks

  • printf,scanf를 이용한 형식....
    yundream의 프로그래밍 이야기 03/10
  • 파일 열기/저장하기 CFileDialog.
    은마군의 나태블록 2009
  • World IT Show 2008.
    상우 :: Oranzie's BLOG 2008
  • cvs서버 설치하기.
    3인3색 2008
  • 속속 공개되는 Google Chart....
    PHP와 Web 2.0 2007

Archive

  • 2010/02 (1)
  • 2010/01 (6)
  • 2009/12 (5)
  • 2009/09 (3)
  • 2009/08 (1)

Calendar

«   2010/03   »
일 월 화 수 목 금 토
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31      

Bookmarks

    • Administration
      • IIS.NET
      • NTFAQ
      • OS의 모든 것
      • 리눅스포털
    • Database
      • SQL Server Central
      • SQL Team
    • Development
      • .NET Heaven
      • ASP Alliance
      • ASP.NET 2.0
      • Bullog.net
      • C# Corner
      • C++ (C PlusPlus.com)
      • C++ Reference
      • CodeGuru
      • CodePlex
      • DebugLab
      • Dev Articles
      • Devpia
      • DotNet Junkies
      • DotNet Zone
      • Driver Online
      • GOSU.NET
      • HOONS 닷넷
      • Joinc 팀블로그
      • KOSR
      • MSDN Home Page
      • OSR Online
      • Sky.ph - 개발자 커뮤니...
      • TAEYO.NET
      • The Code Project
      • WindowsClient.net
      • 김상욱의 개발자 Side
      • 조인시 위키
    • Human Networks
      • belief21c's e-space
      • I think I can
      • Invisible Rover's Blog :D
      • Polarux - Linuxing
      • Rodman®
      • 까만 나비
      • 나를 가꾸는 시간.
      • 단녕
      • 상우 :: Oranzie's BLOG
    • Information Technology
      • Microsoft TechNet
      • 지디넷코리아 - 글로벌...
    • Security
      • FoundStone
      • milw0rm
      • NewOrder
      • OpenRCE
      • Phrack.org
      • Reverse Engineering b1...
      • Reverse Engineering Team
      • RootKit
      • SecurityFocus
      • SecurityXploded by Nag...
      • Wow Hacker
      • Zone-H
Textcube
Louice Studio Inc.
Powered by Textcube. Original designed by Tistory.