해당 포스팅은 컴퓨터 시스템의 요약정리입니다.
컴퓨터 시스템은 하드웨어와 시스템 소프트웨어로 구성되며, 이들이 함께 작동하여 응용 프로그램을 실행합니다.
시스템에 관한 공부는 C프로그램을 실행하면 어떤 일이 일어나고, 어떻게 출력하며, 종료되는지 까지의 수명주기를 추적하는 과정을 추적하는 것으로 시작됩니다.
hello
프로그램은 프로그래머가 에디터로 작성한 소스 프로그램(또는 소스파일)에서 생명주기가 시작됩니다.
#include <stdio.h>
int main()
{
printf("hello, world\n");
return 0;
}
hello.c
는 텍스트 파일로 저장되며, 0 또는 1로 표시되는 비트들의 연속으로 Byte
라는 8bit
단위로 구성딥니다. 대부분에서 각 바이트는 텍스트 문자의 아스키(ASCII) 표준을 사용합니다.
# i n c l u d e SP < s t d...
35 105 110 99 108 117 100 101 32 60 115 116 100...
hello.c
프로그램은 연속된 바이트들로 파일에 저장되며 각 바이트는 특정 문자에 대응되는 정수값을 갖습니다. 오로지 아스키 코드로만 이루어진 파일들을 텍스트 파일이라고 부릅니다. 다른 모든 파일들은 바이너리 파일이라고 부릅니다.
모든 시스템의 내부 정보(디스크 파일, 메모리상의 프로그램, 데이터, 네트워크를 통해 전송되는 데이터 등)는 비트들로 표시됩니다. 이들을 실행하는 컨텍스트가 어떤 것이냐에 따라 동일한 일련의 바이트가 다르게 해석되기도 합니다.
사람이 읽고 쓸 수 있는 형태의 컴퓨터 언어는 'high level program'이라고 한다. 하지만 hello.c
를 실행시키려면 각 문장들을 다른 프로그램에서 이해할 수 있도록 'low level machine-language' 인스트럭션들로 번역되어야 합니다. 이 인스트럭션들은 excutable object programe (실행가능 목적 프로그램)이라고 하는 형태로 합쳐저 바이너리 디스크 파일로 저장됩니다.
인스트럭션(instruction): 컴퓨터에게 일을 시키는 단위로서, 컴퓨터가 알아들을 수 있는 기계어로 이루어진 명령입니다.
컴파일러 드라이버는 유닉스에서 아래와 같이 소스파일에서 오브젝트 파일로 번역합니다.
linux> gcc -o hello hello.c
GCC 컴파일러 드라이버는 소스파일 hello.c
를 읽어서 실행파일인 hello
로 번역합니다. 번역은 다음과 같은 4단계를 거쳐 실행됩니다. 이 네 단계를 실행하는 프로그램들(전처리기, 컴파일러, 어셈블러, 링커)을 합쳐 컴파일 시스템이라고 합니다.
전처리기(cpp)는 C 프로그램을 # 문자로 시작하는 디렉티브(directive)에 따라 수정합니다. 이후 .i
로 끝나는 새로운 C 프로그램을 생성합니다.
ex) #include <stdio.h>
는 전처리기에게 시스템 헤더파일인 stdio.h
를 프로그램 문장에 직접 삽입하라고 지시합니다.
컴파일러(cc1)는 텍스트파일 hello.i
를 텍스트파일인 hello.s
로 번역하며, 이 파일에는 어셈블리어 프로그램이 저장됩니다.
어셈블러(as)가 hello.s
를 기계어 인스트럭션으로 번역하고 이들을 재배치가능 목적프로그램(relocatable object program)의 형태로 묶어서 hello.o
라는 목적 파일에 그 결과를 저장합니다.
printf
와 같은 표준 라이브러리 함수들은 이미 컴파일 된 별도의 목적파일인 printf.o
에 들어가 있으며, 해당 파일이 hello.o
와 결합된다. 링크 단계에서는 프로그래머가 새로 짠 부분과 기존의 기능들을 연결(link) 하는 기능을 수행합니다. 그 결과 hello
파일은 실행가능 목적파일(즉, 실행파일)로 메모리에 적제되어 시스템에 의해 실행됩니다.
버스는 시스템 내 전기적인 배선군으로 컴포넌트 간에 바이트 정보들을 전송합니다.버스는 일반적으로 워드(word)라고 하는 고정 크기의 바이트 단위로 데이터를 전송하도록 설계됩니다.
입출력 장치는 컴퓨터와 외부의 입출력 연결을 담당합니다. 키보드, 마우스, 디스플레이, 메모리 디스크 등이 있습니다.
메인메모리는 프로세서가 프로그램을 실행하는 동안 데이터와 프로그램을 모두 저장하는 임시 저장장치입니다. 메인메모리는 물리적으로 DRAM으로 구성되어있습니다. 논리적으로는 연속적인 바이트의 배열로, 각각 0부터 시작해서 고유의 주소(배열의 인덱스)를 가지고 있습니다.
현대의 프로세서는 메인 메모리에서 레지스터로 데이터를 옮겨와 데이터를 처리한 후 그 내용을 다시 레지스터에서 메인 메모리로 저장하는 로드-스토어 설계를 사용합니다.
프로세서는 메인 메모리에 저장된 인스트럭션을 해독(실행)하는 엔진입니다. 프로세서의 중심에는 프로그램 카운터가 있습니다.
- 레지스터(Register)
CPU 내에서 자료를 보관하는 아주 빠른 저장소입니다. 일반적으로 계산을 수행중인 값을 저장하는 데 사용됩니다.
- 프로그램 카운터(PC, Program Counter)
CPU 내부에 있는 레지스터 중의 하나로서, 다음에 실행될 명령어의 주소를 가지고 있어 실행할 기계어 코드의 위치를 지정합니다. 때문에 명령어 포인터라고도 합니다.
시스템에 전원이 공급되는 순간부터 전원이 끊어질 때 까지 프로세서는 프로그램 카운터가 가르키는 곳의 인스트럭션을 반복적으로 실행하고 프로그램 카운터 값이 다음 인스트럭션을 가리키도록 업데이트 합니다.
- 수식/논리 처리기(ALU, Arithmetic logic unit)
산술연산과 논리연산을 계산하는 디지털 회로입니다.
프로세서는 다음과 같은 과정을 반복적으로 수행합니다.
이때 새로운 인스트럭션의 위치는 방금 수행한 인스트럭션과 메모리 상에서 연속적일 수도 있고, 그렇지 않을수도 있습니다.
hello
프로그램의 실행hello
프로그램은 하드웨어에서 어떤 과정을 거쳐 작동되는가?
./hello
를 입력하면, 쉘 프로그램은 각각의 문자를 레지스터에 읽어들인 후 메모리에 저장합니다.linux> ./hello
hello
를 디스크에서 메인 메모리로 로딩합니다. 직접 메모리 접근기법(DMA)을 이용하여 데이터는 프로세서를 거치지 않고 디스크에서 메인 메모리로 직접 이동합니다.hello
의 목적파일의 코드와 데이터가 메모리에 적재된 후, 프로세서는 hello
프로그램의 main
루틴의 기계어 인스트럭션을 실행하기 시작합니다. 이 인스트럭션들은 hello, world\n
를 메모리로부터 레지스터 파일로 복사하고, 거기로부터 디스플레이 장치로 전송하여 화면에 글자들이 표시됩니다.