2025. 3. 14. 12:45ㆍ개발/Windows
이 글은 윤성우님의 '뇌를 자극하는 윈도우즈 시스템 프로그래밍'을 참고하여 공부한 내용입니다.
지난번 컴퓨터의 구조를 공부하며 CPU가 명령어를 실행하는 Fetch, Decode, Excute 단계들을 배웠습니다. 그리고 32/64비트 시스템 컴퓨터에서 비트는 데이터 한번에 처리할 수 있는 최대양 이란 것을 알게되었습니다. 이번에는 더 나아가 32/64비트 시스템에서 CPU가 레지스터를 통해 어떻게 명령어를 실행하는지 알아보겠습니다.
명령어 구조
초기 개인 컴퓨터는 16비트 시스템을 사용하였습니다. 그래서 16비트 시스템에서는 데이터를 한번에 처리하기 위해 명령어의 구조를 16비트로 구성하였습니다. 그래서 16비트 명령어를 WORD(unsigned short)라고 부르게 되었고, 32비트 시스템에서는 Double WORD인 DWORD(unsigned long) 형식이 되었습니다.
명령어는 Bus Interface를 통해 메모리에서 레지스트리 셋에 Fecth 된 다음, Controll Unit에 있는 IR 레지스터에서 명령어를 Decode 하여 해당 명령을 실행합니다. 여기서 IR 레지스터에 실제로 들어오는 데이터를 분석하며 명령어 구조를 학습해 보겠습니다.
예시는 16비트 ARM Thumb Mode 산술 연산자 ADD 명령어 구조 입니다.
0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 1 |
0 | OPCODE | Rm | Rn | Rd |
- OPCODE: 명령어를 구분할 수 있는 식별자 입니다. (ADD, SUB, MUL, DIV 등)
- Rm(Operand2 Register): 두번째 피연산자 입니다. 값을 가지고 있는 레지스터 입니다.
- Rn(Operand1 Register): 첫번째 피연산자 입니다. 값을 가지고 있는 레지스터 입니다.
- Rd(Destination Register): 연산 결과가 저장될 레지스터 입니다.
최상위 비트는 명령어 집합을 구분짓는 식별자로 사용됩니다. 산술연산자는 0입니다. 그리고 위 예시 비트는 0 000110 011 010 001 => ADD R1, R2, R3 으로 읽을 수 있습니다. R2 레지스터와 R3 레지스터 값을 더해 R1에 저장하는 표현입니다.
0 | 0 | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 |
0 | OPCODE | Rd | Immediate |
- OPCODE: 명령어를 구분할 수 있는 식별자 입니다. (ADD, SUB, MUL, DIV 등)
- Rd(Destination Register): 피연산자이며 결과를 저장할 레지스터 입니다.
- Immediate: 저장되지 않고 즉시 연산에 사용될 상수값입니다. (0 ~ 255)
위 예시 비트는 0 0011 001 00000101 => ADD R1, #5 로 읽을 수 있습니다. R1 레지스터 값에 5를 더해서 저장한다는 뜻입니다.
위 와 같이 모든 명령어의 구조를 16비트 안에서 설계했기 때문에 컴퓨터는 한번의 클락에 명령어를 하나씩 처리할 수 있게 됩니다. 이런 고정길이 명령어 집합을 사용하는 시스템을 RISC(Reduced Instruction Set Computer)라 부르며, 임벤디드와 ARM(Acorn RISC Machine) CPU에서 사용합니다. 명령어를 한번에 규칙적으로 처리할 수 있기 때문에 파이프라이닝이 쉽게 구현됩니다. 파이프라이닝이란 여러 개의 명령어를 각 단계에서 동시에 처리하여 성능을 높이는 기술입니다. 여러개의 명령어들을 Fetch와 Decode, Excute 단계에서 동시에 하나씩 작업을 처리하여 성능을 높일 수 있습니다. 제조공장의 컨베이어 벨트위에 제품들이 여러 공정을 거치면서 만들어지는 모습과 비슷합니다.
반대로 길이가 가변적인 명령어 집합을 사용하는 시스템도 있습니다. 그 시스템을 CISC(Complex Instruction Set Computer)라 부릅니다. CISC의 명령어들은 종류와 기능별로 다양하기에 명령어의 길이가 가변적입니다. 그래서 높은 메모리 주소에 접근하려면 명령어의 길이를 더 늘리면 됩니다. 이렇듯 CISC는 명령어를 더 적게 사용하여 동일한 기능을 하기 때문에 RISC보다 생산성이 더 높습니다. 하지만 명령어가 가변적이기에 파이프라이닝 구현이 매우 어렵고, CPU의 구조설계가 힘들다는 단점이 있습니다.
Direct/Indirect
RISC는 고정길이 명령어기 때문에 성능이 높지만, 메모리의 주소 표현이 제한적입니다. Thumb mode LDR Literal 명령의 경우 구조는 아래와 같습니다.
0 | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 1 |
0 | OPCODE(LDR) | Rd(R1) | Immediate(13*4=52) |
- OPCODE: LDR 명령어 입니다.
- Rd(Destination Register): 메모리에 값을 불러와 저장할 레지스터 입니다.
- Immediate: 메모리 주소 입니다.
여기서 Immediate는 8비트를 4의 배수로 계산하여 0~1020(255x4) 주소까지 접근할 수 있습니다. 이렇게 접근하는 방식을 Direct라 부릅니다. 그런데 주소가 1020을 넘는다면 Direct 방식은 사용할 수 없습니다. 그래서 레지스터 값에 주소를 넣어 해당 레지스터를 통해 주소로 접근한는 Indirect 방식을 사용합니다. 즉 R2 레지스터에 산술 연산자를 통해 주소값을 계산하여 저장하고 R2 레지스터를 통해 해당 주소로 접근하는 것입니다. CISC는 한번의 명령으로 메모리에서 데이터를 가져올 수 있지만, RISC는 위와 같이 Indirect 방식을 통해 가져와야 하기 때문에 CISC의 생산성이 더 높다고 할 수 있습니다.
'개발 > Windows' 카테고리의 다른 글
커널 오브젝트와 오브젝트 핸들 (0) | 2025.03.17 |
---|---|
Windows 프로세스 생성과 소멸 (0) | 2025.03.15 |
Windows 32/64비트 시스템 (0) | 2025.02.26 |
Windows 유니코드 (0) | 2025.02.21 |
Windows 시스템 프로그래밍 시작하기 (0) | 2025.02.21 |