[혼공컴운] CH 04 CPU의 작동 원리

sookyoung.k·2024년 1월 10일
0

🤓 [혼공학습단] 

목록 보기
6/23
post-thumbnail

📍 ALU와 제어장치

🌀 ALU

ALU는 연산한 결괏값과 플래그를 내보낸다.

ALU가 받아들이는 정보

  • 레지스터를 통해 피연산자를 받아들인다
  • 제어장치로부터 수행할 연산을 알려주는 제어 신호를 받아들인다

→ 레지스터와 제어장치로부터 받아들인 정보들을 통해 산술 연산, 논리 연산 등 다양한 연산을 수행한다

ALU가 내보내는 정보

  • 연산을 수행한 결과
    (특정 문자가 될 수도, 메모리 주소가 될 수도)
  • 플래그 (연산 결과에 대한 추가적인 정보)

→ 이 결괏값은 바로 메모리에 저장되지 않고 일시적으로 레지스터에 저장된다

* CPU가 메모리에 접근하는 속도는 레지스터에 접근하는 속도보다 훨씬 느리다 (프로그램 실행 속도를 늦출 수도 있다) 때문에 ALU의 결괏값을 메모리가 아닌 레지스터에 우선 저장함

플래그 종류의미
부호 플래그연산한 결과의 부호
제로 플래그연산 결과가 0인지 여부
캐리 플래그연산 결과 올림수나 빌림수가 발생했는지
오버플로우 플래그오버플로우가 발생했는지
인터럽트 플래그인터럽트가 가능한지
슈퍼바이저 플래그커널 모드로 실행 중인지, 사용자 모드로 실행 중인지

→ 플래그들은 플래그 레지스터에 저장된다

* 이외에도 ALU 내부에는 여러 계산을 위한 회로들이 있다 (덧셈을 위한 가산기, 뺄셈을 위한 보수기, 시프트 연산을 수행해 주는 시프터, 오버플로우를 대비한 오버플로우 검출기 등)

🌀 제어장치

제어장치는 제어 신호를 내보내고, 명령어를 해석하는 부품
CPU의 구성 요소 중 가장 정교하게 설계된 부품

* 제어 신호는 컴퓨터 부품들을 관리하고 작동시키기 위한 일종의 전기 신호

제어장치가 받아들이는 정보

  • 클럭 신호

    * 클럭 - 컴퓨터의 모든 부품을 일사 분란하게 움직일 수 있게 하는 시간 단위

    • 클럭 주기에 맞춰 한 레지스터에서 다른 레지스터로 데이터 이동 or ALU에서 연산 수행 or CPU가 메모리에 저장된 명령어를 읽어들임
  • '해석해야 할 명령어'

    * CPU가 해석해야 할 명령어는 명령어 레지스터라는 특별 레지스터에 저장

    • 레지스터로부터 해석할 명령어를 받아들이고 해석한 뒤, 제어 신호를 발생시켜 컴퓨터 부품들에 수행해야 할 내용을 알려준다
  • 플래그 레지스터 속 플래그 값

    • 제어장치는 플래그 값을 받아들이고 이를 참고하여 제어 신호를 발생시킨다
  • 시스템 버스, 그 중에서 제어 버스로 전달된 제어 신호

    • 제어 신호는 CPU뿐만 아니라 입출력장치를 비롯한 CPU 외부 장치도 발생시킬 수 있다
    • 제어 버스를 통해 외부로부터 전달된 제어 신호를 받아들이기도 한다

제어장치가 내보내는 정보

  • CPU 외부에 전달하는 제어 신호

    • 제어 버스로 제어 신호를 내보낸다
    • 메모리에 전달하는 제어 신호와 입출력장치에 전달하는 제어신호가 있다
  • CPU 내부에 전달하는 제어 신호

    • ALU에 전달하는 제어 신호(수행할 연산 지시)와 레지스터에 전달하는 제어 신호(레지스터 간에 데이터를 이동시키거나 저장된 명령어를 해석하기 위해)가 있다

📍 레지스터

프로그램 속 명령어와 데이터는 실행 전후로 반드시 레지스터에 저장된다!
➡️ 레지스터에 저장된 값만 관찰해도 프로그램의 실행 흐름을 파악할 수 있다!

반드시 알아야 할 레지스터

상용화된 CPU 속 레지스터들은 CPU마다 이름, 크기, 종류가 매우 다양하다 >> 이건 각 CPU 제조사 홈페이지 등에서 확인해야 함

