OneDev

[C] 0. 배경지식 - CPU와 메모리 본문

Language/C

[C] 0. 배경지식 - CPU와 메모리

one_dev 2022. 6. 27. 15:18

※ 시작에 앞서 :

필자는 수학전공자로, 컴퓨터 관련 전공자도 아니고 프로그래밍 관련 전공자도 아닙니다.

오로지 인터넷에서 수집한 여러 강의들과 설명에 의존해서만 (그것도 혼자) 코딩을 공부하고 있습니다. 따라서 글의 내용중 사실과 다른 부분이 충분히 있을 수 있습니다.

지적은 감사히 받겠습니다.

만약 제가 작성한 글과 다른 분이 작성한 글의 내용이 다르다면, 아마 다른 분이 쓰신게 맞을 가능성이 높습니다.

이점 참고바랍니다.

 

▣ 목차

1. 컴퓨터란 뭘까?

- (1) 누가 명령어를 읽어들일까?

- (2) 어디에서 명령어를 읽는가?

- (3) 전체적인 데이터의 흐름

2. 명령어는 어떻게 작성할까?

- (1) 어셈블리(Assembly)

- (2) CPU에서 RAM에 데이터 쓰기

- (3) CPU는 명령어를 어떻게 읽어들일까?

 

 

 

1. 컴퓨터란 뭘까?

컴퓨터의 어원인 compute(계산하다) 에서 알 수 있듯이 일종의 계산하는 장치 일 것 으로 추측된다.

하지만 주변에서 흔히 볼 수 있는 일반 계산기들을 컴퓨터라 부르지는 않는다.

컴퓨터가 일반적인 계산기와 다른 것은 스스로 명령어를 읽어들여서 연산을 수행할 수 있다는 것이다.

이에 대해 여러 의문이 드는데, 이에 대한 답을 정리해보았다.

 

(1)  Q.누가 명령어를 읽는가?

=>  Ans) CPU(central processing unit)가 읽는다

이렇게 생긴 자그마한 반도체가 명령어를 읽는다는 것이다.

왼쪽이 뒤판, 오른쪽이 앞판의 모습이다.

cpu에서 연산이 수행되는 부분은 왼쪽 부분의 중앙의 직사강형 부분이라고 한다.

 

 

 

 

 

(2) Q. 어디에서 명령어를 읽는가?

=> RAM 에서 읽는다

 1) CPU 자체는 연산에 특화되어 있어 데이터를 저장할 수 있는 공간은 굉장히 작다.

CPU가 연산을 수행하기 위해 임시로 데이터를 저장하는 공간을 레지스터 라고 하는데 이 공간은 굉장히 작다.

따라서 CPU는 저장에 특화된 장치를 필요로 한다. 이 저장에 특화된 장치를 흔히 말하는 메모리(램) 이라 한다.

RAM(Random Access Memory)은 CPU옆에 딱 붙어서 저장공간 역할을 한다.

 

(이미지 출처: 모두의 코드 유튜브 )

램의 구조는 간단히 말해서(자세히는 나도 모른다)

데이터를 저장할 수 있는 방들이 일자로 나열된 모양이다.

각 방의 크기는 대부분 1바이트이며, 각 방에는 "주소값"

이라는 번호가 부터있다. 이 주소값은 16진수로 주로 표현한다.

각 방의 주소값은 연속적으로 이어진다.

 

 

 

 

2) RAM 의 특징

1. RAM의 가장 큰 특징은 임의 접근 메모리(Random Access Memory)라는 것이다.

이 말인 즉슨 RAM에서 어느 위치에 있는 방이건 간에 동일한 속도로 데이터를 읽거나 쓸 수 있다는 것이다.

 

2. 또 다른 램의 특징으로는 속도가 빠르다는 것이다.

컴퓨터의 모든 저장장치를 통틀어 가장 빠르다. 보통 데이터를 램에서 불러오고 쓰는데 100나노초 정도 걸린다고 한다.

 

3. RAM은 휘발성 메모리 이다(컴퓨터를 끄면 다 날아간다는 뜻).

(휘발성 메모리 : 전원이 지속적으로 공급되어야지만 데이터를 유지할 수 있는 메모리)

컴퓨터를 꺼도 데이터를 보관하고 싶다면 비휘발성 메모리(하드디스크)가 필요하다.

 

※ 사실 메모리(RAM)는 느리다?

바로 위에서 램이가장 빠르다고 해놨지만, 사실은 아니다.

