본문 바로가기
컴퓨터 구조

Ch04 CPU의 작동 원리

by plum06 2024. 8. 10.

*틀린 내용이 있을 경우 댓글로 알려주신다면 감사하겠습니다!

 

 

 

ALU와 제어장치

  • ALU

: CPU 부품 중 하나로 ALU가 있습니다. ALU는 레지스터를 통해 피연산자를 받아들이고, 제어장치로부터 수행할 연산을 알려주는 제어신호를 받아들입니다.

내보내는 값은 연산 결괏값과 플래그이고, 이때 연산 수행한 결과는 바로 메모리에 저장되지 않고 일시적으로 레지스터에 저장됩니다. 그 이유는 CPU가 메모리에 접근하는 속도가 레지스터에 접근하는 속도보다 느리기에, 연산할 때마다 메모리에 접근해 결과를 저장한다면 프로그램 실행 속도가 느려질 수 있기 때문입니다.

 

  • 플래그(flag)

: 플래그란 연산 결과에 관한 추가적인 상태 정보를 의미합니다. CPU가 프로그램을 실행하는 과정에서 반드시 기억해야 하는 참고 정보같은 것으로, '플래그 레지스터'에 저장됩니다. 종류는 다음과 같습니다.

종류 의미
부호 플래그 연산 결과의 부호를 나타냄. 1=계산 결과 음수 / 0=계산 결과 양수
제로 플래그 연산 결과가 0인지 여부 나타냄. 1= 연산 결과가 0 / 0= 연산 결과가 0 아님
캐리 플래그 연산 결과 올림수나 빌림수가 발생했는지 나타냄. 1= 발생함 / 0= 발생하지 않음
오버플로우 플래그 오버플로우 발생 여부 나타냄. 1= 오버플로우 발생! / 0= 발생 안 함.
인터럽트 플래그 인터럽트가 가능한지를 나타냄. 1= 인터럽트 가능 / 0= 불가능
슈퍼바이저 플래그 운영체제와 관련된 플래그. 커널 모드로 실행 중인지, 사용자 모드로 실행 중인지 나타냄. 1=커널 모드로 실행 중 / 0= 사용자 모드로 실행중

 

더보기

빌림수

: "숫자의 자릿수를 맞추는 기수법에서, 두 수의 어떤 자리에서 뺄셈을 한 결과가 0보다 작은 경우 한 자리 위에서 보내지는 숫자."

컴퓨터구조 조합 논리회로에서 나오는 용어. 감산기에서 사용됨. 반감산기(Half Subtractor)를 예시로 들자면, 각각 1비트짜리인 A와 B라는 값이 입력값으로 주어지고 이 둘의 뺄셈 연산을 하는 것. 출력값으로 두 가지 값이 나오는데, 하나는 '차'이고 다른 하나는 '빌림수'임.

처음에는 표만 보고 음수(1) 양수(0) 표현이라 생각했다가, 정의 그대로 앞자리에서 숫자를 빌려오는 경우가 발생했을 때를 표시하는 값이라 생각하게 됨.

(좌) 반감산기 논리도 / (우) 반감산기 진리표
출처: https://aanrt.tistory.com/13

 

 

커널 모드(Kernel Mode)

: 운영체제의 핵심 기능(프로세스 관리 등)을 수행하는 '커널'이 실행되는 모드. 

 

사용자모드(User Mode)

: 시스템의 중요 부분에 관한 접근이 제한된 모드. 사용자가 접근할 수 있는 영역을 제한해 프로그램 자원에 함부로 침범하지 못하게 함.

 

  • 제어장치

: 제어장치는 제어 신호를 내보내고, 명령어를 해석하기 위한 CPU의 한 부품입니다. 제어 신호는 컴퓨터 부품들을 관리하고 작동시키기 위한 전기 신호라 할 수 있습니다. CPU 제조사마다 제어장치 구현 방식이나 명령어 해석하는 방식, 받아들이고 내보내는 정보에 조금씩 차이가 있습니다. 그렇기에 제어장치가 받아들이고 내보내는 대표적인 정보에 맞춰 정리하고자 합니다.

 

제어장치가 받아들이는 정보는 다음과 같이 있다 할 수 있습니다.

  • 클럭 신호

- '클럭(clock)'은 컴퓨터의 모든 부품을 일사불란하게 움직일 수 있게 하는 시간 단위입니다. 일정한 주기를 가지고 발생하죠. 클럭 주기에 맞춰 CPU가 메모리에서 명령어를 읽거나, 데이터를 저장하는 등의 작업이 수행됩니다. 

