'컴퓨터 밑바닥의 비밀' 챕터 1~2 정리

동그란개발자·2025년 3월 30일

컴퓨터 밑바닥의 비밀

코드가 어떻게 돌아가고, 메모리는 어떻게 구성되어 있으며, CPU는 무얼 하는지 등, 추상화를 한꺼풀씩 벗겨가며 로우레벨에서 뭐가 어떻게 동작하는지를 보여주는 책이다.

읽게 된 계기

컴공 전공하면서 나름 이 세계에 발을 들인지도 9년이 되었지만(이렇게 말하니까 뭔가 대단해보이지만..) 전체적인 그림을 아직 못 보고 있다는 답답함이 항상 서려 있었다🤔 컴퓨터 구조, 운영체제, 네트워크 등 CS 학습 자체는 재미있어 했기에 단편적인 지식들은 열심히 주입했다만.. 그래서 이게 전체적으로 어떻게 맞물려 돌아가는걸까? 하는 궁금증. 그러던 중 우연히 글또에서 스터디원 모집한다는 글을 보고 덥-썩 들어갈 수밖에!

추? 비추?

👍👍 강추!! 👍👍

저자가 워낙 비유를 잘 들어가며 스토리텔링을 맛깔나게 하기 때문에 비전공자도 쉽게 읽을 수 있지 않을까 싶다.

쭉 읽으면서 전체적인 그림을 그리기에 정말정말 좋다고 느꼈다 ... 현재는 CPU를 다루는 챕터 4를 읽고 있는데, 전에 배웠던 게 이거구나~ 반갑고 퍼즐 조각이 맞춰지는 기분 !!

👇👇 저자만의 유머 코드도 살짝 어이없고 웃기다 👇👇

이 글의 구성

🌱 전체적인 그림을 그려볼 겸 스스로 정리하는 글이라 구체적인 개념은 간단히 넘어갈 예정

🌷 관통하는 키워드를 하나 뽑자면 추상화다.
"가상 메모리란 무엇인가?"와 같은 지엽적인 용어보다는 가상 메모리가 어떠한 문제를 해결하기 위해, 어떠한 맥락에서 나왔지?를 위주로 보기를 추천한다

1장

  • 프로그래밍 언어
  • 코드에서 실행 파일까지
    - 컴파일러
    - 링커 (보충 예정 👀)

2장

  • 주요 개념 설명
    - 운영체제, 프로세스, 스레드, 코루틴
    - 콜백 함수, 동기/비동기, 블로킹/논블로킹 (보충 예정 👀)

추상화

개발자들은 컴퓨터의 비밀을 모조리 알아서 코딩할 수 있는 게 아니다.

천공카드 뚫던 조상님들부터 열심히 고민한 결과로, 우린 내부 구현을 몰라도 "활용"하는 데 이상 없다. 이 책이 핵심 테마로 가져가는 "추상화"는, 결국 잘 포장된 겉껍질을 하나씩 까보며 실제 구현을 살펴보는 과정이다.

추상화를 넘어선다는 건, 본질을 파고드는 질문들에서 출발한다:

머신 코드에서 어셈블리어로의 점프는 어떻게, 왜 일어났을까?

Operating System이 왜, 어떤 필요에서 만들어졌을까? 개발자가 이걸 다 관리하려면 무슨 일이 벌어지길래?

실제로 CPU가 할 수 있는 것은? 그러기 위해 어떠한 형태로 코드를 읽어들여서 프로세싱하는걸까? 그렇다면 메모리랑은 어떻게 협력하는걸까?

1장

고급 프로그래밍 언어

저수준 언어와 고수준 언어의 구분을 먼저 깔고 가자.

인간이 이해할 수 있는 고수준 언어는 추상적이라면, 기계가 이해할 수 있는 저수준 언어는 구체적이라는 차이점이 있다.

컴퓨터를 우리가 원하는 방식으로 동작시키기 위해 이러한 갭을 메워야 할 것이다. 이를 위해 코드는 어떻게 발전해왔을까?

저수준이 이해할 수 있는 구체적인 언어에서 일련의 규칙을 발견해 구문이 만들어진다. 여기서 재귀와 같은 수학적인 개념들이 합쳐져, 트리 형식으로 표현할 수가 있었고, 이는 구문 트리로 표현할 수 있다.

코드에서 실행 파일까지

우리가 입맛에 편하게 작성된 소스 코드는 컴퓨터가 이해할 수 없다.

저수준에서 이해하고 실행하기 위해서는 기계어 0과 1로 구성된 실행 파일이 나와야 하고, 여기서는 프로그래밍 언어로 작성된 소스 코드에서 하나의 실행 파일이 나오기까지의 과정을 다루고자 한다.

Step 1) 컴파일러

