📚 목차
- 🔸 Chapter 1: 시스템 프로그래밍의 이해와 접근
🔸 Chapter 4: 컴퓨터 구조의 접근 방법
시스템 프로그래밍이란?
- 시스템(컴퓨터 시스템)의 범위
- 하드웨어 + 운영체제
- 시스템 프로그래밍
- 컴퓨터 시스템을 활용하는 소프트웨어 개발
- 모든 응용 프로그래밍에 시스템 프로그래밍이 있다
- window 운영체제 자체의 기능을 십분 활용하는 프로그래밍
- 응용 소프트웨어 개발과의 차이점
- 시스템 프로그래밍은 모든 응용 프로그램에 포함되는 요소
컴퓨터 시스템의 주요 구성 요소 (컴구조 + 운영체제)

- CPU, 캐쉬
- 컴퓨터 하드웨어 구조
- 운영체제
- 메인 메모리
- 메모리 관리 기법
- 메인 메모리
- 하드디스크
컴퓨터 하드웨어 구성(전체)

- CPU
- 중앙 처리 장치
- 연산이 이뤄지는 원리는 무엇인가?
- 메인 메모리
- RAM
- 프로그램 실행 방식을 이해하는 것
- 입출력 버스
- 데이터 송 수신이 이뤄지는 원리
CPU란

- ALU
- 산술 연산이나 AND, OR 같은 논리 연산을 한다.
- 컨트롤 유닛
- CPU 전체를 총괄함
- 명령어를 해석하고 해석된 결과에 따라 적잘한 신호를 CPU의 다른 블록에 보내는 일을 한다
- 레지스터
- CPU 내부에 임시적으로 데이터를 저장하기 위한 메모리 공간
- 2진 데이터를 저장
- 버스 인터페이스
- I/O 버스 통신 방식을 이해하고 있는 역할을 한다
- cpu 내부에 저장되어 있는 데이터를 I/O 버스에 실어 보내거나, 전송되어 오는 데이터를 수신한다.

- 컴파일된 실행 파일은“하드디스크”에 들어간다. (ex. A.exe)
- 컴파일된 파일에 예를 들어 덧셈 기능이 있다.
- 메인 메모리에 ‘덧셈’, ‘2’, ‘5’가 들어감
- 입출력 버스를 통해 버스 인터페이스에 도착
- 입출력 버스는 **통신 규약**에 따라 데이터를 주고 받을 수 있음 그것을 도와주는 것이 해주는 것이 버스 인터페이스
- 외부(네트워크, 모니터, 키보드) 등에 도 데이터 이동을 위한 입출력 버스가 있다.
- 궁극적으로 연산을 위해 ALU로 가야한다.
- 그 전에 컨트롤 유닛으로 흘러 들어간다.
- 컨트롤 유닛이 명령어를 해석한다. → cpu가 해야 할 일을 정해준다.
- 그 전에 컨트롤 유닛으로 흘러 들어간다.
- ALU는 순차적으로 일들을 처리 중 이기 때문에 cpu 내부에도 작은 메모리 공간을 배치해서 저장시켜 놓자. → 레지스터
- 필요할 때 가져갈 수 있는 메모리 공간
- 덧셈이나 2, 5들이 저장된다.
- 이제 차례가 되면 컨트롤 유닛은 명령어를 해석 후 레지스터에서 데이터를 꺼내 ALU에게 전달해준다.
- ALU는 덧셈 결과를 레지스터에게 저장한다 → 러프하게 이해
클럭 신호 (Clock Pulse)
- 컴퓨터의 모든 장치에는 클럭 신호가 들어가 있다.
- 모든 장치에 일정한 간격으로 신호를 준다
- 해당 **신호에 맞춰서 연산**을 한다
- 동작 타이밍을 결정
- 오버클럭
- 일은 빨리 하지만 열이 발생해 CPU가 탈 수 있다.

