Skip to main content
Summary

부품만으로는 아직 잠든 기계다

CPU, 메모리, 저장장치, 입력 장치, 출력 장치가 모두 연결되어 있다고 해 보자. 기판 위에는 길이 있고, 칩 안에는 수많은 트랜지스터가 있으며, 메모리는 0과 1을 담을 준비가 되어 있다.

그런데 전원을 켠다고 해서 컴퓨터가 저절로 글을 쓰고, 그림을 그리고, 게임을 실행하는 것은 아니다. 부품들은 "무엇을 해야 하는지" 모른다. 아주 빠르게 움직일 수 있는 공장과 도로가 있어도 작업 지시서가 없으면 아무 일도 시작되지 않는 것과 같다.

그 작업 지시서가 바로 소프트웨어다. 소프트웨어는 컴퓨터가 따라야 할 명령들의 묶음이다. CPU는 이 명령을 하나씩 읽고, 해석하고, 실행한다. 앞에서 배운 것처럼 CPU는 메모리에서 명령어를 가져오고, ALU로 계산하고, 레지스터에 값을 저장하고, 필요한 경우 장치와 대화한다.

중요한 점은 이것이다.

컴퓨터는 사람의 의도를 바로 이해하지 못한다. 컴퓨터가 직접 이해하는 것은 결국 정해진 모양의 0과 1로 된 명령어다.

소프트웨어가 하드웨어를 깨우는 법AI 생성 이미지

사람이 쓰는 코드가 숫자 명령으로 바뀌는 길

사람은 보통 이런 식으로 생각한다.

"두 수를 더해서 결과를 저장해."

프로그래밍 언어로 쓰면 다음처럼 보일 수 있다.

result = a + b

이 문장은 사람에게는 꽤 읽기 쉽다. a와 b를 더해 result에 넣으라는 뜻이다. 하지만 CPU는 result라는 영어 단어도, + 기호의 의미도 스스로 이해하지 못한다. CPU는 자기 명령어 집합에 맞는 숫자 명령만 실행할 수 있다.

그래서 중간에 번역 과정이 필요하다.

사람이 쓰는 코드
→ 컴파일러가 낮은 수준의 명령으로 번역
→ 실행 파일 또는 실행할 수 있는 코드가 됨
→ 실행할 때 메모리에 올라감
→ CPU가 한 명령씩 실행

컴파일러는 사람에게 가까운 프로그래밍 언어를 CPU가 실행할 수 있는 낮은 수준의 코드로 바꾸는 번역기다. 외국어 문장을 다른 언어로 옮기는 번역가와 비슷하지만, 더 엄격하다. 컴퓨터 명령은 애매하면 안 된다. "대충 더해 줘"가 아니라 "어느 레지스터의 값을 읽고, 어떤 연산을 하고, 결과를 어디에 저장하라"처럼 정확해야 한다.

예를 들어 result = a + b라는 코드는 실제로는 이런 작은 행동들로 나뉠 수 있다.

메모리에서 a 값을 가져온다.
메모리에서 b 값을 가져온다.
a와 b를 더한다.
결과를 result 위치에 저장한다.

CPU 입장에서는 한 줄짜리 코드가 여러 개의 작은 명령으로 바뀐다. 프로그램이 길어질수록 이런 명령들이 아주 많이 생긴다.

컴파일러가 만든 결과는 보통 바로 끝이 아니다. 여러 코드 조각과 미리 만들어진 기능을 이어 붙여 실행 파일을 만드는 과정도 있고, 실행할 때 운영체제가 그 파일을 메모리에 올리는 과정도 있다. 어떤 언어는 인터프리터나 가상 머신이라는 중간 단계를 거치기도 한다. 하지만 큰 그림은 간단하다. 사람이 읽기 쉬운 코드는 결국 CPU가 움직일 수 있는 낮은 수준의 명령으로 이어진다.

