이번 글에서는 프로그래밍 언어의 발달 과정을 살펴봅시다. 이 부분 역시 프로그래밍을 하는데 반드시 알아야 하는 부분은 아닙니다. 대강의 흐름을 이해하시고 구체적인 언어의 문법을 이해하거나 외우려고 하시지 마세요.

기계어

처음 프로그래머들은 실제로 0과 1을 사용하여 프로그램을 작성하였습니다. 이를 기계어(Machine Language)라고 합니다.

000000 00001 00010 00110 00000 100000

이런 식이죠. 저는 기계어를 모르기 때문에 위 코드는 영문위키(https://en.wikipedia.org/wiki/Machine_code)에서 가져왔습니다. 내용을 해석해 보면,

[  op  |  rs |  rt |  rd |shamt| funct]
 000000 00001 00010 00110 00000 100000    2진법
    0     1     2     6     0     32     10진법

- 0번 오퍼레이션(op)의 32번 기능(funct)을 사용합니다.
- 이 기능은 rs 레지스트리의 값과 rt 레지스트리의 값을 더하여 rd레지스트리에 저장합니다. 여기서 레지스트리는 CPU에서 데이터를 임시로 저장하는 장치를 뜻합니다(윈도우 운영체제의 레지스트리와는 다릅니다).
- 즉 1번 레지스트리의 값(rs)에 2번 레지스트리값(rt)을 더하여 6번 레지스트리(rd)에 전달합니다.

바로 앞 강의에서 살펴 본 칩과 마찬가지로 0과 1로 구성된 32개의 신호를 CPU에 전달하면 CPU가 위 명령을 수행하기 위한 출력 신호를 생성하게 되고, 다음 장치들이 그 값을 받아 명령을 완수하게 됩니다.

기계어로 작성된 코드는 가독성이 굉장히 떨어지기 때문에 사람이 알아 보기 쉽게 하기 위해 나온 것이 어셈블리어(Assembly Language)입니다.

어셈블리어

10110000 01100001 <- 기계어
MOV AL, 61h <- 위 기계어에 해당하는 어셈블리어

저는 어셈블리어 역시 전혀 모르기 때문에 이 또한 영문 위키(https://en.wikipedia.org/wiki/Assembly_language)에서 가져왔습니다.

AL 레지스트리에 61h(h는 hex로 6진법을 뜻합니다. 6진법 61은 10진법으로 97입니다)를 넣으라(move)는 명령입니다.

10110000이라는 명령이 MOV AL이라는 문자로, 2진수인 01100001이 6진법인 61h가 되어 사람이 이해하기에 조금 더 쉬워졌습니다. 하지만 CPU는 0과 1밖에 모르기 때문에 어셈블리어로 작성된 코드는 CPU로 바로 전달될 수 없고 어셈블러(assembler)라는 프로그램을 통해 기계어로 번역된 후 CPU가 처리할 수 있습니다.

어셈블리어는 기계어를 단순히 사람이 보기 좋게 숫자를 문자로, 2진수를 다른 진수의 수로 변경한 것으로 기계어와 1대 1 변환이 가능합니다.

저급언어

기계어와 어셈블리어처럼 CPU에 직접 명령을 내리는 코드를 작성하는데 사용되는 언어를 저급언어라고 합니다. CPU에 명령어를 바로 전달하는 것은 초창기에는 너무 당연한 일이였겠지만, CPU가 발전하고 종류가 다양해지게 되면서 CPU 아키텍처가 바뀌면 프로그램이 작동하지 않게 되는 문제가 발생합니다. 즉 CPU 종류에 따라 각각의 프로그램을 작성해야 하는 것이죠. 기계어/어셈블리어는 하나의 언어가 아닙니다. 해당 CPU 메뉴얼에 맞춰 프로그래밍을 하는 것이 기계어로 코딩하는 것입니다.

또 다른 문제점은 CPU의 작동 방식이 사람이 이해하기에 난해하다는 점입니다. 예를들어, 2+3을 계산하려면 아래와 같은 명령들을 내려야 합니다.

- 2를 1번 레지스트리에 저장
- 1번 레지스트리의 값에 3을 더해서 2번 레지스트리에 저장
- 2번레지스트리의 값을 꺼내서 출력

굉장히 구체적으로 명령을 내려 줘야하기 때문에 익히기가 힘들고 코드가 복잡해지므로 생산성이 높지 않습니다.

고급언어

C언어, 자바, 자바스크립트, 파이썬 등과 같은 고급언어는 운영체제와 컴파일러/인터프리터 등의 프로그램을 통해 시스템에 맞는 기계어의 번역이 이루어 집니다. 또한 번역시 CPU가 처리해야 할 구체적인 과정이 알아서 처리되어 사람의 입장에서 명령을 내릴 수 있게 됩니다. 이것을 명령이 추상화가 되었다고 합니다. 물론 CPU의 입장에서 추상화가 된 것이죠.

2+3을 계산하는 명령을 그냥 2+3이라고 작성하면 이 명령이 기계어로 번역될 때 번역기가 이 문장을 이해하고 해당 CPU에 맞는 기계어를 알아서 생성하게 됩니다.

CPU의 입장에서 프로그래밍하는 언어가 저급언어, 사람의 입장에서 프로그래밍하는 언어가 고급언어라고 할 수 있습니다.

저급, 고급이 언어 자체의 좋고 나쁨을 뜻하지는 않습니다. 오히려 저급언어로 만들어진 프로그램이 CPU를 직접 제어하므로 고급언어보다 훨씬 빠른 프로그램을 만들 수 있습니다. 하지만 요즘은 컴퓨터 성능이 많이 좋아져서 고급언어로 만든 프로그램이 조금 비효율적이라도 여전히 빠르고, 프로그램을 만드는 효율(얼마나 쉽고 빠르게 프로그램을 만들 수 있는지)이 중요하게 여겨지기 때문에 대부분의 프로그램들이 고급언어로 만들어 지고 있습니다.

컴퓨터의 작동 원리는 프로그래밍을 하는데 꼭 알아야 하는 것은 아니지만 프로그램이 어떻게 컴퓨터를 작동시키는지와 밀접하게 연관되어 있고 몇몇 프로그래밍의 기본 개념을 이해하는데 도움을 줍니다.

컴퓨터는 전자계산기

최초의 컴퓨터는 수학식을 계산하는 도구였습니다. 지금은 컴퓨터로 영화도 보고 게임도 하고 뉴스도 읽을 수 있죠. 하지만 이 기능들은 계산기의 기능을 확장한 것입니다.

키보드로 키를 입력하거나, 마우스를 움직이거나 터치스크린을 터치하는 등의 사람의 동작을 컴퓨터가 처리할 수 있게 전기신호로 변환하는 장치를 입력장치라고 합니다. 반대로 전기신호를 사람이 이해할 수 있게 빛(화면), 소리 등으로 변환하는 장치를 출력장치라고 합니다. 이 신호는 숫자로 나타내어질 수 있고, 컴퓨터는 입력장치를 통해 입력받은 값을 연산하여 출력장치에 전달합니다.

이처럼 현대의 컴퓨터도 여전히 숫자들을 바탕으로 하여 정보를 처리하고 계산하는 장치라는 것을 이해하고 컴퓨터가 어떻게 수를 계산하는지 알아봅시다.

아래 내용에 전혀 흥미가 없으신 분들은 컴퓨터가 2진수 계산을 하는 장치라는 것만 알고 넘어가도 됩니다.

컴퓨터와 2진 신호 체계

흔히들 컴퓨터는 0과 1밖에 모른다고 하죠. 컴퓨터의 중앙처리장치인 CPU가 0과 1로만 계산을 하기 때문입니다. 그런데 CPU는 숫자 0과 숫자 1을 어떻게 구별할까요?

사실 CPU는 숫자 0도, 숫자 1도 모릅니다. CPU에는 모든 신호가 전기로 전달되는데 이 때 전압이 일정한 기준보다 높은 경우 이것을 1, 전압이 일정한 기준 보다 낮은 경우 이것을 0으로 사람들이 표현한 것입니다.

CPU는 <전압이 충분히 높음>과 <전압이 충분하지 않음>의 두가지 신호로 된 2진법을 사용합니다. 혹시 여기서 2진법, 10진법에 대해 잘 모르시는 분들은 따로 검색을 통해서 기본 개념을 익히시고 계속 읽어 주세요.

여기서 "전압이 충분함"이 어떤 기준인지, 왜 높고 낮음 두가지로만 구별하는지(10단계의 전압을 구별하면 10진법을 사용할 수 있을텐데 말이죠!)를 이해하려면 CPU의 구성품인 트랜지스터가 무엇인지 알아야 합니다.

트랜지스터와 논리연산

트랜지스터는 CPU를 구성하는 구성품으로 전기 신호의 흐름에 관여하게 됩니다. CPU에는 트랜지스터가 수십억개가 모여있으며 제작기술이 발달함에 따라 그 수가 늘어나고 있죠. (초창기의 컴퓨터의 CPU는 수천개의 트랜지스터로 구성되어 있었음)

CPU 구성에 사용되는 npn 트랜지스터는 아래와 같이 구성되어 있습니다.

트랜지스터에는 전류가 흐를 수 있는 다리가 3개(이미터, 베이스, 컬렉터) 있으며 베이스에 전압이 일정 수준 이상(0.6볼트 이상) 이면 컬렉터로 들어온 전류가 이미터로 전달될 수 있고 그렇지 않으면 컬렉터에서 이미터로 전류가 흐르지 않게 하는 장치입니다. 즉 베이스에 특정한 전압 이상의 전류가 통하고 있는지 아닌지를 통해 이미터에 CPU에 0, 1의 신호를 주게 되는 것입니다.

트랜지스터의 이러한 특성을 이용하면 '논리 게이트'라는 것을 만들 수 있습니다.

 논리 게이트

 구조

 설명

 AND GATE

 

A와 B에 모두 전압이 있어야
Out에 전류가 흐름

 OR GATE

 

A나 B에 중에 한 곳이라도 전압이 있으면
Out에 전류가 흐름

위 표는 트랜지스터로 어떻게 논리 게이트를 구성하는지 보여주기 위한 2가지 예제일 뿐이고 트랜지스터들을 연결하는 방법에 따라 아래와 같은 다양한 논리 게이트를 만들 수 있습니다.

Symbol에서 왼쪽이 입력신호, 오른쪽이 출력신호이며, Truth Table에서 왼쪽 A 혹은 A, B가 입력 값, 오른쪽 X가 출력값을 나타냅니다. 예를 들어, AND 게이트의 경우 X로 1 신호를 보내기 위해서는 (A, B)가 각각 (1, 1)이 되어야 하며, 나머지 (0, 0), (1, 0), (0, 1)인 경우는 0 신호를 보내게 됩니다.

논리 게이트를 또다시 모아모아 계산을 할 수있는 칩을 만들 수 있습니다. 아래는 두자리 2진수 두개의 합을 구하는 칩 구성입니다.

이 칩은 0과 1을 입력받는 부분이 4개, 0과 1을 출력하는 부분이 3개로, 4가지 입력부에 두자리 2진수 2개를 나란히 입력하면 그 합이 출력됩니다. 입력부에 스위치를 연결하고 출력부에 전구를 연결하면 각각 스위치를 켜고 끔에 따라 전구에 불이 들어오는 순서가 달라지겠죠.

위 칩에서 알 수 있는 중요한 점들!!

1. 입력값인 1011이 순서대로 하나씩 1, 0, 1, 1이 들어가는 것이 아니라 1011이 동시에 쑥 들어가서 오른쪽으로 값이 101이 동시에 쑥 나온다는 것

2. 결국 2진수 1011(10진수로는 11)를 입력으면 101(10진수로 5)를 출력하고 있다는 것입니다. 즉, 규칙을 이해하고 사용하는 것이 요구된다는 점이죠,

3. 출력값 101은 숫자 101이 아니라 세개의 출력선에 전류가 흐르고 있는지 아닌지를 나타낸다는 점! 출력으로 나온 전류를 다시 다른 칩에 넣어 변환하여 모니터나 프린터같은 출력장치에서 그 값을 활용할 수 있습니다.

이런식으로 논리 게이트를 구성하여 CPU를 설계하게 됩니다(정확히는 CPU의 연산부분을 이런식으로 설계).

정리

입력장치, 연산장치, 출력장치와 더불어 또 한가지 중요한 장치가 저장장치인데요, 컴퓨터의 저장장치는 0과 1의 전기 신호를 기록해 두었다가 전류가 흐르면 이전에 기록해놓았던 2진 전기 신호들을 재현하는 장치입니다.

결론적으로 컴퓨터는 0과 1의 전기 신호를 처리하는 장치, 프로그램은 0과 1들의 전기 신호들를 저장장치에 기록해 놓은 것이며 프로그래머는 0과 1들의 전기 신호들을 기록하는 사람들이라고 할 수 있습니다.

그러면 프로그래머들은 프로그래밍을 할 때 0과 1을 써넣는 작업을 하나요? 처음에는 진짜 그랬습니다. 하지만 프로그래밍 언어를 통해 0과 1을 직접 써넣을 필요가 없어졌습니다.

다음글에서는 프로그래밍 언어의 발달 과정을 알아 보겠습니다.

+ Recent posts