자, C/C++, Java와 같은 프로그래밍 언어로 짜여진 코드가 있다.
이를 컴퓨터가 씹고 맛보기 편한 형태의 기계어 코드로 변환하기 위해 어떠한 과정을 거칠까?

전체적인 플로우 요약:

코드 구문 분석 👉 구문 트리 만들기 👉 이를 기계 명령어로 번역 👉 CPU or 가상머신으로 👉 실행

여기서 컴파일러가 역할을 수행한다.

컴파일러는 고수준 언어를 저수준 언어로 번역하는 프로그램이다.

대충 어떤 일들을 하는지 보면:
(그냥 이러한 일련의 과정을 통해서 최종적으로 기계어로 변환되는구나 보면 된다)

  1. 토큰 추출 (어휘 분석, lexical analysis)

    소스 코드에서 토큰 추출

  2. 구문 분석 👉 구문 트리 생성

  3. 의미 분석 (semantic analysis)

    생성된 구문 트리에 이상이 없는지 검사

  4. 중간 코드(Intermediate Representation Code) 생성
    (뭔진 잘 모르겠고 이런 게 있다고 한다)

  5. 최종 코드 생성

    중간 코드 -> 어셈블리어 코드 변환, 이를 기계 명령어로 변환

Step 2) 링커

컴파일러를 거치면 여러 개의 대상 파일이 생성되어, 링커는 이를 하나의 최종 실행 파일로 묶는 작업을 수행한다.

2장

핵심 추상화 개념. 운영체제, 프로세스, 스레드, 코루틴

프로세스 & 운영체제

프로그램 코드를 실행하는 CPU의 입장에서 보자. 이 친구는 프로세스니, 스레드니 모르고 그져 명령어 하날 가져와서 실행할 뿐이다(dispatch -> execute) 정확히는 PC 레지스터가 프로그램의 시작 주소를 가리키며 한줄씩 실행된다.

프로그램을 실행시키려면 프로그램을 직접 적재하는 등, 그 과정에서 처리해야 할 태스크가 많다.

하나의 코어에서 여러가지 프로그램을 실행시키기 위해서는 여러개의 프로그램 사이에서 빠르게 전환시켜야 하고, 이에 따라 실행 중인 프로그램을 "관리"해야 할 필요성이 생긴다. (->멀티태스킹)

그래서 나온 게 프로세스(process)다. 추상적으로 생각할 것 없이 실상은 프로그램 실행 상태를 저장하고 복구할 때 쓰는 구조체다.

✔️ 그렇다면 운영체제는?

프로그램 적재해주는 적재 도구, 멀티태스킹 실현해주는 프로세스 관리 도구 등, 개발자가 실행 중인 프로세스가 몇 개인지, CPU가 몇 개인지 신경 쓸 필요없이 작업할 수 있도록 background에서 지원해준다.

스레드

CPU 여러개가 동일한 프로세스의 기계 명령어를 실행하도록 하려면? PC 레지스터가 다른 함수를 가리키게 함으로써 새로운 실행 흐름을 형성할 수 있고, 이를 스레드라고 부른다.

다중 스레드 구조에 따라 메모리 구조는 어떻게 달라질까? 실행 흐름이 여러개 존재하는 만큼, 아마 스레드별 저장해야 할 context가 따로 있을 것이다. 결국 모든 스레드는 각자 자신만의 스택 영역을 가지게 된다.

제어가 어디 있는가?의 관점에서 스레드 vs. 코루틴

여기서 말하는 스레드는 커널 스레드로, 운영체제가 관장하는 것이다. 즉, 프로그래머가 그 생성과 스케쥴링에 관여할 수 없다.

운영체제에 의존하지 않고 직접 스레드를 구현하려면 코루틴을 통해 가능하다.

코루틴

이제 제어권이 개발자에게 넘어갔다. 이걸 왜 사용하는 것이며, 어떤 맥락에서 필요한걸까?

실질적으로 코루틴이 어디에 쓰이는지, 스터디 시간에 듣기로는 종종 싱글 스레드에서 흐름을 관리할 때 활용한다고 한다. (ex. IO 인터럽트 발생 시 어떻게 동작할지? 흐름 관장)

일반 함수와 다르게 이전에 마지막으로 실행된 위치를 알고 있어, 다시 실행될 때 이전에 일시중지된 지점에서 계속 실행이 가능한 특이한 함수다.

코루틴의 중요한 역할 중 하나는 개발자가 동기 방식으로 비동기 프로그래밍을 가능하게 한다는 것이다. (아마 이 말을 처음 듣는다면 대체 무슨 말인지 알아듣기 힘들 것이다. 다음 파트에서 계속 >.0)

1개의 댓글

comment-user-thumbnail
2025년 6월 4일

담글이 넘 기대됨니다><><💝

답글 달기