[Linux] 동적 및 정적 라이브러리

[Linux] 동적 및 정적 라이브러리

젠이지안쿠

Linux 시스템에서 C 표준 라이브러리 및 기타 일반적으로 사용되는 라이브러리를 C 라이브러리라고 하며 일반적으로 /usr/lib또는 /usr/lib64디렉토리에 저장됩니다.

이미지-20230907132423757

Linux 시스템에서는 /usr/include많은 헤더 파일이 디렉터리에 저장됩니다.

이미지-20230907132557509

  • Linux 시스템에는 C/C++ 헤더 파일과 라이브러리 파일이 미리 설치되어 있습니다. 헤더 파일은 메소드에 대한 설명을 제공하고 라이브러리 파일은 메소드의 구현을 제공합니다. 헤더 파일과 라이브러리 파일은 대응 관계를 가지며 다음을 수행해야 합니다. 조합해서 사용하세요.
  • 프로그램을 컴파일하여 실행 가능한 프로그램을 만드는 과정에서 전처리 과정에서 헤더 파일이 도입되고, 링크 과정에서 라이브러리 파일이 링크된다.
  • Visual Studio에서 개발 환경을 설치하면 컴파일러 소프트웨어뿐만 아니라 해당 언어에 해당하는 헤더 파일과 라이브러리 파일도 설치됩니다.
  • 컴파일러는 사용자의 입력을 바탕으로 헤더 파일에서 관련 내용을 자동으로 검색하고 구문 프롬프트 기능을 구현합니다.
  • 컴파일러는 사용자의 입력을 기반으로 자동으로 지속적으로 컴파일하여 구문 오류 보고 기능을 구현합니다.

도서관이 존재하는 이유

프로그래밍 언어는 일반적으로 사용되는 기능을 라이브러리에 추가하여 사용자가 이러한 기능을 직접 사용할 수 있도록 하며 개발 효율성을 향상시킵니다. 예를 들어 printf, 사용자는 데이터를 인쇄하고 싶을 때마다 데이터를 인쇄하는 함수를 작성할 필요가 없습니다.

라이브러리 작성

정적 라이브러리 작성 시뮬레이션

라이브러리를 작성하기 전에 다음 사항을 설명하십시오.

  • 라이브러리는 동적 라이브러리와 정적 라이브러리로 구분됩니다.
  • Linux에서 정적 라이브러리의 이름 지정 규칙:lib库名.a
  • Linux에서 동적 라이브러리의 이름 지정 규칙:lib库名.so
  • 클라우드 서비스에는 일반적으로 기본 제공 정적 라이브러리가 없습니다.
  1. 헤더 파일 및 소스 파일 작성

헤더 파일 myadd.h와 해당 소스 파일 myadd.c, 헤더 파일 mysub.h 및 해당 소스 파일 mysub.c를 작성합니다. 각 파일의 구체적인 코드는 다음과 같습니다.

//myadd.h
#pragma once 

int my_add(int x, int y);

//myadd.c
#include "myadd.h" 

int my_add(int x, int y)//一个简单的加法函数
{
    
    
  return x + y;
}
//mysub.h
#pragma once 

int my_sub(int x, int y);

//mysub.c
#include "mysub.h" 

int my_sub(int x, int y)//一个简单的减法函数
{
    
    
  return x - y; 
}
  1. 소스 파일을 타겟 파일로 컴파일

다음 을 사용하여 gcc -c 源文件名소스 파일을 대상 파일로 컴파일합니다 .

이미지-20230907162247874

  1. 정적 라이브러리에 패키징됨

ar -rc lib库名.a 目标文件대상 파일을 정적 라이브러리로 패키징하는 데 사용합니다 .

이미지-20230907162355901

  1. 시뮬레이션 라이브러리 파일 디렉터리 구조

포함 디렉터리를 만들고 헤더 파일을 이 디렉터리로 이동하고, lib 디렉터리를 만들고 정적 라이브러리를 이 디렉터리로 이동합니다.

이미지-20230907162516768

  1. 라이브러리를 압축된 패키지로 패키징

tar -czf 目标压缩包名 源文件라이브러리를 압축된 패키지로 패키징하는 데 사용합니다 .

이미지-20230907162735301

정적 라이브러리 사용 시뮬레이션

  1. 이전 작업에서 패키징한 정적 라이브러리 압축 패키지를 특정 디렉터리에 복사하고 압축을 풀어 라이브러리 다운로드 프로세스를 시뮬레이션합니다.

이미지-20230907164341754

  1. 현재 디렉터리에 소스 파일 main.c를 생성하고 정적 라이브러리를 호출하는 코드를 작성하며, 구체적인 코드는 다음과 같이 구현됩니다.
#include <stdio.h>
#include "myadd.h"
#include "mysub.h"

