튜링 머신과 기계어, 어셈블리 그리고 C언어

Coder One
방문: 100

프로그래밍 초보자C언어나 파이썬 공부할 때 먼저 알아두면 좋은 튜링머신. 튜링머신은 컴퓨터가 동작하는 기본 원리를 이해하는데 기반입니다. 튜링머신에서 C언어까지 어떻게 이어지는지 흐름을 살펴봅니다!


튜링 머신이란?

튜링 머신은 앨런 튜링(Alan Turing)이 제안한 이론적인 컴퓨터 모델입니다.

앨런 튜링과 컴버배치

(사진은 앨런 튜링 선생님. 그리고 영화에서 튜링 역할을 한 컴버배치)

이 분이 제시한 건 간단한 동작들로도 복잡한 계산이 가능하다는 개념인데요. 튜링 머신에는 세 가지 요소가 있습니다: 무한히 긴 테이프(메모리 역할), 그 테이프 위에서 움직이는 헤드(읽고 쓰는 장치), 그리고 상태(동작 모드)입니다.

turingmachine

튜링머신에서 말하는 상태(동작 모드)란 헤드가 현재 칸의 값을 읽었을 때, 그 값에 따라 어떻게 동작할지를 정의하는 것입니다. 아래에서는 right, carry, done 이라는 동작 모드들을 정의했습니다.

right - 0이나 1이면 오른쪽으로 이동
- 빈칸이면 왼쪽으로 이동, 모드는 carry로
carry - 1이면 그 칸에 0을 적고, 왼쪽으로 이동
- 0이나 빈칸이면 그 칸에 1을 적고 왼쪽으로 이동, 모드는 done으로

done

- 아무것도 안함

논리적인 정의라서 복잡해 보이지만, 사실 각 모드는 현재 칸을 읽은 후, 칸의 값을 수정하기도 하고, 왼쪽이나 오른쪽으로 이동하고, 다른 모드로 상태를 변경하는 동작 밖에는 없습니다.

아래 그림은 튜링머신 실험 사이트 https://turingmachine.io/ 에서 위의 모드들을 동작하게 해본 예제입니다.

turing machine simulation

세개의 모드 right과 carry, done 만으로 주어진 2진수에 1이 더해지는 것(1011 -> 1100)을 볼 수 있습니다. 즉 덧셈 기계를 만든 셈입니다! (사이트에서 직접 눌러보세요!)

튜링 머신은 이렇게 단순한 동작으로 이루어지지만, 충분한 시간과 올바른 규칙만 있다면 어떤 계산이라도 수행할 수 있는 보편적인 계산 장치의 개념입니다. 현대 컴퓨터의 논리적 원리입니다.

기계어란?

기계어(Machine Code)는 실제 컴퓨터가 이해하는 가장 낮은 수준의 언어입니다. 기계어가 튜링 머신과 동일하게 구현되지는 않았지만, 메모리(테이프)에서 하나씩 읽고, 내부 상태(레지스터의 상태 등)에 따라 결과를 내며, 다시 다음 명령어로 넘어가는 구조는 튜링 머신과 원리와 같습니다. 컴퓨터의 CPU는 튜링 머신의 헤드처럼 메모리에서 명령들을 차례로 읽어 실행하는데, 이 명령들이 바로 기계어로 작성되어 있습니다. 기계어는 0과 1로 이루어진 이진수들로써 전기 신호의 켜짐(1)과 꺼짐(0)에 대응합니다.

메모리에서 데이터를 가져오는 CPU

(그림. math.hws.edu)

초기의 컴퓨터 프로그래머들은 기계어를 입력하기 위해 패널의 스위치를 직접 0과 1에 맞춰 올리고 내리기도 했습니다. 사진에 보이는 Altair 의 초기모델은 주소를 지정하는 16개의 스위치와 데이터나 명령어를 입력하는 8개의 스위치로 각각의 비트를 직접 설정합니다. 그리고 RESET, RUN 같은 버튼도 달려있었습니다.

알테어 초기 모델

(사진. computercloset.org )

천공카드에 구멍을 뚫어서 기계어를 입력하기도 했는데요. 꽤 번거롭고 사람이 실수하기 쉬웠겠죠.

천공카드

(사진. 천공카드, 선배님들 중에는 실물을 보신분도 있다고 들었습니다.)

아무튼, 기계어에 대해서 설명을 이어가면, 2진 코드는 하나의 명령어를 나타내는데요. 사람에겐 기억하기 어려운 값이지만, CPU는 이해할 수 있는 명령입니다. 실제로 x86 CPU에서 "AL 레지스터에 값 5를 대입하라"는 명령어는 10110000 00000101입니다. 이처럼 기계어는 숫자로만 이루어져 있어 사람이 직접 다루기엔 너무 불편합니다. 컴퓨터는 이해할 수 있지만 인간에게는 난해한 언어인 것이죠.

어셈블리 언어란?

기계어를 사람에게 조금 더 친숙한 형태로 대응시킨 것이 어셈블리 언어(Assembly Language)입니다. 어셈블리는 기계어의 명령 하나하나에 대응하는 짧은 영단어나 약어로 구성됩니다. 즉, 기계어와 1:1로 대응되는 기호라고 할 수 있습니다. 덕분에 프로그래머는 긴 이진수를 직접 쓰는 대신 의미 있는 짧은 명령어 이름을 사용할 수 있습니다. 어셈블리 언어로 작성한 코드는 어셈블러(Assembler)라는 프로그램에 의해 기계어(이진 코드)로 변환됩니다.

