[혼자 공부하는 컴퓨터 구조+운영체제]01
컴퓨터 구조: 컴퓨터가 이해하는 정보(데이터/명령어)
+ 컴퓨터의 4가지 핵심 부품(CPU, 메모리, 보조기억장치, 입출력장치)
데이터
- 숫자, 문자, 이미지, 동영상 등의 정적인 정보
- 컴퓨터와 주고 받는 / 내부에 저장된 정보
- 0과 1로 숫자/문자를 표현하는 방법
명령어
- 컴퓨터를 실질적으로 움직이는 정보
- 데이터는 명령어를 위한 일종의 재료
CPU
메모리에 저장된 명령어를 `읽고`, `해석`하고, `실행`하는 부품
ALU: 계산기
레지스터: CPU 내부의 `작은 저장` 장치
제어장치: 제어 신호(메모리 읽기/쓰기와 같은 전기 신호)를 내보내고, 명령어 해석하는 장치
ALU에서 내보내는 정보
플래그 종류 | 의미 | 사용 예시 |
부호 플래그 | 연산한 결과의 부호 | 1: 계산 결과 음수 0: 계산 결과 양수 |
제로 플래그 | 연산 결과가 0인지 여부 | 1: 연산 결과 0 0: 연산 결과 !0 |
캐리 플래그 | 연산 결과 올림수나 빌림수 발생 여부 | 1: 발생 0: !발생 |
오버플로우 플래그 | 오버플로우 발생 여부 | 1: 발생 0: !발생 |
인터럽트 플래그 | 인터럽트 가능 여부 | 1: 가능 0: !가능 |
수퍼바이저 플래그 | 커널 모드와 사용자 모드 여부 | 1: 커널 모드 실행 중 0: 사용자 모드 실행 중 |
레지스터의 종류
레지스터의 종류 | 하는 일 |
프로그램 카운터(instruction pointer) | 메모리에서 가져올 명령어의 주소 |
명령어 레지스터 | 방금 메모리에서 읽어 들인 명령어 |
메모리 주소 레지스터 | 메모리 주소(CPU가 읽고자하는 주소를 주소 버스로 보낼 때 거쳐감) |
메모리 버퍼 레지스터 | 메모리와 주고 받을 값(데이터 버스로 주고 받을 때 거쳐감) |
플래그 레지스터 | 연산 결과 || CPU 상태에 대한 부가적 정보 |
범용 레지스터 | 다양하고 일반적인 상황에서 자유롭게 사용 |
스택 포인터 | 주소 지정, 스택의 꼭대기 가리킴 |
베이스 레지스터 | 주소 지정, 기존 주소 저장 |
스택 주소 지정 방식: 스택과 스택 포인터(스택의 꼭대기를 가리킴)를 이용한 주소 지정 방식
*스택의 꼭대기를 가리키니 어디까지 채워져있는지 알 수 있음
변위 주소 지정 방식: 오퍼랜드 필드의 값(변위)과 특정 레지스터(프로그램 카운터, 베이스 레지스터)의 값을 더해 유효 주소 얻음
💡용어 정리
인터럽트(interrupt): CPU가 꼭 주목해야 할 때, 얼른 처리해야 할 다른 작업이 생겼을 때 발생
동기 인터럽트(예외): 예기치 못한 상황을 접했을 때
`폴트`, `트랩`, `중단`, `소프트웨어 인터럽트`
비동기 인터럽트(하드웨어 인터럽트): 주로 입출력장치에 의해 발생
알림과 같은 것, 입출력 작업 도중에 효율적으로 명령어를 처리하기 위해 사용
입출력 작업 동안 CPU는 다른 일을 할 수 있음
처리 순서: `인터럽트 요청 신호` - `인터럽트 플래그` - `인터럽트 벡터` - `인터럽트 서비스 루틴`
1) 입출력창기가 CPU에 인터럽트 요청 신호를 보냄
2) CPU는 실행 사이클이 끝나고 명령어 인출 전, 항상 인터럽트 여부 확인
3) CPU가 인터럽트 요청을 확인하고 인터럽트 플래그를 통해 현재 인터럽트 받을 수 있는지 여부 확인
4) 인터럽트 받을 수 있으면 CPU가 지금까지의 작업 백업
5) CPU가 인터럽트 벡터 참조해 인터럽트 서비스 루틴 실행
6) 루틴 실행이 끝나면 백업해둔 작업 복구해 실행 재개
Memory
`저장` 담당
실행되는 프로그램의 명령어와 데이터를 저장
메모리에 저장된 값의 위치는 `주소`로 알 수 있음
보조기억장치
보관할 정보를 저장하는 장치
전원이 꺼져도 보관될 프로그램을 저장하는 부품
입출력장치
컴퓨터 외부에 연결되어 컴퓨터 내부와 정보를 교환할 수 있는 부품
메인보드
- 메인보드에 연결된 부품은 버스를 통해 정보를 주고 받음
- 버스는 컴퓨터의 부품끼리 정보를 주고 받는 일종의 통로
- 다양한 종류의 버스가 있음
- 컴퓨터의 핵심 부품을 연결하는 버스는 `시스템 버스`
`시스템 버스`
- 주소 버스: 주소를 주고 받는 통로
- 데이터 버스: 명령어와 데이터를 주고 받는 통로
- 제어 버스: 제어 신호를 주고 받는 통로
그러면, 예시 상황을 통해 컴퓨터 내부에서 어떻게 작동을 하는지 알아보자.
CPU에서 메모리의 1번지에 있는 값을 읽고 싶은 상황이다.
CPU에서 `제어 신호` 출발 - 읽고자 하는 1번지 주소 출발 - 시스템 버스의 `제어 버스`와 `주소 버스`를 거쳐 메모리에 도착! - 메모리에서는 `1번지` + `읽기`를 받고 1번지에 저장된 데이터/명령어를 다시 CPU에게 전달
정보 단위
`bit(비트)` : 0과 1을 표현하는 가장 작은 정보 단위
1byte(1바이트) | 8bit(8비트) |
1KB(1킬로바이트) | 1,000byte |
1MB(1메가바이트) | 1,000KB |
1GB(1기가바이트) | 1,000MB |
1TB(1테라바이트) | 1,000GB |
`word(워드)` : CPU가 한 번에 처리할 수 있는 정보의 크기 단위
half word(하프 워드) | 워드의 절반 크기 |
full word(풀 워드) | 워드 크기 |
double word(더블 워드) | 워드의 2배 크기 |
`Binary(이진법)`
- 0과 1로 수를 표현하는 방법
- 숫자가 1을 넘어가는 시점에 자리 올림
+ 숫자가 9을 넘어가는 시점에서 자리 올림하는 것은 `Decimal(십진법)`
+ 숫자가 15를 넘어가는 시점에서 자리 올림하는 것은 `십육진법`
십진수 | ... | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | ... |
십육진수 | ... | 9 | A | B | C | D | E | F | 10 | 11 | ... |
문자 집합과 인코딩
문자 집합(character set) : 컴퓨터가 이해할 수 있는 문자 모음
인코딩(encoding) : 코드화 하는 과정 = 문자를 0과 1로 이루어진 문자 코드로 변환하는 과정
디코딩(decoding) : 코드 해석 과정 = 0과 1로 표현된 문자 코드를 문자로 변환하는 과정
* 인코딩은 쉽게 보자면 컴퓨터로 들어가는 거고, 디코딩은 컴퓨터에서 나오는 거
고급 언어와 저급 언어
고급 언어: 개발자가 이해하기 쉽게 만든 언어
#inclue <studio.h>
int main() {
printf("Hello, World!");
return 0;
}
저급 언어: 컴퓨터가 이해하고 실행하는 언어
* 저급 언어에는 기계어와 어셈블리어가 있음
* 기계어: 0과 1로 이루어진 명령어로 구성
* 어셈블리어: 0과 1로 이루어진 기계어를 읽기 편한 형태로 번역한 저급 언어
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-4], 1
mov DWORD PTR [rbp-8], 2
mov edx, DWORD PTR [rbp-4]
mov edx, DWORD PTR [rbp-8]
add eax, edx
mov DWORD PTR [rbp-12], eax
mov eax, 0
pop rbp
ret
컴파일 언어로 작성된 소스 코드는 컴파일러에 의해서 저급 언어로 변환되고(: compile) 그 결과로 목적 코드 생성
컴파일 언어 vs 인터프리트 언어
컴파일 언어는 컴퓨터에서 소스 코드를 보내면 목적 코드를 생성해줘서 한 번에 다 번역해 가져다주는데
인터프리트 언어는 소스 코드를 한 줄씩 보기 때문에 오휴가 발생하면 실행을 중지함
C언어 컴파일 과정
`test.c` Preprocessing - `test.i` Compiling - `test.s` Assebling - `test.o` Linking `test.exe`
전처리 과정(preprocessing)
- 컴파일 하기 전에 처리할 작업
- 외부에 선언된 다양한 소스 코드, 라이브러리 포함(e.g. `#include`)
- 프로그래밍의 편의를 위해 작성된 매크로 변환(e.g. `#define`)
- 컴파일할 영역 명시(e.g. `#if`, `#ifdef`, ...)
* `test.c`파일이 전처리 과정을 통해 `test.i` 파일을 생성
컴파일 과정(compiling)
- 전처리 완료되어도 여전히 소스 코드
- 전처리 완료된 소스 코드를 저급 언어(어셈블리 등)로 변환
* `test.i` 파일이 컴파일 과정을 통해 `test.s` 파일 생성
어셈블 과정(assembling)
- 어셈블리어를 기계어로 변환
- 목적 코드(object file)를 포함하는 목적 파일이 됨
*`test.s` 파일이 어셈블 과정을 통해 `test.o` 파일 생성
용어 정리
목적 파일 vs 실행 파일
둘 다 기계어로 이루어진 파일
but, 목적 파일은 `linking`을 거친 이후에 실행 파일이 됨
링크 과정(linking)
- 결과물로 실행 파일 생성
*`test.o` 파일이 링킹 과정을 통해 `test.exe` 파일 생성
명령어의 구조
: 연산 코드 + 오퍼랜드
= 수행할 연산 + 연산에 사용될 데이터/데이터가 저장된 위치(무엇을 대상으로)
저장해라 | 10을 | 메모리 102번지에 |
*`저장해라`가 연산 코드 부분이고 나머지가 오퍼랜드 부분이라고 생각하면 쉬움
*오퍼랜드가 없는 경우, 여러 개인 경우가 있음
연산 코드 종류
1. 데이터 전송
`MOVE`: 데이터 옮기기
`STORE`: 메모리에 저장
`LOAD`(`FETCH`): 메모리에서 CPU로 데이터 가져오기
`PUSH`: 스택에 데이터 저장
`POP`: 스택의 최상단 데이터 가져오기
2. 산술/논리 연산
`ADD` / `SUBTRACT` / `MULTIPLY` / `DIVIDE`: 덧셈 / 뺄셈 / 곱셈 / 나눗셈 수행
`INCREMENT` / `DECREMENT`: 오퍼랜드에 1 더하기 / 빼기 수행
`AND` / `OR` / `NOT`: 연산 수행
`COMPARE`: 2개의 숫자 또는 true/false 값 비교
3. 제어 흐름 변경
`JUMP`: 특정 주소로 실행 순서 옮기기
`CONDITIONAL JUMP`: 조건에 부합할 때, 특정 주소로 실행 순서 옮기기
`HALT`: 프로그램 실행 멈추기
`CALL`: 되돌아올 주소 저장한 채로 특정 주소로 실행 순서 옮기기
`RETURN`: `CALL`을 호출할 때 저장했던 주소로 되돌아가기
4. 입출력 제어
`READ`(`INPUT`): 특정 입출력 장치로부터 데이터 읽기
`WRITE`(`OUTPUT`): 특정 입출력 장치로부터 데이터 쓰기
`START IO`: 입출력 장치 시작
`TEST IO`: 입출력 장치 상태 확인
명령어 주소 지정 방식
1. 유효 주소(effective address)
: 연산에 사용할 데이터가 저장된 위치
2. 명령어 주소 지정 방식(addressing modes)
: 유효 주소를 찾는 방법
3. 즉시 주소 지정 방식(immediate addressing mode)
: 연산에 사용할 데이터를 오퍼랜드 필드에 직접 명시
: 가장 간단한 형태
: 연산에 사용할 데이터 크기는 작을 수 있지만, 빠름
4. 간접 주소 지정 방식(indirect addressing mode)
: 오퍼랜드 필드에 유효 주소의 주소 명시
: 상대적으로 속도 느림
5. 레지스터 주소 지정 방식(register addressing mode)
: 연산에 사용할 데이터가 저장된 레지스터 명시
: 메모리 접근 속도보다 레지스터에 접근하는 것이 빠름
6. 레지스터 간접 주소 지정 방식(register indirect addressing mode)
: 연산에 사용할 데이터를 메모리에 저장
: 해당 주소를 저장한 레지스터를 오퍼랜드 필드에 명시