int main()
{
    
    
  int x = 20;
  int y = 10;
  printf("%d + %d = %d\n", x, y, my_add(x, y));
  printf("%d - %d = %d\n", x, y, my_sub(x, y));
  return 0;
}
  1. 소스 파일 main.c를 실행 가능한 프로그램으로 컴파일합니다.

이미지-20230907165017051

컴파일된 디렉터리에 타사 헤더 파일이 없기 때문에 -I 路径헤더 파일 경로를 지정하는 옵션이 필요하며, 타사 라이브러리 컴파일러가 자체적으로 찾아 사용하지 않기 때문에 -L 路径라이브러리 파일 경로명과 -l 库名라이브러리 이름을 지정해야 합니다.

Linux 시스템에서 타사 라이브러리의 사용을 요약합니다.

  • 헤더 파일 경로와 라이브러리 파일의 경로 및 이름을 지정해야 합니다.
  • 컴파일러가 검색한 기본 경로에 헤더 파일과 라이브러리 파일이 설치되어 있지 않은 경우 사용자는 해당 옵션을 지정해야 합니다.
    • 헤더 파일 경로( -I 路径)
    • 라이브러리 파일 경로( -L 路径)
    • 라이브러리 이름( -l 库名)
  • 헤더 파일과 라이브러리 파일 설치의 핵심은 해당 파일을 시스템 기본 경로에 복사하는 것입니다.
  • 헤더 파일과 라이브러리 파일을 설치한 후 컴파일 시 라이브러리 이름 옵션을 지정해야 합니다.

동적 라이브러리 작성 시뮬레이션

동적 라이브러리 작성을 시뮬레이션할 때 정적 라이브러리를 패키징하기 위해 이전 기사에서 사용한 헤더 파일 myadd.h 및 해당 소스 파일 myadd.c와 헤더 파일 mysub.h 및 해당 소스 파일 mysub.c가 사용됩니다.

  1. 소스 파일 컴파일

gcc -fPIC -c 源文件名동적 라이브러리를 패키징할 때 소스 파일을 대상 파일로 컴파일 해야 합니다 .

이미지-20230907173438259

  1. 대상 파일을 동적 라이브러리로 패키징

gcc -shared -o lib库名.so 目标文件대상 파일을 동적 라이브러리로 패키징하는 데 사용합니다 .

이미지-20230907173847412

  1. 시뮬레이션 라이브러리 파일 디렉터리 구조

포함 디렉터리를 만들고 헤더 파일을 이 디렉터리로 이동하고, lib 디렉터리를 만들고 정적 라이브러리를 이 디렉터리로 이동합니다.

이미지-20230907174146799

  1. 라이브러리를 압축된 패키지로 패키징

tar -czf 目标压缩包名 源文件라이브러리를 압축된 패키지로 패키징하는 데 사용합니다 .

이미지-20230907174229960

정적 라이브러리 사용 시뮬레이션

  1. 이전 작업에서 패키징한 동적 라이브러리 압축 패키지를 특정 디렉터리에 복사하고 압축을 풀어 라이브러리 다운로드 프로세스를 시뮬레이션합니다.

이미지-20230907184457085

  1. 현재 디렉터리에 소스 파일 main.c를 생성하고 정적 라이브러리를 호출하는 코드를 작성하며, 구체적인 코드는 다음과 같이 구현됩니다.
#include <stdio.h>
#include "myadd.h"
#include "mysub.h"

int main()
{
    
    
  int x = 20;
  int y = 10;
  printf("%d + %d = %d\n", x, y, my_add(x, y));
  printf("%d - %d = %d\n", x, y, my_sub(x, y));
  return 0;
}
  1. 소스 파일 main.c를 실행 가능한 프로그램으로 컴파일합니다.

동적 라이브러리 헤더 파일의 경로, 라이브러리 파일의 경로, 라이브러리 이름을 지정하면 컴파일러가 성공적으로 컴파일할 수 있습니다. 동적 라이브러리이므로 프로그램 실행 시 OS가 동적 라이브러리에 연결해야 합니다. 프로그램의 동적 라이브러리 주소에 따라 성공적으로 실행되지만 OS에서 동적 라이브러리를 찾을 수 없어 다음과 같은 상황이 발생합니다.

이미지-20230907184623458

  1. 환경변수를 import해서 프로그램을 실행시키는 방법을 사용하세요(임시해결책)

동적 라이브러리 경로를 환경 변수로 가져 오면 export LD_LIBRARY_PATH=LD_LIBRARAY_PATH:动态库所在目录路径OS는 프로그램을 실행할 때 환경 변수의 경로에서 동적 라이브러리를 찾아 성공적으로 실행합니다.

이미지-20230907185214813

