[System Software] Program Execution

diveintoo·2021년 10월 16일
0

본 내용은 권진욱 교수님의 System Software 및 실습 강의를 기반으로 개인적인 공부를 더한 글입니다.

우리가 작성한 코드가 메모리에 로딩되기까지의 과정은 다음과 같습니다.

  1. 전처리기(preprocessor)
  2. 컴파일러(compiler)
  3. 어셈블러(assembler)
  4. 링커(Linker)
  5. 로더(Loader)

각 단계를 알아보도록 합시다.

1. 전처리기(preprocessor)

compile하기 직전에 #include, #define과 같은 지시자를 찾아내서 텍스트를 치환하거나, 디버깅에 도움을 줍니다.

만약 아래의 코드를 가지고 있는 x.h 파일이 있다고 합시다.

x.h

extern char x;

그리고 다른 파일에서 x.h를 참조하고 있다면

a.c

# include "x.h"
x++;

전처리기는 x.h를 포함하고 있는 파일들을 찾아내서 그 자리를 x.h 내용으로 치환해줍니다 치환.

전처리 후 a.c

extern char x;
x++;

2. 컴파일러(compiler)

컴파일러는 고급언어 프로그램을 어셈블리 프로그램으로 바꿔줍니다.

  • 고급 언어 : C나 Java와 같이 인간이 가장 이해하기 쉬운 언어
  • 어셈블리어 : 기계어와 1대 1로 매핑되어있으며, 인간의 이해도를 향상시키기 위해 만들어진 프로그래밍 언어
  • 기계어 : 0과 1의 조합으로 구성되며, 컴퓨터가 직접 이해가능한 언어

파일에서 extern을 사용하는 등 다른 파일을 참조하고 있더라도 각각의 파일이 따로 compile 됩니다.
이때 symbol의 위치가 불명확하기 때문에 symbol의 타입만 파악합니다.

파악한 symbol들을 각 object 파일마다 가지고 있는 Symbol Table에 저장합니다.

3. 어셈블러(assembler)

어셈블러는 어셈블리어 프로그램을 기계어 프로그램으로 바꿔줍니다.
이때 만들어진 기계어 프로그램을 object file이라고 부릅니다다.
driver.c -> driver.o

4. 링커(linker)

링커는 여러 object file과 Static library file들을 하나의 executable file로 만듭니다다.
driver.o, hd.o, vm.o -> a.out


링커는 모든 파일을 살펴보면서 컴파일 과정에서 파악하지 못한 symbol의 위치를 찾아내어 binding해줍니다.

Static Linking

Lingking에는 Static Linking과 Dynamic Linking이 있습니다.
Static Linking은 링커에서, Dynamic Linking은 로더에서 진행되는데요.
여기는 링커 파트니까 먼저 Static Linking에 대해 알아봅시다.

Static Linking은 라이브러리 그 자체의 코드가 실행 파일 코드에 포함되는 것입니다.
예를 들어 한 object파일에서 printf 함수를 사용했다고 한다면, 링커는 printf를 가지고 있는 library를 찾아서 해당 library를 실행하는 코드를 executable file에 포함시킵니다.

필요로 하는 라이브러리가 미리 compile 되어있기 때문에 컴파일 시간이 단축된다는 장점이 있습니다.
하지만 실행 파일에 라이브러리의 코드가 포함되기 때문에 실행 파일의 크기가 커지고, 동일한 라이브러리를 각각의 프로세스가 메모리에 올리므로 메모리 낭비가 발생한다는 것이 단점입니다.

5. 로더(loader)

로더는 executable file과 실행에 필요한 Shared library를 로딩하여 메모리에 올립니다.

Dynamic Linking

Dynamic Linking은 라이브러리가 실행될 때 link되는 것입니다.

링커는 실행 파일을 만들 때 Shared library가 필요한 경우, 라이브러리 자체를 포함시키는 것이 아니라 라이브러리의 위치를 찾기 위한 코드를 남겨둡니다.

파일을 실행시 Shared library가 호출되었을 때, 해당 라이브러리가 메모리에 있으면 그 루틴의 주소로 가서 실행하고 돌아옵니다. 메모리에 라이브러리가 존재하지 않다면 디스크에서 읽어옵니다.
라이브러리가 메모리에 한 번 로딩되면 그 다음부터는 이 라이브러리가 계속해서 수행되는 것입니다.

따라서 Dynamic Linking은 메모리의 요구사항이 적다는 것이 장점입니다.
하지만 run time에서 library call-bind-load를 진행하기 때문에 느려지고, 어디로 점프해야하는지를 저장해야하기 때문에 overhead가 있다는 단점이 있습니다.

Static Linking과 Dynamic Linking을 그림으로 비교하자면 다음과 같습니다.

실행 파일이 메모리 로딩된 후 명령어들이 CPU로 Fetch됩니다. 이 명령어들은 복호화 과정을 걸쳐서 실행됩니다.


지금까지 프로그램이 실행되는 과정을 알아보았습니다.
그저 코드를 짜고 run을 누르고 output만 보다가 내부에서 어떻게 동작하는지 알아보니 심적으로 더 가까워지는 느낌이 드네요...

0개의 댓글