어셈블리와 기계어

비유하자면, 어셈블리 언어는 공장에서 일하는 로봇에게 간단한 줄임말 지시서를 건네는 것과 같습니다. 이전까지 로봇에게 일일이 스위치를 켜고(1) 꺼서(0) 지시를 했다면, 이제는 “MOVE”(이동), “ADD”(더하기) 같은 짧은 단어로 말해주는 셈입니다.

예를 들어, 앞서 기계어 예시로 든 10110000 00000101이라는 이진 코드는 어셈블리로 MOV AL, 5로 표현할 수 있습니다. 또 거기에 3을 더하는 동작도 어셈블리 한 줄로 작성할 수 있습니다. 아래는 간단한 어셈블리 코드 예제입니다.

MOV AL, 5        ; AL 레지스터에 5를 넣는다
ADD AL, 3        ; AL 레지스터 값에 3을 더한다 (이 결과 AL에는 8이 저장됨)

위 어셈블리 코드는 사람이 읽기 훨씬 쉽습니다. MOV는 어떤 곳에 값을 이동시키라는 의미이고, ADD더하라는 의미입니다. 세미콜론(;) 뒤쪽은 주석으로, 해당 명령에 대한 설명을 적을 수 있습니다. 어셈블리 한 줄은 대체로 하나의 기계어 명령과 대응되므로 아직 상당히 저수준(low-level)이지만, 그래도 이진수만 있는 것보다는 이해하기 쉽습니다. 단, 어셈블리 언어는 사용하는 CPU의 종류마다 명령어가 다르다는 단점이 있습니다. 예를 들어 x86과 ARM CPU의 어셈블리는 문법이 서로 다르기 때문에, 어셈블리로 코드를 짜면 특정 CPU에서만 동작하는 프로그램이 됩니다.

C 언어란?

C 언어는 어셈블리보다 더 높은 수준의 프로그래밍 언어로, 사람이 이해하기 쉬운 문법과 구조를 제공합니다. C 언어 소스 파일은 컴파일러(Compiler)라는 프로그램에 의해 해당 시스템의 어셈블리나 기계어로 번역됩니다. 즉, 어떤 컴퓨터에 C언어 컴파일러가 있다면, C 코드를 해당 CPU에 맞는 어셈블리 코드로 만들 수 있습니다. 이제 프로그래머는 기계어나 CPU에 신경 쓰지 않고 논리적인 서술만으로 프로그램을 만들 수 있게 되었습니다.

간단한 C 언어 예제를 살펴보겠습니다. 두 개의 숫자를 더하는 프로그램을 C로 작성하면 아래와 같습니다.

#include <stdio.h>

int main() {
    int a = 5;
    int b = 3;
    int c = a + b;          // a와 b를 더해 c에 넣는다
    printf("%d\n", c);      // 결과값 8을 화면에 출력한다
    return 0;
}

위의 C 코드에서는 int a = 5;변수를 만들고 값을 대입하는 문장, c = a + b;처럼 변수들을 더하는 문장, printf를 이용해 출력하는 함수 호출 등이 보입니다. 이런 한 줄 한 줄의 C 코드 뒤에는 여러 줄의 어셈블리(그리고, 기계어) 명령들이 필요하겠지요. 하지만, 프로그래머는 그 복잡한 과정을 신경 쓰지 않아도 됩니다. 예를 들어 c = a + b; 한 줄을 컴파일하면 앞서 본 MOVADD 같은 여러 어셈블리 명령으로 컴파일러가 변환해줍니다. 이제 프로그램의 의도를 사람이 이해하기 쉬운 형태로 표현할 수 있고, 세부 구현은 컴파일러와 컴퓨터에 맡길 수 있게 되었습니다.

C언어에서 기계어로 전환과정

(그림. Computer Systems - Randal 에서)

정리 및 결론

지금까지 튜링 머신부터 기계어, 어셈블리, C 언어까지 컴퓨터 언어의 발전 과정을 살펴보았습니다. 튜링 머신은 이 모든 것의 논리적 토대로서, 단순한 원리만으로 어떤 계산도 가능하다는 것을 보여줍니다. 실제 컴퓨터는 기계어(이진수 명령어)를 사용해 튜링 머신과 같은 방식으로 동작하지만, 인간에게는 기계어로 직접 프로그래밍하는 것이 매우 어렵습니다. 이를 극복하기 위해 사람이 이해하기 쉬운 형식인 어셈블리 언어가 나왔고, 나아가 더 높은 수준의 C 언어가 개발되었습니다.

이처럼 컴퓨터 과학은 낮은 수준에서 높은 수준의 추상화로 발전해 왔습니다. 튜링 머신의 원리 위에 기계어라는 구현이 있고, 그 위에 어셈블리라는 얇은 표현 계층, 다시 그 위에 C와 같은 고급 언어 계층이 생겨난 것입니다. 결국 우리가 C 언어처럼 편리한 언어로 프로그램을 작성해도, 가장 밑바탕에는 튜링 머신이 제시한 것과 같은 원리로 동작하는 기계 논리이진 코드가 자리하고 있습니다. 컴퓨터는 이러한 단계별 발전을 통해 인간에게 이해하기 쉽고, 기계에게 실행하기 쉬운 형태로 서로 소통하게 된 것입니다.


Related Posts