2023.08.30 컴퓨터구조
4.3 명령어 사이클과 인터럽트
컴퓨터 내부 동작 이해하기 위해 중요하다!!
CPU가 메모리로부터 값을 가져와서 실행하고 저장하기도 한다.
이 과정이 일정한 흐름 주기가 있다.
정해진 흐름대로 처리하게 된다.
이 주기를 명령어 사이클이라고 한다.
간혹 이 정해진 흐름을 방해하는 것을 인터럽트라고 한다.
4.3.1 명령어 사이클
프로그램속 명령어들은 일정한 주기를 반복되며 실행된다.
이 주기를 명령어 사이클이라고 한다.
메모리에 저장된 명령어를 실행하려면?
인출 - 메모리의 값을 CPU로 가져와야한다.
이 인출하는 주기를 인출 사이클이라고한다.
가져와서 실행해야한다.
이 실행하는 주기를 실행 사이클이라고한다.
보통 인출 - 실행 - 인출 - 실행 이 순서를 가진다.
그런데 CPU로 명령어를 가지고와도 바로 실행이 불가능한 경우도 있다.
간접주소지정시 유효주소의 주소에 있다면 몇번더 인출을 해야한다.
이 과정을 간접 사이클이라고 하며
인출사이클 실행사이클 사이에서 작동한다.
4.3.2 인터럽트
정해진 흐름을 끊어버리는게 인터럽트이다.
CPU가 주목해야할때 얼른 처리해야할 다른 작업이 생겼을때 발생한다.
인터럽트의 종류에는
동기인터럽트(예외) 비동기인터럽트(하드웨어 인터럽트)가 있다.
예외 Exception은 CPU가 예기치 못한 상황을 접햇을때 발생한다.
디버깅을 할때 실행할수 없느 명령어가 잇을때 발생한다.
폴트 트랩 중단 소프트웨어 인터럽트 등이 있다.
이번에는 비동기 인터럽트에 대해서 자세하게 다뤄볼것이다.
4.3.3 비동기 인터럽트
주로 입출력 장치에 의해 발생한다.
문제라기보다는 알림과 같은 역할이다. 세탁기 완료알림 전자레인지 조리알림
입출력 작업 도중에도 효율적으로 명령어를 처리하기 위해서 사용한다.
입출력장치는 cpu에 비해 느리다.
인터럽트가 없다면 CPU는 프린트 완료여부를 확인하기 위해 주기적으로 확인해야한다.
만약 있다면 입출력 작업 동안 CPU는 다른 일을 할 수 있다.
끝나면 그때야 중단하고 알아보면된다.
인터럽트의 종류를 막론하고 인터럽트 처리 순서는 대동소이하다.
1.입출력장치는 CPU에 인터럽트 요청신호를 보낸다.
2.CPU는 실행사이클이 끝나고 명령어를 인출하기 전 항상 인터럽트 여부를 확인한다.
3.CPU는 인터럽트 요청을 확인하고 인터럽트 플래그를 통해 현재 인터럽트를 받아들일수 있는지 확인한다.
4.받아들일수있다면 CPU가 지금까지의 작업을 백업한다.
5.CPU가 인터럽트 벡터를 참조하여 인터럽트 서비스 루틴을 실행한다.
6.인터럽트 서비스 루틴이 끝나면 백업해둔 작업을 복구하여 실행을 재개한다.
낯선 용어들인
인터럽트 요청신호 인터럽트 플래그 인터럽트벡터 인터럽트 서비스루틴을 알아보자.
인터럽트 요청신호는 입출력장치들이 cpu에게 끼어들어도되는지 요청하는 신호이다.
만약 요청신호를 받아들이려면 인터럽트 플래그를 확인해야한다.
플래그 레지스터에서 확인한다.
받아들일 수 있다면 처리하게 된다.
모든 인터럽트를 인터럽트 플래그로 막을 수 있는 것은 아니고 정전과 같은 꼭 처리해야하는 것들은 먼저 처리하게 된다.
이런 것을 non maskable intterupt라고한다.
막을 수있는것을 maskable interrupt라고 한다.
대부분은 막을 수 있는 요청신호를 보내게 된다.
받아들이기로 했다면 인터럽트 서비스 루틴을 실행한다.
인터럽트가 발생했을때 메모리에서 실행하는 인터럽트를 어떻게 처리하기 위한 프로그램이다.
인터럽트 서비스 루틴도 프로그램이기에 메모리에 저장된다.
인터럽트 벡터는 각각의 인터럽트를 구분하기 위한 정보이다.
인터럽트마다 고유한 인터럽트 서비스 루틴 시작 위치가 있다.
이런식으로
인터럽트가 발생하면 프로그램을 실행하다가
인터럽트 마다 잇는 고유한 인터럽트 루틴 시작위치(인터럽트 벡터)를보고
돌아가서 인터럽트 서비스 루틴을 실행하고
다시 돌아와서 실행하던 프로그램을 마저 처리한다.
근데 레지스터안에 프로그램 값들이 저장되어 있다.
인터럽트로 간다고 버려버리면 안된다.
그래서 현재 상태를 스택영역에 백업을 해두고 돌아가게 되는 것이다.
인터럽트 처리가 끝나면 스택영역에서 백업을 해서 다음 실행명령어부터 실행하게 된다.
5 CPU성능향상기법
CPU에 대한 심화된 기법을 알아보자.
5.1 빠른 CPU를 위한 설계 기법
몇코어 몇스레드를 지원한다. 이런것에 대해서 알아보자
크럵 코어& 멀티코어 스래드 & 멀티스레드
컴퓨터 부품들은 클럭 신호에 맞춰 일사분란하게 움직인다.
CPU는 명령어 사이클이라는 정해진 흐름에 맞춰 명령어들을 실행한다.
그럼 클럭주기를 빠르게하면? 빠르게 될 것이다?
일반적으로는 YES
5.1.1 클럭속도
헤르츠(Hz)단위로 측정한다.
헤르츠(Hz) 1초에 클럭이 반복되는 횟수
클록이 똑딱 하고 1초에 한번 반복되면 1헤르츠 100번이면 100헤르츠이다.
클럭신호가 영어로는 시계라서 일정하다고 이해할수있는데
빠를땐 빠르게 느릴땐 느리게 할 수 있다.
그러면 마냥 높이면 속도가 무진장빨라지나?
그건또 아니다.
필요이상으로 높이면 발열이 심해진다.
이외로 빨라지는 방법은 코어수를 늘리고 스레드를 늘리는 방법이다.
이 두가지 키워드를 알아야한다.
5.1.2 코어
코어란
현대적인 관점에서 CPU라는 용어를 재해석해야한다.
명령어를 실행하는 부품이라고 이해하고있었다.
전통적으로 명령어를 실행하는 부품은 원칙적으로 하나만 존재했다.
그러나 오늘날 CPU에는 명령어를 실행하는 부품이 여러개 존재한다.
명령어를 실행하는 부품을 코어라는 용어로 사용한다.
결론적으로 코어란 CPU안에 여러 CPU가 있는 것이라고 생각하면된다.
이런 코어를 멀티코어라고 한다.
싱글-듀얼-트리플-쿼드-헥사-옥타-데카-도데카 ...
두개이상의 CPU를 가지고 있는 CPU를 멀티코어 CPU라고한다.
코어마다 비례해서 증가하나? 그것은 아니다.
조별과제를 생각해보자. 모두가 똑같이 참여해서 각 사람당 1인분을 하는 경우는 없다.
코어마다 처리하는 것이 적절하게 분배되지 않으면 적절하게 성능이 높아지지는 않는다.
코어에게 얼마나 적절하게 분배하느냐가 중요하다.
5.1.3 스레드
실행 흐름 단위
스레드는 하드웨어적인 스레드와 소프트웨어적 스레드가 있다.
5.1.3.1 하드웨어 스레드
하나의 코어가 동시에 처리하는 명령어 단위이다.
CPU내부에 각각의 코어가 2개명령어를 각 처리할 수 있다면
2코어 4스레드 CPU가 되는 것이다.
5.1.3.2 소프트웨어 스레드
하나의 프로그램에서 독립적으로 실행되는 단위 -> 내가 아는 스레드이다.
한프로그램 동시에 여러개를 실행하는 것이다.
워드프로세스 기능이 있다고 해보자.
1.사용자로부터 입력한 내용을 화면에 보여주는 기능
2.사용자가 입력한 내용이 맞춤법에 맞는지 검사하는기능
3.사용자가 입력한 내용을 수시로 저장하는 기능
하나의 프로그램에서 독립적으로 실행되는 단위로 3개를 만들면 동시에 실행될 수 있는 것이다.
운영체제에서 멀티프로세스와 멀티스레드에 대해서 알아볼 것이다.
하드웨어스레드가 하나여도 여러개의 소프트웨어 스레드를 만들 수 있다.
엄청나게 빠르게 번갈아가면서 실행하기 때문에 동시에 실행하는 것처럼 보이기 때문이다.
그래서 스레드를 단순히 문자그대로만 이해하지 말자.
하드웨어 스레드, 멀티스레드 프로세서를 실제로 설계하는 것은 매우복잡하지만
핵심은 레지스터이다.
하나의 명령어를 실행하기 위해 꼭필요한 레지서터들을 편의상 레지스터세트라고 해보자.
예를들어 하나의 코어가 프로그램카운터를 두개 가지고 있다고 생각해보자.
두개있으면 두개를 가리킬 수 있다. 이런식으로 이 세트를 여러개 가지고 있다면
멀티스레드를 할 수 있다.
메모리입장의 프로그램은 명령어를 몇개를 가져가는지 알뿐이다.
이 프로그램들이 컴퓨터가 몇개씩 처리할 수 잇는지 아는 것은 불가능하다.
그래서 보이지 않는 논리적인 프로세서, 논리 프로세서 라고 부르기도한다.
5.2 명령어 병령처리 기법
CPU를 배울때 너무너무 중요하다.
어떻게 시간낭비없이 시간을 알뜰살뜰 쓰면서 명령어를 처리하는지도 중요하다.
5.2.1 명령어 파이프 라인
하나의 명령어가 처리되는 과정을 비슷한 실행간격으로 나누면?
크게보면 다음과 같다.
1.명령어인출
2.명령어해석
3.명령어실행
4.결과저장
같은 단계가 겹치지만 않으면 CPU는 각 단계를 동시에 실행할 수 있다.
명령어 1 2 3 4를 단계를 다르게해서 여러개 실행할 수 잇는 것이다.
이렇게 명령어를 병렬로 처리하는 것을 명령어 파이프라인이라고 하고 이 기법을 명령어 파이프라이닝이라고 한다.
만약 이 파이프라인을 사용하지 않는다면
하나 명령어를 다처리하고 다음 것처리하고 이런식으로 시간이 더 길어진다.
5.2.2 파이프라인 위험
파이프라이닝이 높은 성능을 가져오기는 하지만 항상 이상적으로 처리되지 못하는 경우가 있다.
명령어 파이프라인이 성능향상에 실패하는 경우가 있다.
이것을 파이프라인 위험이라고 한다.
크게 세가지 종류가 있다.
데이터 위험 제어위험 구조적위험
5.2.2.1 데이터 위험
data hazard
명령어간 데이터 의존성에 의해 발생한다.
모든 명령어를 동시에 처리할 수 없다.
이전 명령어를 끝까지 실행해야한 비로소 실행할 수 있는 경우
명령어1 R1 <- R2 + R3
명령어2 R4 <- R1 + R5
명령어1의 결과를 명령어2에서 사용하고 있다.
그래서 명령어1을 해석하는 동안 명령어2에서 인출할 수 없다.
이런 상황일때 마냥 겹처서 실행할 수 없다.
5.2.2.2 제어위험
프로그램 카운터의 갑작스로운 변화로 일어난다.
-> JUMP등 으로인해 CPU가 실행하고자하는 명령어가 바뀌는 경우이다.
미리 실행했던 것들이 헛수고가 된다.
이런 상황에서 발생하는 위험이 제어위험이다.
이런 것을 방지하기 위해 프로그램카운터가 어디로 갈지 미리 예측하는데 이것을 분기예측이라고 한다.
5.2.2.3 구조적위험
서로다른 명령어가 같은 CPU부품을 쓰려고할때 발생한다.
정리하자면 명령어 파이프라인은 명령어를 겹쳐서 실행하는 것이다.
다만 언제나 가능한 것으 아니고 파이프라인 위험이 발생햇을때 제대로 실행되지 않는다.
5.2.3 슈퍼스칼라
CPU내부에 여러개의 파이프라인을 둔 구조이다.
오늘날의 멀티스레드 프로세서라고 할 수 있다.
이 경우에 슈퍼스칼라 구조를 사용할 수 있다.
이론적으로는 파이프라인 개수에 비례해서 처리속도가 증가하지만
파이프라인 위험도의 증가로 인해서 반드시 비례해서 처리속도가 증가하지는 않는다.
5.2.4 비순차적 명령어처리
전공서적에서 많이 다루지는 않지만 현대 CPU발전에 있어서 크게 기여한 기법이다.
합법적인 새치기이다.
파이프라인 중단을 막기위해 명령어를 순차적으로 처리하지않는 기법이다.
현재까지는 순차적으로 명령어를 처리하는 방식이엇다.
순차적으로만 처리하면 이상적으로 겹쳐서 실행이 불가능하다.
만약 100번지 101번지 실행결과를 102번지에 저장하라고해보자.
순서대로 실행한다면 100번지 101번지 저장이 마무리된후 102번지 저장이되어야해서 다음 명령어가 멈춰있는다.
이를 해결하기 위해 102번지 저장 명령어를 맨 뒤로 보내버린다면
전체결과에는 영향이 없으니 효율적으로 처리를 할 수 있게 된다.
이렇게 순서를 바꿔 실행하면 파이프라인이 좀 더 원활하게 처리할 수 있게 된다.
의존성이 없는 명령어를 순서를 바꾼다면 좀더 원활한 처리가 되는 것이다.
5.3 CISC와 RISC
명령어가 어떻게 생겨야 명령어 파이프라이닝에 유리할까?
이세상의 모든 CPU가 다 똑같이 생긴 명령어를 처리하나?
아니다. CPU마다 이해하는 명령어의 종류가 다르다.
5.3.1 명령어 집합
명령어 집합(구조) CPU가 이해할 수 있는 명령어의 모음
인텔 CPU에서 만든 실행파일을 그대로 아이폰에 옮겨서 실행하는 것은 불가능하다.
CPU는 명령어 집합에 속해있는 명령어만 이해할 수 있다.
같은 소스코드를 컴파일하더라도 다른 CPU라면 다른 어셈블리어로 변환될 수 있다는 것을 학습했었다.
명령어집합은 CPU의 언어인셈이다.
실제 언어에서 생활습관이나 양식이 달라지는 경우가 있다.
명령어 집합이 달라지면 나이효과로 많은게 달라진다.
명령어 해석방식, 레지스터의 종류와 개수, 파이프라이닝의 용이성 등등
그래서 CPU의 구조가 바뀌기 때문에 명령어 집합을 명령어 집합구조라고 부르기도 한다.
이 명령어 집합이 달라지면 그 명령어를 조금더 효율적으로 실행하는 하드웨어도 달라진다.
소프트웨어를 어떻게 해석해야할지에 대한 약속이라고 할 수 있다.
가장 큰 카테고리가 CISC와 RISC가 있다.
5.3.2 CISC
Complex Instruction Set Computer
복잡한 명령어 집합을 활용하는 컴퓨터(CPU)이다.
인텔이나 AMD가 사용하는 x86, x86-64가 CISC기반을 사용한다.
복잡하고 다양한 명령어를 활용한다.
그래서 형태와 크기가 다양한 가변길이 명령어를 활용한다.
다양하고 강력한 명령어를 활용하기 때문에
상대적으로 적은수의 명령어로도 프로그램을 실행할 수 있다.
그럼 여기까지 보면 가장 좋아보인다.
실제로 메모리를 최대한 아끼며 개발해야했던 시절에는 인기가 높았다.
그러나 명령어 파이프라이닝이 불리하다는 치명적인 단점이 있다.
명령어가 워낙 복잡하고 다양한 기능을 제공해서
명령어의 크기와 실행되기까지의 시간이 일정하지 않다.
명령어 하나를 실행하는데 여러 클럭 주기가 필요하다.
게다가 대다수의 복잡한 명령어는 사용빈도가 낮다.
자주 쓰는 명령어만 사용하게 된다.
5.3.3 RICS
Resuded Instruction Set Computer
명령어의 종류가 적고 짧고 규격화된 명령어를 사용한다.
가급적 1클럭에서 돌아가는 것을 중요하게 여긴다.
메모리 접근 최소화 레지스터를 잘 활용하게 된다.
다만 명령어가 CISC 보다 적기에
더많은 명령어로 프로그램을 실행시키게 된다.
예를드러 CISC 한줄로 가능한걸 RICS 3줄이 필요하게 될 수도 있다.

그런데 현대에선 CPU사들에서 CICS입장에서도 명령어를 조금더 잘게 쪼개서 실행하는 경우도 많다.
그래서 CISC기반도 실제 CPU를 들여다보면 잘게 쪼개서 실행해서 내부적으로는 RISC처럼 사용하기도 한다.
2023.08.30
cpu의 구조 명령어를 실행하는 방법에 대해 알게되엇다.
스레드가 단순히 멀티스레드로 실행흐름으로 이해햇엇는데 하드웨어와 소프트웨어의 스레드를 이해할 필요를 알게되었다.