TIL

[TIL/혼공컴운] 2025/01/15

오늘도 역시 여자친구와 함께했다.Overview ✍️CPU의 세부적인 구성 요소와 더불어 성능 향상 기법에 관한 논의까지 다룰 예정이다. 다만 구성 요소와 성능 향상 기법을 한 편의 글에 모두 담기에는, 글을 쓰는 사람도 읽는 사람도 부담된다. 오늘은 구성 요소에 집중

2025년 1월 15일6min read

오늘도 역시 여자친구와 함께했다.

Overview ✍️

CPU의 세부적인 구성 요소와 더불어 성능 향상 기법에 관한 논의까지 다룰 예정이다. 다만 구성 요소와 성능 향상 기법을 한 편의 글에 모두 담기에는, 글을 쓰는 사람도 읽는 사람도 부담된다. 오늘은 구성 요소에 집중하고 내일은 성능 향상 기법에 대해 정리하도록 하겠다. 추가적으로, 데이터와 명령어 파트를 학습하지 않은 방문객은 이전 글로 이동해 주기를 바란다.

이전 글: https://velog.io/@minkwan/TIL%ED%98%BC%EA%B3%B5%EC%BB%B4%EC%9A%B4-20250109

CPU 작동 원리 ✍️

1. ALU와 제어장치 ⚙️

CPU는 ALU, 제어장치, 그리고 레지스터로 구성된다.

1-1. ALU 🌿

ALU는 CPU에서 ``계산기의 역할``을 수행하는 부품이다. 계산을 한다는 것은 input을 받아들인 뒤 가공해서 output을 도출한다는 것을 의미한다. 무엇을 받아들이고, 무엇을 반환하는지를 이해하는 것이 곧 ALU를 이해하는 것이라고 할 수 있다.

ALU는 레지스터를 통해, 연산의 대상인 '피연산자'를 받는다. 동시에 제어장치로부터 수행할 연산을 알려주는 '제어신호'를 받아들인다.

ALU가 나름대로의 연산을 마치면, 해당 결괏값은 일시적으로 레지스터에 저장된다. CPU가 메모리에 접근하는 속도는 레지스터에 접근하는 속도보다 훨씬 느리기 때문이다.

연산의 결과를 output으로 반환하여 또 다른 레지스터에 저장한다. 이때, 결괏값과 더불어 '플래그'를 내보낸다. '플래그'는 연산 결과에 대한 추가 정보다. 이진수를 다루었던 데이터 파트에서 언급한 바 있다.

플래그의 종류에는 ``부호 플래그, 제로 플래그, 캐리 플래그, 오버플로우 플래그, 인터럽트 플래그, 슈퍼바이저 플래그``등이 있다. 이번 파트에서는, 연산 결과와 더불어 추가적인 정보를 제공하는 '플래그'라는 개념이 있고, 그 '플래그'라는 것이 ALU의 output 중 하나라는 점만 확인하고 넘어가는 게 좋겠다.

1-2. 제어장치 🌿

제어장치는 제어신호를 내보내고, ``명령어를 해석``하는 부품이다. 제어장치의 흐름을 도식화하면 다음과 같다.

제어장치는 블루 라인으로 표시된 네 가지 요소(클럭, 명령어, 플래그, 제어신호)를 받아들이고, 레드 라인으로 표현된 '제어신호'를 내보낸다.

가장 먼저 클럭은, 컴퓨터의 모든 부품을 일사불란하게 움직일 수 있게 하는 시간 단위다. 달리기를 할 때 '후후하하' 같은 호흡법이 가장 중요한데, 클럭 신호는 일종의 컴퓨터의 호흡 주기라고 볼 수 있다.

다음으로 해석해야 할 명령어를 받아들인다. CPU가 해석해야 할 명령어는 명령어 레지스터로부터 온다. 명령어 레지스터로부터 받은 '해석해야 할 명령어'를 받아들이고 해석한 뒤, '제어신호'를 통해 컴퓨터 부품들에게 수행해야 할 내용을 전달한다.

그리고 플래그 레지스터 속 플래그 값을 받아들인다. 플래그 값은, 제어신호를 통해 컴퓨터 부품들을 제어할 때 중요한 참고 사항이다. 제어장치는 이러한 플래그 값을 받아들이고 참고하여 제어신호를 발생시킨다.

추가적으로, 제어신호(전기신호)는 CPU 외부 장치도 발생시킬 수 있다. 제어장치는, 외부로부터 발생된 제어신호를, 제어 버스를 통해 받아들이기도 한다.