OS에서 타사 동적 라이브러리를 찾을 수 없는 문제를 해결하는 방법:

  1. 환경 변수 가져오기: export LD_LIBRARY_PATH=LD_LIBRARAY_PATH:动态库所在目录路径동적 라이브러리 경로를 사용하여 환경 변수를 가져옵니다. 셸을 다시 열면 환경 변수가 다시 로드되므로 임시 해결 방법입니다.
  2. 시스템 경로 아래에 동적 라이브러리에 대한 소프트 링크 설정: sudo ln -s 动态库路径 /lib64/lib库名.so시스템 경로에 동적 라이브러리에 대한 소프트 링크를 추가하는 데 사용
  3. 구성 파일 수정: 경로 아래에 접미사를 사용하여 파일을 /etc/ld.so.conf.d/만들고 파일에 정적 라이브러리의 경로를 쓴 다음 구성 파일을 적용하는 데 사용합니다..confsudo ldconfig

라이브러리 로딩 원리

정적 라이브러리 로딩 원리

동적 라이브러리의 로딩 과정은 링크 과정에서 정적 라이브러리의 구현을 실행 프로그램에 직접 복사하여 실행 프로그램을 형성하는 것입니다. 따라서 정적 라이브러리는 많은 리소스(디스크, 메모리, 네트워크 리소스)를 차지합니다.

동적 라이브러리 로딩 원리

우선, 동적 라이브러리를 사용하여 실행 가능한 프로그램을 생성할 때 링크 과정에서 라이브러리의 메소드를 나타내는 외부 기호만 실행 가능한 프로그램의 해당 주소로 대체됩니다. , 실행하기 어렵기 때문에 운영 체제는 일련의 작업을 수행합니다. 프로그램이 프로세스를 형성하기 위해 메모리에 로드된 후 운영 체제는 프로세스 제어 블록, 프로세스 주소 공간 및 페이지 테이블을 유지 관리합니다.

이미지-20230908133542761

프로세스가 동적 라이브러리의 메소드를 만난 후 운영체제는 페이지 테이블에서 매핑을 찾아 메모리에 매핑된 것이 특정 메소드 구현이 아닌 해당 주소뿐이라는 것을 알게 됩니다. 이 동적 라이브러리를 찾아 일정한 전략에 따라 동적 라이브러리로 변환하고, 라이브러리는 메모리에 로드되고, 운영 체제는 메모리에 로드된 동적 라이브러리를 스택 영역과 힙 사이의 공유 영역에 매핑합니다. 프로세스 주소 공간의 영역:

이미지-20230908133642887

그런 다음 프로세스가 라이브러리의 메서드를 실행할 때마다 프로그램 실행을 완료하기 위해 프로세스 주소 공간의 공유 영역으로 점프하기만 하면 됩니다.

이미지-20230908133705099

또한, 라이브러리가 메모리에 로드된 후 후속 실행 프로세스에서 라이브러리 메소드를 실행해야 할 때 라이브러리를 메모리에 로드할 필요 없이 공유 영역 매핑을 직접 생성한 후 메소드를 실행합니다. 도서관에서 사용됩니다.

실행 가능한 프로그램의 라이브러리에 대한 주소 지정 전략

실행 가능한 프로그램에서 정적 라이브러리의 주소 지정 전략

실행 가능한 프로그램이 형성되면 실행 가능한 프로그램에 논리 주소가 생기고, 정적 라이브러리를 사용하면 실행 가능한 프로그램에 있는 정적 라이브러리의 메소드도 주소가 지정되어 논리 주소를 얻게 됩니다. 프로세스가 되어 실행되며, 논리 주소를 기준으로 점프만 하면 됩니다.

실행 가능한 프로그램에서 동적 라이브러리의 주소 지정 전략

실행 가능한 프로그램이 형성되면 실행 가능한 프로그램에 논리 주소가 생기고, 동적 라이브러리를 사용하면 실행 가능한 프로그램에 있는 동적 라이브러리의 메소드도 주소가 지정되지만 이 주소가 메소드의 시작 주소가 됩니다. 라이브러리에 시작 오프셋이 있습니다. 대상 파일을 얻기 위해 동적 라이브러리를 만들 때 gcc를 사용하여 이를 추가하면 -fPIC이 주소 오프셋을 얻을 수 있는데 이를 주소 독립적 코드라고 합니다. 실제 프로세스가 실행 중일 때 프로세스는 필요한 것만 필요합니다. 라이브러리에서 메서드를 기다리기 위해 메모리에 로드되고 공유 영역에 매핑된 다음 공유 영역 매핑과 오프셋 주소를 사용하여 작업을 완료합니다.

설명하다:

  • gcc/g++ 컴파일러는 실행 가능한 프로그램을 컴파일할 때 기본적으로 동적 라이브러리를 사용합니다.
  • gcc/g++ 컴파일러가 실행 가능한 프로그램을 컴파일할 때 -static이 옵션을 사용하면 정적 라이브러리가 사용됩니다.
  • gcc/g++ 컴파일러가 실행 가능한 프로그램을 컴파일할 때 일부 동적 라이브러리가 존재하지 않으며 동적 라이브러리를 혼합하여 사용합니다.

추천

출처blog.csdn.net/csdn_myhome/article/details/132758600