CS

[CSAPP] 3챕터 - 프로그램의 기계수준 표현(1) - 인스트럭션 셋 아키텍처(ISA)

Zeka_P 2025. 7. 30. 22:30

 1챕터에서는 우리의 언어가 컴퓨터로 전달될땐 어떻게 번역되는지, 그리고 그 과정에서 거치는 물리적인 경로는 어떻게되는지, 이를 저장하는 저장 장치와, 처리의 전반적인 운영 과정은 어떻게되는지를 배웠습니다. 축약하자면, 우리의 명령이 하달되는 과정과 그 명령이 처리되는 공정 시스템 개요를 공부한 겁니다. 이제는 현장직의 소리에 귀를 기울일 시간입니다. 단순히 프로세스가 작업하는 내용이 아닌 프로세스가 사용하는 언어, 현장의 의사소통을 공부할 시간입니다. 

 

1. 인스트럭션(Instruction)

 프로그래밍을 하면서 우리는, 정말 많은 함수를 사용합니다. 단순히 사칙연산만 사용하는게 아니죠. 제곱도 시키고... 괴상한 그래프도 만들고.. 매칭도 시키고.. 그런데 이런 함수들이 번역되어 프로세서에게 전달될 때, 프로세서는 이 함수가 어떤 함수든 전부 동작시킬 수 있어야 합니다. 이 번역된 언어를 기계어 라고 할 때, 우리가 만든 모든 함수들이, 그 형태 그대로 기계어로 똑같이 번역될 수 있을까요? 근데 그러면.. 애당초 번역의 의미가 있는 걸까요? 전원을 껐다 켰다만 할 수 있는 기계들을 위해 마련된 기계어는, 이에 걸맞게 굉장히 간단한 구성을 이룹니다. add, sub, lw, sw, beq.... 이러한 산술/논리/전송/제어 계열의 명령어가 존재하며, 이 명령어 각각을 인스트럭션(Instruction)이라 부르며, 이 명령어의 집합을 인스트럭션 셋(Instruction Set, IS)이라 부릅니다.

 

우리가 얼마나 복잡한 함수를 작성하던, 번역되는 과정에선 단순히 함수를 옮겨적는게 아닌, 기계어에 맞추어 재배치, 재구성 시키는 마술같은 과정이 사이에서 자동으로 동작합니다. 첨언하자면, 구현된 환경에 따라, 이 기계어 코드 형태도 조금씩 달라집니다. 완전히 문법부터 달라지는 수준까진 아니고, 약간의 정책이 바뀌는 수준입니다. 쉽게 설명하면 add 만 사용하는 환경도 있고, 일종의 단위가 옆에 붙어서 addq 같은 식으로, 길이까지 정의하는 환경도 존재합니다. 미국영어와 영국영어의 차이 정도로 생각해보면 어떨까 싶습니다.. 크로커다일.. 엘리게이터...

 

인스트럭션은 세 구성 요소로 이루어져 있습니다. 연산 코드오퍼랜드, 주소 지정 모드가 이들을 의미합니다. 단어 자체는 생소하지만 예시를 통해 설명드리면 생각보다 간단함을 알 수 있습니다. MOV R1 R2 명령어가 있습니다. 이는 R2의 값을 R1에 저장하라 라는 의미입니다. 이때 MOV가 연산 코드(복사, 데이터 이동)이고, R1과 R2가 오퍼랜드(명령어가 처리할 데이터, 위치를 지정하는 값, 레지스터에 저장됩니다.)에 속합니다. 다른 명령어도 볼까요. LOAD R1 [1000] 명령어는 어떨까요 LOAD는 메모리에서 값을 복사해 오라는 명령의 연산코드입니다. R1은 오퍼랜드고, 이 [1000]이 메모리 주소, 주소지정모드입니다. 해당 명령어는 메모리 주소 1000의 데이터를 R1에 저장하라는 지시를 담고 있습니다.

2. 인스트럭션 셋 아키텍처(Instruction Set Architecture)

단순히 명령어 집합을 넘어서, 이를 구축하고 구성하고 있는 모든 구조체의 집합을 말합니다. IS는 어디까지나, 명령어의 집합에 불과합니다. 인스트럭쳐를 지시받아 동작하는 하드웨어도 존재할 것이고, 인스트럭처를 생성하거나, 관리하는 소프트웨어 또한 존재할 것입니다. 그리고 이는 사용목적과 장비에 따라 제각각일 것입니다. 예를 들어, 서버의 용도로 사용될 ISA는, 고연산 멀티스레드 구현이 우선될 것이며, 발열이 심하면 에어컨을 사용하거나, 대자연의 힘을 빌어 바다에 수장시킬 것입니다. 하지만 모바일용 ISA라면 반대로, 발열이 적은, 다르게 말하면 연비가 좋은 ISA 구축이 중요할 것입니다. 0.0001초만 더 빠르게 끌어치기를 할 수 있느냐 없느냐에 따라 사느냐 죽느냐가 달린 자율주행 자동차라면? 최속의 ISA 구축이 중요하겠죠.