가장 빠르다는 것은 어디까지나 다른 저장장치들에 비해서 빠르다는 것이고, CPU에 비해서는 턱없이 느리다.

  • CPU에서 연산 1개를 하는데 걸리는 시간 : 0.3나노초
  • 램에서 읽는데만 걸리는 시간 : 100나노초

따라서 CPU는 RAM에서 데이터가 올 때 까지 기다리는 동안 연산 300개 수행할 시간을 낭비하는 셈인 것이다.

이에 CPU개발자들은 CPU에 데이터를 보관할 수 있는 작은 공간을 마련하였는데 이를 캐시(Cache) 라 부른다.

 

 

왼쪽의 그림은 CPU의 실리콘 다이 부분을 확대한 모습이다.

Core라고 써진 부분은 CPU에서 연산을 처리하는 부분이고,

그 밑에 Shared L3 Cache라고 적힌 부분이 각 코어들이 공유하는 캐시 메모리이다.

또한 각 Core 마다 L1, L2라 불리는 캐시가 따로 있다. (그림에서 각 코어칸 하단 모서리부분에 직사각형 모양)

캐시 번호가 커질수록 캐시의 공간은 커지지만 그만큼 읽고 쓰는게 느리다. 즉 L1캐시가 가장 작지만 빠르고, L3시가 가장 크지만 느리다는 것이다.

 

 

 

(3). 전체적인 데이터의 흐름

보통 컴퓨터에서 프로그램을 실행하면 다음과 같은 순서로 진행된다 :

1) 하드디스크나 SSD에서 프로그램 명령어들을 RAM에 불러온다

(하드디스크에 저장되어 있는 프로그램의 위치를 찾아 RAM에 복사해놓는다)

2) 필요한 명령어들을 CPU가 RAM에서 읽어 와서 연산을 수행한다.

 

이미지 출처: 모두의 코드 유트브

 

CPU 입장에서 하드디스크로 부터 데이터가 올 때까지 가만히 기다리고만 있다면 연산을 3천만번 할 수 있는 시간을 낭비하는 셈이다.

요즘에는 SSD성능이 좋아지면서 그 격차가 줄어들긴 했지만 그래도 여전히 비휘발성 메모리는 굉장히 느리다.

 

2. 명령어를 어떻게 작성하는가?

CPU 는 설계 방식과 사용하는 명령어 체계가 여러가지가 있다.

각 종류의 CPU 마다 알아들을 수 있는 명령어의 모음을 명령어 집합(ISA)라고 한다.

한 명령어 집합을 기반으로 작성된 코드는 다른 명령어 집합 기반 CPU에서는 동작되지 않는다.

(예를 들어 인텔 x86기반의  CPU 가 ARM 명령어 셋으로 작성된 코드를 받는다면 실행되지 않는다).

 

(1) 어셈블리(Assembly) : 

아무튼 우리가 명령어를 작성한다면  CPU에 이진수로 이루어진 데이터가 전달된다.

문제는 CPU가 이해하는 명령어는 결국 숫자들의 나열인데 이는 사람이 이해하기 어렵다는 점이다.

이에 이진수로 표현된 명령어를 그나마 사람이 이해하기 쉽게 대응시킨 것어셈블리(Assembly)이다.

이미지 출처 : 모두의 코드 유튜브

 

 

(2) CPU에서 RAM에 데이터 쓰기

C언어를 공부하는데 필수적인 것은 아니라지만, 그래도 CPU에서 RAM의 특정 주소값에 데이터를 쓰는 어셈블리를 살펴보자.

 

먼저 CPU 에서 RAM에 데이터를 쓰기 위해서는 아래와 같은 정보들이 필요하다.

  • 램의 어디에 쓸지
  • 램에 어떤것을 쓸지
  • 램의 해당위치부터 얼마만큼의 공간을 쓸지

이제 실제로 어셈블리가 어떻게 구성되어있는지 살펴보자.

# 주소값 0x1234 를 가진 메모리 방 한칸에 3을 쓰는 명령어 
mov eax, 4660 # 4600은 0x1234 의 십진표기
mov BYTE PTR [rax], 3 # mov 는 값을 이동, 복사 시키라는 명령어
# mov A, B 는 B에서 A로 복사하라는 의미(오른쪽에서 왼쪽)

먼저 첫 번째 줄을 보자,

mov eax, 4660 은 4660을 eax에 복사하라는 말이다.