클럭, 명령어, 플래그, 제어신호```를 받아들여서 제어신호를 발생시키고, 그것을 레지스터나 ALU 같은 CPU 내부 요소에 반환하기도 하고, 메모리나 입출력장치 같은 CPU 외부 요소에 제어 버스를 통해 전달하기도 한다.

## 2. 레지스터 ⚙️

명령어와 데이터는 실행 전후로 반드시 레지스터에 저장된다. 레지스터라는 데이터의 길목만 잘 관찰해도 프로그램의 자세한 실행 프로세스를 파악할 수 있다.

### 2-1. 대표적인 레지스터 🌿

글에서 다룰 대표적인 레지스터는 총 8개인데, 우선 ```프로그램 카운터, 명령어 레지스터, 메모리 주소 레지스터, 메모리 버퍼 레지스터```에 관해 알아보자. 

1000번지 메모리에 저장된 1101(2)라는 데이터를 가져오는 상황이라고 가정하고, 해당 flow에서 각각의 레지스터가 어떤 역할을 수행하는지 살펴봤다.

![](https://velog.velcdn.com/images/minkwan/post/f946014b-ddd2-455d-a62e-ae0329f7d386/image.png)

프로그램 카운터에는 ```메모리에서 읽어 들일 명령어 주소```가 저장된다. 서적에 따라서 프로그램 카운터를 명령어 포인터(Instruction Pointer)라고 부르기도 한다.

가장 먼저 프로그램 카운터에 1000(=명령어 주소)이 저장된다. 1000번지를 실제로 읽어 들이기 위해서는 주소 버스로 1000번지 주소를 내보내야 한다. 

이를 위해 메모리 주소 레지스터에는 1000이 저장된다. 메모리 주소 레지스터는 ```읽어 들이고자 하는 주솟값을 주소 버스로 보낼 때 거치게 되는```레지스터인 것이다. 주소를 보내는 행위와 더불어 '메모리 읽기'를 위한 전기 신호도 보내야 할 것이다. 제어장치를 통해 메모리 읽기라는 제어신호가 메모리로 전달된다. 지금 정리한 내용이, 이미지에서 확인할 수 있는 초록색 선이다.

메모리 버퍼 레지스터는, ```메모리와 주고받을 값을 저장```하는 레지스터다. 우리는 1000번지에 있는 1101(2)라는 데이터를 받을 것이다. 데이터 버스를 통해 메모리 버퍼 레지스터로 1000번지에 있는 1101(2) 값이 전달된다. 이때 프로그램 카운터에 있는 내용이 핑크색으로 처리되어 있음을 확인할 수 있다. 명령어를 읽어 들였다면, 프로그램 카운터는 다음 명령어를 읽어들이기 위해 카운터를 증가시킨다.

명령어 레지스터는 ```방금 메모리에서 읽어 들인 명령어를 저장```하는 레지스터다. 따라서 방금 읽어 들인 1101(2)를 명령어 레지스터에 저장한다.

추가적으로 범용 레지스터와 플래그 레지스터가 있다.

메모리 주소 레지스터는 주소 버스로 내보낼 주소값만 저장하고, 메모리 버퍼 레지스터는 데이터 버스로 주고받을 값만 저장한다. 범용 레지스터는 메모리 주소와 데이터를 모두 저장할 수 있는, 말 그대로 '범용적인' 레지스터다.플래그 레지스터는 ALU 연산 결과에 따른 플래그를 저장하는, 즉 부가적인 정보를 저장하는 레지스터다.

이제 스택 포인터와 베이스 레지스터에 대해 알아볼 자격이 주어졌다.

2-2. 스택 주소 지정 방식 🌿

주소 지정 방식이란, ``연산에 사용할 데이터의 위치를 찾는 방법``이라고 이전에 학습했다.

따라서 스택 주소 지정 방식이란, ``스택을 통해 데이터의 위치를 찾는 방법``이다. 스택은 한쪽 끝이 막혀 있는 통과 같은 저장 공간이고, 해당 저장 공간의 top-level 주소를 저장하고 있는 레지스터를 스택 포인터라고 부른다.

스택은 메모리 안에 있다. 정확히는 메모리 안에 스택처럼 사용할 영역이 정해져 있다. 이를 스택 영역이라 부른다.

2-3. 변위 주소 지정 방식 🌿

다음으로는, 변위 주소 지정 방식이다.

변위라는 용어는 사실 물리학에서 사용하는 용어다. 나중 위치의 값에서 처음 위치의 값을 뺀 벡터량을 의미한다. ``데이터의 위치를 찾는 방법이 주소 지정 방식인데, 그 기준이 벡터량이라는 뜻``이다. 무슨 말인지 모르겠다고? 나도 모르겠다. 그러니까 고민하고 공부하는 거시다~~~

오퍼랜드와 레지스터 값을 더해 유효 주소를 얻어내는 주소 지정 방식이 변위 주소 지정 방식이다. 책에서는 오퍼랜드 필드의 값을 변위로 표현했다. 즉, 우리가 데이터의 위치를 찾을 때의 기준이 벡터량(=변화량)이고, 오퍼랜드 필드의 값이 변화량이라는 것이다.

변위 주소 지정 방식에는 ``상대 주소 지정 방식``베이스 레지스터 주소 지정 방식``이 있다. 아래 이미지를 보면 위에서 언급한 말이 쉽게 이해될 것이다.

위 그림이 상대 주소 지정 방식이다. 상대 주소 지정 방식은, 오퍼랜드와 ``프로그램 카운터의 값을 더하여`` 유효 주소를 얻는 방식이다.

오퍼랜드가 음수, 만약 -2라면 CPU는 읽어 들이기로 한 명령어로부터 '두 번째 이전' 번지로 접근한다. 오퍼랜드 값이라는 벡터량을 통해 데이터의 위치를 찾고 있다는 점을 확인할 수 있다.

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

베이스 레지스터는 기준 주소의 역할을 한다. 가령 200번지가 기준 주소이고 오퍼랜드의 값이 50이라면, 기준 주소로부터 50만큼 떨어진 250번지에 접근하게 된다.

3. 명령어 사이클과 인터럽트 ⚙️

3-1. 명령어 사이클 🌿

프로그램 속 각각의 명령어들은 일정한 주기가 반복되며 실행될 것이고, 이를 명령어 사이클이라 한다.

인출 사이클은, 말 그대로 ``메모리에 있는 명령어를 CPU로 가져오는`` 'fetch' 사이클이다.

실행 사이클은 ``가져온 명령어를 CPU에서 실행하는`` 단계를 뜻한다.

기본적으로 프로그램은 이러한 인출과 실행의 반복으로 동작한다. 다만, 간접 사이클도 알아봐야 한다.

이전 글에서 간접 주소 지정 방식에 대해 다루었다. 간접 주소 지정 방식은 오퍼랜드 필드에 메모리 주소가 명시되어 있다. 이런 경우, 명령어를 인출하여 CPU로 가져왔다고 해도 바로 실행 사이클에 돌입할 수 없다. 단지 주솟값이기 때문이다.

따라서 메모리 접근을 한 번 더 해야 하는 소요가 발생하는데, 이 처리를 수행하는 단계를 '간접 사이클'이라고 부른다.

그런데 명령어 사이클의 '킥'은 인터럽트다. 인터럽트에 대해 알아보자.

3-2. 인터럽트 🌿

인터럽트란 ``CPU의 작업을 방해하는 신호``를 뜻한다. 사이클을 얘기하는 중인데 '방해'가 있다는 것은 주목할 만한 이슈임이 분명하다.

참고로 인터럽트의 종류를 구분하는 통일된 기준은 없다. 인텔의 공식 문서를 참조한 기준으로 설명을 이어나가고자 한다.

동기 인터럽트란, ``CPU에 의해 발생``하는 인터럽트다. 프로그래밍 상의 오류와 같은 예외적 상황을 마주했을 때 발생하는 인터럽트가 동기 인터럽트다.

비동기 인터럽트란, 주로 ``입출력장치에 의해 발생``하는 인터럽트다. 세탁기 완료 알림, 전자레인지 조리 완료 알림 등으로 비유할 수 있겠다. 하드웨어 인터럽트의 처리 순서는 다음과 같다.

인터럽트 서비스 루틴은, ``인터럽트를 처리하기 위한 프로그램``이다. '키보드가 어떤 인터럽트 요청을 보냈을 때에는 이런 루틴을 실행한다'와 같은 정보로 이루어진 프로그램이다.

따라서 CPU가 인터럽트를 처리한다는 말은 인터럽트 서비스 루틴을 실행한 후 본래 수행하던 작업으로 다시 되돌아온다는 말과 동일한 의미를 갖는다.

인터럽트 벡터는, ``인터럽트 서비스 루틴을 식별하기 위한 정보``다.

전체적인 처리 흐름은 위 이미지와 같다. 그런데 실행 중이던 작업을 ``백업`해야 하는데 어디에 백업을 하는지를 살펴보니 `스택 영역``이었다.

아 so crazy!, 스택 영역은 결국 "인터럽트 처리 과정에서 데이터 백업을 위해 확보해놓은 영역이구나!"라는 깨달음을 얻어버렸다.

인터럽트까지 확인했으니 명령어 사이클은 최종적으로, 다음과 같은 형태로 도출된다.

내일은 성능 향상 기법에 대해 정리할 예정이다. 읽고, 정리하고, 구현하라!