답변:
소스 코드 (작성한 것)에서 실행 가능한 코드 (실행하는 것)로가는 데는 2 단계 (대부분의 경우 해석 된 코드 할인)가 있습니다.
첫 번째는 소스 코드를 객체 모듈로 만드는 컴파일입니다.
두 번째, 연결은 객체 모듈을 결합하여 실행 파일을 형성하는 것입니다.
그 중에서도 소스 코드 (데이터베이스 액세스, 네트워크 통신 및 그래픽 사용자 인터페이스 용 라이브러리 등)를 보거나 다른 언어로 코드를 컴파일하지 않고도 타사 라이브러리를 실행 파일에 포함시킬 수 있습니다. 예를 들어 C 및 어셈블리 코드)를 모두 연결합니다.
당신이 때 정적 실행 파일로 파일을 링크 해당 파일의 내용은 링크시에 포함되어 있습니다. 즉, 파일의 내용은 실행할 실행 파일에 실제로 삽입됩니다.
동적으로 링크하면 링크 되는 파일에 대한 포인터 (예 : 파일의 파일 이름)가 실행 파일에 포함되고 해당 파일의 내용은 링크 타임에 포함되지 않습니다. 나중에 경우에만의 실행 이 동적으로 링크 된 파일에서 구입하고 그들 만 디스크에 실행 파일이 아닌 하나의 메모리 복사본으로 구입하고있는 실행 파일을.
기본적으로 지연된 연결 방법입니다. 더있다 더 당신이 실제로 그 안에 함수를 호출 할 때까지 동적으로 링크 된 파일에 가져 오지 않습니다 (말 일부 시스템에 바인딩이라고도 함) 연기 방법.
정적으로 링크 된 파일은 링크 타임에 실행 파일에 '고정'되므로 변경되지 않습니다. 실행 파일이 참조하는 동적으로 링크 된 파일은 디스크의 파일을 바꾸는 것만으로 변경 될 수 있습니다.
이를 통해 코드를 다시 연결하지 않고도 기능을 업데이트 할 수 있습니다. 로더는 실행할 때마다 다시 연결됩니다.
이것은 좋고 나쁘다-한편으로는, 더 쉬운 업데이트와 버그 수정을 허용하고, 다른 한편으로는 업데이트가 호환되지 않으면 프로그램이 작동을 멈출 수 있습니다. 동적으로 연결된 라이브러리를 호환되지 않는 라이브러리로 바꾸면 응용 프로그램이 손상 될 수 있습니다 (이를 수행하는 개발자는 심각하게 사냥하고 처벌해야합니다).
예를 들어 , main.c
정적 및 동적 연결을 위해 사용자가 파일을 컴파일하는 경우를 살펴 보겠습니다 .
Phase Static Dynamic
-------- ---------------------- ------------------------
+---------+ +---------+
| main.c | | main.c |
+---------+ +---------+
Compile........|.........................|...................
+---------+ +---------+ +---------+ +--------+
| main.o | | crtlib | | main.o | | crtimp |
+---------+ +---------+ +---------+ +--------+
Link...........|..........|..............|...........|.......
| | +-----------+
| | |
+---------+ | +---------+ +--------+
| main |-----+ | main | | crtdll |
+---------+ +---------+ +--------+
Load/Run.......|.........................|..........|........
+---------+ +---------+ |
| main in | | main in |-----+
| memory | | memory |
+---------+ +---------+
정적 인 경우 메인 프로그램과 C 런타임 라이브러리가 링크 타임에 (개발자에 의해) 서로 링크되어 있음을 알 수 있습니다. 사용자는 일반적으로 실행 파일을 다시 연결할 수 없으므로 라이브러리의 동작에 갇혀 있습니다.
동적 경우, 주 프로그램은 C 런타임 가져 오기 라이브러리 (동적 라이브러리에있는 내용을 선언하지만 실제로 정의 하지는 않는 )와 연결됩니다. 이렇게하면 실제 코드가 없어도 링커가 연결될 수 있습니다.
그런 다음 런타임시 운영 체제 로더는 기본 프로그램을 C 런타임 DLL (동적 링크 라이브러리 또는 공유 라이브러리 또는 기타 명명법)과 늦은 링크합니다.
C 런타임 소유자는 언제든지 새 DLL을 삭제하여 업데이트 또는 버그 수정을 제공 할 수 있습니다. 앞서 언급했듯이, 이것은 장단점이 있습니다.
.dll
또는 .so
확장명 이있을 수 있음 ) . 정확한 설명이 아니라 개념 을 설명하는 것으로 답을 생각하십시오 . 그리고 텍스트에 따라, 이것은 단지 C 런타임 파일에 대한 정적 및 동적 링크를 보여주는 예제이므로 그렇습니다.`crt가 모든 파일에서 나타냅니다.
이 질문에 대한 좋은 대답 은 연결 이 무엇인지 설명해야한다고 생각합니다 .
일부 C 코드를 컴파일하면 (예 : 기계 언어)로 변환됩니다. 실행될 때 프로세서가 바이트를 추가, 빼기, 비교, "고토", 메모리 읽기, 메모리 쓰기 등을 발생시키는 일련의 바이트. 이 내용은 객체 (.o) 파일에 저장됩니다.
오래 전, 컴퓨터 과학자들은이 "서브 루틴"을 발명했습니다. 이 코드 청크를 실행하십시오. 그들이 가장 유용한 서브 루틴이 특별한 장소에 저장 될 수 있고 그것들을 필요로하는 모든 프로그램에 의해 사용될 수 있다는 것을 깨닫기까지 너무 오래 걸리지 않았습니다.
이제 초창기 프로그래머는이 서브 루틴이 있던 메모리 주소를 입력해야했습니다. 같은 것 CALL 0x5A62
. 메모리 주소를 변경해야하는 경우에는 지루하고 문제가있었습니다.
따라서 프로세스가 자동화되었습니다. 을 호출하는 프로그램을 작성 printf()
하면 컴파일러는의 메모리 주소를 모릅니다 printf
. 따라서 컴파일러는 단지을 쓰고 CALL 0x0000
객체 파일에 "이 0x0000을 printf 의 메모리 위치로 바꿔야합니다"라는 메모를 추가합니다 .
정적 연결은 링커 프로그램 (GNU 파일은 ld 라고 함 )이 printf
실행 파일에 직접 기계 코드를 추가 하고 0x0000을 주소로 변경 함을 의미합니다 printf
. 실행 파일을 만들 때 발생합니다.
동적 연결은 위 단계가 발생하지 않음을 의미합니다. 실행 파일 에는 여전히 "0x000을 printf의 메모리 위치로 바꿔야합니다"라는 메모가 있습니다. 운영 체제의 로더는 printf 코드를 찾아 메모리에로드 하고 프로그램이 실행될 때마다 CALL 주소를 수정해야합니다 .
프로그램이 정적으로 링크되는 함수 ( printf
일반적으로 정적으로 링크 된 것과 같은 표준 라이브러리 함수 )와 동적으로 링크 된 다른 함수를 호출하는 것이 일반적입니다. 정적 파일은 실행 파일의 "일부"가되고 동적 파일은 실행 파일이 실행될 때 "가입"합니다.
두 가지 방법 모두 장단점이 있으며 운영 체제마다 차이가 있습니다. 그러나 당신이 묻지 않았으므로 여기서 끝내겠습니다.
ld
문서 를보고 싶을 수도 있습니다 .
위의 게시물 중 어느 것도 실제로 정적으로 링크 하는 방법 을 보여 주지 않고 올바르게 수행했는지 확인 하므로이 문제를 해결할 것입니다.
간단한 C 프로그램
#include <stdio.h>
int main(void)
{
printf("This is a string\n");
return 0;
}
C 프로그램을 동적으로 연결
gcc simpleprog.c -o simpleprog
그리고 file
바이너리에서 실행 하십시오 :
file simpleprog
그리고 그것은 다음과 같이 동적으로 연결되어 있음을 보여줍니다.
"simpleprog : GNU / Linux 2.6.26, ELID 64 비트 LSB 실행 파일, x86-64, 버전 1 (SYSV), 동적 링크 (공유 라이브러리 사용), GNU / Linux 2.6.26, BuildID [sha1] = 0xf715572611a8b04f686809d90d1c0d75c6028f0f, 스트립되지 않음"
대신 이번에 프로그램을 정적으로 링크하자 :
gcc simpleprog.c -static -o simpleprog
이 정적으로 링크 된 바이너리에서 파일을 실행하면 다음이 표시됩니다.
file simpleprog
"simpleprog : GNU / Linux 2.6.26 용 ELF 64 비트 LSB 실행 파일, x86-64, 버전 1 (GNU / Linux), 정적으로 링크 됨, BuildID [sha1] = 0x8c0b12250801c5a7c7434647b7dc65a644d6132b, 제거되지 않음"
그리고 당신은 그것이 정적으로 연결되어있는 것을 볼 수 있습니다. 그러나 안타깝게도 모든 라이브러리가 이러한 방식으로 정적으로 연결하는 것이 간단하지는 않으며 libtool
, 수작업으로 객체 코드와 C 라이브러리를 사용 하거나 연결하는 데 많은 노력이 필요할 수 있습니다 .
같은 다행히 많은 임베디드 C 라이브러리 musl
제공 정적 거의 모든 옵션을 연결하는 모든 경우에 자신의 라이브러리.
이제 strace
작성한 바이너리는 프로그램을 시작하기 전에 액세스 한 라이브러리가 없음을 알 수 있습니다.
strace ./simpleprog
이제 strace
동적으로 링크 된 프로그램 의 출력과 비교 하면 정적으로 링크 된 버전의 strace가 훨씬 짧습니다.
(C #을 모르지만 VM 언어에 대한 정적 연결 개념을 갖는 것이 흥미 롭습니다)
동적 연결에는 프로그램에서 참조 할 수있는 필수 기능을 찾는 방법을 알아야합니다. 언어 런타임 또는 OS는 파일 시스템, 네트워크 또는 컴파일 된 코드 캐시에서 참조와 일치하는 코드를 검색 한 다음 재배치와 같이 메모리의 프로그램 이미지에 통합하기 위해 몇 가지 조치를 취합니다. 그들은 모두 런타임에 완료됩니다. 수동으로 또는 컴파일러로 수행 할 수 있습니다. 엉망의 위험이있는 업데이트 기능 (즉, DLL 지옥)이 있습니다.
정적 링크는 컴파일 타임에 수행되며, 컴파일러는 모든 기능 부분이 어디에 있는지 알려주고 통합하도록 지시합니다. 재 컴파일 없이는 검색, 모호함, 업데이트 기능이 없습니다. 모든 종속성은 실제로 프로그램 이미지와 하나입니다.