참고로 eax란 CPU의 RAX레지스터 의 하위 32비트, 즉 마지막 4바이트를 의미한다.

(RAX레지스터는 뭐냐? -> 저도 자세히 모릅니다 검색해보시길..)

이미지 출처 : 모두의 코드 유튜브

 

위의 명령어를 실행하면 다음과 같이 RAX의 하위 4바이트에 0x1234가 들어가게 된다.

 

이제 두 번째 줄을 보자.

먼저 어셈블리에서 [ rax ] 의 의미는 RAX에 저장되어 있는 값(1234)이 위치한 방을 의미한다.

그 다음 BYTE PTR 은 "해당 방으로부터 1바이트 만큼"을 의미한다.

마지막으로 3은 해당 방에 3이라는 값을 복사하라는 의미가 된다.

이를 요약하자면 다음과 같다.

mov BYTE PTR [rax], 3
--> 0x1234번 주소값으로부터 1바이트 만큼 공간에 3을 쓴다.

 

해당 명령어를 실행한 결과를 보면 0x1234라는 방에 3이라는 값이 쓰여진 것을 볼 수 있다.

이미지 출처 : 모두의 코드 유튜브

 

어셈블리에 관한 내용은 여기서 마치기로 하고, 이제 CPU가 어떻게 명령어를 읽어들이는지 알아보도록 하자.

 

(3) CPU는 명령어를 어떻게 읽어들일까?

우리는 앞서 CPU는 RAM에서 실행할 명령어를 가져온다는 사실을 학습하였다.

이 때 CPU는 RAM의 어디에서 명령어를 가져올지 어떻게 아는걸까?

CPU에는 다음에 실행할 명령어를 보관하고 있는 레지스터가 있는데, 이 레지스터를 "명령어 포인터"라 한다.

특히 인텔사의 x86 CPU에서는 이 레지스터를 RIP 라 부른다.

다음의 예에서 CPU가 명령어를 어떻게 읽어들이는지 살펴보자.

이미지 출처: 모두의 코드 유튜브

왼쪽의 0x401340 ~ 0x401347  는 각 명령어들의 램에서의 위치를 의미한다.

 

위의 예제에서 CPU가 명령어들을 읽어들이는 과정은 다음과 같다 :

1) CPU는 0x1401340 (첫 줄) 에 위치한 push rbp 라는 명령어를 읽어들인다.

2) 각 명령어들의 길이가 정해져 있기 때문에 CPU 는 RIP레지스터의 값을 알아서 업데이트 한다.

(push rbp 의 경우 명령어의 길이가 1바이트기 때문에 다음 RIP의 값은 0x401341이 된다)

3) 마찬가지로 RIP 의 값이 0x401341(두 번째 줄) 이기 때문에 해당 위치에 있는 mov rbp, rsp 를 실행하고 RIP를 그 다음인 0x401344로 업데이트 한다.

4) 이렇게  CPU는 RIP 레지스터 값을 알아서 업데이트 해가며 명령어들을 읽어들인다.

 

※ 프로그램을 작성한다는 것의 의미

- 하드디스크에서 프로그램의 정보를 램에 복사한다.

- 프로그램의 시작위치로 RIP를 설정한다.

 

 

 

★ 결론  

아무튼 CPU에 명령을 내리기 위해서는 CPU의 ISA에 맞게 어셈블리로 명령어를 작성하면 된다.

하지만 어셈블리는 굉장히 난해하다.

이에 사람들은 일단 사람이 이해하기 쉽게 명령어를 작성한 다음 이를 어셈블리로 바꿔주는 프로그램을 생각하였는데 ,

이렇게 탄생한 것이 사람이 이해하기 쉽게 작성한 명령어, 즉 프로그래밍 언어이다.

  • 프로그래밍 언어 = 사람이 이해하기 쉽게 작성하는 명령어
  • 컴파일 = 어셈블리로 바꿔주는 과정

다음 부터는 프로그래밍 언어의 아버지 격이라 할 수 있는 C 언어에 대해 본격적으로 학습해보겠습니다.

 

'Language > C' 카테고리의 다른 글

[C] 5. 변수(Variables)  (0) 2022.07.05
[C] 4. 주석  (0) 2022.06.28
[C] 3. Hello World 분석 - #include/stdio.h/main()/printf()/ \n  (0) 2022.06.27
[C] 2. 첫 프로그램 : Hello World 출력해보기  (0) 2022.06.27
[C] 1. Visual Studio 설치  (0) 2022.06.27
Comments