컴퓨터는 어떻게 명령어를 실행하고 데이터를 읽을까 ?
컴퓨터 구조
컴퓨터 구조
Fri Jun 28 2024
이 글은 컴퓨터 구조의 게시글이예요
해당 컴퓨터구조 시리즈는 교재 컴퓨터 구조 및 설계와 🪢 고려대학교 컴퓨터구조 - 최린 교수님 의 강의를 토대로 공부하고 작성되었습니다.
- 1 . CPU 성능 시간에 대한 이야기들
- 2 . 컴퓨터는 어떻게 명령어를 실행하고 데이터를 읽을까 ?
컴퓨터는 단순히 1과0으로 이뤄진 전기 신호를 읽어 작동한다.
Computer
라는 이름에서 알 수 있듯이 컴퓨터는 단순히 계산을 시행하는 장치이다. 다만 매우 빠르게 실행하는
컴퓨터 내부의 연산 장치인 CPU는 전기 신호의 ON/OFF
만으로 산술 연산과 논리 연산을 시행하며 시행된 결과값들을 저장한다.
자세한 CPU의 동작 원리는 해당 영상을 참고하자 정말 멋진 영상이다.
이런 전기 신호는 1 (ON 혹은 True) , 0 (OFF 혹은 False) 으로 이뤄진 이진수 형태로 CPU에서 연산되며 , 저장된 값은 메모리나 보조메모리에 이진수 형태로 저장된다.
이라는 코드는 CPU는 읽을 수 없다. CPU는 단순히 1과0으로 이뤄진 이진수를 읽고 수행 할 수 있기 때문이다.
따라서 코드들은 CPU
가 읽을 수 있는 이진수 형태 (기계어 , machine language
) 형태로 변환 되어 CPU
에게 전달되고 CPU는 기계어를 토대로 작동한다.
컴퓨터가 정보들을 이진수로 변환하는 과정
위 a = 5 + 10
을 예시로 들어 생각해보자
우선 해당 코드에선 =, +
와 같은 연산자가 존재하고 , a
라는 변수와 5 , 10
이라는 피연산자가 존재한다.
하지만 위에서 말했듯 컴퓨터는 이진수만 이해하기 때문에 해당 체계들을 이진수 형태로 변환하는 과정이 필요하다.
해당 포스트에선 십진수를 이진수로 변환하는 과정이나 문자열등을 이진수로 변환하는 과정을 모두 알고 있단 가정하에 작성한다.
또한 32bit 메모리 체계를 사용하는 CPU를 가정하고 실행한다. 메모리 체계가 32bit 라면 레지스터의 용량도 32bit이다.
산술 명령어를 이진수로 변환하는 과정
위 코드에선 + , =
와 같은 산술 명령어들이 존재한다.
기계어에선 명령어들을 가리키는 이진수들이 테이블 형태로 저장되어 있다.
예를 들어 +
라는 명령어는 기계어로 000000
이고 =
라는 명령어는 기계어로 101011
이다.
변수를 이진수로 변환하는 과정
해당 과정을 이해하기 위해선 우선 레지스터에 대한 개념을 이해해야 한다.
CPU
는 단순히 기계어로 이뤄진 명령을 처리하는 장치이다. 그렇다면 연산이 이뤄지기 위한 값을 저장하거나 연산이 이뤄진 후의 값을 저장할 저장소가 필요하다.
이렇게 CPU
가 연산에 필요한 데이터를 저장하는 저장소를 레지스터라고 한다.
레지스터는 CPU
와 가까운 거리에 존재하여 데이터를 불러오는 시간이 매우 빠르다. (레지스터에 저장되는 데이터의 형태들도 모두 이진수 형태이다.)
레지스터의 개수들은 사용하는 하드웨어에 따라 다르겠지만 약 32개 가량이 일반적이다.
레지스터 개수가 많으면 많을 수록 좋은게 아닌가 라는 생각이 들 수 있지만, 개수가 늘어나게 된다면 그 만큼 CPU
와의 거리가 멀어지기 때문에 가까워 데이터를 불러오는 속도가 빠르다는 장점이 줄어들기 때문에 레지스터의 개수들은 제한적이다.
이렇게 레지스터의 개수들은 32개로 고정되어 있기에 각 레지스터들을 가리키는 주소는 5bit 인 00000
로 표현 가능하다.
예를 들어 첫 번째 레지스터는 00000
이고 아홉 번째 레지스터는 00101
으로 표현 가능하다.
그렇다면 위 예시에서 a
라는 변수를 아홉 번째 레지스터인 01010
으로 둘 수 있다.
기억하자, 연산에 필요한 모든 데이터들은 레지스터에 저장되며, 각 레지스터들은 레지스터를 가리키는 이진수 형태로 참조한다.
이 때 고정된 값들이 존재해야 하는 레지스터들은 고정된 값이 저장되어 있다. 예를 들어
00000
와 같은 첫 번째 레지스터에선0
이란 값이 담겨 있으며해당 레지스터는 0이라는 숫자를 참조 할 때 사용한다.
데이터를 이진수로 변환하는 과정
십진수인 5 , 10
은 컴퓨터가 이해 할 수 있는 이진수 형태로 변환되어야 한다.
이에 숫자 5와 10은 이진수인 00101
과 01010
로 변환된다.
이진수로 변환된 명령을 CPU가 실행하는 과정
위 예시에서 변수와 명령어, 데이터들을 모두 이진수 형태로 변환했다.
그렇다면 CPU는 어떻게 명렁들을 시행할까 ?
그것은 바로 변수와 명령어, 데이터들을 실행하기 위한 명렁어 필드가 존재하며 해당 필드에 맞게 이진수들을 나열해주면 된다.
명령어 필드
Field | Bits | Description |
---|---|---|
opcode | 6 | Operation code |
rs | 5 | Source register |
rt | 5 | Target register |
rd | 5 | Destination register |
shamt | 5 | Shift amount |
funct | 6 | Function code (for R-type only) |
각 명령어 필드들은 명령어 타입 타입 별로 필드의 생김새가 다른데, 우선 산술에 사용하는 R type 의 명령어 필드를 살펴보자
우선 6비트로 이뤄진 opcode
들이 존재한다. 위에서 예시로 들었던 +,=
와 같은 산술 명령어들이 들어가는 필드이다.
rs , rt , rd
는 각 레지스터들을 가리키는 주소를 담은 비트이다. 32개의 레지스터는 5비트의 이진수로 표현 가능하기 때문에 5비트만큼의 필드가 필요하다.
shamt
는 비트를 왼쪽,오른쪽으로 몇만큼씩 이동 시킬 때 사용하는 필드이며 현재 코드에선 사용하지 않는다.
다만 어떤 경우에 필요하냐면 어떤 이진수에 2^4 만큼 곱해주고 싶다면 실제 곱셈 연산을 하는 것 보다
4비트만큼 비트를 왼쪽으로 이동시켜준다면 곱해준 것과 같은 연산 결과를 갖는다.
funct
비트는 opcode
와 같이 실행 할 연산에 대한 정보를 제공한다.
이러한 명령어 필드들은 명령어의 타입들에 따라 유동적으로 변환이 가능한데 , 이러한 변환들은 추후 어셈블리어 때 더 자세히 설명하도록 하자.
위의 기계어들을 명령어 필드에 맞춰 살펴보자
해당 코드를 명령어 필드와 함께 살펴보자,
001000
은 상수 연산 후 할당을 의미하는 opcode
이다. 해당 opcode
는 레지스터에 저장 된 값들을 연산 할 때 사용하는 것이 아닌 상수 그 자체로 연산을 시행한다.
이해하기 쉽게 이야기 하자면 에를 들어 5라는 값을 01001
레지스터에 담고 싶을 때 00000
레지스터 (0 값을 담고 있는 레지스터) + 5 의 값 (funct
필드에 존재하는 상수)을 01001
레지스터에 담으라는 것을 의미한다.
만약 상수 연산을 사용하지 않는다면 00101
(5) 라는 값을 어떤 레지스터에 담아준 후 다시 레지스터간 연산을 해야하는데, 연산 시 상수 연산자를 사용하면 불필요한 할당 과정을 스킵 할 수 있다.
해당 연산도 동일하다. 01010
레지스터에 00000
레지스터 + 1010
(10) 값을 담으라는 기계어이다.
마지막 기계어는 00000
: 더하고 할당해라 , 01001 , 01010
: 레지스터의 값들을 , 01000
: 레지스터에 를 의미한다.
결국 a = 5 + 10
이라는 상위 수준 언어는 5라는 값을 상수 연산을 통해 레지스터에 저장하고 , 10이란 값을 레지스터에 저장하고
a 변수를 가리키는 레지스터에 저장 된 레지스터들의 더한 값을 할당하는 과정을 거친다.
기계어와 1:1 매칭이 되는 어셈블리 언어
인간이 이해 할 수 있는 상위 수준의 언어가 등장하기 전 까지 프로그래머들은 위처럼 기계어로 코딩을 작성했다.
기계어는 컴퓨터가 이해하기엔 매우 적합한 언어였지만 인간이 이해하기에는 직관적이지 못한 언어이다.
이에 기계어와 1:1 매칭이 가능하면서 인간이 이해하기 쉬운 어셈블리 언어의 개념이 등장했다.
해당 시리즈에선 MIPS
어셈블리어를 이용하여 포스트를 작성하도록 할 것이다.
위의 어셈블리어들은 기계어들을 어셈블리어로 변환한 코드이다.
첫 ~ 두번째 어셈블리어까지는 $t0 (01001 레지스터)
와 $t1 (01010 레지스터)
에 0+5 , 0+10
의 값을 담는 것이고
마지막 어셈블리어는 $t2 (01000 레지스터)
에 $t0 + $t1
의 값을 담는 것이다.
각 레지스터에는 $
와 같은 달러 사인이 붙는데 해당 기호는 실제 레지스터를 표현하기 위한 기호이다.
어셈블리 어는 위의 명령어 필드들과 1:1 매칭 되기 위해 타입 별 명령어 필드들과 호환되는 명령어 필드를 또 갖는다.
명령어 필드들에 대해서는 추후 어셈블리어를 이용한 다양한 연산들을 이야기 할 때 상황에 맞춰 설명하도록 하겠다.
정리
컴퓨터는 이진수를 이용해 산술 연산 및 논리 연산들을 시행하며 각 이진수들은 각자 역할에 맞는 필드에 맞춰 구분되어 읽힌다.
이런 언어를 기계어라고 하며 인간이 기계어를 이해 할 수 있게 사용하는 언어가 어셈블리어라는 것이다.
연산 과정에선 값들을 저장하기 위한 레지스터들이 존재하며 연산은 레지스터에 저장된 값들을 이용해 CPU
가 연산한다는 것이다.