이 글은 전문가가 아닌 학부생이 개인 공부 목적으로 작성하는 글인 만큼, 내용상 오류 및 번역 오류가 있을 가능성이 있습니다. 참고용으로만 봐 주세요!
2025-08-15 WIP (~46p)
Chapter 1 - A Tour of Computer Systems
- 컴퓨터 시스템 : 하드웨어 + 시스템 소프트웨어
- 모든 컴퓨터 시스템은 비슷한 기능을 하는, 비슷한 소프트웨어 및 하드웨어 요소들로 구성되어 있음
- Chapter 1은 아래의
hello 프로그램의 작성부터 실행까지의 과정을 살펴보며 이후에 등장할 개념들을 소개하는 구성임
#include <stdio.h>
int main()
{
printf("hello, world\n");
return 0;
}
1.1 Information is Bits + Context
hello 프로그램은 프로그래머가 에디터로 작성하고 텍스트 파일(hello.c)로 저장한 소스 프로그램(소스 파일)로 시작함
- 소스 프로그램은 0 또는 1의 값을 가지는 비트(bit)의 배열이며, 이 비트들은 8개씩(8비트씩) 묶여 1개의 바이트(byte) 단위로 구성됨
- 대부분 컴퓨터 시스템들은 ASCII 표준으로 문자를 나타내는데, 이때 하나의 문자는 1바이트 크기의 숫자로 표현됨
hello.c는 ASCII에 따라 아래의 표의 기준대로 저장됨

- 모든 바이트는 어떠한 문자(character)에 대응하는 정수 값을 저장하고 있음
- 이때 모든 줄은 사용자에게 보이지 않는,
10의 정수값을 가지는 개행문자(newline) \n로 끝남
hello.c와 같이 ASCII 문자로만 구성되어 있는 파일을 텍스트(text) 파일이라고 하며, 다른 모든 파일을 바이너리(binary) 파일이라고 함.
- 컴퓨터 시스템의 모든 정보는 비트 덩어리로 구성되어 있음
- 데이터 덩어리들을 구분하는 유일한 기준은 그 데이터를 보는 맥락(context)임
- 같은 바이트의 배열도 맥락에 따라 정수를 나타내거나, 부동소수점을 나타내거나, 문자열을 나타내거나, 기계어 명령 등을 나타낼 수 있음.
hello 프로그램은 인간이 읽을 수 있는 형태의 고수준(high-level) C 프로그램으로 시작함
hello.c를 컴퓨터에서 실행하려면 C언어 문장들이 다른 프로그램에 의해 저수준(low-level)인 기계어(machine-language) 명령들로 변환되어야 함
- 번역된 명령들은 실행 파일(executable object program)로 변환되어 바이너리 파일로 저장됨
linux> gcc -o hello hello.c
- UNIX 시스템에서의 소스 파일 -> 실행 파일(오브젝트) 변환은 컴파일러 드라이버(compiler driver)가 실행함
gcc 컴파일러 드라이버는 소스 파일(hello.c)를 읽고 실행 파일 hello로 변환함

- 전처리기(preprocessor), 컴파일러(compiler), 어셈블러(assembler), 링커(linker) 총 4계의 단계를 거치며, 이 4단계를 합쳐서 컴파일러 시스템(compilation system)이라고 함
전처리기 과정 (Preprocessing phase)
- 전처리기(
cpp)는 코드를 #으로 시작하는 지시문에 따라 수정함
#include <stdio.h> 지시는 전처리기에 시스템 헤더 파일 stdio.h의 내용을 프로그램에 삽입하라는 지시임
- 이 과정으로 수정된 C 프로그램 (
.i로 끝남)이 생성됨
컴파일러 과정 (Compilation phase)
- 컴파일러(
cc1)은 hello.i를 hello.s로 변환하는데, 이때 hello.s는 어셈블리어 프로그램을 포함하는 텍스트 파일임
main 함수의 경우 다음과 같이 변환됨
main:
subq $8, %rsp
movl $.LC0, %edi
call puts // puts 함수 호출
movl $0, %eax
addq $8, %rsp
ret
- subq ~ ret는 모두 각각 1개의 기계어 명령을 텍스트 형태로 나타낸 것임
어셈블리 과정 (Compilation phase)
- 어셈블러(
as)는 hello.s를 기계어 명령들로 변환한 후 오브젝트 파일(relocatable object program)이라는 형태로 포장해 hello.o로 저장함
hello.o는 main 함수의 명령들을 저장하는 바이너리 파일임
링크 과정 (Compilation phase)
hello.c는 C 표준 라이브러리의 printf 함수를 호출하는데, 이때 printf 함수는 printf.o라는 미리 컴파일되어 있는 다른 파일에 저장되어 있음
- 프로그램이 작동하려면
printf.o를 hello.o랑 합쳐야 하는데, 링커(ld)가 이 합치는 과정을 담당하며, 결과물로 hello라는 실행 파일(executable)을 생성함
- 이
hello 파일은 이제 메모리에 탑재해 실행할 수 있음
1.3 It Pays to Understand How Compilation Systems Work
- 간단한 프로그램에서는 컴파일러가 보통 알아서 잘함
- 그러나 프로그래머들이 컴파일러 시스템의 작동 원리를 이해해야 하는 중요한 이유들이 있음
- 현대의 컴파일러들은 대부분의 경우 좋은 코드를 출력하고, 굳이 우리가 컴파일러의 내부 구조를 알 필요는 없음
- 그러나, C 프로그램을 만들 때 올바른 결정을 내리기 위해서는 기계어 및 컴파일러 과정을 이해할 필요가 있음
링크 타임 오류 이해 (Understanding link-time errors)
- 프로그래밍에서 가장 어렵고 이해하기 어려운 오류 중 하나는 링커 오류임
- 일부 링커 오류는 프로그램 실행 전까지 나타나지 않기도 함
보안 실수 피하기 (Avoiding security holes)
- 버퍼 오버플러우(buffer overflow) 취약점은 수년 동안 네트워크 및 인터넷 분야에서 보안 이슈가 되었음
- 신뢰하지 않는 출처에서 온 데이터는 조심스럽게 접근하고 다룰 필요가 있음
1.4 Processors Read and Interpret Instructions Stored in Memory
- 위의 과정을 통해 소스 프로그램
hello.c는 실행 가능한 hello 오브젝트 파일로 바뀜
- UNIX 시스템에서는 실행을 하기 위해
shell(셸)이라는 프로그램에 프로그램 이름을 입력함
linux> ./hello
hello, world
linux>
shell(셸)은 명령어 해석기로, 사용자의 명령을 입력받아 수행함
- 명령의 첫 번째 단어가 셸의 내장 명령이 아니면 해당 단어가 실행 파일 이름이라고 간주함
- 셸은 다음과 같은 과정을 반복함
1. 특정 프롬프트를 출력 (linux>)
2. 입력 대기 -> 입력 받음 (./hello)
3. 입력된 명령 수행 (hello, world)
4. -> 다시 1번부터 시작함 (>linux)
1.4.1 Hardware Organization of a System
hello 프로그램을 실행했을 때 어떤 일이 일어나는지 이해하기 위해서 일반적인 컴퓨터 시스템의 하드웨어 구조를 알아야 함
- 아래의 구조는 최신 Intel 시스템의 구조를 따른 것으로, 모든 컴퓨터 시스템들은 대체로 비슷한 구성을 가짐
- 아래 그림은 대략적인 요약임 (실제로는 더 복잡하다)

Buses (버스)
- 버스(bus)는 시스템 구성요소 사이에서 정보를 전달하는 전기 회로임
- 버스는
word(워드)라고 하는 정해진 크기의 바이트들을 단위로 정보를 전달하며, 이때 워드 1개 안에 있는 바이트 수 (= 워드 크기, word size)는 매우 중요한 시스템 변수임
- 현대 컴퓨터 시스템의 워드 크기는 시스템에 따라 다르지만 보통 4바이트(32비트) 또는 8바이트(64바이트)임
cf. 32비트, 64비트
- 컴퓨터 프로그램을 설치하다 보면 종종 설치 파일이 32비트/64비트 (혹은 x86/x64) 구분이 되어 있는 것을 볼 수 있다.
- 이는 워드 크기의 차이로 인한 것으로, 32비트 워드를 가진 시스템에는 32비트 설치 파일을, 64비트 시스템에는 64비트 설치 파일을 이용해야 한다.
- (Windows와 같은 운영 체제에서는 64비트 시스템에서도 호환성을 위해 지원한다.)
I/O Devices (입출력 장치)
- Input/Output Devices (I/O Devices, 입출력 장치)는 시스템을 외부(사용자)와 연결함
- 예를 들어, 위 그림에서 I/O 장치는 마우스, 키보드, 디스플레이, 디스크 드라이브 총 4개임
- 각각의 I/O 장치는
controller(컨트롤러) 또는 adapter(어댑터)에 의해 I/O 버스에 연결되며, 컨트롤러/어댑터는 I/O 버스와 I/O 장치 간 정보 전달을 가능하게 함
- 컨트롤러와 어댑터의 차이는 패키징의 여부임
- 컨트롤러는 장치에 붙어있거나 시스템의 메인 회로 (메인보드)에 장착되어 있음
- 어댑터는 메인보드에 꽂을 수 있는 형태의 장치임
Main Memory (메인 메모리)
- 메인 메모리(Main Memory)는 임시 저장 공간으로, 프로그램이 실행되는 동안 프로그램 그 자체및 프로그램이 다루는 데이터를 저장함
- 물리적으로, 메인 메모리는 DRAM(Dynamic Random Access Memory) 칩들로 구성되어 있음
- 논리적으로, 메인 메모리는 바이트들의 배열로, 이때 모든 칸들은 0부터 시작하는 고유한 주소(배열 인덱스)를 가짐
- (ex) [ 0x0 | 0x1 | 0x2 | 0x3 | 0x4 | 0x5 | ... ]
- 간단하게 말하자면, 프로그래밍 언어에서의 배열(array)와 같은 구조를 생각하면 됨
- 프로그램을 구성하는 기계어 명령은 일정한 바이트 수를 차지함
- 자료형에 따라 서로 다른 공간을 메모리에서 차지함 (
char는 보통 1바이트, int는 보통 4바이트...)
Processor (프로세서)
- 프로세서(Processor/CPU)는 메인 메모리에 저장된 명령들을 해석(실행)함
- 프로세서의 중심에는 프로그램 카운터(Program Counter, PC)가 있음
- PC는 레지스터(register)라고 불리는 워드 사이즈 크기의 저장 장치임
- PC는 항상 메인 메모리에 저장되어 있는 어떤 기계어 명령의 주소값을 저장함
- 시스템이 시작될 때부터 종료될 때까지, PC는 저장되어 있는 주소값에 있는 명령을 수행한 후, 다음 명령을 수행할 수 있도록 PC에 저장된 주소 값을 업데이트하는 과정을 반복함
- 프로세서는 ISA(Instruction Set Architecture)라는 간단한 명령 모델에 따라 작동한다고 볼 수 있음
- 여기서 명령은 일정한 순서에 따라 실행되며 하나의 명령을 실행하려면 다음의 과정을 거쳐야 함
- 프로세서는 프로그램 카운터의 주소를 가지고 메모리에서 명령을 읽고 해석함
- 명령에서 지시하는 연산을 실행함
- 프로그램 카운터가 다음 명령를 지시하도록 주소값을 업데이트함
- 연산은 메인 메모리, 레지스터 파일(register file), 산술 논리 장치(ALU, Arithmetic/Logic Unit)을 통해 실행됨
- 레지스터 파일은 워드 사이즈만큼의 크기를 가지는 레지스터 여러 개로 구성된 저장 장치로, 각각의 레지스터는 고유한 이름을 가짐
- ALU는 데이터 및 메모리 주소값을 계산함
- 일반적으로 다음과 같은 연산이 시행됨
- 로드(Load) : 데이터를 바이트/워드만큼 메인 메모리에서 레지스터로 복사함 (레지스터에 기존에 있던 데이터는 지워짐)
- 저장(Store) : 데이터를 바이트/워드만큼 레지스터에서 메모리 공간으로 복사함 (메모리 상에 기존에 있던 데이터는 지워짐)
- 계산(Operate) : 레지스터 2개의 값을 ALU로 복사해서 수학적 연산을 시행한 후 결과값을 어떠한 레지스터에 저장함 (해당 레지스터에 기존에 있던 데이터는 지워짐)
- 점프(Jump) : 명령어 자체에서 데이터를 워드만큼 추출해서 프로그램 카운터로 복사함 (PC의 이전 주소값은 지워짐)
- 예시) A레지스터에 'n'의 값을 로드하고, B레지스터에 'm'의 값을 로드하고, A와 B의 값으로 계산을 한 결과를 C레지스터에 저장...
- 위에 나온 ISA는 간소화된 모델로, 실제로 최근의 프로세서는 매우 복잡한 작동 원리를 이용해 프로그램의 실행을 가속함
- 실제 프로세서의 구현 방식을 설명하는 것을 마이크로아키텍처(microarchitecture)라고 함.
cf. ISA란?
텍스트에서 간략하게 설명한 ISA를 조금 더 알아보기
- ISA는 하드웨어와 소프트웨어 사이의 인터페이스 역할을 하며, 프로세서가 무엇을 할 수 있으며 어떻게 일을 하는지 기술함
- ISA는 프로세서가 지원하는 데이터형, 프로세서의 레지스터, 하드웨어가 메모리를 관리하는 방식, 주요 프로세서 기능 (ex. 가상 메모리), 프로세서가 실행할 수 있는 명령 등을 기술함.
- "프로그래머의 메뉴얼"이라고 할 수 있음
- 마이크로아키텍쳐는 "ISA의 구현"이라고 요약할 수 있겠음
참고: ARM 단어 목록 (Glossary)
1.4.2 Running the hello Program
1.5 Caches Matter
cf. 컴파일러와 printf
hello.c의 어셈블리어 출력을 보면 call puts라는 부분을 볼 수 있음
Q)hello.c에서는 분명 printf 함수를 사용했는데 왜 어셈블리어 출력에는 puts가 있는 걸까?
A) 컴파일러의 코드 최적화