CPU가 일하는 방식
이번 포스팅에서는 어떻게 저희가 작성한 코드가 어떻게 프로세서(CPU)에게 전달되어 실행되는 과정에 대해 알아보겠습니다.우선 CPU가 동작하는 원리를 알아보기 전에 선수적으로 필요한 지식은 저희가 작성한 소스 파일이 어떻게 기계어까지 변환되는가에 대한 내용입니다.이에 대한 내용은 저번 포스팅에 존재하니 참고하시길 바랍니다.
복습 삼아 이번 포스팅과 관련된 중요한 점 하나를 말씀드리겠습니다.저희가 아래와 같은 스와핑 함수 코드를 작성했다고 가정합시다.
swap(int v[],int k){
int temp;
temp = v[k];
v[k] = v[k+1];
v[k+1] = temp;
}
그러면 위 코드를 컴파일 시키면 자연스레 컴파일러에 의해 어셈블리어가 생깁니다.그러면 모든 컴파일러는 모두 동일한 어셈블리어를 만들어 낼까요? 예를 들어,애플이 사용하는 컴파일러와 윈도우가 사용하는 컴파일러가 동일하여 동일한 어셈블리어를 만들어 낼까요?겉으로만 보았을 때는 둘 다 비슷한 결과를 내니 그럴것만 같습니다.하지만 실상은 그렇지 않습니다.이 질문에 대한 자세한 설명은 포스팅의 끝자락에 있습니다.우선은 CPU가 어떻게 일을 하는지 알아보겠습니다.
시스템 하드웨어의 구성요소
CPU가 어떻게 일하는지를 알아보기 전에 CPU와 함께 일을 하는 주변 요소들이 어떤게 있는지 알아보겠습니다.
- Processor : CPU이자 컴퓨터의 두뇌라고 보시면 됩니다.
- Memory : 저희가 알고 있는 DRAM이라고 생각하시면 됩니다.
- Input,output : 키보드,마우스 등 입출력 장치입니다.
- Bus : 위와 같은 각각의 요소들의 연결을 담당하는 전기적 배선군입니다.
CPU는 위의 요소들과 긴밀하게 소통하며 연산을 처리해줍니다.이제부터 본격적으로 CPU가 명령어를 처리하는 방식을 알아봅시다.
CPU의 추상적인 구분
우선 CPU가 하는 일을 간략하게 소개를 하겠습니다.대부분의 사용자는 컴퓨터에게 다양하고 복잡한 연산을 많이 시킵니다.그리고 인간이 하기 귀찮은 대부분의 연산들을 컴퓨터는 곧 잘해냅니다.그리고 이러한 연산을 도맡아 하는 요소가 바로 CPU입니다.
지금부터는 이러한 계산기 역할을 CPU가 어떻게 하는가에 대해 알아봅시다.우선 CPU를 추상적인 역할 두가지로 구분할 수 있습니다.
- DataPath
- Control
지금부터 하나의 기계어로 된 이진파일이 실행될 경우,그 이후 일어나는 일련의 과정을 그림과 함께 소개해보겠습니다.우선 시스템 하드웨어의 간략한 도식화를 아래처럼 그렸습니다.
아래와 같은 구조에에서 Input을 통해 다음과 같은 이진 파일이 들어온다고 가정합시다.
우선 인풋으로 들어온 이진파일은 아래와 같이 메모리 영역에 할당 됩니다.
이후 해당 메모리에 적재된 Instruction과 Value는 CPU의 Control에 의해서 Fetch되어 CPU내로 들어오게 됩니다.여기서 말하는 fetch라 함은 메모리에 존재하는 일련의 명령어와 값을 CPU로 가져오는것을 의미합니다.
그리고 들어온 해당 명령어와 값은 Control에 의해서 Decode됩니다.decode의 경우 가져온 명령어와 값에 대해 무엇을 실행시킬지 파악하는 단계입니다.예를 들어,가져온 명령어의 Instruction(명령어)가 더하기인지 뺄셈인지 그리고 가져온 Value(값)이 어떤 값인지를 파악을 하는 단계입니다.
이후 Execute단계에서는 Controle에서 해석한 명령어와 값을 실제로 실행시켜 일종의 결과를 내는 단계입니다.이렇게 생긴 결과는 레지스터에 담깁니다.
이후 위와 같은 3가지 단계를 반복하며, CPU는 메모리에 적재되어 있는 기계어를 계속 가져오면 연산을 수행해줍니다.
잠깐 정리해보겠습니다.
- Control : 메모리로부터 명령어를 가져오고 해당 명령어가 명령어인지 값인지 또한 어떤 명령어를 수행해야할지를 Datapath에게 알려줍니다.(fetch,decode)
- Datapath : Control로부터 받은 명령어를 실제로 연산합니다.(execute)
결국 CPU는 스스로가 연산할 명령어가 없을 때까지 연산을 하고 해당 결과를 메모리에 다시 내보내 줍니다.이후 해당 결과를 output을 통해 내보내줍니다.
이렇게 CPU가 하는 일을 추상적인 구분을 통해 알아보았습니다.이제부터는 실제 CPU가 어떻게 구성되어있는지 위와 같은 추상적인 역할을 어떤 요소가 맡는지 알아봅시다.
CPU의 물리적인 구분
앞서 설명드린 추상적인 구분을 통해 CPU가 일하는 방식을 배우셨습니다.지금부터는 어떤 요소가 이러한 일을 하는지에 중점을 맞추어 진행하겠습니다.아래의 그림은 추상적인 구분 파트에서 나온 그림과 사뭇 다른점이 보이실 껍니다.CPU의 구성요소 자체에 초점을 둔 그림이며 위의 그림 예시보다 더욱 구체적입니다.또한 각각의 요소들은 내부 bus에 의해 연결되어집니다.
- Control Unit(제어장치) : 모든 컴퓨터의 장치들의 동작을 지시하고 제어하며 명령과 입출력 값을 처리합니다.즉,일전의 fetch를 통해 메모리로부터 명령어와 값을 받아온 후, 해당 명령어를 해독(decode)합니다.이후 제어 신호를 보내 연산장치(ALU)가 작업을 수행하도록 지시합니다.
- ALU(Arithmetic logic unit) : CU(제어장치)의 명령에 따라 실제로 연산을 수행하는 구성요소입니다.여기서 연산이라 함은 사칙연산과 같은 수학적인 산술연산뿐만 아니라 논리연산(and, or 등)까지 진행(execute)합니다.ALU를 통해 연산이 완료된 명령어의 결과는 System Bus를 통해 CPU에서 Memory로 빠져나갑니다.
- Register : 사전적 정의로는 CPU 내부에서 처리할 명령어나 연산의 중간 결과값 등을 일시적으로 기억하는 임시 기억장소입니다.즉,CPU내의 일종의 메모리라고 이해하셔도 됩니다.
이해를 위해 조금 더 쉽게 설명해보겠습니다.만약 (1+4)라는 명령어가 0과1로 구성된 이진파일로 들어온다고 가정합시다.사람인 저희의 입장에서는 연산자와 피연산자인 1,4와 ‘+’ 기호를 구별가능하지만 컴퓨터는 이를 어떻게 구분하고 센스있게 계산을 해줄까요?이를 해결하기 위해 연산자용 그릇과 피연산자용 그릇을 CPU가 준비해둔다고 생각해봅시다.그러면 연산자는 연산자용 그릇에 피연산자는 피연산자용 그릇에 자연스레 담기게 될것입니다.이러한 일종의 그릇의 역할을 수행해주는 것이 레지스터라고 이해하셔도 좋습니다.레지스터 또한 굉장히 많은 종류가 존재하지만 우선은 레지스터가 CPU내에서 어떠한 역할을 맡고 있는지를 이해하시는게 더욱 중요하다고 생각합니다.(참고로 다음 포스팅의 주제인 캐시보다 레지스터가 더욱 빠른 기억 장치입니다)
ISA(Instruction Set Architecture)
포스팅의 서두에 나왔던 설명이 기억나시나요?윈도우 계열과 맥 계열의 컴파일러는 서로 다르기에 서로다른 어셈블리어를 나타낸다고 했습니다.그러면 당연히 맥과 윈도우에서 만들어내는 이진파일 또한 서로다른 형태를 띄고 있을 것입니다.하지만 윈도우에서 쓴 C코드와 맥에서 쓴 C 코드는 거의 비슷한 결과를 항상 내보내 줍니다.도대체 연산을 하는 과정에 어떤일이 적용된 것일까요?
이에 대한 해답으로 ISA를 알려드리려합니다.쉽게 말해 CPU가 이해할 수 있는 명령어들의 모음이라 할 수 있습니다.즉,CPU는 ISA에 속한 명령어들만 연산 가능합니다.좀 더 단적인 예로 윈도우의 실행파일인 .exe는 맥OS에서는 실행이 되지 않는것 또한 이 때문입니다.(실제 Intel계열 CPU는 x86 명령어 집합을 애플계열은 ARM 명령어 집합을 사용합니다)또한 동일한 C 파일이더라도 컴파일 이후 생성되는 어셈블리어는 CPU가 취하는 ISA에 따라 서로 상이합니다.
정리해보겠습니다.저희가 동일한 실행 파일을 생성을 하더라도 이는 컴파일러에 의해 서로 다른 결과의 어셈블리어와 기계어가 생성됩니다.(인텔계열의 CPU와 애플계열을 생각하시면 됩니다)이렇게 서로 다른 제품군의 CPU들은 각자의 입맛에 맞는 ISA를 취하고 이러한 ISA에 알맞은 기계어를 생성해내기 위해 애플과 인텔에서의 에셈블리어가 서로 다른것입니다.
아래는 실제 소프트웨어와 하드웨어간에 존재하는 ISA의 위치입니다.(Micro Architecture가 CPU라고 이해하시면 됩니다)
이렇게 서두에 나왔던 질문에 대한 설명까지 포함해 CPU가 전반적으로 어떻게 동작하는지에 대한 포스팅이였습니다.