컴퓨터 시스템 : CSAPP 3장 정리 - 3.2장 프로그램의 인코딩 Part. 1
🧠 핵심 질문: 우리가 만든 프로그램은 컴퓨터에게 어떻게 전달될까?
우리가 C 같은 언어로 프로그램을 짜면, 그 코드는 컴퓨터가 직접 이해할 수 없다. 컴퓨터는 오직 기계어라는 아주 특별한 형태의 언어만 이해한다. 그래서 우리가 만든 코드는 컴퓨터가 이해할 수 있는 모양으로 인코딩(Encoding) 되어야 한다.
이 인코딩은 기계 수준 코드(Machine-Level Code)라고 부르며, 그 과정을 거치면 컴퓨터가 그 프로그램을 메모리에 올리고, 실행할 수 있게 된다.
🏗️ 컴퓨터가 프로그램을 실행할 준비를 하는 과정
컴퓨터는 다음과 같은 과정으로 우리가 만든 코드를 처리한다:
- 컴파일러가 C 코드 같은 고급 언어를 받아서 어셈블리 코드라는 저수준 언어로 바꿔준다.
- 어셈블러가 이 어셈블리 코드를 읽어서 기계가 이해할 수 있는 숫자 코드, 즉 기계어(binary code)로 바꾼다.
- 링커가 여러 파일들을 합쳐서 실행 가능한 파일을 만든다.
- 로더가 그 실행 파일을 컴퓨터 메모리에 올려서 준비시킨다.
- 프로세서(CPU)가 이걸 한 줄 한 줄 읽어서 실행한다.
즉, 우리가 짠 코드는 여러 과정을 거쳐 컴퓨터가 이해할 수 있는 숫자들의 나열이 된다.
💾 프로그램은 메모리에 저장되고 주소로 접근한다.
컴퓨터는 명령어나 데이터를 메모리에 저장하며 메모리는 숫자로 된 주소(address)로 나눠져 있어서, 컴퓨터는 그 주소를 보고 어떤 명령을 실행해야 할지 결정한다.
예를 들어, 주소 0x4005b4에 있는 명령을 실행하고, 그다음 주소로 이동하면서 프로그램을 진행시킨다.
📈 무어의 법칙(Moore's Law)
🌟 무어의 법칙이란?
“컴퓨터 칩 안에 들어가는 트랜지스터 수는 약 18개월마다 2배로 증가한다”
— 고든 무어(Gordon Moore), 인텔 공동 창업자
이 말은 컴퓨터가 점점 더 빠르고 똑똑해진다는 뜻이다.
- 예전에는 컴퓨터가 아주 느리고 간단한 계산만 했지만, 지금은 엄청나게 복잡하고 빠른 작업도 할 수 있다.
- 이 모든 발전은 트랜지스터(컴퓨터 내부의 전기 스위치 역할)가 아주 작아지고 많아졌기 때문이다.
하지만 요즘은 무어의 법칙이 속도 저하를 겪고 있다. 물리적으로 트랜지스터를 너무 작게 만들기 힘들어졌기 때문이다.
🤔 왜 이걸 배우는 걸까?
- 우리가 짠 프로그램이 실제로 어떻게 작동하는지, 어떻게 기계어로 바뀌는지 이해하면, 성능 좋은 프로그램을 만들 수 있고, 버그나 보안 문제도 더 쉽게 찾고 고칠 수 있다.
예를 들어, 컴파일러가 어떤 식으로 코드를 바꾸는지 이해하면 우리가 어떻게 짜야 더 빠른 코드가 나오는지 알 수 있다.
💻 3.2.1 기계수준 코드
🧠 기계수준 코드란?
🧾 컴퓨터가 진짜로 이해하는 언어
우리가 C 언어 같은 걸로 코딩하면, 그건 사람이 읽기 편한 언어이다. 하지만 컴퓨터는 오직 숫자(0과 1)로 된 기계어(machine code)만 이해할 수 있다. 이 기계어는 너무 복잡하고 어려워서, 사람은 보통 어셈블리 언어(assembly language)로 읽고 해석한다.
🧩 컴퓨터 프로그램은 메모리에 저장된다
컴퓨터는 프로그램을 실행하기 위해 그 내용을 메모리에 저장한다. 그리고 이 메모리는 작은 칸(바이트 단위)으로 나눠져 있고, 각 칸은 주소라는 숫자를 갖고 있다. 컴퓨터는 주소를 따라가며 명령을 차례차례 읽고 실행한다.
🖥️ 프로그램의 상태 (Processor State)
컴퓨터가 프로그램을 실행할 때는 몇 가지 중요한 내부 상태를 가지고 있다:
1. 프로그램 카운터 (Program Counter, PC)
- 지금 실행할 다음 명령어가 저장된 메모리 주소를 가리킴.
- 매번 명령을 실행할 때마다 한 칸씩 앞으로 이동한다.
2. 레지스터 (Registers)
- 컴퓨터 안에 있는 작은 기억장소 계산할 때 필요한 숫자를 잠깐 저장하는 공간이다.
- x86-64 컴퓨터에는 16개의 64비트 레지스터가 있다. 예를 들어 rax, rbx, rcx, rdx, rsp, rbp, rsi, rdi 등이 있다.
3. 조건 코드 (Condition Codes)
- 방금 한 계산의 결과가 0인지, 음수인지, 오버플로우가 났는지 등을 표시해준다.
- 이 정보는 if문이나 반복문 같은 조건 판단에 사용된다.
4. 벡터 레지스터 (Vector Registers)
- 여러 데이터를 한꺼번에 저장하고 처리할 수 있는 특수한 레지스터이다.
- SIMD(한 번에 여러 데이터 처리) 연산에서 쓰인다.
📋 어셈블리 언어는 기계어의 친구!
기계어는 너무 어렵기 때문에, 프로그래머는 보통 어셈블리 언어를 사용한다. 이건 기계어와 거의 1:1로 대응되지만, 사람이 읽을 수 있게 글자와 단어로 표현돼 있다.
예시:
movq $5, %rax # rax 레지스터에 5를 넣어라
addq $3, %rax # rax에 3을 더해라
이런 명령은 기계어로는 10110000 00000101 같은 식의 0과 1로 바뀌게 된다.
🚀 왜 기계수준 코드를 알아야 할까?
- C 코드가 어떻게 동작하는지 깊이 이해할 수 있어.
- 컴파일러가 어떻게 코드를 변환하는지, 성능을 높이기 위해 어떤 트릭을 쓰는지도 볼 수 있어.
- 보안 문제(예: 버퍼 오버플로우) 를 이해하고 막을 수 있어.
- 디버깅(버그 찾기) 할 때 큰 도움이 돼!