brunch

You can make anything
by writing

C.S.Lewis

by 허클베리 Jul 23. 2022

2. 소프트웨어적 사고 - 4

CPU, 기계어와 프로그래밍 언어

 또 다른 아키텍처인 양자컴퓨터에 관한 이야기는 장을 달리해 다루도록 하고 현재 널리 보급되어 사용되고 있는 Intel, AMD 같은 제조사에서 만든 프로세서 흔히 modern CPU architecture라고 불리는 마이크로프로세서를 기반으로 표현된 기계어 프로그램의 모양이 대충 어떠한지 보자. 



 참고로 modern CPU architecture라고 해서 하나의 아키텍처를 의미하는 건 아니다. 약간씩 다르긴 하지만 구성 원리나 형태가 서로 닮아있거나 유사하기도 하고.. 이 글을 이해하기 위해 굳이 아키텍처 이름을 콕 집어 이야기할 필요는 없다고 생각할 뿐 CISC(Complex Instruction Set Computer)니 RISC(Reduced Instruction Set Computer)니 하는, 앞서 얘기한 CPU는 명령 집합(instruction set)을 물리적으로 구현한 것이란 근본적인 관점에서의 분류에서부터 X86(32비트), X86-64(64비트), AMD64(64비트)처럼 프로세서가 한 번에 처리하는 데이터의 크기 ('워드(word)'라고 한다)에 따른 분류, 또 Intel, AMD, ARM, Sun, IBM 같은 프로세서를 설계하는 회사마다 그리고 같은 제조사 내에서도 다른 구조설계를 갖는 등 여러 기준에 따라 아키텍처의 수는 굉장히 많다. 



    0000100010010011

    0001100100000011

    0010100000000111

    0001000100100001

    0111000000011111

    ...



 정말 단순하다. 딱 보면 이해가 간다. '아.. 0과 1이구나..'


 패턴을 잡아내는데 천재성이 있는 사람이라면 위와 같은 기계어 코드를 몇 페이지 들여다보면 어느 정도의 패턴은 인식 가능해서 의미부여 정도는 할 수 있을지 모르겠다. 하지만 그렇다고 해서 기계어 코드만으로 어떤 동작을 하는 프로그램인지 완벽하게 밝혀내긴 아주 어렵고 한다 해도 시간이 아주 많이 걸리는 일이다. 


 나 같은 일반인들은 이걸 보며 괜한 스트레스나 안 받으면 다행이다. 그러니 기계어는 아예 상종을 하지 않는 편이 정신건강에 좋다.   



 어찌 되었든 기계어 프로그램은 숫자 0과 1로 되어있다. 기본적으로 반도체를 이용한 전자회로인 CPU는 전기에너지에 따른 전자의 이동을 통해 '있다' '없다' 내지는 '어떤 상태'와 그 어떤 상태와는 '다른 상태'처럼 두 가지 상태의 배열로 모든 걸 표현한다. 그리고 우린 이걸 알기 쉽게 숫자 0과 1로 상징화한 것이다. 



참고로 배열(array)은 동일한 타입의 데이타가 끊어지지 않고 연속적으로 늘어선 것을 말한다. 데이타의 '타입(type)'은 데이타의 도메인(domain)과 더불어 컴퓨터나 프로그램에서 인식하는 데이타를 이야기할 때 필수적으로 수반되는 중요한 내용을 담고 있지만 지금은 그냥 편하게 '모양' 또는 '생김새'라고만 생각하자. 





 그럼 저 '기계어 프로그램'을 보고 사람인 우리가 이해하기 쉬운 형태로 코딩할 수는 없을까? 그래서 나온 게 '어셈블리어'라고 하는 low level의 프로그래밍 언어이다. 



 예를 들어 위 예시로 든 기계어 프로그램 코드의 첫 번째 행 (0000100010010011)이다음과 같이 해석된다고 가정해 보자. 



 앞에서 6자리(000010)는 인스트럭션(처리명령), 그다음 4자리(0010)는 레지스터, 다음 2자리(01)와 4자리(0011)는 데이타. 물론 이건 어디까지나 이해를 돕기 위한 구조이지 정말 이렇진 않다.  



 어셈블리어는 위에서 구분한 인스트럭션, 레지스터, 데이타를 사람이 알기 쉬운 기호(이걸 mnemonic symbol이라고 한다) 등으로 표현해 그나마 해당 CPU 인스트럭션을 직접 사람이 프로그래밍할 수 있도록 만들어진 언어이다. 



 그렇다 보니 어셈블리어는 CPU 제조사마다 조금씩 다를 수밖에 없고(제조사가 만들어 제공한다는 이야기다. 그래서 어셈블리어로 프로그래밍하려면 해당 프로세서 혹은 CPU 제조사의 기술지원 페이지에서 '데이타 시트'와 함께 제공되는 어셈블리 명령 집합을 참조해야 한다) 그다지 효율적이지도 않다. 논리적으로 연산을 표현한 사람의 언어라고 하기엔 너무 CPU친화적으로 생겨먹었다. 



 생각해보라. X = 13 + 14라는 연산을 X라는 변수가 지정된 메모리 주소에 13을 move 하고 레지스터 al(그냥 레지스터 이름이 al이라 하자)에 있던 14를 add 한다 뭐 이런 식으로 생각하며 코딩하는 것이 얼마나 번거로운 일일지. 





 그래서! 지금은 전설이 된 몇몇 개발자들이 C나 JAVA, Python처럼 사람이 이해하기 쉽고 사용하기 쉬운 프로그래밍 언어를 만들게 되었고 그런 것들을 우린 high level programming language (고수준 프로그래밍 언어)라고 한다.



 뭐 그렇다고 고수준 프로그래밍 언어라고 해서 누구나 딱 보면 아~ 할 만큼 편하게 읽히진 않는다. 우리가 어떤 언어를 '배우고 익히는 과정'없이 그냥 이해하고 술술 말할 수 없듯이 프로그래밍 언어도 배우고 익히는 과정을 거쳐야 한다. 하지만 개인적으로 프로그래밍 언어는 일반적인 언어의 습득과정보다 짧은 기간에 활용할 수 있는 정도로 습득이 가능하다고 생각한다. 



 물론 사람에 따라 이해력과 사고체계가 다르니 습득하는 방법과 기간에도 차이가 있기 마련이지만 보통 한 가지 언어를 익혀 프로그램하는 데는 그리 오랜 시간이 걸리지 않는다. 그리고 하나의 프로그래밍 언어를 잘 사용할 수 있게 되면 다른 프로그래밍 언어를 익히는 것도 훨씬 수월하다. 



 대부분의 프로그래밍 언어들은 결국엔 CPU에서 이해하는 언어 즉 기계어 그리고 기계어와 1:1로 매핑되는 어셈블리어로 분할되고 변환될 수 있어야 하기 때문에 이것이 용이하도록 하려면 어느 정도 유사한 구조를 가질 수밖에 없기 때문이다. 

 

 

 이야기가 길어졌지만 기계어 코드까지 꺼내면서 CPU의 처리와 프로그래밍 언어에 대한 이야길 한 건, 우리가 코딩이라고 하는 행위를 통해 작성하는 프로그램은 결국 CPU가 이해하고 수행할 수 있게 CPU가 갖고 있는 인스트럭션 셋으로 변환되어 전달되어야 한다는 것이고 처리되는 데이타 역시 CPU가 갖고 있는 레지스터란 그릇에 담겨 모든 표현은 전자회로에서의 상태 표현 즉 0과 1 (기계어)로 나타내게 되므로 우리가 흔히 메모리라고 부르는 휘발성 저장매체에 프로그램을 올렸을 때 그건 이미 0과 1의 조합으로 표현된 것이란 것을 말하고자 함이다.



 소프트웨어는 하드웨어에게 처리해 달라고 던지는 명령어와 데이타들의 집합이다. 그리고 CPU는 이를 0과 1의 배열로 상징되는 일련의 전기적 신호로 해석하고 처리한다. 


매거진의 이전글 2. 소프트웨어적 사고 - 3
브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari