[크래프톤정글] Week 1 : 컴파일 시스템

컴퓨터 시스템의 기초에 대해 알아보자. 사용자가 명령어를 입력하면 그 화면이 출력되기 까지의 절차까지를 알아보겠다. 

 

정보의 단위

소스 프로그램은 0과 1로 이루어진 비트의 연속이다. 바이트는 8 비트 단위로 구성된다. 모든 텍스트 파일은 ascii 문자로 이루어진 파일을 의미하며, 그 외 모든 파일은 바이너리 파일이다. 

컴파일 시스템

컴파일이란?

우리가 보는 프로그램은 사람이 이해하고 쓸수 있는 형태로 되어 있지만, 컴퓨터에게 일을 시키기 위해서는 저급 기계어로 바꿔줘야 한다. 이 과정을 컴파일이라 한다. 

 

컴파일은 다음과 같은 단계를 거친다. 

  1. 전처리 단계 : hello.c -> hello.i 라는 새로운 C 프로그램이 생성되며, 본래의 프로그램을 #문자로 시작하는 디렉티브에 따라 수정한다
  2. 컴파일 단계 : hello.i -> hello.s 로 텍스트 파일에서 다른 텍스트 파일로 번역되며, 이 파일에는 어셈블리어 프로그램이 저장된다. 여기에는 아래의 main 함수 정의가 포함된다. 
main :
	subq	$8, %rsp
    movl	$.LCO, %edi
    call	puts
    movl	$0, %eax
    addq	$8, %rsp
    ret
  1. 어셈블리 단계 : hello.s 기계어 인스트럭션을 재배치 가능한 목적 프로그램 형태로 묶어서 hello.o 파일에 저장한다. 이는 main 함수인스트럭션을 인코딩하기 위한 바이너리 파일이다. 
  2. 링크 단계 : C 라이브러리에서 printf 함수를 호출하고, 함수 목적파일인 prinf.o 와 hello.o 를 링커 프로그램으로 통합한다. 이는 hello 파일이 실행가능 목적파일로 메모리에 적재된다. 

컴파일에 대해 왜 알아야 하는가?

  1. 컴파일러가 기계어로 어떻게 변환하는지를 이해하면, 어떤 코드가 메모리 효율이 높은지 그리고 그 이유는 무엇인지에 대해 깊이 이해할 수 있다. 
  2. 링커 에러 발생 시, 이것이 무엇을 의미하는지 이해하기 쉽다.
  3. 프로그램 스택에 데이터와 제어 정보가 어떻게 저장되는지를 이해하면 버퍼 오버플로우와 같은 보안 취약점을 보완할 수 있다. 

시스템의 하드웨어 조직

시스템의 하드웨어 조직에 대한 이해를 통해 쉘에 입력된 명령어가 어떤 경로로 실행되는지 알아보자. 

 

들어가기에 앞서 과정을 먼저 살펴 보자. 

소스 코드로 작성된 프로그램은 컴파일러를 통해 기계어로 번역되어 OS (운영체제)에서 프로그램을 로드한다. CPU는 이런 명령을 받고 실행하는 과정에서 하드웨어 (RAM, 디스크, 입출력 장치 등)와 상호작용 한다. 

버스 

컴퓨터 내부에서 데이터를 주고 받는 통로로 크게는 데이터버스, 주소 버스, 제어 버스 등으로 나뉜다. 데이터 버스는 CPU와 메모리 저장장치 사이에서 데이터를 전달한다. 전달된 데이터는 주소 버스에서 데이터의 주소를 지정해주며, 제어 버스가 이 주소를 기반으로 명령을 전달받아 Read / Write 연산을 시행한다. 이때, 제어 버스에는 워드라는 고정된 byte 단위로 데이터가 전송된다. 

 

버스는 데이터가 다니는 통로라는 것인데, 물리적인 실체가 있는것일까? 정답은 있다! 버스는 여러 장치들과 연결되기에 여러 곳에서 그 통로를 연결해주어야 한다. 기본적으로 마더보드 혹은 PCB 기판에 물리적인 회로가 존재하며, 연결 대상인 CPU, RAM 등에도 버스의 회로가 내장되어 있으며, 이 둘이 연결되었을때 상호작용 한다. 

입출력 장치

시스템과 외부 세상을 연결해주는 것으로 방식은 크게 입출력 버스, 컨트롤러, 어댑터를 통해 연결된다. 이 연결 방식의 차이는 쉽게 packaging의 차이라 볼 수 있다. 

컨트롤러는 디바이스가 칩셋이거나 머더보드에 장착되는 것으로, HDD/SDD가 SATA 포트를 통해 연결되는 것 또는 USB 포트를 통해 연결되는 키보드와 마우스를 예로 들 수 있다. 

어댑터는 머더보드 슬롯에 장착되는 것으로, 노트북에 별도로 연결하는 외장 그래픽카드나 USB to Ethernet 어댑터가 그 예다. 

메인 메모리

프로세서가 프로그램을 실행하는 동안 데이터와 프로그램을 모두 저장하는 임시 저장장치이다. 물리적으로는 DRAM 이며 논리적으로는 연속적인 byte의 배열로 고유의 주소 (배열의 인덱스)를 가진다. 

프로세서 (CPU) 

메인 메모리에 저장된 인스트럭션을 해독한다. 메인 구성은 다음과 같다. 

 

PC (Program Counter) : 레지스터 중 하나로 지금 실행하는 명령어의 위치를 저장한다

Control Unit : 명령어를 해석하고 실행한다

ALU : 연산을 처리하며, 새 데이터의 주소값을 연산한다