따라서 이 ISA는 시야를 넓게 보아, IS에 레지스터, 메모리, 기타 엑세스 모든 것을 고려한, 말 그대로 아키텍쳐로 구성된 집합체를 이룹니다.

전반적인 ISA 요약표 입니다. 프로젝트를 진행할 때, 최적의 서비스 아키텍처를 고민하듯, 환경 구축 자체에서도 IS에 대한 아키텍처를 고민합니다.

 

ISA의 구성 요소는 크게 다섯가지를 소개해 드릴 수 있을 것 같습니다.

우선 레지스터 집합입니다. 잠시 값을 보관하는 레지스터가 뭘 소개해줄게 있냐 싶겠지만, 단순히 잡히는대로 저장하는 방식이 당연 아니기 때문에 그렇습니다. 모든 레지스터는 각자의 역할이 정해져 있습니다. 대표적으로 %rip 레지스터는 PC 역할을 맡습니다. 그리고 이는 CPU의 부품이며, 정책이기 때문에, CPU 제조사에 따라 그 형태가 꽤나 다릅니다. 또한 이 레지스터도, 범용적으로 사용되는 범용 레지스터와, 오직 특정 목적용으로만 한정되며, 따라서 격리되어 관리되는 특수 레지스터로 나뉘게 됩니다.

 

메모리 집합은 어떨까요. 명령어와 데이터를 동일 버스로 전달해, 동일 메모리로 저장하는 폰 노이만(von Neumann) 아키텍처는, 해당 아키텍처의 처럼 명령어와 데이터를 구분하지 않고, 경로와 저장공간을 동일화 시켜 구현이 간단합니다. 하지만 명령어 읽기와 데이터 사용 모두 활발하게 호출된다면, 명령하는 주체는 둘이고, 이를 담당하는 메모리는 하나가 되니, 대응이 느리거나, 꼬일겁니다.(향후 병목 현상을 공부하게 될텐데, 이에 해당합니다.)  하버드(Harvard) 아키텍처는 이 문제를 해결하기 위해, 데이터와 명령어를 메모리 차원에서까지 구분하여 저장합니다. 별개의 메모리로 나뉘게 되는 것이죠. 구현 난도가 더 높고, 비용이 있으나, 각자의 대응 역할이 명확해졌으니, 문제가 생길 일은 없어진다는 장점이 있습니다. 이렇듯 메모리 또한, 구축 기술에 따라 고유의 아키텍처를 가진다는 뜻이 됩니다. 참고로, 일반적인 PC나 스마트폰 CPU, 서버는 폰 노이만 아키텍처를 채용합니다. 만약 하버드 아키텍처가 디폴트였다면, 우린 이전에 메인 메모리가 두개인 하드웨어 그림을 보며 공부했을 것입니다.

 

명령어 형식 또한, 아키텍처마다 차이를 보입니다. 위에서 언급한 차이점도 당연 존재하고, 인스트럭션의 길이(바이트 단위)에서까지도 차이를 보입니다. x86 환경에선, 가변 길이 인스트럭션이 기능합니다. 상황에 맞춰 인스트럭션의 바이트 단위가 유연하게 조정할 수 있습니다. 하지만 ARM 환경에선, 32비트 고정길이의 인스트럭션이 사용됩니다. 그리고 이 인스트럭션의 길이가 고정된다는 의미는, 레지스터 값, 메모리의 주소 값까지도 제한이 걸림을 의미합니다.

명령어 타입 역시 속해 있습니다. 내용 자체는 프로세서가 수행하는 연산을 정의함을 의미합니다. 사칙연산이나 전송, 제어까지 이 명령어 타입에 포함됩니다. 

 

주소 지정 모드는 단어 자체가 조금 헷갈리게 다가올지도 모르겠습니다. 이 모드는 피연산자가 저장되는 방식을 결정시키는 역할을 맡습니다. 쉽게 풀어 이야기하자면, 저장되는 방식, 메모리 주소 자체를 주는 직접 주소 지정방식을 채용할 것인지, 메모리나 레지스터에 임시 저장된 어떤 주소(포인터) 값을 불러와 검색하는 방식의 간접 주소 지정, 인덱스, 상대주소 지정 등 다양한 정책이 선택권에 오릅니다. 구축중인 프로그램과 저장된 변수들, 이후 작성할 로직을 종합적으로 고려하여, 가장 효과적인 주소 지정 모드 채택이 요구됩니다.