때문에 이 책에서 다루는 레지스터는 여러 전공 서적에서 중요하게 다루는 레지스터, 많은 CPU가 공통으로 포함하고 있는 여덟 개의 레지스터를 다룬다! 잘 알아보자!

✔️ 프로그램 카운터

메모리에서 가져올 명령어의 주소를 저장한다

바꿔 말하면 메모리에서 읽어들일 명령어의 주소를 저장한다
* 명령어 포인터라고 부르는 CPU도 있다

✔️ 명령어 레지스터

해석할 명령어를 저장한다

해석할 명령어라는 것은 방금 메모리에서 읽어 들인 명령어를 말한다!

✔️ 메모리 주소 레지스터

메모리의 주소를 저장한다

CPU가 읽고자 하는 주소 값을 주소 버스로 보낼 때 메모리 주소 레지스터를 거친다

✔️ 메모리 버퍼 레지스터

메모리와 주고받을 값(데이터와 명령어)을 저장한다

메모리에 쓰고 싶은 값 혹은 메모리로부터 전달받은 값 → 메모리 버퍼 레지스터를 거치게 된다
데이터 버스로 주고받을 값은 메모리 버퍼 레지스터를 거친다

* 메모리 데이터 레지스터(MDR; Memory Data Register)라고도 부른다


메모리에 저장된 프로그램을 실행하는 과정

  1. CPU에 실행할 프로그램이 1000번지부터 1500번지까지 저장되어 있다고 가정!

1000번지에 저장된 것 - 1101⑵

  1. 프로그램을 처음부터 실행하기 위해 프로그램 카운터에 1000이 저장된다
    ➡️ 메모리에서 가져올 명령어1000번지에 있다는 것을 의미

  2. 1000번지를 읽어들이기 위해서 해야 하는 일 → 주소 버스로 1000번지를 내보내야 한다! 이를 위해서 메모리 주소 레지스터1000이 저장된다

  3. (제어장치에서 보내는) '메모리 읽기' 제어신호는 제어 버스, 메모리 주소 레지스터 값이 주소 버스를 통해 메모리로 보내진다

  4. 메모리 1000번지에 저장된 값은 데이터 버스를 통해 메모리 버퍼 레지스터로 전달되고, 프로그램 카운터는 증가되어 다음 명령어를 읽어들일 준비를 한다 (증가값: 1001⑵)

  5. 메모리 버퍼 레지스터에 저장된 값은 명령어 레지스터로 이동한다.

  6. 제어장치명령어 레지스터의 명령어를 해석하고 제어 신호를 발생시킨다

➡️ 5단계에서 프로그램 카운터 값이 증가한다 그러므로 CPU는 1000번지 명령어 처리가 끝나면 다음 명령어(1001번지)를 읽어들인다

이렇게 프로그램 카운터가 지속적으로 증가하며 다음 명령어를 읽어들일 준비를 한다 이 과정이 반복되면서 CPU가 프로그램을 차례대로 실행해 나간다!

프로그램이 실행된다는 것 = CPU 속 프로그램 카운터가 꾸준히 증가하고 있다!


✔️ 범용 레지스터

다양하고 일반적인 상황에서 자유롭게 사용할 수 있는 레지스터

일반적으로 메모리 버퍼 레지스터는 데이터 버스로 주고받을 값만 저장하고, 메모리 주소 레지스터는 주소 버스로 내보낼 주소 값만 저장한다

하지만 범용 레지스터는 데이터와 주소를 모두 저장할 수 있다

✔️ 플래그 레지스터

연산 결과 또는 CPU 상태에 대한 부가적인 정보를 저장한다

✔️ 스택 포인터

스택에 마지막을 저장한 값의 위치를 저장한다

= 스택의 꼭대기

✔️ 베이스 레지스터

기준 주소

📮 특정 레지스터를 이용한 주소 지정 방식

여러가지 주소 지정 방식 중 특정 레지스터를 알아야만 이해할 수 있는 주소 지정 방식들이 있다.

프로그램 카운터(위에서 말함), 스택 포인터, 베이스 레지스터는 주소 지정에 사용될 수 있는 특별한 레지스터 들이다!

스택 포인터는 스택 주소 지정 방식이라는 주소 지정 방식에 사용되고,
프로그램 카운터베이스 레지스터는 변위 주소 지정 방식이라는 주소 지정 방식에 사용된다

🪧 (1) 스택 주소 지정 방식

스택과 스택 포인터를 이용한 주소 지정 방식이다

* 스택 포인터란? 스택의 꼭대기를 가리키는 레지스터이다
➡️ 스택의 어디까지 데이터가 채워져 있는지에 대한 표시라고도 할 수 있다

스택은 메모리 안에 있다

정확하게는 메모리 안에 다른 주소 공간과는 다르게 스택처럼 사용할 영역이 정해져 있다 (스택 영역)

🪧 (2) 변위 주소 지정 방식

명령어는 연산 코드와 오퍼랜드로 이루어져 있다
오퍼랜드 필드에는 메모리의 주소가 담길 때도 있다

변위 주소 지정 방식이란 오퍼랜드 필드의 값(변위)과 특정 레지스터의 값을 더하여 유효 주소를 얻어내는 주소 지정 방식이다

변위 주소 지정 방식을 사용하는 명령어는 다음과 같이 구성되어 있다

연산 코드 필드레지스터 필드오퍼랜드
(이 명령을 수행해라)(이 레지스터의 값과)(이 주소를 더한 곳에 있는 데이터로)

오퍼랜드 필드의 주소와 어떤 레지스터를 더하는지에 따라 방식이 나뉜다

🏷️ 상대 주소 지정 방식

오퍼랜드와 프로그램 카운터의 값을 더하여 유효 주소를 얻는 방식

프로그램 카운터에는 읽어들일 명령어의 주소가 저장되어 있다

연산 코드프로그램 카운터-3

➡️ 실행할 명령어가 담긴 주소의 세 칸 이전 번지의 명령어를 실행한다
{실행할 명령어 -3}번지

왜... 이러는 거지...

모든 코드를 실행하는 것이 아닌 분기하여 특정 주소의 코드를 실행할 때 사용된다.

뭐... 그렇다고 하네요... if문처럼 생각하라고 한다. 흠...

🏷️ 베이스 레지스터 주소 지정 방식

오퍼랜드와 베이스 레지스터의 값을 더하여 유효 주소를 얻는 방식

* 베이스 레지스터는 '기준 주소'를 의미한다
* 오퍼랜드는 '기준 주소로부터 떨어진 거리'의 역할을 한다

베이스 레지스터 속 기준 주소로부터 얼마나 떨어져있는 주소에 접근할 것인지를 연산하여 유효 주소를 얻어내는 방식이다

연산 코드베이스레지스터50

이 때, 베이스 레지스터에 200이라는 값이 있다면?!
➡️ 기준 주소 200번지로부터 50번지 떨어진 코드를 실행하라는 의미!

📍 명령어 사이클과 인터럽트

하나의 명령어를 처리하는 정형화된 흐름을 명령어 사이클이라고 한다

하지만 이를 끊기게 만드는 상황이 발생하기도 하는데 이를 인터럽트라고 한다

🌀 명령어 사이클

프로그램 속 각각의 명령어들이 일정한 주기가 반복되며 실행하는 것

명령어 사이클의 과정

  1. 인출 사이클
    → 메모리에 있는 명령어를 CPU로 가져온다

  2. 실행 사이클
    → CPU로 가져온 명령어를 실행한다
    제어창치가 명령어 레지스터에 담긴 값을 해석하고, 제어 신호를 발생시키는 단계

프로그램의 수많은 명령어는 일반적으로 인출과 실행 사이클을 반복하며 실행된다

하지만! 당연히 이렇게 간단하게 실행되기만 하는 것은 아니다

간접 사이클

간접 주소 지정 방식(이 생각이 나시나요...? 앞부분 다시 보고 오기)
→ 오퍼랜드 필드에 유효 주소의 주소를 명시한다

이런 경우 명령어를 인출하여 CPU로 가져오더라도 바로 실행 사이클에 돌입할 수 없다!
➡️ 명령어를 실행하기 위해서는 메모리 접근을 한 번 더 해야 한다.

🌀 인터럽트

CPU의 작업을 방해하는 신호

✔️ CPU가 꼭 주목해야 할 때
✔️ CPU가 얼른 처리해야 할 다른 작업이 생겼을 때

발생하게 된다

동기 인터럽트 = 예외
CPU에 의해 발생하는 인터럽트

→ 프로그래밍상의 오류와 같은 예외적인 상황에 마주쳤을 때 발생한다

비동기 인터럽트 = 하드웨어 인터럽트
주로 입출력장치에 의해 발생하는 인터럽트

→ 알림 역할을 한다!

하드웨어 인터럽트 = 알림

이게 왜 필요할까? 라고 생각했는데 입출력 작업 도중에도 효율적으로 명령어를 처리하기 위해 이런 알림과 같은 하드웨어 인터럽트를 사용한다.

우리가 토스터기 알람이 없다면... 빵이 타진 않았을지 계속 확인해야 하기 때문에 다른 일을 못 함. 하지만 알림이 있으면, 안심하고 알림이 울리기 전까지 다른 일을 할 수 있음. 똑같은 원리이다.

CPU가 프린터에 출력을 명령했다면, 입출력장치는 CPU보다 속도가 훨씬 느리기 때문에 CPU가 입출력 작업의 결과를 바로 받아볼 수가 없다. 하드웨어 인터럽트가 없다면 CPU는 주기적으로 프린터의 완료 여부를 확인해야 한다. = 사이클 낭비!

하드웨어 인터럽트를 이용하면 CPU는 주기적으로 프린트 완료 여부를 확인할 필요가 없다. 프린트 완료 인터럽트를 받을 때까지는 다른 작업을 처리할 수 있다!

하드웨어 인터럽트 처리 순서

  1. 입출력장치는 CPU에 인터럽트 요청 신호를 보낸다.
  2. CPU는 실행 사이클이 끝나고 명령어를 인출하기 전 항상 인터럽트 여부를 확인한다.
  3. CPU는 인터럽트 요청을 확인하고 인터럽트 플래그를 통해 현재 인터럽트를 받아들일 수 있는지 여부를 확인한다.
  4. 인터럽트를 받아들일 수 있따면 CPU는 지금까지의 작업을 백업한다.
  5. CPU는 인터럽트 벡터를 참조하여 인터럽트 서비스 루틴을 실행한다.
  6. 인터럽트 서비스 루틴 실행이 끝나면 4에서 백업해 둔 작업을 복구하여 실행을 제개한다.

* 인터럽트 요청 신호 > 정상 실행 흐름을 끊는 것이기에 지금 끼어들어도 되는지... 여부를 묻는 것

* 인터럽트 플래그가 활성화되어 있어야 인터럽트 요청을 수용할 수 있다
→ 하드웨어 인터럽트를 받아들일지, 무시할지 결정하는 플래그

✔️ 무시할 수 없는 인터럽트 요청도 있다 (정전, 하드웨어 고장으로 인한 인터럽트)

* 인터럽트 서비스 루틴 (인터럽트 핸들러)
→ 인터럽트를 처리하기 위한 프로그램
→ 어떤 인터럽트가 발생했을 때 해당 인터럽트를 어떻게 처리하고 작동해야 할지에 대한 정보로 이루어진 프로그램이다

* 인터럽트 벡터
→ 인터럽트 서비스 루틴을 식별하기 위한 정보
→ 인터럽트 벡터를 알면 인터럽스 서비스 루틴의 시작 주소를 알 수있기 때문에 CPU는 인터럽트 벡터를 통해 특정 인터럽트 서비스 루틴을 처음부터 실행할 수 있다

정리!
➡️ CPU가 인터럽트를 처리한다 = 인터럽트 서비스 루틴을 실행하고, 본래 수행하던 작업으로 다시 되돌아온다.
➡️ CPU가 인터럽트 서비스 루틴을 실행하려면 인터럽트 서비스 루틴의 시작 주소를 알아야 하는데 이는 인터럽트 벡터를 통해서 알 수 있다

인터럽트가 발생하기 전까지 레지스터에 저장되어 있던 값들은 어떻게 할까요?

CPU는 인터럽트 서비스 루틴을 실행하기 전에 프로그램 카운터 값 등 현재 프로그램을 재개하기 위해 필요한 모든 내용을 스택에 백업한다

그 후 인터럽트 서비스 루틴의 시작 주소가 위치한 곳으로 프로그램 카운터 값을 갱신하고 인터럽트 서비스 루틴을 실행한다

인터럽트를 처리하고 나면 스택의 저장해 둔 값을 다시 불러온 뒤 이전까지 수행하던 작업을 재개한다

🔎 더 알아보기: 예외의 종류

인터럽트에는 동기 인터럽트(예외)가 있고 비동기 인터럽트(하드웨어 인터럽트)가 있따

예외가 발생하면 CPU는 하던 일 중단 후 해당 예외를 처리한다!

  • 폴트(fault) 예외를 처리한 직후 예외가 발생한 명령어부터 실행을 재개하는 예외

  • 트랩(trap) 예외가 발생한 명령어의 다음 명령어부터 실행을 재개 (디버깅할 때 사용)

  • 중단(abort) CPU가 실행 중인 프로그램을 강제로 중단시킬 수밖에 없는 심각한 오류를 발견했을 때 발생하는 예외

  • 소프트웨어 인터럽트(software interrupt) 시스템 호출이 발생했을 때 나타난다

profile
영차영차 😎

0개의 댓글