문제점
- 만약 출력 장치가 데이터를 가져오는 속도가 더 빠르다면, 출력 장치는 이미 한번 가져간 데이터를 다시 가져다가 출력하는 문제가 발생할 것이고,
- 연산 장치가 더 빠르다면, 버퍼를 덮어쓰게 되어 연산결과의 일부가 출력되지 않는 문제점이 발생한다
해결 방안
- 속도가 느린 장치의 장단에 맞춰주면 된다.
- 가장 낮은 신호로 동기화해서 시스템을 안정화 시킨다.
폰 노이만 아키텍처
“프로그램이라는 것이 존재하고, 이 프로그램은 컴퓨터 내부에 저장되어서 순차적으로 실행 되어야한다.”
프로그램의 실행 과정

- 전처리기에 의한 치환 작업
- #include, #define 과 같이 #으로 시작하는 지시자의 지시에 따라 소스코드를 변경하는 작업을 한다.
- 컴파일러에 의한 번역
- 전처리에 이어 변경된 소스코드는 여전히 C 언어로 구성되어 있으며, 이 소스코드는 컴파일러에 의해서 어셈블리 코드로 변역된다.
- 어셈블러에 의한 바이너리 코드 생성
- 텍스트 코드에 상대되는 뜻
- 1과 0으로만 구성되는 코드이다.
- 컴파일러에 의해 번역된 어셈블리 코드는 컴퓨터에 의해 실행되기에 앞서서 바이너리 코드로 번역되어야 한다. 왜나하면 컴퓨터는 오로지 1과 0만 이해하기 때문이다.
- 텍스트 코드에 상대되는 뜻

4. 링커에 의한 연결과 결합
- 라이브러리와의 결합
- 목적 파일과 라이브러리 파일이 합쳐져서 exe파일이 만들어진다.
- 모든 작업이 끝나면 실행 가능한 실행파일이 생성된다.
Stored Program Concept
- 폰 노이만 아키텍처 : 프로그램이 메모리에 저장되는 시스템
- 프로그램의 기본은 Fetct, Decode, Exeution 단계를 거친다.

- Fetch
- 메모리상에 존재하는 명령어를 CPU로 가져오는 작업
- Decode
- 가져다 놓은 명령어를 CPU가 *석하는 단계
- Execution
- 해석된 명령어의 명령대로 CPU가 실행하는 단계
Stored Program Concept + 컴퓨터 구조

프로그램 실행 플로우
메모리 → I/O버스 → 레지스터 → 컨트롤 유닛 → ALU
데이터 이동의 버스 시스템
- 데이터 버스
- 데이터를 이동하기 위한 버스
- 어드레스 버스
- 주소값을 이동하기 위해 필요한 버스
- 메모리 영역의 주소값과 해당 주소의 데이터를 전송하는 컨트롤러를 전달해준다
- 컨트롤 버스
- CPU가 원하는 바를 메모리에 전달할 때 사용된다.
- 컨트롤 신호 이동
- 양방향으로 주고 받는 작업을 컨트롤한다.
1장 : 이것만은 알고가자
- ALU와 컨트롤 유닛의 기능적 역할
- 레지스터의 필요성
- 클럭 펄스의 필요성
- Fetch, Decode, Execution
- 버스 인터페이스
4장 : 컴퓨터 구조의 접근 방법
컴퓨터(CPU)를 디자인하자
- ALU와 컨트롤 유닛은 이미 존재한다고 가정하고 레지스터를 디자인 해보자
레지스터를 디자인하자
- 레지스터를 디자인하는데 있어서 결정해야 할 요소
- 레지스터를 몇 비트로 구성할 것인가?
- 몇 개 정도로 레지스터를 구성할 것인가?
- 레지스터 각각을 무슨 용도로 사용할 것인가?
- 책에서는 이해하기 편하도록 16비트로 진행 및 레지스터 개수 8개로 제한한다.

