1강. 내가 작성한 코드는 어떻게 작동되는걸까?

이동재·2021년 2월 20일
0

컴퓨터구조

목록 보기
2/3
post-thumbnail

1-1. 컴퓨터구조를 배우기 앞서!


위에 있는 그림은 전자공학 및 컴퓨터과학 분야를 계층별로 분류한 그림입니다. 초록색으로 칠해진 계층은 하드웨어를 의미하며, 전기전자공학부에서 배우는 과목들을 예로 들어보자면 반도체공학, 전자회로, 디지털시스템, VLSI 이런 과목들이 있습니다. 하늘색으로 칠해진 계층은 소프트웨어를 의미하며 운영체제, 컴파일러, 자료구조, 알고리즘 이런 과목들이 있습니다.

컴퓨터구조라는 과목의 경우, ISA 및 Microarchitecture 계층에 대해 배우며 ISA는 소프트웨어 및 하드웨어를 연결시켜주는 인터페이스, microarchitecture는 ISA 구현 방법을 의미합니다. 즉, 컴퓨터구조를 이해하면 소프트웨어가 하드웨어를 어떻게 이해하는지, 하드웨어가 소프트웨어를 어떻게 이해해야 하는지에 대해 알게되므로 필자의 개인적인 의견으로는 전기전자공학을 배우시는 분에게는 매우 중요한 과목이 아닐까라고 생각합니다.

1-2. 작성한 C 프로그래밍 코드는 어떻게 작동하는걸까?

많은 분들이 대학교 수업이나 고등학교 수업 때 프로그래밍 코드를 한 번쯤은 작성하시는 경우가 있을텐데 정작 코드가 어떻게 하드웨어 상에서 돌아가는 지에 대해 배우신 적은 따로 없으실겁니다. 이에 대해 알고나면 앞으로 있을 컴퓨터구조 시리즈 글을 이해하시는데 많은 도움이 될 거 같아서 적어둡니다. (엄밀하게 하기보다는 이해할 수 있게 간략하게 적어봤습니다)

a. 간단한 배경지식


컴퓨터 (하드웨어)는 0과 1을 사용하여 입력을 받고 이를 연산을 하고 0과 1을 출력합니다. 즉, 하드웨어는 2진법을 토대로 구현되어 있으며 각종 논리소자 (AND, OR, NOT 등)들로 구성되어 있습니다.
이러한 하드웨어를 작동시키기 위해서는 명령어가 필요한데, 앞서 말했듯이 컴퓨터는 0과 1만을 입력으로 받을 수 있습니다. 이러한 0과 1들로 조합된 명령어 및 데이터들 (2강쯤에서 자세히 설명할 예정입니다)을 binary code라고 부릅니다.

참고로 위키피디아의 경우, A binary code represents text, computer processor instructions, or any other data using a two-symbol system 라고 정의합니다.

다만, 0과 1로만 컴퓨터를 다루기에는 사람이 보기에 매우 어렵습니다. 예를 들어, 덧셈 기능을 하는 명령어가 10000인데, 실수로 개발자가 100000이라고 쳐버리면 아예 다른 명령어로 컴퓨터가 인식을 하게 될겁니다. 또 10000과 100000이 무슨 의미인지 사람이 직관적으로 알기 매우 어렵다는 단점이 있어서 나온게 assembly입니다. 0과 1로 이루어진 조합들을 사람이 보기 쉽게 ADD, SUBTRACT, STORE, MULTIPLY 등과 같이 심볼화시킨게 assembly language입니다.

참고로 이 사이트를 통해서 기계어(0과 1)랑 assembly language의 차이점을 더 잘 파악할 수 있습니다. https://www.geeksforgeeks.org/difference-between-machine-language-and-assembly-language/#:~:text=Machine%20language%20is%20the%20low,represented%20by%200s%20and%201s.&text=Assembly%20language%20is%20the%20more,instead%20of%200s%20and%201s.

그러나, assembly language는 하드웨어랑 밀접하게 관련이 있다는 점이 개발 상에 문제가 있었습니다. 필자가 만약 계산기 프로그램을 만들고 있다 해보겠습니다. 그리고 A라는 방식으로 구성된 하드웨어가 있고 B라는 방식으로 구성된 하드웨어가 있는데 두 컴퓨터에 다 작동하는 프로그램을 만들고 싶으면 A에 맞는 assembly로 작성하고 B에 맞는 assembly에 작성해야합니다. 왜 굳이 이렇게 해야 하냐면, 앞서 말했듯이 assembly language는 0과 1에 대응되는 심벌이고 이 0과 1들은 하드웨어 논리 연산이랑 밀접하게 연관되어 있습니다. 즉, 하드웨어 설계 방식이 상이하다면 A에서 작동하는 assembly 프로그램이 B에서 작동하리란 보장이 없습니다. 그리고 assembly language는 여러분이 아시는 for문이나 if문도 상당히 제한적이고 구조체 개념도 없어서 작성이 여전히 까다롭습니다. 즉, 사람한테 친화적인 방식이 아니라는 것입니다. 이러한 문제를 해결하기 위해 나온게 high level programming language입니다.

위키피디아에서는 high-level programming language is a programming language with strong abstraction from the details of the computer. In contrast to low-level programming languages, it may use natural language elements, be easier to use, or may automate (or even hide entirely) significant areas of computing systems (e.g. memory management), making the process of developing a program simpler and more understandable than when using a lower-level language 라고 정의합니다.

이러한 고레벨 언어를 통해 코드 작성이 훨씬 더 쉬워졌고 컴파일러만 지원된다면 하나의 고레벨 언어 코드로 작성해도 다양한 하드웨어에서 작동시킬 수 있게 되었습니다. 컴파일러는 주어진 프로그래밍 언어를 다른 프로그래밍 언어로 번역하는 프로그램인데, C언어로 작성한 코드를 A에 맞는 어셈블리나 B에 맞는 어셈블리로 번역할 수 있습니다. 아래 그림은 위키피디아에 가져온 그림인데, 이해하시는데 도움이 될거 같아서 첨부합니다.

(출처: https://en.wikipedia.org/wiki/Compiler#/media/File:Compiler_design.svg)

b. 코드가 거쳐가는 과정...


그래서 여러분 짠 C 코드는 컴파일이라는 과정을 거쳐서 assembly language로 바뀌고 이를 다시 assembler가 0과 1로 이루어진 기계어로 바꿔줍니다. 결국 나오게 된 기계어 (0과 1)를 토대로 마침내 하드웨어 작동시킵니다. 그러면 이제 차근차근 한 번 살펴보도록 하겠습니다.

컴파일러와 어셈블러라는 개념을 모르시는 분도 계실거 같아서 정의를 위키피디아에 참조하여 적어둡니다.
Compiler is a computer program that translates computer code written in one programming language (the source language) into another language (the target language)
Assembler (computing), a computer program which translates assembly language to machine language

0단계: 프로그래밍 과제가 나왔다...


교수님이 갑자기 E = (A + B) - (C + D)라는 기능을 하는 함수를 짜오라고 하면...? 다들 밑에 있는 코드처럼 짜시겠죠?

1단계: C언어로 작성 끝!!!


이렇게 작성이 끝났으면 여러분은 저 C 코드를 바로 하드웨어에 올릴 수가 없습니다. 앞서 말했듯이 C언어는 high level language이므로 바로 0과 1로 바뀔수가 없으므로 컴파일이라는 과정을 거칩니다.

2단계: 컴파일러


컴파일러는 특정 프로그래밍 언어를 입력받아서 다른 언어로 번역하는 과정을 거칩니다. 그래서 위 그림을 보시면 C언어 코드가 어셈블리 언어로 번역된 것을 확인하실 수 있습니다. Assembly language 종류도 다양한데, 여기서는 MIPS로 번역했습니다.

3단계: 어셈블러


이제 어셈블리 언어로 번역되었으니까 기계어로 바꿔주는 작업을 진행해야 하는데 이는 어셈블러가 담당합니다. 왼쪽 그림은 어셈블러 코드고 오른쪽은 실제로 해당 어셈블리 심벌들이 어떤 0과 1로 바뀌는지 정리해둔 표입니다. 어셈블리 5번째 코드를 한번 보겠습니다.

add $t0, $a0, $t1

이라는 명령어가 있는데 add는 100000 으로 번역되고 나머지는 레지스터를 표현한 기호인데 추후 자세히 설명할거니 여기서는 생략하겠습니다. 그러면 저 명령어를 기계어로 바꾸면 아래와 같이 나옵니다.

00000000100001010100000000100000

4단계: 하드웨어 작동


마지막 단계입니다! 3단계에서 번역한 기계어가 하드웨어에 들어오면 해당 기계어를 하드웨어가 해독하기 시작합니다. 간략하게만 말씀드리면 MIPS라는 어셈블리 언어는 기계어 위치마다 표현하는 내용이 다 다릅니다. 26번째부터 31번째는 해당 명령어 종류를 나타내며, 21번째부터 25번째는 레지스터 번호1, 16번째부터 20번째는 레지스터 번호2, 11번째부터 15번째 레지스터는 값을 업데이트할 레지스터를 의미합니다 (자세히 알 필요는 없고 그냥 기계어를 해독하는거다~~ 정도만 이해하시면 됩니다). 해독한 내용을 토대로 하드웨어를 작동시킴으로서 여러분이 작성한 코드를 실행합니다.

1강 글은 여기서 마치고 2강에서 폰노이만 구조에 대해 설명하도록 하겠습니다!!! 감사합니다.

profile
자투리 시간

0개의 댓글