기계어: CPU가 직접 읽는 숫자 카드

기계어는 CPU가 직접 실행하는 0과 1의 명령어다. CPU마다 이해하는 명령어의 종류와 모양이 다를 수 있다. 어떤 CPU는 특정 숫자 묶음을 "더하기"로 해석하고, 다른 CPU는 같은 숫자 묶음을 전혀 다른 뜻으로 해석할 수도 있다.

기계어는 사람 눈에는 매우 불친절하다.

10110000 00000101
00000100 00000011

이런 숫자만 보고 "아, 값을 넣고 더하는구나" 하고 알아보기는 어렵다. CPU에게는 정확한 명령이지만, 사람에게는 거의 암호처럼 보인다.

그래서 사람이 기계어에 조금 더 가까이 다가가기 위해 만든 언어가 있다. 바로 어셈블리어다.

어셈블리어: 기계어에 붙인 짧은 이름표

어셈블리어는 CPU 명령어와 매우 가까운 언어다. 기계어 숫자를 사람이 조금 읽기 쉬운 짧은 단어로 바꿔 놓은 것에 가깝다.

예를 들어 어떤 CPU를 상상해 보자. 이 CPU에서는 다음과 같은 어셈블리어를 쓸 수 있다고 하자.

LOAD R1, a
LOAD R2, b
ADD R3, R1, R2
STORE result, R3

뜻은 이렇다.

  • LOAD R1, a: 메모리의 a 값을 레지스터 R1에 가져온다.

  • LOAD R2, b: 메모리의 b 값을 레지스터 R2에 가져온다.

  • ADD R3, R1, R2: R1과 R2를 더해 R3에 넣는다.

  • STORE result, R3: R3 값을 메모리의 result 위치에 저장한다.

어셈블리어는 고급 프로그래밍 언어보다 훨씬 자세하다. "두 수를 더해"라고 한 번에 말하지 않고, 값을 어디에서 가져오고 어느 레지스터를 쓰며 어디에 저장할지 직접 적는다.

어셈블리어가 CPU 명령어와 가까운 이유는 보통 어셈블리어 한 줄이 기계어 명령 하나와 거의 대응되기 때문이다. ADD라는 단어는 실제로는 CPU가 이해하는 특정한 0과 1의 숫자로 바뀐다. 이 일을 해 주는 프로그램을 어셈블러라고 한다.

정리하면 층은 이렇게 볼 수 있다.

사람이 읽기 쉬운 코드
↓ 컴파일러
어셈블리어에 가까운 낮은 수준의 명령
↓ 어셈블러
기계어 0과 1
↓ CPU
실제 회로 동작

물론 실제 컴퓨터에서는 이 과정이 더 복잡할 수 있다. 컴파일러가 바로 기계어를 만들기도 하고, 중간 표현을 거치기도 하며, 여러 파일을 하나로 묶는 과정이 끼어들기도 한다. 하지만 핵심은 변하지 않는다. 소프트웨어는 점점 아래층으로 내려가며 더 구체적인 명령이 되고, 마지막에는 전기 신호가 회로를 움직인다.

운영체제: 모든 프로그램의 무대 감독

이제 기계어 프로그램을 만들 수 있다고 해 보자. 그렇다면 프로그램은 바로 하드웨어를 마음대로 써도 될까?

작은 실험용 컴퓨터라면 가능할 수도 있다. 하지만 우리가 쓰는 컴퓨터에는 동시에 많은 일이 일어난다. 음악이 재생되고, 웹 브라우저가 열리고, 키보드 입력을 받고, 파일을 저장하고, 화면을 계속 새로 그린다.

이 많은 일을 프로그램들이 제멋대로 하게 두면 금방 문제가 생긴다. 두 프로그램이 같은 메모리 칸을 동시에 쓰려고 할 수도 있고, 한 프로그램이 저장장치를 혼자 오래 붙잡을 수도 있다. 화면에 서로 다른 그림을 아무 규칙 없이 그리려 할 수도 있다.

