"이것이 취업을 위한 컴퓨터 과학이다 with CS 기술면접" 책을 참고했습니다.
1. CPU
CPU는 컴퓨터에서 가장 중요한 핵심 부품 중 하나이지만, 개발자가 내부 회로나 작동 방식을 모두 알아야하는 것은 아니다. 필요한 핵심 정보만 차례차례 알아보자
1-1 레지스터
레지스터는 CPU 내부에 있는 작은 임시 저장 장치로 다양하게 존재하며 각기 다른 이름과 역할을 지니고 있다. 또한 데이터나 명령어가 실행 전후로 레지스터에 저장되기 때문에 저장된 값만 확인해도 프로그램이 어떻게 동작하는지 알 수 있다.이제 각각의 공통적으로 지니는 핵심적인 레지스터에 대해서 알아보자.
- 프로그램 카운터 : 메모리에서 다음으로 읽어들일 명령어의 주소를 저장한다.
- CPU에 따라서는 명령어 포인터라고 부르기도 한다.
- 일반적으로 순차적으로 1씩 증가하며 실행되지만 실행 흐름에 따라 임의의 위치로 변경되기도 한다.
- 명령어 레지스터 : 해석할 명령어, 즉 메모리에서 방금 읽어 들인 명령어를 저장하는 레지스터이다. 제어장치는 이 레지스터 내부의명령어를 해석한뒤 ALU로 하여금 연산하도록 시키거나 다른 부품으로 제어 신호를 보내 작동시킨다.
- 범용 레지스터 : 일반적인 상황에서 자유롭게 사용 가능한 레지스터이며, 데이터와 명령어, 주소 모두를 저장할 수 있다.
- 플래그 레지스터 : 연산의 결과 혹은 CPU 상태에 대한 부가 정보인 플래그 값을 저장하는 레지스터이다. CPU가 연산을 할 때 반드시 참조해야할 상태 정보를 의미하는 비트를 플래그라고 하며 플래그의 종류는 다음과 같다.
- 부호 플래그 : 연산 결과의 부호를 표현하는 플래그
- 제로 플래그 : 연산 결과가 0인지 아닌지를 표현하는 플래그
- 캐리 플래그 : 연산 결과에 올림수나 빌림수가 발생했는지 여부를 체크
- 오버플로우 플래그 : 오버플로우 발생 여부
- 인터럽트 플래그 : 인터럽트가 가능한지 여부
- 슈퍼바이저 플래그 : 커널 모드인지 사용자모드인지 여부 확인
- 스택 포인터 : 메모리 내 스택 영역의 최상단 스택 데이터 위치를 가리키는 특별한 레지스터를 말한다. 메모리에는 실행 중인 프로그램들이 순서대로 스택과 같은 형태로 적재되어 있다. 따라서 현재 스택이 채워진 정도를 나타내는 레지스터로 이해하는 게 편하다.
1-2 인터럽트(interrupts)
인터럽트는 "방해하다, 중단시키다" 라는 의미로, CPU가 수행 중인 작업은 방해를 받아 잠시 중단될 수 있는데, 이렇게 CPU의 작업을 방해하는 신호를 인터럽트라고 헌더다. 인터럽트의 종류는 크게 다음과 같다.
- 동기 인터럽트 : CPU에 의해 발생하는 인터럽트로 가령 프로그래밍 오류와 같은 예외적인 상황에 발생하는 인터럽트이다. 이런 점에서 동기 인터럽트는 예외라고 부르기도 한다.
- 비동기 인터럽트 : 입출력장치에 의해 발생하는 인터럽트로 일종의 알림 역할을 한다.
용어의 혼동을 줄이기 위해 비동기 인터럽트는 하드웨어 인터럽트로 대치해서 설명하도록 하겠다.
하드웨어 인터럽트
앞에서 비동기 인터럽트, 즉 하드웨어 인터럽트는 알람과 같은 역할을 한다고 설명했다. 하드웨어 인터럽트는 CPU가 효율적으로 명령어를 처리하기 위해 사용한다. 무슨 뜻이냐면, 입출력장치는 CPU에 비해 속도가 현저히 느리기 때문에 CPU가 입출력장치에 명령을 했다고 하더라도 그 결과를 받아보기까지는 시간이 오래 걸린다. 만약 인터럽트를 사용하지 않는다면, CPU는 명령의 결과를 확인하기 위해 한없이 기다려야만 한다. 따라서 하드웨어 인터럽트를 사용하면 입출력장치가 작업이 완료될 때까지 CPU는 다른 작업을 할 수 있다. CPU가 하드웨어 인터럽트를 처리하는 과정은 다음과 같다.
- 입출력장치가 CPU에게 인터럽트 요청 신호를 보낸다.
- CPU는 실행 사이클이 끝나고 명령어를 인출하기 전에 항상 인터럽트 여부를 확인한다.
- CPU는 요청을 확인하고, 인터럽트 플래그를 통해 현재 인터럽트를 수용할 수 있는지 확인한다.
- 받아들일 수 있다면, 지금까지의 작업을 백업한다.
- 인터럽트 벡터를 참조해 인터럽트 서비스 루틴을 실행한다.
- 인터럽트 서비스 루틴이 끝나면 백업해 둔 작업을 복구해 실행을 재개한다.
위의 일련의 과정에서 아직 익숙하지 않은 단어들이 보인다. 인터럽트에 대해 이해하기 위해서는 필요한 단어이니 하나씩 확인해보자.
- 인터럽트 요청 신호 : CPU의 정상적인 실행 흐름을 끊기 전, 인터럽트가 가능한지 여부를 확인해야 하는데, 이를 위한 신호를 의미한다.
- 인터럽트 플래그 : 인터럽트 요청을 수용하기 위해 현재 수용할 수 있는지 여부를 표시하는 플래그(플래그 레지스터)이다. 불가능(0)으로 설정되어 있다면 인터럽트 요청을 무시한다. 다만 모든 하드웨어 인터럽트를 무시할 수 있는 것은 아니다. 예를 들어 하드웨어 고장이나 정전과 같이 막을 수 없는 인터럽트(non maskable Interrupt)의 경우는 불가능 여부와 상관없이 처리한다.
- 인터럽트 서비스 루틴 : CPU가 인터럽트 요청을 받아들인다고 결정했다면, 인터럽트 서비스 루틴이라는 프로그램을 실행한다. 이는 어떤 인터럽트가 발생했을 때 해당 인터럽트를 어떻게 처리해야되는지에 대한 정보가 담긴 프로그램이다. 다른 말로 인터럽트 핸들러라고 부르기도 한다. 요약하자면 CPU가 인터럽트를 처리한다는 뜻은 인터럽트 서비스 루틴을 실행한 뒤 본래 수행하던 작업으로 되돌아오는 것을 의미한다.
- 인터럽트 벡터 : 수많은 인터럽트 서비스 루틴들을 구분하기 위한 정보를 의미하며, 입출력장치가 인터럽트 요청 시에 함께 보낸다. 인터럽트 벡터는 인터럽트 서비스 루틴의 시작 주소를 포함하고 있기 때문에 CPU가 특정 인터럽트 서비스 루틴을 인지하고 실행할 수 있다.
이제 앞에서 배운 인터럽트의 과정을 확인하면, 지금까지 학습한 명령어 사이클의 일련의 과정은 다음과 같다.
예외(Exception)
예외의 종류에는 폴트, 트랩, 중단, 소프트웨어 인터럽트 등이 있다. 예외가 발생하면 CPU는 하던 일을 중단하고 예외를 먼저 처리한다. 다만 처리한 이후에 원래 작업으로 돌아왔을 때, 예외가 발생한 명령어부터 실행할지, 명령어 예외가 발생한 명령어의 다음 명령어부터 실행하느냐에 따라 폴트와 트랩으로 나뉘게 된다.
- 폴트 : 예외가 발생한 명령어부터 실행을 재개하는 예외를 의미한다. 가령 명령어 실행을 위한 데이터가 메모리가 아닌 보조기억장치에 있다면, CPU의 명령어 실행을 위해서는 메모리에 저장이 되어 있어야 하므로 해당 명령어부터 다시 실행해나간다.(페이지 폴트)
- 트랩 : 예외가 발생한 명령어의 다음 명령어부터 실행을 재개하는 예외이다. 대표적으로 디버깅 시에 브레이킹 포인트를 걸어놓으면 디버깅이 끝나면(트랩이 끝나면) 그 포인트 이후부터 실행하게 된다.
- 중단 : 실행 중인 프로그램을 강제로 중단시킬 수 밖에 없는 심각한 오류 발생 시 생기는 예외이다.
- 소프트웨어 인터럽트 : 시스템 콜이 발생했을 때 발생하는 예외이다. 추후 운영체제에 대해 학습할 때 인지하면 된다.
1-3 CPU 성능 향상을 위한 설계
이번에는 CPU의 성능을 향상시키는데 관련된 주요 개념이나 설계 기법, 그리고 기타 정의에 대해 알아보자.
CPU 클럭 속도
CPU를 실제로 구매하려고 할 때 빠지지 않고 나오는 단어 중의 하나는 클럭이다. 클럭이란 컴퓨터의 부품을 일시불란하게 움직일 수 있게 하는 시간의 단위이다. 클럭 속도는 헤르츠 단위로 측정되는데 이는 클럭이 1초에 몇 번 반복되는지 나타낸다. 클럭 속도가 높아질 수록 다른 기타 컴퓨터의 부품도 빠르게 작동할 것을 기대할 수 있다. 일반적으로 클럭 속도가 높은 CPU는 성능이 좋으므로 클럭 속도는 CPU의 속도 단위로 간주되기도 한다. 다만 클럭 속도를 필요 이상으로 과하게 올리게 되면 그로 인해 발생하는 컴퓨터의 발열이 심해질 수 있기 때문에 클럭 속도를 높이는 것만으로 CPU 성능을 높이는 데에는 한계가 있다.
멀티코어와 멀티스레드
클럭 속도 이외에도 CPU의 코어나 스레드 수를 늘리는 것 또한 성능을 개선하는 방안이 될 수 있다. 코어는 CPU 내에서 명령어를 읽고 해석하고 실행하는 부품을 의미한다. 전통적인 관점에서는 CPU가 코어의 역할을 했지만, 이제는 시대가 바뀌고 CPU 내부에 명령어를 읽고 해석하는 부품이 여러 개가 존재할 수 있게 되어 코어라는 이름을 붙이게 되었다. 이렇게 여러 개의 코어를 지닌 CPU를 멀티코어 CPU 혹은 멀티코어 프로세서 라고 부르며, 코어의 개수에 따라 다양한 이름을 지닐 수 있게 되었다.
스레드는 하나의 실행 흐름의 단위를 의미한다. 하지만 이런 의미로 이해하면 혼동이 올 수 있기 때문에 CPU에서 사용하는 하드웨어 스레드와 프로그래밍 언어나 운영체제에서 사용하는 소프트웨어적 스레드(이하 스레드)를 나눠 기억하는 것이 좋다.
하드웨어 스레드란 하나의 코어가 동시에 처리하는 명령어의 단위를 의미한다.
- 동일한 의미로 하나의 코어로 여러 명령어를 동시에 처리하는 스레드를 멀티스레드 프로세서 혹은 멀티스레드 CPU라고 부른다.
- 하나의 코어가 한 번에 하나의 명령어를 읽고 처리하면 이는 1코어 1스레드 CPU라고 볼 수 있다.
- 프로그램의 입장에서 봤을 때는 2코어 4스레드 같은 경우에는 한 번에 4개의 명령어를 처리할 수 있으므로, 4개의 CPU가 있는 것처럼 보일 수 밖에 없다. 이러한 논리로 하드웨어 스레드를 논리 프로세서라고 부르기도 한다.
소프트웨어 스레드란 하나의 프로그램에서 독립적으로 실행되는 단위를 의미한다. 운영체제에서 사용하는 스레드가 이걸 의미한다.
- 1코어 1스레드인 CPU도 여러 스레드로 만들어진 프로그램을 실행할 수 있다.
하드웨어 스레드와 소프트웨어 스레드의 가장 큰 차이는 동시성과 병렬성이란 키워드를 통해 구분할 수 있다.
동시성(Concurrency) | 병렬성(Parallelism) |
동시에 실행되는 것 같이 보이는 것 | 실제로 동시에 여러 작업이 처리되는 것 |
싱글 코어에서 멀티 쓰레드를 동작 시키는 방식 | 멀티 코어에서 멀티 쓰레드를 동작시키는 방식 |
논리적인 개념 | 물리적인 개념 |
1-4 파이프라이닝을 통한 명령어 병렬 처리
명령어 병렬 처리 기법은 여러 명령어를 동시에 처리해 CPU를 쉬지 않고 사용해 성능을 높이는 기법을 말한다. 현대 CPU 명령어 처리에서 빠질 수 없는 핵심 기능 중 하나이다. 다양한 병렬 처리 기법 중 제일 중요한 기법은 명령어 파이프라이닝이다.
우선 명령어가 처리되는 과정을 비슷한 시간 간격으로 나누어보아야 한다. 각각 다음과 같다.
- 명령어 인출(Instruction Fetch)
- 명령어 해석(Instruction Decode)
- 명령어 실행(Execute Instruction)
- 결과 저장(Write back)
위와 같은 과정이 있을 때 핵심은 겹치지만 않는다면 각각의 단계를 동시에 CPU가 실행할 수 있다는 점이다. CPU는 하나의 명령어가 인출되는 동안 다른 명령어를 실행하거나 해석할 수 있다.
위 그림과 같이 명령어를 파이프라인에 넣고 동시에 처리하는 기법을 명령어 파이프라이닝이라고 한다.
요즘에는 하나의 파이프라인만을 쓰는 것이 아니라 CPU 내부에 여러 파이프라인을 두기도 하는데 이런 구조를 슈퍼스칼라라고하고, 그런 CPU를 슈퍼스칼라 프로세서(CPU)라고 한다.
다만 CPU가 이해하고 실행하는 명령어 집합 중에는 병렬 처리가 유리한 집합과 불리한 집합이 있다. 대표적인 성능 차이를 보이는 명령어 집합 유형으로는 CISC와 RISC가 존재한다.
- CISC : 다채로운 기능을 제공하는 복잡한 명령어 집합으로 적은 수의 명령어로도 프로그램 실행이 가능하다. 다만 하나의 명령어 실행을 위해 여러 클럭이 필요하기도 하고 수행 시간이 각양각색이기 때문에 파이프라이닝에 비효율적일 수 있다.
- RISC : 짧고 규격화된 명령어, 되도록이면 1클럭 내로 실행되는 명령어를 지행한다. 크기가 규격화되어 있고 1클럭 내외로 실행되는 특성 때문에 파이프라이닝에 효과적이다.
추가적으로 파이프라이닝이 항상 성공하는 것은 아니기 때문에 실패하는 경우들이 있고 이를 파이프라인 위험(pipeline hazard)이라고 부른다. 파이프라인 위험에는 크게 데이터 위험, 제어 위험, 구조적 위험으로 나눌 수 있다.
데이터 위험(Data Hazard)
명령어 간의 데이터 의존성으로 인해 발생하는 위험을 말한다. 간단히 말하면 아직 수행되지 않은 명령어의 결과값을 참조하게 됨으로써 발생하게 되는 위험이다.
예를 들어, 명령어 2는 명령어 1에 의존적이라고 가정한다. 그럼 명령어 2는 명령어 1이 끝나야만 인출할 수 있는데, 이를 고려하지 않고 무작정 파이프라인에 맞춰 겹쳐 실행하면 정상적으로 실행되지 않는 문제가 발생할 수도 있다. 다만 이런 경우에는 파이프라인에 명령어 단계를 일부 지연(stall)시켜 해결하기도 한다.
제어 위험(Control Hazard)
Jump, Call, Interupt와 같은 분기 명령에 의해 프로그램 카운터(PC)의 갑작스러운 변화에 의해 발생한다. 일반적으로 명령어는 프로그램 카운터가 순차적으로 증가하며 실행된다. 하지만 분기 명령에 의해 프로그램 실행 흐름이 바뀌고 PC 값에 변화가 생기면 미리 인출하거나 해석 중인 명령어가 쓸모없게 되어버릴 수도 있다.
구조적 위험(Structural Hazard)
서로 다른 명령어가 동시에 ALU, 레지스터 등 같은 CPU 부품을 사용하려고 할 때 발생하며, 이 때문에 자원 위험이라고 부르기도 한다.
참고
컴퓨터의 구조: 명령어 파이프라인
1. 명령어 파이프라인(Instruction Pipeline)명령어 파이프라인(Instruction Pipeline)은 CPU의 성능을 향상시키기 위해 명령어 처리를 여러 단계로 나누어 동시에 실행하는 명령어 병렬 처리 기법입니다.CPU
www.robotstory.co.kr
동시성(Concurrency) vs 병렬성(Parallelism)
서점에 가면 다양한 언어별로 동시성을 다루는 책들을 많이 볼 수 있습니다. 프로그래밍을 하다 보면 이러한 동시성 처리가 필요한 경우가 있습니다. 그런데 동시성이라는 말을 종종 병렬성과
seamless.tistory.com
'Computer Science' 카테고리의 다른 글
[CS] 3-3. 운영체제 - 가상 메모리, 파일 시스템 (0) | 2024.10.09 |
---|---|
[CS] 3-2. 운영체제 - 동기화와 교착 상태, CPU 스케줄링 (4) | 2024.10.05 |
[CS] 3-1. 운영체제 - 전체 개요, 프로세스 & 스레드 (3) | 2024.10.04 |
[CS] 2-3. 컴퓨터 구조 - 메모리, 보조기억장치, 입출력장치 (4) | 2024.09.27 |
[CS] 2-1. 컴퓨터 구조 - 개요, 정보 (0) | 2024.09.12 |