OS - How Computer Systems Work
1. 컴퓨터 시스템의 동작
컴퓨터 시스템은 사용자의 입력을 받아 데이터를 처리하고, 그 결과를 출력하는 과정을 반복합니다. 이 과정은 하드웨어와 소프트웨어가 협력하여 이루어지며, 모든 작업은 명령어를 통해 제어됩니다.
1.1 컴퓨터 시스템의 작업 처리 순서
컴퓨터가 데이터를 처리하는 일반적인 과정은 다음과 같습니다:
- 입력(Input) 단계
- 입력 장치(키보드, 마우스, 센서 등)를 통해 사용자가 정보를 입력합니다.
- 입력된 정보는 메모리에 저장됩니다.
- 처리(Processing) 단계
- 프로그램의 제어에 따라 메모리에 저장된 데이터와 명령어를 인출(fetch)합니다.
- 중앙처리장치(CPU)의 연산장치(ALU)가 데이터를 계산하거나 논리 연산을 수행합니다.
- 출력(Output) 단계
- 연산이 완료된 결과는 출력 장치(모니터, 프린터, 스피커 등)에 표시됩니다.
- 또는 보조기억장치(HDD, SSD, USB 등)에 저장하여 나중에 다시 사용할 수 있습니다.
이와 같은 과정이 순차적으로 반복되며, 컴퓨터는 다양한 작업을 수행합니다.
1.2 명령어와 데이터
컴퓨터가 실행하는 모든 작업은 명령어와 데이터를 기반으로 이루어집니다.
- 명령어(Instruction):
- 컴퓨터가 수행해야 하는 연산을 지정하는 명령문입니다.
- 예를 들어, 숫자 두 개를 더하는 명령어가 있다면, “이 두 숫자를 더하라”는 동작을 지시하는 것입니다.
- 여러 개의 명령어가 모이면 프로그램(Program)이 됩니다.
- 데이터(Data):
- 명령어가 처리해야 하는 값(숫자, 문자, 이미지 등)입니다.
- 예를 들어, “10 + 20”이라는 연산을 수행하는 경우, “10”과 “20”은 데이터이고, ”+”는 명령어입니다.
1.3 컴퓨터가 명령어를 이해하는 과정
컴퓨터는 0과 1로 이루어진 기계어(Machine Code)만 이해할 수 있습니다. 따라서 사람이 작성한 고급 언어(High-Level Language) 프로그램은 반드시 기계어(Binary Code)로 변환되어야 합니다.
이 과정은 보통 다음과 같은 변환 단계를 거칩니다:
- 프로그래밍 언어 작성 (C, Java, Python 등)
- 컴파일(Compile) 또는 인터프리트(Interpret)를 통해 기계어로 변환
- 기계어 명령어를 CPU가 실행하여 연산 수행
즉, 사용자가 작성한 프로그램은 컴파일러(Compiler)나 인터프리터(Interpreter)를 통해 0과 1로 변환된 기계어 명령어로 변환되어야만 컴퓨터가 실행할 수 있습니다.
2. 명령어의 구조
컴퓨터가 실행하는 명령어는 연산 부호(OPcode)와 피연산자(Operand)로 구성됩니다. 프로세서는 이 명령어를 해석하고 실행하여 연산을 수행합니다.
2.1 명령어의 기본 구조
명령어는 다음과 같은 두 가지 주요 요소로 구성됩니다:
- 연산 부호 (OPcode, Operation Code)
- 프로세서가 실행할 연산의 종류를 지정
- 연산의 종류: 산술 연산(+, -, ×, ÷), 논리 연산(AND, OR, NOT), 시프트(Shift), 보수 연산 등
- OPcode가 n비트라면, 최대 $( 2^n )$ 개의 연산을 정의할 수 있음
- 피연산자 (Operand)
- 연산에 사용될 데이터 또는 데이터의 위치를 저장
- 피연산자는 다양한 저장 위치에 있을 수 있음:
- 레지스터(Register)
- 메모리(Main Memory)
- 가상 기억장치(Virtual Memory)
- 입출력 장치(I/O Device)
2.2 메인 메모리에 저장된 명령어 예시
다음은 메인 메모리에 저장된 명령어의 예시입니다.
| 메모리 주소 | 명령어 (Machine Code) | 설명 |
|---|---|---|
| 1000 | 0001 0001 0010 | R1 = R1 + R2 (R1에 R2 값을 더함) |
| 1001 | 0010 0011 0100 | R3 = R3 - R4 (R3에서 R4 값을 뺌) |
| 1010 | 0011 0101 0000 | R5 = Load 1000 (메모리 1000번지의 값을 R5에 로드) |
| 1011 | 0100 0110 1001 | Store R6, 1001 (R6 값을 1001번지에 저장) |
위의 예시에서 각 명령어는 연산 부호(OPcode) + 피연산자(Operand)의 형태로 저장됩니다.
2.3 직접 주소와 간접 주소
피연산자가 어디에 위치하는지를 명시하는 방법에는 직접 주소(Direct Addressing)와 간접 주소(Indirect Addressing)가 있습니다.
| 주소 지정 방식 | 설명 |
|---|---|
| 직접 주소(Direct Addressing) | 피연산자가 레지스터나 메모리 주소에 직접 저장됨 |
| 간접 주소(Indirect Addressing) | 피연산자의 주소를 보관하는 별도의 메모리 위치를 참조함 |
2.4 직접 주소와 간접 주소 사용 예시
(1) 직접 주소(Direct Addressing) 예시
1
LOAD R1, 5000 // R1에 메모리 5000번지의 값을 로드
- 5000번지에 저장된 데이터가 직접 R1에 로드됨
- 피연산자(Operand)인
5000은 데이터가 저장된 실제 주소임
(2) 간접 주소(Indirect Addressing) 예시
1
LOAD R1, @6000 // R1에 메모리 6000번지가 가리키는 주소의 값을 로드
- 6000번지에는 실제 데이터가 있는 주소(예: 5000)가 저장됨
- 즉, 6000번지를 참조하여 5000번지의 데이터를 R1에 로드
2.5 모드 비트를 포함한 명령어 형식
주소 지정 방식을 명시하기 위해 모드 비트(Mode Bit)를 사용합니다.
예를 들어, 모드 비트가 1비트, 연산 부호(OPcode)가 3비트, 피연산자가 6비트라면 명령어 형식은 다음과 같습니다:
| 모드(1bit) | OPcode(3bit) | 피연산자(6bit) |
|---|---|---|
0 | 001 | 110101 |
1 | 010 | 101010 |
명령어 해석
- 모드 비트가 0 (직접 주소)
0 001 110101→ OPcode =001(ADD), 피연산자 =110101(메모리 주소)- 결과:
ADD R1, 110101→ 110101번지의 값을 R1에 더함
- 모드 비트가 1 (간접 주소)
1 010 101010→ OPcode =010(LOAD), 피연산자 =101010(간접 주소)- 결과:
LOAD R1, @101010→ 101010번지에 저장된 주소의 값을 로드
3. 명령어의 실행
컴퓨터 시스템에서 명령어가 실행되는 과정은 일련의 단계적인 과정을 거치며 수행됩니다. 이 과정은 일반적으로 명령어 사이클(Instruction Cycle)이라고 하며, 명령어 인출(Fetch), 해석(Decode), 실행(Execute) 단계로 나뉩니다.
3.1 명령어 실행 과정
- 명령어 인출(Fetch)
- 다음 실행할 명령어를 메모리에서 읽어 명령어 레지스터(IR, Instruction Register)에 저장
- 프로그램 카운터(PC, Program Counter) 값을 증가시켜 다음 명령어를 가리킴
- 명령어 해석(Decode) & 프로그램 카운터 변경
- 명령어를 해독하여 어떤 연산을 수행할지(OPcode 해석) 결정
- 프로그램 카운터를 다음 명령어를 가리키도록 업데이트
- 피연산자 호출(Operand Fetch)
- 연산에 필요한 데이터를 메모리 또는 레지스터에서 가져옴
- 피연산자가 메모리에 저장된 경우, 주소 계산 후 데이터를 불러옴
- 명령어 실행(Execute)
- ALU(Arithmetic Logic Unit)를 이용하여 연산 수행
- 산술 연산(+, -, ×, ÷), 논리 연산(AND, OR, NOT), 데이터 이동, 조건 분기 등의 작업 수행
- 결과 저장(Write Back)
- 연산 결과를 다시 레지스터 또는 메모리에 저장
- 필요한 경우 상태 레지스터(Flags Register) 값 업데이트
- 다음 명령어로 이동
- 명령어 사이클을 반복하여 다음 명령어를 실행
3.2 명령어 실행 사이클
명령어 실행 과정은 일반적인 명령어 사이클과 세분화된 명령어 사이클로 나눌 수 있습니다.
1) 일반적인 명령어 사이클
명령어가 실행되는 일반적인 과정은 다음과 같습니다.
- 인출 사이클(Fetch Cycle): 메모리에서 명령어를 가져옴
- 해석 사이클(Decode Cycle): 명령어를 해석하고 필요한 연산을 결정
- 실행 사이클(Execution Cycle): 실제 연산을 수행
- 저장 사이클(Write Back Cycle): 연산 결과를 저장
2) 세분화된 명령어 사이클
보다 세부적으로 보면 다음과 같은 단계로 나눌 수 있습니다.
| 단계 | 설명 |
|---|---|
| 인출(Fetch) | 명령어를 메모리에서 읽어옴 |
| 해석(Decode) | 명령어를 분석하여 실행 방법 결정 |
| 피연산자 호출(Operand Fetch) | 연산에 필요한 데이터 불러옴 |
| 실행(Execute) | 연산 수행 |
| 저장(Write Back) | 연산 결과를 메모리 또는 레지스터에 저장 |
3.3 명령어 실행 단계 상세 설명
1) 인출 사이클(Fetch Cycle)
- 메모리에서 명령어를 읽어 명령어 레지스터(IR)에 저장
- 다음 명령어를 가리키도록 프로그램 카운터(PC)를 증가
- 이 과정에서 소요되는 시간을 명령어 인출 시간(Instruction Fetch Time)이라고 함
2) 실행 사이클(Execution Cycle)
- 인출한 명령어를 해석하고, 제어 신호를 발생시켜 명령어를 실행
- 연산의 종류에 따라 ALU를 통한 산술 연산, 논리 연산, 데이터 이동, 분기 연산 수행
- 실행 과정에서 소요되는 시간을 명령어 실행 시간(Instruction Execution Time)이라고 함
3) 간접 사이클(Indirect Cycle)
- 간접 주소 지정 방식을 사용하는 경우, 명령어 실행 전에 실제 데이터가 저장된 주기억장치 주소(유효 주소)를 읽어오는 단계
- 즉, 피연산자가 직접 주어진 것이 아니라, 피연산자가 저장된 주소를 찾기 위해 한 번 더 메모리를 조회하는 과정이 필요함
4) 인터럽트 사이클(Interrupt Cycle)
- 인터럽트(Interrupt)는 프로세서가 프로그램을 수행하는 도중 예기치 못한 사건이 발생했을 때 실행을 중단하고 특정 처리를 수행하는 과정
- 인터럽트가 발생하면, 현재 수행 중인 프로그램의 주소(프로그램 카운터 값)를 저장한 후 인터럽트 서비스 루틴(Interrupt Service Routine, ISR)으로 이동
3.4 인터럽트(Interrupt)
인터럽트 명령어
- 현재 실행 중인 프로그램을 중단하고 다른 프로그램을 실행하도록 요구하는 명령어
- 입출력 장치, 오류 발생, 긴급 시스템 요청, 정전 등 다양한 이유로 발생
인터럽트의 목적
- 컴퓨터의 처리 효율 향상 (입출력 대기 시간 동안 다른 작업 수행 가능)
- 멀티태스킹 및 다중 프로그래밍 지원 (다양한 프로그램을 동시에 실행)
3.5 인터럽트 요청 회선(IRQ, Interrupt Request Line)
- 프로세서가 외부 장치의 상태를 직접 확인하지 않아도 자동으로 요청을 받을 수 있는 신호선
- 인터럽트 요청이 발생하면 인터럽트 서비스 루틴(ISR)을 실행하여 해당 작업을 처리
인터럽트 요청 회선의 종류
| 회선 유형 | 설명 |
|---|---|
| 단일 회선(Single Line) | 모든 장치가 하나의 공통 회선을 사용하여 인터럽트 요청 |
| 다중 회선(Multiple Line) | 각 장치가 고유한 인터럽트 요청 회선을 사용하여 즉시 판별 가능 |
3.6 인터럽트 처리 과정
인터럽트가 발생하면, 프로세서는 현재 작업을 저장하고 인터럽트 처리 루틴으로 이동한 후 다시 원래 작업을 수행합니다.
- 인터럽트 발생 전
- 프로그램 A 실행 중
- 프로그램 카운터(PC)가 현재 명령어를 가리킴
- 인터럽트 발생
- 현재 명령어를 종료하고, 레지스터 값을 스택(Stack) 또는 프로세스 제어 블록(PCB, Process Control Block)에 저장
- PC를 인터럽트 서비스 루틴(ISR)의 시작 위치로 변경
- 인터럽트 처리 프로그램(프로그램 B) 실행
- 인터럽트 처리 후
- 원래 수행 중이던 프로그램(A)의 레지스터 값을 복구
- PC를 이전 값으로 복원하여 프로그램 A 실행 재개