그러나 "컴퓨터의 모든 부품이 클럭 신호에 맞춰 작동한다" ≠ "컴퓨터의 모든 부품이 한 박자마다 작동한다"로 알아야 합니다. 모든 컴퓨터 부품들이 같은 주기에 따라 실행되는 것이 아닙니다.

  • 해석해야 되는 명령어

명령어 레지스터에 저장된 CPU가 해석해야 할 명령어를 받아들입니다.

  • 플래그 레지스터 속 플래그 값
  • 제어 버스로 전달된 제어 신호

- 제어 신호는 CPU뿐만이 아닌 입출력장치 등 외부 장치로부터도 발생이 가능합니다. 

 

제어장치는 CPU 외부나 내부로 제어 신호를 전달하는 두 가지 경우가 있습니다. 전자는 메모리에 저장된 값을 읽어야 하거나 저장된 값을 수정하고 싶을 때, 혹은 입출력장치의 값을 읽어들이는 걸 예시로 들 수 있습니다. 후자의 경우 ALU에 수행할 연산을 지시할 때나, 레지스터 간에 데이터를 이동시키는 등의 작업을 예시로 들 수 있겠습니다.

 


 

레지스터

: 프로그램 속 명령어와 데이터는 실행 전후로 반드시 레지스터에 저장되게 됩니다. 교재에서 설명하는 레지스터를 위주로 정리했습니다.

  • 프로그램 카운터(Program Counter)

- 메모리에서 읽을 명령어 주소를 저장합니다. 그래서 '명령어 포인터'라 지칭하는 경우도 있습니다.

  • 명령어 레지스터(Instruction Register)

- 제어장치가 해석할 명령어, 메모리에서 읽은 명령어를 저장하는 레지스터입니다. 이 정보를 받아들이고 해석한 뒤, 제어 신호를 내보냅니다.

  • 메모리 주소 레지스터(Memory Address Register)

- 말 그대로 메모리 주소를 저장하는 레지스터입니다. CPU가 읽으려 하는 주소 값을 주소 버스로 보낼 때 메모리 주소 레지스터를 거칩니다.

  • 메모리 버퍼 레지스터(Memory Buffer Register)

- 메모리와 주고받을 값(데이터, 명령어)을 저장하는 레지스터를 말합니다. 다른 말로 메모리에 쓰고 싶은 값이나 메모리로부터 전달받은 값은 메모리 버퍼 레지스터를 거칩니다. 데이터 버스로 주고받을 값들이 거치는 레지스터란 것이죠.

  • 플래그 레지스터

- 연산 결과 혹은 CPU 상태에 관한 부가적인 정보를 저장하는 레지스터입니다.

  • 범용 레지스터(General Purpose Register)

- 다양한 상황에서 자유롭게 사용 가능한 레지스터를 의미합니다. 예를 들어 메모리 버퍼 레지스터는 데이터 버스로 주고받을 값만 저장하는데, 범용 레지스터는 데이터와 주소 모두 저장 가능합니다.

  • 스택 포인터

- 스택 주소 지정 방식이란 주소 지정 방식에 사용되는 레지스터입니다.

  • 베이스 레지스터

- 변위 주소 지정 방식이란 주소 지정 방식에 프로그램 카운터와 함께 사용됩니다.

 

 

스택 포인터와 베이스 레지스터에 관해 정리하기 위해, 주소 지정 방식인 '스택 주소 지정 방식'과 '변위 주소 지정 방식'으로 넘어가기 전에 다른 레지스터들이 어떻게 작동되는지를 먼저 정리하고자 합니다.

 

메모리 100번지부터 130번지까지 저장된 프로그램을 실행한다고 가정해봅니다.

처음부터 실행하기 위해서는 100번지에 있는 값부터 읽어야 하므로 프로그램 카운터에 100이 들어갑니다. 그리고 100번지에 저장된 값을 읽기 위해서 메모리에 요청을 보내야 하는데, 이때 메모리 주소 레지스터에 100이 저장됩니다. 

레지스터에 값 저장 과정1

그 후에 제어장치에서 메모리에 저장된 값을 읽겠다는 제어 신호를 내보내, 메모리 주소 레지스터 값과 함께 각각 제어 버스와 주소 버스를 통하여 메모리에 보내집니다.

이후 메모리에서는 전달받은 주소 값에 따라 100번지에 저장된 값인 1100을 데이터 버스를 통해 메모리 버퍼 레지스터로 전달합니다. 그 후, 프로그램 카운터의 값이 증가해 101이 되고, CPU에서는 다음 명령어를 읽을 준비를 합니다.

레지스터에 값 저장 과정2