- 레지스터의 각각의 용도를 셋팅
- r4~r7 은 특수한 목적으로 사용하기 위한 레지스터
- r0~r3는 범용(연산을 위한) 레지스터
명령어 구조 및 명령어를 디자인하자
- cpu에게 일을 시키기 위한 명령어 종류를 디자인해야 한다.
- 명령어를 몇 비트로 구성할 것인지 결정 해야 한다.
- 앞서 16비트로 레지스터 크기를 결정하였기 때문에 명령어 길이도 16비트로 구성

- cpu에게 일을 시킬 때 “레지스터 r1에 있는 값과 숫자 7을 더해서 레지스터 r2에 저장하라” 는 형태로 일을 시키게 된다.
- 이처럼 덧셈 연산을 위한 피연산자 둘, 연산결과 저장을 위한 피연산자 등을 16비트짜리 명령어 하나에 담을 수 있다.

- ADD 연산자 뒤를 잇는 세 개의 비트엔 저장할 저장소로 구성
- 레지스터가 총 8개 이므로 세 비트만 할당하면 충분히 표현 가능하다
- 일반적인 ALU는 연산결과를 레지스터에 저장하기 때문에 이러한 규칙으로 설정
- 피연산자를 담는 비트에는 정보나 숫자가 올 수 있도록 디자인
- 처음 두 개의 비트 용도는 나중에 셋팅


- 연산에 대한 정보는 세 개의 비트만 할당하였기 때문에 연산자의 개수는 총 8개를 넘지 못한다.
- 우선 덧셈, 뺄셈, 곱센, 나눗셈 4가지를 정의
- 레지스터에 대한 심볼과 코드를 정의

저장소에 레지스터를 저장

- 이번에는 피 연산자 두 개를 표현할 때 숫자 or 레지스터가 될 수도 있다.
- r2 = r1 + r3
- r2 = r1 + 4
- r2 = 5 + r1
- r2 = 3 + 5
- 그렇기 때문에 숫자 혹은 레지스터 정보를 구분해주기 위해서 하나의 비트를 희생한다.
- 네 개의 비트 중에 첫 번째 비트가 1이면 레지스터 저장, 0이면 숫자를 저장한다.

1장에 이어 공부한 컴퓨터 구조의 전체 내용 이미지
- 위 그림에서 명령어가 ir에 저장되는데, 이 때 ir은 다음 번에 실행 될 명령어를 미리 가져다 놓는 용도로 사용된다.
RISC vs CISC
- CPU 설계 철학의 차이를 말하는 개념
- 명령어를 복잡하게 많이 제공할 것인가?
- 간단한 명령어만 제공하고 대신 빠르게 처리한 것인가?
CISC
- 복잡한 명령어 세트를 가진 CPU 구조
- 한 명령어로 여러 동작을 한 번에 수행할 수 있음
- 명령어 종류가 많고 길이가 가변적
- 하드웨어 설계가 복잡해짐
- 속도는 명령어마다 다르고, 실행 시간이 일정하지 않음
RISC
- 단순하고 규칙적이며 빠른 명령어 세트를 가진 CPU 구조
- 명령어 수가 적고 구조가 단순
- 하나의 명령은 하나의 작업만 수행
- 명령어 길이가 고정되고 단순
- 파이프라인 최적화에 매우 좋음
- 실행 시간이 일정해 고성능 설계 유리
- F, D, E F, D, E 이러한 방식으로 클럭수는 n + 2로 진행되어 빠르
CISC = 한 메뉴로 모든 게 들어있는 도시락
- 하나 주문하면 여러 작업(반찬)이 한 번에 처리됨
- 하지만 만들기 복잡하고 느릴 때도 있음
RISC = 필요한 반찬을 하나씩 꺼내서 먹는 뷔페
- 동작은 작지만 빠르고 규칙적
- CPU가 처리하기 훨씬 쉬움
추가 꿀팁: 사실 요즘 CPU는 혼합형
- x86(CISC) CPU도 내부는 사실 RISC 마이크로-연산으로 변환해서 실행함
- 즉 "겉은 CISC, 속은 RISC"
LOAD & STORE 명령어의 필요성
int a = 10; // 0x10번지 할당
int b = 20; // 0x20번지 할당
int c= 0; // 0x30번지 할당
c = a + b;
- 피연산자로 올 수 있는 것을 숫자와 레지스터로 제한했기 때문에 위의 코드는 주소를 참조 해야 하기 때문에 연산을 할 수 없다.
- "0x10번지(a)에 저장된 값과, 0x20번지(b)에 저장된 값을 더해서 Ox30번지(c)에 저장해라” 가 가능해야함

