C 언어 프로그램은 어떻게 실행될까?

한 문장 요약

C 코드 → 컴파일 → 기계어(바이너리) → 운영체제가 CPU에서 직접 실행


📚 배경 개념

프로그래밍 언어의 두 가지 방식

┌──────────────────────────┐
│ 컴파일 언어 (Compiled)     │
├──────────────────────────┤
│ C, C++, Go, Rust         │
│                          │
│ 특징:                    │
│ - 사전에 기계어로 변환    │
│ - 속도 매우 빠름         │
│ - 실행 전 에러 감지      │
└──────────────────────────┘

┌──────────────────────────┐
│ 인터프리터 언어 (Interpreted)│
├──────────────────────────┤
│ Python, JavaScript, Ruby │
│                          │
│ 특징:                    │
│ - 실행 중에 해석         │
│ - 개발 속도 빠름         │
│ - 느린 실행 속도         │
└──────────────────────────┘

⚙️ 컴파일 과정 (C 언어 예시)

전체 흐름

┌──────────────────────────────┐
│ 1️⃣ 소스 코드 작성             │
│  (database.c)               │
│  - 인간이 읽을 수 있는 C 코드 │
└──────────────┬───────────────┘
               │
┌──────────────▼───────────────┐
│ 2️⃣ 컴파일러 (gcc, clang)     │
│  - C 코드 문법 검사           │
│  - C 코드를 기계어로 변환     │
│  - 최적화                    │
└──────────────┬───────────────┘
               │
┌──────────────▼───────────────┐
│ 3️⃣ 바이너리 파일              │
│  (postgresql 실행 파일)       │
│  - 기계어 (0101101...)       │
│  - CPU가 직접 실행 가능       │
│  - .exe (Windows)            │
│  - (macOS, Linux)            │
└──────────────┬───────────────┘
               │
┌──────────────▼───────────────┐
│ 4️⃣ 운영체제 (OS)            │
│  - 바이너리를 메모리에 로드   │
│  - CPU에서 기계어 실행        │
└──────────────────────────────┘

💻 구체적인 예시

PostgreSQL 컴파일 과정

# 1️⃣ 소스 파일들
src/backend/parser/parse.c
src/backend/executor/execMain.c
src/backend/storage/file/bufmgr.c
... (1000개 이상의 .c 파일)
 
# 2️⃣ 컴파일
$ gcc -O2 src/backend/*.c -o postgresql
 
# 과정:
# - 각 .c 파일을 .o (오브젝트) 파일로 변환
# - 모든 .o 파일을 링크
# - 최적화 (-O2 플래그)
 
# 3️⃣ 결과
postgresql (바이너리 실행 파일)
├─ 크기: ~50MB
├─ 내용: 기계어 명령어
└─ 구성: CPU가 이해하는 바이너리
 
# 4️⃣ 실행
$ ./postgresql
또는
$ postgres -D /data/postgres

🔍 C 언어와 기계어의 관계

C 코드 vs 기계어

C 코드 (인간이 읽음)

// C 코드
int sum(int a, int b) {
    return a + b;
}

기계어 (CPU가 읽음)

; x86-64 어셈블리 (기계어를 사람이 읽을 수 있게)
sum:
    add edi, esi      ; a + b 연산
    mov eax, edi      ; 결과를 반환 레지스터에
    ret               ; 함수 반환

실제 바이너리 (CPU가 읽음)

01100101 01110001
01001000 10010100
... (0과 1의 나열)

🚀 컴파일 vs 인터프리터 성능 비교

실행 속도

┌──────────────────────────────────┐
│ 컴파일 언어 (C)                  │
│ [컴파일] → [실행]                │
│ 느림     빠름                     │
│                                  │
│ 프로그램 실행 시간: 0.001초 ⚡⚡⚡ │
└──────────────────────────────────┘

┌──────────────────────────────────┐
│ 인터프리터 언어 (Python)         │
│ [인터프리터 시작]                │
│ [라인별 해석] → [실행]           │
│ 빠름     느림                     │
│                                  │
│ 프로그램 실행 시간: 0.05초 ⚡    │
└──────────────────────────────────┘

백만 개 정수 합계:

C 언어:     0.002초 ⚡⚡⚡
Python:     0.052초 ⚡
50배 빠름!

🐍 CPython의 경우 (Python은 인터프리터인데?)

CPython = C로 만들어진 인터프리터

┌────────────────────────────────┐
│ Python 코드 (script.py)        │
│ def sum(a, b):                │
│     return a + b              │
└──────────────┬─────────────────┘
               │
┌──────────────▼─────────────────┐
│ CPython (C로 작성된 인터프리터) │
│                                │
│ [Python 코드 해석]             │
│ ├─ 문법 검사                   │
│ ├─ 바이트코드 생성             │
│ └─ 바이트코드 실행             │
└──────────────┬─────────────────┘
               │
┌──────────────▼─────────────────┐
│ 결과                            │
│ 3 (sum(1, 2)의 결과)           │
└────────────────────────────────┘

핵심:

  • CPython은 C로 컴파일된 프로그램
  • 하지만 Python 코드는 런타임에 해석
  • 그래서 Python은 느림

📊 언어별 실행 방식 비교

언어컴파일 방식작성 언어실행 방식속도
C컴파일CCPU 직접 실행⚡⚡⚡ 빠름
Go컴파일GoCPU 직접 실행⚡⚡⚡ 빠름
Java컴파일 → 바이트코드JavaJVM 해석⚡⚡ 중간
Python없음PythonCPython 해석⚡ 느림
JavaScript없음JavaScriptV8/Node 해석⚡ 느림

🎯 왜 PostgreSQL과 MySQL은 C로 만들어졌나?

성능이 중요한 이유

데이터베이스는 초당 수천 개의 쿼리를 처리해야 함
  ↓
각 쿼리가 빨라야 함
  ↓
C 언어로 컴파일해서 CPU 직접 실행
  ↓
Python/Java보다 50-100배 빠름

선택지

❌ Python으로 DB 엔진 만들기
   - 초당 10 쿼리 처리
   - 1000명 동시 접속 불가능

✅ C로 DB 엔진 만들기
   - 초당 10,000 쿼리 처리
   - 1000명 동시 접속 가능

🔗 컴파일과 드라이버의 관계

드라이버도 C로 컴파일됨

┌────────────────────────────────┐
│ JDBC 드라이버 (Java)           │
│ - Java로 작성                  │
│ - JVM에서 실행                 │
│ → 약간 느림                    │
└────────────────────────────────┘

┌────────────────────────────────┐
│ libpq 드라이버 (C)             │
│ - C로 작성                     │
│ - 컴파일되어 실행              │
│ → 매우 빠름                    │
└────────────────────────────────┘

결과:

  • C 드라이버 + C 엔진 = 초고속 ⚡⚡⚡
  • Java 드라이버 + C 엔진 = 중간 ⚡⚡

💡 핵심 요약

컴파일 과정

C 소스 코드
  ↓ (컴파일러)
기계어 바이너리
  ↓ (운영체제)
CPU 직접 실행
  ↓
초고속 ⚡⚡⚡

인터프리터 과정

Python 소스 코드
  ↓ (CPython 인터프리터)
라인별 해석 & 실행
  ↓
느림 ⚡

🔗 관련 개념

다음 단계:

관련 포스트: