크래프톤 정글 (컴퓨터 시스템: CSAPP)/3장 프로그램의 기계수준 표현

컴퓨터 시스템 : CSAPP 3장 정리 - 3.2장 프로그램의 인코딩 Part. 1

고웅 2025. 4. 5. 08:45

🧠 핵심 질문: 우리가 만든 프로그램은 컴퓨터에게 어떻게 전달될까?

우리가 C 같은 언어로 프로그램을 짜면, 그 코드는 컴퓨터가 직접 이해할 수 없다. 컴퓨터는 오직 기계어라는 아주 특별한 형태의 언어만 이해한다. 그래서 우리가 만든 코드는 컴퓨터가 이해할 수 있는 모양으로 인코딩(Encoding) 되어야 한다.

이 인코딩은 기계 수준 코드(Machine-Level Code)라고 부르며, 그 과정을 거치면 컴퓨터가 그 프로그램을 메모리에 올리고, 실행할 수 있게 된다.


🏗️ 컴퓨터가 프로그램을 실행할 준비를 하는 과정

컴퓨터는 다음과 같은 과정으로 우리가 만든 코드를 처리한다:

  1. 컴파일러가 C 코드 같은 고급 언어를 받아서 어셈블리 코드라는 저수준 언어로 바꿔준다.
  2. 어셈블러가 이 어셈블리 코드를 읽어서 기계가 이해할 수 있는 숫자 코드, 즉 기계어(binary code)로 바꾼다.
  3. 링커가 여러 파일들을 합쳐서 실행 가능한 파일을 만든다.
  4. 로더가 그 실행 파일을 컴퓨터 메모리에 올려서 준비시킨다.
  5. 프로세서(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 코드가 어떻게 동작하는지 깊이 이해할 수 있어.
  • 컴파일러가 어떻게 코드를 변환하는지, 성능을 높이기 위해 어떤 트릭을 쓰는지도 볼 수 있어.
  • 보안 문제(예: 버퍼 오버플로우) 를 이해하고 막을 수 있어.
  • 디버깅(버그 찾기) 할 때 큰 도움이 돼!