그래서 필요한 것이 운영체제다. 운영체제는 컴퓨터의 자원을 관리하는 기본 소프트웨어다. 스마트폰의 안드로이드나 iOS, PC의 윈도우, macOS, 리눅스 같은 것들이 운영체제다.

운영체제는 무대 감독과 비슷하다. 배우인 프로그램들이 무대에 올라와 움직일 수 있게 하지만, 조명과 문과 소품을 아무나 마음대로 만지게 두지는 않는다.

운영체제가 하는 중요한 일은 크게 네 가지로 볼 수 있다.

첫째, 메모리 관리다. 프로그램마다 쓸 수 있는 메모리 공간을 나누어 준다. 한 프로그램이 다른 프로그램의 공간을 함부로 망가뜨리지 못하게 막는다.

둘째, 파일 관리다. 저장장치 위의 0과 1을 파일과 폴더로 정리한다. 사용자는 사진.jpg나 숙제.txt 같은 이름으로 파일을 보지만, 실제 저장장치 안에서는 위치와 크기, 빈 공간 정보가 함께 관리된다.

셋째, 장치 관리다. 키보드, 마우스, 화면, 스피커, 저장장치, 네트워크 장치처럼 다양한 부품과 프로그램 사이를 이어 준다. 이때 장치 사용법을 알려 주는 특별한 소프트웨어가 드라이버다.

넷째, 프로그램 실행 관리다. 운영체제는 어떤 프로그램을 실행할지, CPU 시간을 어떻게 나눌지, 프로그램이 끝나면 자원을 어떻게 돌려받을지 관리한다. 단순한 CPU 한 코어를 떠올리면 한순간에 한 흐름의 명령만 실행하는 것처럼 볼 수 있다. 그래도 운영체제가 아주 빠르게 순서를 바꾸어 주면 사람에게는 여러 프로그램이 동시에 움직이는 것처럼 보인다. 실제 컴퓨터는 여기에 여러 코어와 더 복잡한 실행 방식까지 더해져 동시에 더 많은 일을 처리한다.

프로그램은 운영체제에게 부탁한다

프로그램이 파일을 저장하고 싶다고 해 보자. 프로그램이 저장장치의 세세한 구조를 전부 알아야 한다면 너무 힘들 것이다. SSD인지 하드디스크인지, 어느 위치가 비어 있는지, 파일 이름은 어디에 기록해야 하는지 모두 직접 처리해야 한다.

대신 프로그램은 운영체제에게 부탁한다.

"이 이름으로 파일을 만들어 줘."
"이 데이터를 파일에 써 줘."
"키보드에서 들어온 글자를 알려 줘."
"화면에 이 그림을 보여 줘."

운영체제는 이 부탁을 받아 알맞은 장치와 드라이버를 통해 실제 일을 처리한다. 이렇게 하면 프로그램은 모든 하드웨어의 세부 구조를 몰라도 된다. 하드웨어가 바뀌어도 운영체제와 드라이버가 중간에서 번역해 주면 같은 프로그램을 계속 쓸 수 있다.

이 구조는 층층이 쌓인 건물과 같다.

응용 프로그램: 그림판, 웹 브라우저, 게임
운영체제: 메모리, 파일, 장치, 실행 관리
드라이버: 장치별 사용법 번역
기계어 명령: CPU가 직접 실행
하드웨어: CPU, 메모리, 저장장치, 입출력 장치

위층의 프로그램은 아래층 전체를 직접 다루지 않는다. 필요한 일은 아래층에 부탁하고, 아래층은 더 낮은 층의 규칙으로 일을 처리한다.

전원이 켜진 뒤 첫 명령은 어디서 올까?

여기서 중요한 질문이 생긴다.

운영체제도 소프트웨어라면, 운영체제는 누가 처음 실행할까?