- 메인 메모리에 저장된 데이터를 레지스터로 일단 옮겨다 놓은 다음에, 덧셈을 진행해야 한다.
- 레지스터와 메모리 사이에 데이터를 전송할 수 있는 명령어가 필요한 상황.
- 메인 메모리 → 레지스터 명령어 LOAD
- 레지스터 → 메인 메모리 명령어 STORE
LOAD & STORE 명령어의 디자인
LOAD 디자인

- 목적지 정보와 데이터가 존재하는 위치(source) 정보를 담을 수 있도록 디자인
- 목적지에는 메모리 주소가 올 수 있도록 정의하였고, 데이터가 존재하는 위치에는 레지스터 정보를 담을 수 있도록 정의하였다.

- LOAD | r3 | 0x07
- 0x07번지에 존재하는 데이터를 레지스터 r3에 저장하라 라는 의미로 해석된다.
STORE 디자인

- LOAD와 마찬가지로 목적지와 데이터가 존재하는 위치 정보를 담을 수 있도록 디자인
- 다만 명령어 LOAD와 비교해서 데이터 이동 방향이 정 반대 이다
- 레지스터 정보가 sorce가 되고, 메모리 주소 정보가 destination이 된다.

- SOTRE | r2 | 0x08
- 레지스터 2에 존재하는 데이터를 메인 메모리 Ox08번지에 저장하라. 라는 의미로 해석된다.
LOAD 와 STROE을 이용한 덧셈 과정

- 위 그림에서 a, b, c는 각각에 주소 번지에 할당 되어 있다.
LOAD r1, 0x10 // 0x10번지에 저장된 데이터를 1로 이동
LOAD r2, 0x20 // 0x20번지에 저장된 데이터를 2로 이동
ADD r3, r1, r2 // rl, r2에 저장된 값을 더해서 3에 결과 저장
STORE r3, 0x30 // r3에 저장된 값을 0x30번지에 저장
- 피연산자가 레지스터가 되게 끔 하기 위해서 메모리에 저장된 값을 레지스터에 옮겨다 놓은 후 덧셈 연산을 한다.

Direct 모드와 Indirect 모드
- 16비트로 디자인된 명령어에 많은 정보를 담음으로 문제점이 발생한다. 이를 해결하기 위한 방안.
Direct 모드의 문제점
- 하나의 명령어에 여러 정보를 담다 보니 데이터 크기 제한이 따른다는 문제점 등장
- LOAD 명령어를 보면, 메인 메모리의 주소 값을 나타내는 source 부분에서 문제가 생길 수 있다.
- 이 부분에 여덟 개의 비트 수가 할당되어서, 표현할 수 있는 값의 범위가 0x0000에서 0x00ff까지가 전부 이기 때문이다.
- 지금까지 우리가 언급한 메모리 접근방법을 가리켜 Direct 모드라 한다.
- 이 방법을 통해서는 메모리의 모든 영역에 대한 접근이 불가능하다.
- 할당된 비트 수 안에서 표현 가능한 범위의 메모리 영역만 접근이 가능하기 때문.
- 이 방법을 통해서는 메모리의 모든 영역에 대한 접근이 불가능하다.
Indirect 모드의 이해