이후 메모리 버퍼 레지스터에 저장된 값을 명령어 레지스터로 보내고, 제어장치는 명령어 레지스터 안에 든 값(명령어)을 해석해 제어 신호를 발생시킵니다.

 

이렇게 위의 과정을 반복하는 것으로, 프로그램 카운터 값을 증가시키면서 명령어를 읽어 프로그램을 순차적으로 실행합니다. 

 

 

다음으로 스택 포인터와 베이스 레지스터를 정리하기 위해 특정 레지스터를 이용한 주소 지정 방식을 적고자 합니다. 

 

1) 스택 주소 지정 방식

- 스택과 스택 포인터를 이용한 주소 지정 방식입니다. 스택은 LIFO(Last-In First-Out) 구조이기에 가장 최근에 저장한 값부터 꺼낼 수 있습니다. 이때 '스택 포인터'는 스택 꼭대기, 그러니까 스택에 마지막으로 저장된 값의 위치를 가리키는 레지스터를 의미합니다. 

2) 변위 주소 지정 방식(Displacement Addressing Mode)

출처: https://csarassignment.wordpress.com/category/chapter-3-instruction-set-architecture/

 

- 명령어 구성 중 오퍼랜드 필드에 메모리 주소가 담기는 경우가 있습니다. 이 방식은 오퍼랜드 필드 값(변위)과 특정 레지스터 값을 더해 유효 주소를 얻어내는 주소 지정 방식을 의미합니다.

- 이 방식을 사용하는 명령어는 연산 코드 필드, 레지스터 필드(어떤 레지스터와 더할지.), 오퍼랜드 필드(주소를 담음)로 구성됩니다. 레지스터 값과 오퍼랜드 필드의 값을 더한 곳에 있는 데이터를 가지고 어떤 작업을 수행하라고 나타낸 것이죠.

더보기

  → 헷갈려서 적는데 그림 속 명령어에 있는 레지스터는 CPU 속 특정 레지스터를 가리키는 것. 어떤 레지스터에 있는 값과 오퍼랜드에 있는 값을 더하는 거니까 그 레지스터가 무엇인지를 가리키는 것(아마...). 교재 그림만 봤을 때 레지스터가 두 번 더해지는 것처럼 보여서 적음.

- 오퍼랜드 필드 주소와 어떤 레지스터(프로그램 카운터, 베이스 레지스터)를 더하는지에 따라 '상대 주소 지정 방식', '베이스 레지스터 주소 지정 방식'으로 나눠집니다(CPU 종류에 따라 여러 가지 방식들이 있다고 했는데 대표적인 것들만 우선 살펴봄).

  • 상대 주소 지정 방식(Relative Addressing Mode)

- 오퍼랜드와 프로그램 카운터 값을 더해 유효 주소를 얻는 방식입니다.

- if문 같이 모든 코드를 실행하는 것이 아니라 특정 주소의 코드를 실행할 때 주로 사용합니다.

출처: https://byjus.com/gate/relative-addressing-mode-notes/

- 위 사진을 예시로 말하자면, PC(프로그램 카운터)가 메모리 2050번지를 가리키고 있고, 명령어 오퍼랜드 필드에 25란 값이 저장되어 있습니다. 2050번지에 저장된 191이란 값과 25란 값을 '더하라'라는 연산코드에 따라 216이란 값을 ACCUMULATOR에 저장합니다.

여기서 ACCUMULATOR는 ALU에 있는 부품으로, CPU에서 산술논리 데이터를 잠깐 저장하기 위해 사용되는 일종의 레지스터라고 합니다. 레지스터와의 경계가 흐릿한 것 같은데, 굳이 차이를 두자면 ACCUMULATOR는 산술논리 연산에서만, 레지스터는 다양한 범위로 사용되는 것 같습니다.

  • 베이스 레지스터 주소 지정 방식(Base-Register Addressing Mode)

- 오퍼랜드와 베이스 레지스터 값을 더해 유효 주소를 얻는 방식을 말합니다. 베이스 레지스터는 여기서 '기준 주소', 오퍼랜드는 '기준 주소로부터 떨어진 거리'의 역할을 합니다. 이 방식을 달리 말하면, 베이스 레지스터 속 기준 주소로부터 얼마나 떨어져 있는 주소에 접근할 건지를 연산해 유효 주소를 얻어내는 것입니다. 

- 예시로 베이스 레지스터에 저장된 값이 100이고 오퍼랜드가 30인 상황이 주어졌다 하겠습니다. 여기서 100이 '기준 주소'가 되는 것이고 30이 '기준 주소로부터 떨어진 거리'를 의미하니, 결과적으로 메모리 주소가 130번지인 곳에 접근하라는 것이 됩니다.

