[혼자 공부하는 컴퓨터구조 + 운영체제] 2회독 정리
01 . 컴퓨터 구조 시작하기
1-1 실력 있는 개발자가 되기 위해 컴퓨터 구조를 알아야 하는 이유
- 문제 해결의 실마리를 다양하게 찾을 수 있다. 단순 코드 개발뿐만이 아닌 다양한 문제를 스스로 해결 할 수 있다.
- 성능, 용량, 비용을 결정 할 수 있게 된다. 단순 입출력을 넘어서 성능, 용량, 비용까지 고려하며 개발할 수 있다.
1-2 컴퓨터 구조의 큰 그림
컴퓨터 구조 지식은 크게 두 가지이다.
- 컴퓨터가 이해하는 정보
- 컴퓨터의 네 가지 핵심 부품
컴퓨터가 이해하는 정보
컴퓨터는 0과 1로 표현된 정보만을 이해한다. 이 정보에는 크게 데이터와 명령어가 있다.
컴퓨터가 이해하는 숫자, 문자, 이미지 등 정적인 정보를 데이터라 부른다.
이러한 데이터를 움직이고 컴퓨터를 작동 시키는 정보를 명령어라 부른다.
명령어가 없으면 컴퓨터를 작동시킬 수 없기 때문에 명령어가 더 중요한 정보이다.
숫자 1과 2는 데이터이며, 더하라, 1과 2를은 명령어이다. CPU에서 이와 같은 명령어를 읽고 실행하지 않는 한 우리는 3이라는 결과값을 볼 수 없다.
컴퓨터의 네 가지 핵심 부품
- 중앙처리장치 (CPU : Central Processing Unit)
- 주기억장치 (Main Memory)
- 보조기억장치 (Secondary Storage)
- 입출력장치 (I/O Device)
CPU (Central Processing Unit)
메모리에 저장된 명령어를 읽어 들이고, 읽어 들인 명령어를 해석하고 실행하는 부품.
CPU의 중요한 세 가지 구성요소는 아래와 같다.
- 산술논리연산장치 (ALU : Arithmetic Logic Unit)
- 계산을 담당하는 부품으로 컴퓨터 내부의 대부분의 계산을 담당한다.
- 레지스터 (Register)
- CPU 내의 작은 임시 기억 장치로 프로그램을 실행하는 필요한 값들을 임시로 저장한다
- 제어장치 (Control Unit)
- 제어신호라는 전기신호를 내보내고 명령어를 해석하는 장치로 여기서의 제어신호랑 컴퓨터 부품을 관리하고 작동시키기 위한 일종의 전기 신호를 일컫는다.
주기억장치 (메모리)
현재 실행되는 프로그램의 명령어와 데이터를 저장하는 부품.
프로그램이 실행되기 위해선 반드시 메모리에 저장되어 있어야 한다.
우리가 집주소를 찾아가듯이, CPU에서는 메모리에 접근하여 명령어를 읽어야하는데,
저장된 값에 빠르고 효율적으로 접근하기 위해 주소라는 개념을 사용한다.
메모리의 단점으로는 대표적으로 두 가지가 존재한다.
- 가격이 비싸 저장 용량이 작다.
- 전원이 꺼지면 저장된 내용을 잃는다. (휘발성)
보조기억장치 (Secondary Storage)
전원이 꺼져도 저장된 프로그램을 저장하는 부품이다.(비휘발성)
메모리가 현재 실행되는 프로그램을 저장한다면, 보조기억장치는 보관할 프로그램을 저장한다.
HDD, SDD, USB, DVD, CD-ROM과같은 저장 장치를 보조기억장치라 부른다.
입출력장치 (I/O Device)
마이크, 키보드, 스피커, 마우스처럼 컴퓨터 외부에 연결되어 컴퓨터 내부와 정보를 교환하는 장치.
메인보드와 시스템 버스
위 네가지 핵심 부품은 모두 메인보드라는 판에 연결된다.
메인보드를 마더보드라고도 부르며 이러한 부품들은 서로 정보를 주고 받을 수 있는데 이러한 정보를 주고 받을 수 있는 통로를 버스(bus)라고 부르며, 메인 보드 내의 가장 중요한 버스로는 시스템 버스가 있다.
시스템 버스는 아래 세 가지로 구성된다.
- 주소 버스 (Address Bus) - 주소를 주고 받는 통로
- 데이터 버스 (Data Bus) - 명령어와 데이터를 주고 받는 통로
- 제어 버스 (Control Bus) - 제어 신호를 주고 받는 통로
02. 데이터
2-1. 0과 1로 숫자를 표현하는 방법
0과 1만을 이해하는 컴퓨터 7과 같은 숫자를 이해할 수 있는 이유는 이러한 값들을 모두 0과1로 표현 할 수 있기 때문이다.
정보 단위
컴퓨터가 이해하는 가장 작은 정보 단위는 비트(bit)다.
즉, 0과 1을 표현할 수 있는 가장 작은 정보 단위이다.
비트는 0과1 두 가지 상태만 표현할 수 있으며, 몇개의 정보를 표현할 수 있는지는 경우의수를 생각하면 쉽게 이해할 수 있다.
2비트는 2^2 4개의 정보를 표현할 수 있고 3비트는 2^3 8개의 정보를 표현할 수 있는 식이다.
이러한 비트로 프로그램은 이루어져있다. 그러나, 우리가 흔히 어떤 파일을 가르킬때 비트라는 표현보다는 바이트라는 한 단계 큰 정보 단위를 주로 사용한다.
바이트(Byte)
여덟 개의 비트를 묶은 정보 단위.
즉, 1 바이트 === 8비트와 같으므로 총 2^8 256개의 정보를 표현 할 수 있다.
바이트보다 더 큰 단위로는 아래와 같은 것들이 있다.
- 1 킬로바이트 (KB) === 1000 Byte
- 1 메가바이트 (MB) === 1000 KB
- 1 기가바이트 (GB) === 1000 MB
- 1 테라바이트 (TB) === 1000 GB
- 1 페타바이트 (PB) === 1000 TB ....
중요 정보 단위로 "워드" 라는 것도 존재한다.
워드(Word)란 CPU가 한번에 처리할 수 있는 데이터 크기를 의미한다.
만약 CPU가 한번에 16비트를 처리할 수 있다고 하면 1워드 === 16비트를 의미한다.
현대 컴퓨ㅓ는 대부분 32비트 또는 64비트가 1워드인데 가령 intel CPU에서 보였던 x86 CPU는 32비트의 워드 CPU를 의미하며 x64 CPU는 64비트의 워드 CPU를 의미한다.
이진법
그래서 7과 같은 숫자를 컴퓨터는 어떻게 이해할 수 있을까?
수학에서 0과 1만으로 모든 숫자를 표현하는 방법을 이진법이라고 한다.
기본적으로 이진법으로 컴퓨터에게 이해를 시킬 수 있다.
이진법, 십진법, 십육진법 ... 등이 존재한다.
숫자 10을 이진수인지, 십진수인지를 컴퓨터는 어떻게 구분할까?
- 이진수 끝에 아래첨자(2)를 붙히거나
- 이진수 앞에 0b를 붙힌다. (0b1000 === 8)
이진수의 음수 표현
0과 1만으로 음수를 표현하는 방법 중 가장 널리 사용되는 방법은 2의 보수를 구해 이 값을 음수로 간주하는 방법이다.
사전적 의미는 "어떤 수를 그보다 큰 2^n에서 뺀 값"을 의미한다.
(주입식으로는 "모든 0과 1을 뒤집고 거이에 1을 더한 값"으로 구할 수 있다)
하지만 실제로 보수표현에는 한계가 있기에 컴퓨터 내부에서 어떤 수가 양수인지 음수인지를 구분하기 위한 플래그(Flag)를 사용하여 컴퓨터는 어떠 수가 음수인지 양수인지를 정확히 인지 할 수 있다, 이후 레지스터를 다룰 때 또 등장한다.)
십육진법
이진수로 모든 숫자를 표현하다 보면 숫자의 길이가 너무 길어진다는 단점이 있다.
그래서 이진법을 대신하여 십육진법도 자주 사용하는데 십육진법이 이진법을 대신해서 사용되는 핵심 이유는 이진수를 십육진수로 십육진수를 이진수로 변환하기 쉽기 때문이다.
2-2. 0과 1로 문자를 표현하는 방법
문자 집합과 인코딩
0과 1을 문자로 표현하는 방법에 대해 알기 전에 아래 세 가지 용어를 이해해보자.
- 문자집합 (Character Set)
- 컴퓨터가 인식하고 표현할 수 있는 문자의 모음
- 문자 인코딩 (C.. Encoding)
- 문자를 0과 1로 컴퓨터가 이해할 수 있는 문자로 변환하는 과정
- 문자 디코딩 (C.. Decoding)
- 0과 1로 이루어진 문자 코드를 사람이 이해할 수 있는 문자로 변환하는 과정
아스키 코드 (ASCII)
128개의 고유한 수에 숫자를 일대일로 매핑한 초창기 문자 집합 중 하나이다. (아스키코드, UTF 등은 팀내에서 발표했던 자료를 추후 포스팅 하겠다)
아스키 코드는 아메리칸 스탠다스 즉, 영어권 국가에서 한정하여 사용할 수 있는 문자 집합이었다.
결국, 한국을 포함한 영어권 외의 나라에서 자신들의 언어로 0과 1을 표현할 수 있는 고유한 문자 집합과 인코딩 방식이 필요해졌다.
EUC-KR
위와 같은 이유로 등장한 한글 문자 집합이다.
한글은 다른 언어와 달리 두 가지 인코딩 방식이 존재한다.
- 완성형 인코딩
- 조합형 인코딩
가 라는 문자를 1로 인코딩하는 방식을 완성형
가 라는 문자를 "ㄱ", "ㅏ" 등으로 나누어 초성, 중성, 종성에 해당하는 코드를 합하여 하나의 글자 코드로 만드는 인코딩 방식이 있다.
ECU-KR은 대표적인 완성형 인코딩 방식이다. 모두 결합된 한글 단어에 2바이트 킈의 코드를 부여한다.
정리하자면, ECU-KR은 한글을 2바이트 크기로 인코딩 할 수 있는 완성형 인코딩 방식이다.
ECU-KR은 총 2,350개의 한글 단어를 표현할 수 있지만, 쀍, 쀓과 같은 글자는 표현할 수 없었다. 결국 모든 글자를 표현할 수 없었기에 MS에서 CP949라는 ECU-KR의 확장된 버전을 내놓았지만 이마저도 한글 전체를 표현하기에는 부족했다.
유니코드와 UTF-8
ECU-KR -> CP949 만으로는 모든 한글을 표현할 수 없는 한계가 있었다.
더욱이 언어별로 인코딩을 다르게해야하는 상황에서 다국어를 지원하는 프로그램은 지원하는 모든 나의 인코딩을 모두 알아야하는 번거로움이 있었다.
이에 유니코드라는 문자 집합이 등장한다.
유니코드는 대부분의 나라의 문자, 특수문자, 화살표와 같은 이모티콘까지 코드로 표현할 수 있는 통일된 문자 집합으로 현대 문자를 표현할 때 가장 많이 사용되는 표준 문자 집합이다.
즉, 유니코드는 여러 나라의 문자를 광범위하게 표현할 수 있는 통일된 문자 집합이다.
아스키코드, EUC-KR은 글자에 부여된 값을 그대로 인코딩 값으로 기준을 잡았는데,
유니코드는 위와 다르게 다양한 방법으로 인코딩을하는데 이러한 인코딩 방법으로 UTF-8, UTF-16, UTF-32등이 있다.
즉, UTF는 유니코드 문자에 부여된 값을 인코딩 하는 방식을 말한다.
UTF(Unicode Transformation Format) : 유니코드를 인코딩하는 방법
이중 가장 대중적인 것 인코딩 방법은 UTF-8이라고 한다.
UTF-8은 통상 1~4바이트까지의 인코딩 결과를 만들어 내는데, 자세히 알고 싶다면 책을 읽어보자!
아스키 코드로부터 시작해서 컴퓨터가 문자를 어떻게 0과 1로 이해하는지 플로우를 이해하는게 더 중요하다고 생각한다.
03. 명령어
3-1. 소스 코드와 명령어
컴퓨터는 명령어를 처리하는 기계 라고 표현할 수 있다.
명령어는 컴퓨터를 실직적으로 작동시키는 중요한 정보인데 C, Java와 같은 프로그래밍 언어로 만든 소스 코드는 무엇일까?
컴퓨터는 이러한 프로그래밍 언어를 이해하고 있는걸까? -> No
이해할 수 없다. 컴퓨터는 0과 1만을 이해할 수 있다.
따라서 위와 같은 모든 ~~~ 소스 코드는 컴퓨터 내부에서 명령어로 변환되는 과정을 거친다.
고급 언어와 저급 언어
개발자가 작성하는 프로그래밍 언어는 사람이 이해하기 쉽게 만들어진 언어이다.
이러한 언어를 고급 언어라 부른다. 반대로, 컴퓨터가 이해하고 실행할 수 있는 언어를 저급 언어라 부른다.
저급 언어는 명령어로 이루어져 있다.
컴퓨터가 이해하고 실행할 수 있는 언어는 오로지! 저급 언어 뿐이다.
이러한 저급 언어에는 기계어와 어셈블리어가 있다.
- 기계어 - 0과 1의 명령어 비트로 이루어진 언어
8000 00b0 8100 2140 ........
- 어셈블리어 - 기계어를 읽기 편한 현태로 번역한 언어
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-4], 1
....
ret
그래서 왜 저급 언어를 알아햐 하나요?
관찰할 일이 거의 없는 개발자도 있지만, 임베디드, 게임, 정보 보안 쪽 개발자는 이러한 어셈블리어를 매우 중요한 관찰의 대상으로 바라봐야한다.
어셈블리어를 읽으면 컴퓨터가 프로그램을 어떤 과정으로 실행하는지, 즉 프로그램이 어떤 절차로 작동하는지를 가장 근본적인 단계에서부터 하나하나 추적하고 관찰할 수 있다.
개발자의 영역에 따라 저급 언어의 중요성은 달라질 수 있다. 그러나 고급 언어와 저급 언어의 차이를 이해하는 것은 개발 분야를 막론하고 매우 좋은 교양이기에 알아두는 것이 좋다.
컴파일 언어와 인터프리터 언어
개발자가 작성한 소스 코드는 결국 저급 언어로 변환된다.
이때 두 가지 방식으로 변환되는데 컴파일과 인터프리터 방식이 있다.
컴파일 언어로 작성된 소스 코드는 컴파일러에 의해 저급 언어로 변환이 된다. 이 과정을 컴파일 이라 하고, 컴파일 결과로 저급 언어로 변환된 목적 코드가 생성된다.
인터프리터 언어로 작성 소스 코드는 인터프리터에 의해 저급 언어로 변환이 된다. 이때 인터프리터는 소스 코드를 한 줄 한줄 차례로 실행하면서 한 줄씩 저급 언어로 변환하여 실행한다.
컴파일 언어와 인터프리터 언어, 칼로 자르듯이 구분될까?
하나의 프로그래밍 언어가 반드시 둘 중 하나의 방식만으로 작동한다고 생각하는 것은 오개념이다. 컴파일이 가능한 언어라고해서 인터프리터가 불가능하거나, 인터프리터가 가능한 언어라고 해서 컴파일이 불가능한 것은 아니다.
따라서, 칼로 자르듯 구분하기 보다는 고급 언어가 저급 언어로 변환되는 대표적인 방법에는 컴파일 방식과 인터프리터 방식이 있다는 정도로 이해하자.
이를 이해하는데 더 도움이 되는 예시는 독일어를 모르는 친구에게 독일어로 쓰인 책을 설명해주는 것이다.
컴파일 언어로 독일어로 쓰인 책 전체를 한국어로 번역 책을 건네주는 방식이라면,
인터프리터언어는 친구와 함께 한줄한줄 읽으면서 바로 번역해서 알려주는 방식과 같다.
컴파일하는 과정을 거치고 안 거치고 등의 차이가 있지만, 한국어로 번역한 책을 건네주는 방식이 훨씬 빠르게 읽을 수 있다.
목적 파일 vs 실행 파일
목적 코드로 이루어진 파일을 목적 파일.
실행 코드로 이루어진 파일을 실행 파일이라고 부른다. 대표적으로 윈도우 .exe 확장자를 가진 파일이 있다.
목적 파일과 실행 파일은 같은 의미일까? -> No
목적 코드가 실행 파일이 되기 위해서는 링킹 작업을 거처야 한다.
A.c, B.c라는 두 개의 소스 코드를 작성했다고 가정해보자.
- A.c 안에서는 "OOO 더하기"라는 기능이 구현되어있다.
- B.c 안에서는 A.c에 구현된 OOO 더하기 기능과 프로그래밍 언어가 기본으로 제공하는 화면 출력이라는 기능을 사용한다.
이 들을 각각 컴파일하여 목적 코드를 생성하면 B.c는 저급 언어니까 바로 실행할 수 있을까? -> No
B.c에서는 존재하지 않는 OOO 더하기 기능을 어떻게 실행하는지 알지 못하기 때문이다.
따라서 B.c를 실행하면 B.c에는 없는 외부 기능들, 즉, OOO 더하기 기능을 B.c와 연결짓는 작업이 필요한데 이러한 연결 작업을 링킹이라고 한다.
이렇게 링킹 작업까지 거치고 나면 비로소 하나의 실행 파일이 만들어진다.
03-2. 명령어의 구조
명령어는 연산 코드와 오퍼랜드로 구성된다.
연산 코드
명령어가 수행할 연산을 말한다.
연산 코드는 연산자라고도 부른다.
연산 코드는 아래 가장 기본적인 연산 코드 유형 네 가지로 나눌 수 있다.
- 데이터 전송 : MOVE, STORE, LOAD, PUSH..
- 산술/논리 연산 : ADD, DIVIDE, AND, OR, COMPARE ..
- 제어 흐름 변경 : JUMP, HALT, CALL, RETURN ..
- 입출력 제어 : READ, WRITE, TEST IO, START IO ...
오퍼랜드
연산에 사용될 데이터 또는 연산에 사용될 데이터가 지정된 위치를 말한다.
오퍼랜드는 피연산자라고도 부른다.
위의 어셈블리어의 예시를 가지고 와보자, 기계어 예시는 우리가 봐도 알아볼 수 없으므로
아래와 같이 오퍼랜드가 하나도 없는 명령이 존재할 수 있는데 0-주소 명령어, 1-주소 명령어 ..., 3-주소 명령어 라고도 한다.
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-4], 1
....
ret
// 연산 코드 | 오퍼랜드
push | rbp
mov | rbp, rsp
mov | DWORD PTR [rbp-4], 1
....
ret |
주소 지정 방식
명령어의 오퍼랜드 필드에는 메모리나 레지스터의 주소를 담는 경우가 많다. 따라서 오퍼랜드 필드를 주소 필드라고 부르기도 한다.
오퍼랜드 필드를 굳이 두지 않고, <연산 코드, 연산 코드에 사용될 데이터> 형식으로 명령어를 구성하지 않게 되면 명령어의 길이가 굉장히 길어질 수 있는 문제가 생길 수 있다.
보통 오퍼랜드에 연산에 사용될 데이터를 직접적으로 담는 경우는 드물며, 연산 대상이 되는 데이터가 지정된 위치를 담는 경우가 대부분이며 이때 데이터가 지정된 위치를 유효 주소라고 부른다.
오퍼랜드 필드에 데이터가 지정된 위치를 명시할때 연산에 사용할 데이터의 위치를 찾는 방법을 주소 지정 방식이라고 한다.
유효 주소를 찾는 방법과도 동일한 의미를 가진다.
현대 CPU에서는 다양한 주소 지정 방식을 사용한다.
- 즉지 주소 지정 방식
- 직접 주소 지정 방식
- 간접 주소 지정 방식
- 레지스터 주소 지정 방식
- 레지스터 간접 주소 지정 방식
이러한 접근 방식은 이후에 등장하게 될 용어인 외부 단편화 문제를 해결하는 방식과 크게 다르지 않다.
주소 지정 방식은 연산에 사용할 데이터의 위치를 찾는 방법이라는 점과 데이터의 위치를 찾을때 효율적인 방법으로 찾기 위한 방식으로 여러가지 방식이 존재한다는 것 정도 이해하면 좋을 것 같다.
04. CPU의 작동 원리
04-1 ALU와 제어장치
CPU 구성 요소
- 내부 계산 담당 ALU (Arithmetic Logic Unit) 산술 논리 장치
- 명령어를 읽어 들이고 해석하는 제어장치
- 작은 임시 저장 장치인 레지스터
ALU
1+2 라는 계산을 할때 1, 2는 피연산자, "더하기"라는 수행할 연산을 필요로 한다.
즉, 레지스터로 부터 피연산자를 받아들이고, 제어장치로부터 수행할 연산을 알려주는 제어 신호를 받아들인다.
ALU는 수행할 결과를 바로 메모리에 저장하지 않고 레지스터에 임시로 저장한다. CPU가 메모리에 접근하는 속도보다 레지스터로 접근하는 속도가 매우 빠르다. CPU의 연산 결과가 매번 메모리에 바로 저장된다면 프로그램 실행 속도가 느려진다
ALU는 수행 결과와 더불어 플래그도 함께 내보낸다. (위에서 이진수를 보고 음수, 양수인지를 판단할때 플래그를 보고 판단한다고 하였는데, 이 플래그는 ALU가 추가적으로 보내는 상태 정보이다.)
이 밖에도, 제로, 캘리, 오버플로우, 인터럽트 그리고 슈퍼바이저 플래그 등 대표적인 플래그들이 존재한다.
제어장치
제어 신호를 내보내고, 명령어를 해석하는 부품. 제어 신호는 컴퓨터 부품들을 작동시키기 위한 일종의 전기 신호이다.
CPU의 구성 요소 중 가장 정교하게 설계된 부품이며, CPU를 제조하는 제조사 별로 구현 방식이나, 명령어를 해석하는 방식 등의 차이가 있다.
제어 장치가 받아들이는 정보.
- 제어 장치는 클럭 신호를 받아 들인다.
- clock : 컴퓨터의 모든 부품을 일사불란하게 움직 일 수 있게하는 시간 단위.
- 해석해야 할 명령어를 받아 들인다.
- CPU가 해석할 명령어는 명령어 레지스터에 저장된다. 제어 장치는 이 명령어 레지스터로부터 해석할 명령어를 받아 들이고 제어 신호를 발생시킨다.
- 플래그 레지스터의 플래그 값을 받아 들인다.
- 시스텀 버스 중 제어 버스로 전달되는 제어 신호를 받아 들인다.
제어 장치가 내보내는 정보
제어 장치가 CPU 외부에 제어 신호를 전달한다 === 제어 버스로 제어 신호를 내보낸다.
- CPU 외부에 전달하는 제어 신호
- 메모리
- get, set 등을 수행하고 싶을때 제어 신호를 내보낸다.
- 입출력장치 (보조기억장치 + 입출력장치)
- get, set 등을 수행하고 싶을때 제어 신호를 내보낸다.
- 메모리
- CPU 내부에 전달하는 제어 신호
- ALU
- 수행할 연산을 지시하기 위해 제어 신호를 내보낸다.
- 레지스터
- 레지스터간 데이터 이동, 레지스터에 저장된 명령어를 해석하기 위해 제어 신호를 내보낸다.
- ALU
04-2 레지스터
프로그램 속 명령어와 데이터는 실행 전후로 반드시 레지스터에 저장된다.
CPU 제조사 별로 레지스터가 다양하지만 대표적으로 반드시 알아야 할 레지스터로는 아래 8가지가 있다고 한다.
- 프로그램 카운터 (명령어 포인터)
- 메모리에서 읽어 들일 명령어 주소를 저장
- 프로그램 카운터는 지속적으로 증가하며 계속해서 다음 명령어를 읽어 들일 준비를 한다. 이 과정이 반복되면서 CPU는 프로그램을 차례대로 실행해 나간다. 결국 CPU가 메모리 속 프로그램을 순차적으로 읽어 들이고 실행해 나갈 수 있는 이유는 프로그램 카운터가 꾸준히 증가하기 때문이다.
- 명령어 레지스터
- 메모리에서 읽어 들인 명령어를 저장
- 메모리 주소 레지스터
- CPU가 읽어 들이고자 하는 메모리 주소를 저장
- 메모리 버퍼 레지스터 (메모리 데이터 레지스터)
- 메모리와 주고 받을 값 (데이터, 명령어)을 저장
- 플래그 레지스터
- 연산 결과 또는 CPU 상태에 대한 부가적인 정보를 저장
- 범용 레지스터
- 자유롭게 사용할 수 있는 레지스터
- 데이터, 주소 등 모두 저장할 수 있으며, CPU 안에는 여러개의 범용 레지스터가 존재한다. 즉, 메모리 버퍼 레지스터, 메모리 주소 레지스터 등 다양한 역할을 수행할 수 있다.
- 스택 포인터
- 메모리 주소 지정 방식 중 스택 주소 지정 방식에 사용되는 레지스터
- 스택의 마지막으로 저장한 값의 위치를 저장하는 레지스터이다. 즉, 스택 포인터로 어디까지 데이터가 채워져 있는지에 대한 표시라고 이해하면 된다. 여기서 스택은 메모리 안에 존재하는 영역이며, 암묵적으로 약속된 영역이다.
- 베이스 레지스터
- 메모리 주소 지정 방식 중 변위 주소 지정 방식에 사용되는 레지스터
- 명령어는 연산 코드와 오퍼랜드로 이루어져있다고 했었다. (3장) 그리고 오퍼랜드 필드에는 메모리의 주소가 담기는 경우도 존재한다. 변위 주소 지정 방식은 이처럼 오퍼랜드 필드의 값과 베이스 레지스터의 값을 더하여 유효 주소를 얻어내는 주소 지정 방식이다.
- 베이스 레지스터 속 기준 주소로부터 얼마나 떨어져 있는 주소에 접근할 것인지를 연산하여 유효 주소를 얻어내는 방식으로 베이스 레지스터는 기준 주소, 오퍼랜드는 기준 주소로 부터 떨어진 거리의 역할을 한다.
04-3 명령어 사이클과 인터럽트
명령어 사이클
CPU는 명령어들을 하나씩 실행하는데 이때 프로그램 속 각각의 명령어들은 일정한 주기가 반복되며 실행되는데 이 주기를 명령어 사이클이라 한다.
메모리에 저장된 명령어를 하나 실행한다고 가정하자.
메모리에 있는 명령어를 CPU로 가져온느 단계를 인출 사이클
CPU로 가져온 명령어를 실행하는 단계, 제어 장치가 명령어 레지스터에 담긴 값을 해석하고 제어 신호를 발생시키는 단계를 실행 사이클이라 한다.
CPU로 명령어를 인출했다고 해도 바로 실행할 수 없는 경우가 존재하는데, 이는 실행 사이클 진입 전 메모리 접근이 한번 더 필요한 경우를 말하는데 이를 간접 사이클이라 한다.
CPU가 수행 중인 작업은 방해를 받아 잠시 중단 될 수 있는데, 이러한 신호를 인터럽트(Interrupt)라고 부른다.
인터럽트
인터럽트는 크기 2가지 종류가 있다.
동기 인터럽트 (예외), 비동기 인터럽트(하드웨어 인터럽트)
동기 인터럽트
프로그래밍 상의 오류와 같은 예외적인 상황을 마주쳤을때 발생하는 인터럽트. 예외(Exception)라고 부른다.
비동기 인터럽트
주로 입출력장치에 의해 발생하는 인터럽트
CPU가 프린터에게 프린터를 작업을 수행하라고 명령하면 프린터는 프린터 작업을 마치고 CPU에게 완료 알림(인터럽트)를 보내며 CPU는 이러한 인터럽트가 발생하면 해당 작업을 먼저 수행한 후 이전 작업으로 돌아와 진행하고 있던 작업을 마저 수행하게 된다. 이때 이러한 인터럽트를 비동기 인터럽트, 하드웨어 인터럽트라고 부르며 비동기 인터럽트는 마치 알림과 같은 역할을 한다.
비동기 언터럽트는 CPU가 명령어를 효율적으로 처리하는 것과 관련이 있다.
CPU가 프린터에게 출력을 명령했다고 가정해보자.
입출력장치는 CPU보다 속도가 현저히 느리다. 따라서 CPU는 입출력 작업의 결과를 바로 받아볼수 없다.
비동기 인터럽트를 사용하지 않는다면 CPU는 프린터 작업이 언제 끝날지 모르기 때문에 계속 완료 여부를 확인해야 할 것이고 CPU 사이클 낭비로 이어진다. 마치 전자레인지 앞에서 언제 조리가 끝날지 알 수 없어 하염없이 그 자리에서 기다리고 있는 것과 같다.
하지만 비동기 인터럽트를 이용하면 CPU는 주기적으로 완료 여부를 확인 할 필요가 없게된다.
비동기 인터럽트의 처리 순서
- 입출력 장치는 CPU에 인터럽트 요청 신호를 보낸다.
- CPU는 실행 사이클이 끝나고 명령어를 인출하기 전 항상 인터럽트 여부를 확인한다.
- CPU가 인터럽트 요청을 확인하고 인터럽트 플래그를 통해 현재 인터럽트를 받아 들일 수 있는지 여부를 확인한다.
- 인터럽트를 받아들일 수 있다면 CPU는 지금까지의 작업을 스택 영역에 백업한다.
- CPU는 인터럽트 백터를 참조하여 인터럽트 서비스 루틴을 실행한다.
- 인터럽트 서비스 루틴 실행이 끝나면 백업해둔 작업을 복구하여 실행을 재개한다.
인터럽트 요청 신호
CPU의 정상저긴 흐름을 끊기 위해 인터럽트를 하기 전 CPU에 요청을 보내는 신호.
인터럽트 플래그
CPU가 인터럽트 요청을 수용하기 위해 플래그 레지스터에 있는 인터럽트 플래그가 활성화되어 있어야 한다.
만약 인터럽트 플래그가 불가능으로 설정되어 있더라도 무시할 수 없는 인터럽트 요청도 존재하는데 반드시 가장 먼저 처리해야하는 인터럽트가 있다. 대표적으로 정전, 하드웨어 고장과 같은 인터럽트가 이에 해당한다.
인터럽트 서비스 루틴
인터럽트가 발생하면 어떻게 행동할지 알려주는 인터럽트를 처리하기 위한 프로그램이다. 키보드에서 인터럽트 요청이 발생하면 어떻게 작동한다, 마우스에서 인터럽트 요청이 발생하면 어떻게 작동한다와 같이 인터럽트가 발생하면 어떻게 행동해야 할지 알려주는 프로그램이다.
즉 명령어와 데이터로 이루어진 프로그램이다.
CPU가 인터럽트를 처리한다는 말은 === 인터럽트 서비스 루틴을 실행하고 본래 수행하던 작업으로 돌아온다는 것을 의미한다.
이러한 인터럽트 서비스 루틴은 메모리에 저장이 되어있다. 그렇다면 CPU는 마우스, 키보드, 프린터와 같이 각긱 다른 인터럽트 서비스 루틴을 어떻게 구별하는 걸까??
인터럽트 백터
인터럽트 서비스 루틴을 식별하기 위한 정보
CPU가 인터럽트 서비스 루틴을 실행하려면 인터럽트 서비스 루틴의 시작 주소를 알아야하는데 이를 인터럽트 백터를 통해 알 수 있다.
[출처 : 혼공컴운 (저자: 강민철)_한빛 미디어 ]