- 첫 번째 상황은 Direct 모드, 두 번째 상황은 Indirect 모드에 해당된다.
- 앞에서의 LOAD 명령어에서는 레지스터에 저장할 데이터가 존재하는 주소를 직접적으로 표현하였지만, Indirect 모드에선 명령어에서 지정하는 번지에 저장된 값을 주소 값으로 참조하게 된다.
Direct Addressing Mode vs Indirect Addressing Mode


- 명령어에서 표현하고 있는 0x10번지에서 값을 읽어오는 것이 아니라, 0x10번지에 저장된 값을 주소값으로 참조해서 값을 읽어오고 있다. 이것이 Indirect 모드의 메모리 참조 방식이다.
Direct 모드 연산 구분 방법

- 앞서 만들어놓은 첫 두 비트의 용도를 Direct모드와 Indirect 모드를 구성
문제 상황

- 문제는 0x0100번지에 저장된 데이터를 2로 이동시키는 두 번째 명령어에 있다.
- source를 표현하기 위한 8비트 안에 0x100을 표현할 수 없기 때문이다.
- 이 문제는 Indirect 모드를 도입하면 가능하다.
- r0에 MUL 명령어를 사용해 만들어낸 값 16을 저장하고, r2에도 마찬가지로 16을 만들어 저장한다.
- 마지막은 이 두 레지스터에 저장된 값을 곱해서 그 결과를 r3에 저장한다.
- 이제 r3에 저장된 주소 값을 참조하여 메모리로부터 값을 읽어다가 레지스터 r2로 저장하면 된다.
- LOAD | r2 | r3
- 문제는 r3에 저장된 주소값을 참조하여 메모리로부터 값을 읽어 레지스터 r2에 저장하는 것이다.
- STORE | r3 | 0x0030
- 그리고 메인 메모리를 INdirect 모드로 참조해서 값을 읽어오도록 LOAD 명령어를 구성
- LOAD | r2 | 0x0030
- ro, 4, 4 // 1에는 데이터 0x0010이 저장되어 있으므로 r0에 연산결과 저장 MUL MUL 12, 4, 4 MUL r3, r0, r2
LOAD rl, 0x0010
MUL r0, 4, 4
MUL r2, 4, 4
MUL r3, r0, r2
STORE r3, 0x0030
LOAD r2, [0x0030]
ADD r3, r1, r2
- c = a + b에 해당하는 명령어 조합
4장 : 이것만은 알고가자
- 명령어
- cpu의 디자인 과정에서 cpu에게 일을 시키기 위한 명령어를 어떻게 구성하는지.
- 연산자에 대한 정보뿐만 아니라, 피 연산자에 대한 정보도 들어간다.
- CPU의 내부 연산과 레지스터
- 레지스터의 필요성과 덧셈 연산이 이뤄지는 과정을 레지스터와 관련해서 이해
- Direct vs Indirect
- direct모드는 명령어에서 지정하는 위치의 메모리를 참조하는 방식
- Indirect 모드는 명령어에서 지정하는 위치에 저장된 값을 주소값으로 하여 메모리를 참조하는 방식
- Indirect 모드는 direct 모드가 지니는 계를 극복하는 데 도움을 준다.
📒 참고 자료
https://www.youtube.com/watch?v=GVX-m3RF-K0&list=PLVsNizTWUw7E2KrfnsyEjTqo-6uKiQoxc
https://www.youtube.com/watch?v=ucJbpFGRYFI&list=PLVsNizTWUw7E2KrfnsyEjTqo-6uKiQoxc&index=7
'개발 서적 정독 > 뇌를 자극하는 윈도우즈 시스템 프로그래밍' 카테고리의 다른 글
| [뇌를 자극하는 윈도우즈 시스템 프로그래밍] Chapter3, Chapter10 (1) | 2026.06.05 |
|---|