더보기

그림을 구글에서 찾는데 처음 봤을 때 적절해보이는 그림이 있었음. 아래 사진.

출처: https://medium.com/computer-architecture-club/simplified-understanding-of-addressing-modes-baef6d4caa38

레지스터에 저장된 값인 2로부터 10만큼 떨어진 거리에 있는 12번지에 접근하는 것을 표현한 사진. 이걸 쓰려고 하다가 Index register라고 되어 있어서 base register랑 다르다고 생각함. 그래서 둘의 차이를 찾아보는데 

 

" The base register often holds the beginning location of a memory array, while the index register holds the relative posi­tion of an element in the array." -https://www.byclb.com/TR/Tutorials/microprocessors/ch3_1.htm

 

라고 뜸. 이 문장을 정확히 해석을 못 해서 잘 이해는 못했는데 base register는 메모리의 시작 부분을 가리키고, index register는 말 그대로 메모리의 원소를 가리키는 것 같음. 여기서 말하는 memory array는 코딩할 때 배열 선언한 걸 의미하지 않을까 생각함. 

 

https://www.quora.com/What-is-the-difference-between-base-register-addressing-and-indexed-register-addressing

관련된 다른 질문 글이 있었음. 이것도 해석 때문에 이해가 제대로 된 것 같지는 않은데 아마 위와 비슷한 맥락인 것 같았음. 위 사진의 방식을 Index Addressing Mode라 함. 그림만 봤을 때는 base register addressing mode와 동일한 것 같은데 레지스터 이름 때문에 긴가민가함. 그래서 일단 다른 걸로 생각.

 


 

명령어 사이클과 인터럽트

: CPU가 하나의 명령어를 처리하는 과정에 어떤 정해진 흐름이 있고, CPU는 그 흐름을 반복해 명령어들을 처리합니다. 이렇게 하나의 명령어를 처리하는 정형화된 흐름을 '명령어 사이클(Instruction Cycle)'이라 합니다. 명령어 사이클의 과정(인터럽트를 제외한)은 다음과 같습니다.

1) 인출 사이클

- 메모리에 있는 명령어를 CPU로 가지고 오는 단계

2) 실행 사이클

- CPU로 가져온 명령어를 실행하는 단계. 제어장치가 명령어 레지스터에 담긴 값을 해석, 제어 신호를 발생시킴.

3) 간접 사이클

- 간접 주소 지정 방식의 경우 명령어를 인출해 CPU로 가져왔다 하더라도 바로 실행 사이클을 진행할 수 없음. 유효 주소의 주소를 가져온 것이니 한 번 더 메모리에 접근해야 하기 때문. 그래서 간접 사이클이란 단계가 필요.

 

인터럽트(Interrupt)

: CPU가 수행 중인 작업이 방해로 인해 중단될 수 있는데, 이렇게 CPU의 작업을 방해하는 신호를 의미합니다. 인터럽트 종류는 크게 두 가지로 나눌 수 있습니다. 마인드맵으로 정리하고 아래에 좀 더 자세히 적고자 합니다.

1) 동기 인터럽트(Synchronous Interrupts)

- CPU에 의해 발생하는 인터럽트를 말합니다. CPU가 명령어들을 수행하다가 예상치 못한 상황과 마주했을 때(프로그래밍상의 오류 등)를 예시로 들 수 있습니다. 그래서 동기 인터럽트를 예외(exception)이라고 부릅니다.

접근한 메모리 주소에 원하는 데이터가 없는 경우, 실행할 수 없는 명령어가 적혀 있을 경우..

2) 비동기 인터럽트(Asynchronous interrupts)

- 주로 입출력장치에 의해 발생합니다. 입출력장치가 CPU에게 보내는 알림 같은 것이죠. 인터럽트라 칭하기도 하지만, ‘하드웨어 인터럽트라고도 합니다. 이 인터럽트가 발생하는 경우의 예시는 다음과 같습니다.

  • CPU가 프린터 같은 입출력장치에 입출력 작업을 요청하면, 그 작업을 다 끝낸 입출력장치가 CPU에게 완료 알림을 보냅니다.
  • 키보드, 마우스 같은 입출력장치가 어떤 입력을 받았을 때 이를 처리하기 위해 CPU에 입력 알림을 보냅니다.

비동기 인터럽트가 필요한 이유가 있습니다. 만약 없을 경우 입출력장치가 CPU보다 작업 처리 속도가 느리기에, CPU가 입출력장치가 작업을 다 끝냈는지 수시로 확인해야 합니다. CPU가 신경써야 할 것이 더 많아진다는 뜻이죠. 그런데 비동기 인터럽트가 있다면 입출력장치로부터 알림을 받기 전까지 다른 작업을 수행할 수 있기에 효율적인 작업 처리가 가능해지는 것입니다.

 

비동기 인터럽트 처리 순서는 다음과 같습니다.

1) 입출력장치가 CPU에 인터럽트 요청 신호*를 보냄

2) CPU는 실행 사이클이 끝나고 명령어를 인출하기 전 항상 인터럽트 여부를 확인함.

3) CPU는 인터럽트 요청을 확인하고 인터럽트 플래그*를 통해 현재 인터럽트를 받아들일 수 있는지 여부 확인

4) 인터럽트를 받아들일 수 있으면 CPU는 지금까지의 작업을 백업함.

5) CPU는 인터럽트 벡터*를 참조해 인터럽트 서비스 루틴*을 실행함.

6) 인터럽트 서비스 루틴 실행이 끝나면 4에서 백업한 작업을 복구해 실행을 재개함.

더보기

'인터럽트 요청 신호'

: CPU의 정상적인 실행 흐름을 끊는 것이 인터럽트니까, 도중에 그래도 괜찮은지 물어보는 신호.

 

'인터럽트 플래그'

: CPU가 인터럽트 요청을 수용하기 위해 활성화되어 있어야 하는 플래그 레지스터의 한 플래그. 비동기 인터럽트를 수용할지 안 할지를 결정함. CPU가 중요한 작업을 처리할 때나 방해받으면 안 될 때는 인터럽트 플래그가 불가능으로 설정됨. 그러나 비동기 인터럽트 중에서 인터럽트 플래그가 설령 신호 안 받는다 설정되었어도 무시할 수 없는 것도 존재함. 정전이나 하드웨어 고장으로 인한 인터럽트 등, 가장 먼저 처리해야 하는 신호가 그 예시.

 

'인터럽트 서비스 루틴'

: CPU가 인터럽트 요청을 받아들였을 경우 실행하는 프로그램으로, 인터럽트를 처리하는 일을 함. 그러니까 어떤 인터럽트가 발생했을 경우, 해당 인터럽트를 어떻게 처리하고 작동해야 할지에 대한 정보로 이루어져 있음. 인터럽트 핸들러라고도 함.

 

'인터럽트 벡터'

: 인터럽트를 보내는 주체에 따라서 각기 다른 인터럽트 서비스 루틴 시작 주소를 가짐. CPU는 이런 여러 인터럽트 서비스 루틴을 구분할 수 있어야 하기에, 이를 구분하기 위해 사용하는 것이 인터럽트 벡터. 인터럽트 서비스 루틴을 식별하기 위한 정보(인터럽트 서비스 루틴 시작 주소)를 의미하고, CPU는 이를 참조해 알맞는 인터럽트 서비스 루틴을 실행할 수 있게 됨. 인터럽트 벡터를 모아 표의 형태로 메모리에 저장하는데 이를 '인터럽트 벡터 테이블'이라 함.

 

비동기 인터럽트 처리 과정을 그림과 함께 정리하고자 합니다. 

(좌) 인터럽트 서비스 루틴 실행 전 모습 / (우) 인터럽트 서비스 루틴 실행 과정 모습

 

왼쪽 그림에서는 CPU가 1500번지에 있는 프로그램을 실행하고 있습니다. 그런데 갑자기 인터럽트가 발생합니다. 이 인터럽트와 관련된 인터럽트 서비스 루틴의 시작 주소는 10번지입니다. 인터럽트 서비스 루틴을 실행한 후, 다시 1500번지에 있던 프로그램을 이어서 실행하려면 그전까지의 정보 전부가 다른 곳에 저장되어 있어야 합니다. 프로그램 카운터 값, 메모리 주소 레지스터, 명령어 레지스터 등등 관련된 모든 정보를 스택에 저장해둡니다. 그러고 나서 프로그램 카운터의 값을 10번지로 변경하고 인터럽트 서비스루틴을 실행합니다. 이 프로그램이 종료되면 스택에 저장해 둔 값을 다시 불러와 이전 작업을 이어서 수행합니다. 

 

아래는 인터럽트 사이클까지 추가한 명령어 사이클을 간략하게 표현한 것입니다.

 

 

 

'컴퓨터 구조' 카테고리의 다른 글

Ch03 명령어  (0) 2024.08.02
Ch01~02 컴퓨터 구조 시작하기-데이터  (5) 2024.08.02
공부 계획  (0) 2023.08.19