전원을 막 켠 순간 RAM은 비어 있거나 믿을 수 없는 상태다. 저장장치 안에는 운영체제가 들어 있지만, CPU가 처음부터 저장장치의 파일을 알아서 찾아 읽지는 못한다. CPU는 그저 정해진 위치에서 명령을 읽기 시작하도록 만들어진 회로다.

그래서 컴퓨터에는 전원이 꺼져도 지워지지 않는 작은 시작 프로그램이 필요하다. 이것을 보통 펌웨어라고 부른다. PC에서는 BIOS나 UEFI 같은 이름으로 자주 만난다. 펌웨어는 하드웨어 가까이에 붙어 있는 기본 안내서다.

부팅 과정은 대략 이렇게 흘러간다.

1. 전원이 들어온다.
2. CPU가 초기 상태로 리셋된다.
3. CPU는 정해진 위치의 펌웨어 명령을 읽기 시작한다.
4. 펌웨어가 메모리와 장치의 기본 상태를 확인하고 준비시킨다.
5. 저장장치에서 다음 시작 프로그램을 찾는다.
6. 부트로더가 운영체제의 핵심 부분을 RAM에 올린다.
7. 운영체제 커널이 실행된다.
8. 드라이버와 기본 서비스가 준비된다.
9. 사용자가 프로그램을 실행할 수 있는 상태가 된다.

여기서 부트로더는 운영체제를 깨우는 작은 배달원이다. 저장장치 속 운영체제 파일을 찾아 RAM으로 옮기고, CPU가 그쪽 명령을 실행하도록 길을 넘겨준다.

커널은 운영체제의 중심 부분이다. 커널은 메모리, CPU 시간, 파일, 장치 접근 같은 가장 중요한 일을 맡는다. 컴퓨터가 켜진 뒤 우리가 보는 바탕화면이나 앱 화면은 이 커널 위에 더 많은 프로그램이 올라와 만들어진 결과다.

소프트웨어는 회로에 순서를 준다

하드웨어는 가능성을 만든다. 트랜지스터는 0과 1을 만들고, 논리 게이트는 판단을 만들고, ALU는 계산을 만들고, 메모리는 값을 붙잡고, 버스는 부품들이 대화하게 한다.

하지만 가능성만으로는 충분하지 않다. 어떤 계산을 먼저 할지, 어떤 값을 어디에 저장할지, 어떤 장치에서 입력을 받을지 정해야 한다. 이 순서를 정한 것이 소프트웨어다.

소프트웨어가 하드웨어를 깨운다는 말은 마법 같은 뜻이 아니다. 저장된 명령어가 메모리에 올라오고, CPU가 그 명령어를 하나씩 가져와 실행하면서 회로의 스위치들이 정해진 순서대로 움직인다는 뜻이다.

전기가 흐르면 부품이 깨어난다. 그러나 명령이 있어야 컴퓨터가 일을 시작한다.

우리가 만든 컴퓨터의 그림은 이제 거의 완성되었다. 아래에는 전기와 트랜지스터가 있고, 그 위에는 논리 게이트와 계산 회로가 있다. 그 위에는 메모리와 CPU, 버스와 장치가 있고, 다시 그 위에는 기계어, 어셈블리어, 컴파일러, 운영체제, 응용 프로그램이 층을 이룬다.

컴퓨터는 하나의 부품이 아니다. 전기에서 시작해 명령으로 움직이는 여러 층의 약속이다.

책 목록

처음부터 이해하는 컴퓨터 만들기

전체 목차 보기

이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.

틸노트는 사용자가 AI를 활용해 노트를 쉽게 작성하거나 편집할 수 있는 서비스입니다.

AI 스퀘어 단톡방에 함께해요

AI 활용과 틸노트 사용 팁을 나누는 오픈채팅방입니다. 입장 비밀번호는 tilnote입니다.

참여하기