비전공자를 위한: 컴퓨터에서 프로그램이 돌아가는 원리

우현민·2021년 3월 22일
1

최근 코딩을 해 본 적 있는 비전공자 친구에게 다음과 같은 질문을 받았다.

코딩으로 툴을 만들고 명령어를 만들면, 결국 코딩으로 코딩을 하는 거고, 그렇게 무한 코딩으로 소급하면 최초에는 뭐가 있지?

어떻게든 설명을 해 보려 했는데 깔끔하게 설명되지 않아, 나름대로의 생각을 정리해보려 한다.

사실 결론부터 말하자면 "코딩으로 코딩한다, 무한 코딩으로 소급한다"라는 말은 틀린 말이다. 이를 이해하기 위해서는 프로그램이 무엇인지 이해해야 한다.

개요

프로그램이란

프로그램은 다른 말로 실행 파일이라고 한다. 당연한 소리긴 한데 실행할 수 있는 파일이라는 뜻이다. 컴퓨터에 있는 파일들을 생각해 보자.

이건 pdf파일이다. 알다시피 프로그램이 아니다.

이건 Microsoft Excel이다. 여러분이 잘 아는 응용 프로그램 중 하나이다. 코드가 실행되는 실행 파일, 응용 프로그램에 쓰일 이미지 assets, 그리고 기타 외부 라이브러리들이 다 같이 여기에 저장된다.

이건 실제 프로그램이다. MacOSX 기준이라 확장자가 없는데, Windows에 있는 프로그램들은 일반적으로 .exe 확장자를 가진다. 이게 진짜 메모리에 올라가서 실행되는 프로그램이다.

사진은 아까 그 Excel의 실제 프로그램인데,

  1. 윈도우 유저라면 가장 간단한 예시로 C://Windows/explorer.exe를 한번 보면 되겠다 (걔가 바로 파일 탐색기이다. 혹시 다른 프로그램들도 보고 싶다면 .exe로 된 것들 아무거나 찾아 봐라.)
  2. 맥 유저라면 응용 프로그램 폴더로 가서, 프로그램을 우클릭하고 패키지 내용 보기를 해서, Contents/MacOS로 들어가 보면 뭐가 있을 것이다.

자, 이제 이 실제 프로그램을 메모장을 이용해 열어 보자.

정확히 말하면 메모장 말고 sublime text 3라는 메모장 비스무리한 걸로 열긴 했는데 대충 넘어가 주길 바란다

무려 657만 5956줄짜리 알 수 없는 숫자와 영어 조합이 보인다. 이게 바로 프로그램의 실체이다. (윈도우에서 열어 봐도 이와 비슷하게 나온다: 앞서 말한 explorer.exe는 Windows 10 Home 기준 294047줄이더라. 사진은 귀찮아서 첨부 안 했는데 궁금하면 직접 해 보시길)

기계어와 컴퓨터

기계어

위에서 본 게 바로 여러분이 수도 없이 들었던 "기계어"가 맞다. 0과 1로만 이루어진.

0과 1 말고도 알파벳도 있던데요?

아니 기계어 맞다. 이건 조금 논제에서 벗어난 이야기이긴 한데, 원래 프로그램은 0과 1로만 이루어져 있다. 하지만 실제로 프로그램을 0과 1로만 쓴다면, 용량의 낭비가 너무 커지고 읽기도 힘들어진다.

이에 따라 사람들은 획기적인 방법을 고안해내는데, 바로 16진수의 사용이다.

0101000101001100 // 2  진수
514C             // 16 진수

같은 값인데도, 16진수를 사용하니 무려 4배나 짧고, 읽기도 편하고, 컴퓨터에게도 좋다. (2진수와 16진수 사이의 변환은 아주 쉽다)

4배라고 하니 별로 효율적이지 않아 보일 수 있겠으나 여러분이 128기가짜리 usb 살 거 32기가짜리 usb만 사도 되게 해 주는 아주 획기적인 효율이다.

아무튼 결론은 저게 16진수로 읽어서 저렇게 보이는거지 2진수로 읽으면 010101로만 되어 있다.


아무튼 본론으로 돌아가, 왜 0과 1로만 이루어져 있을까? 그건 컴퓨터가 0과 1밖에 알아먹지 못하기 때문이다. 전기가 통하면 1, 통하지 않으면 0. 이 두 가지 상황을 트랜지스터가 표현할 수 있다. 사람들은 이걸 이용해서 컴퓨터가 무언가를 하도록 만들었다.

이때 당연히 (0 또는 1) 이 정보 하나만 가지고는 할 수 있는 행동에 한계가 있다. 때문에 일반적으로 이들은 32개, 또는 64개가 한 군데 묶여 함께 행동한다. (32일지 64일지는 CPU마다 정해져 있다.) 가령 intel x86 CPU에서 다음 조합은 %rsp 레지스터에서 16을 빼라는 이야기로, 64개가 한 군데 묶여 행동하는 예시이다.

0000000000000000000000000000000000000000000000000001000100111101

앞에서 말한 것처럼 16진수를 사용해 표현하면 그나마 좀더 간단해 보인다.

000000000000113d

프로그램을 설치할 때 어디서든 32-bit랑 64-bit 중에 고르라는 이야기를 본 적이 한 번쯤 있을 텐데, 위의 내용과 같은 내용이다. 0과 1이 32개씩 묶여 행동한다면 32bit CPU인 거고, 64개씩 묶여 행동한다면 64bit CPU인 거다. 요즘 컴퓨터는 거의 다 64bit CPU를 사용한다.

어셈블리

자, 그런데 정말로 사람들이 0과 1로 프로그램을 만들어야 할까? 아니 그건 선을 넘어도 너무 넘는다. 이에 똑똑한 사람들은 "어셈블리"라는 걸 고안해내었다. 이는 훨씬 인간 친화적이다.

가령 방금 그 코드를 보자.

000000000000113d

이걸 작성하는 건 상당한 에바이다. 대신 사람들은 이렇게 작성할 수 있다.

sub $0x10, %rsp

훨씬 낫다. 이런 식으로 쓰면 "어셈블러"라는 친구가 자동으로 위의 기계어로 변환해 준다. 당연히 막 쓴다고 다 변환해주는 건 아니고, 어셈블리 코드를 짜는 데에도 정해진 규칙이 있다. 여기까지 가면 이야기가 너무 길어지니까, 더 궁금하다면 ISA (Instruction Set Architecture)를 읽어보면 될 것이다.

아무튼, 이제 사람들은 0과 1만 가지고 뻘짓을 하는 대신 어셈블리로 프로그램을 작성할 수 있게 되었다. 당연히 어셈블러는 어셈블리로 짜여 있을 리가 없고.. 안타깝게도 최초에는 0과 1만 이용해 구현했을 것이다. 사실 여기까지는 cpu에 내장된 프로그램인지라, 하드웨어에 프로그램이 박혀 있을 가능성이 더 높다.

프로그래밍 언어의 등장

언어가 등장한 이유

자, 이제 훌륭한 어셈블리가 생겼으니 우리는 쉽게 코드를 짤 수 있어!

어림도 없다. 아무리 인간 친화적이어도, 어셈블리로 아까 그 엑셀을 짜려면 그런 코드 657만 5956줄을 짜야 한다. 말도 안되는 소리이다.

즉 어셈블리 언어도 기계어보다는 훨씬 낫다만, 좀더 본질적으로 인간이 좋아할 만한 언어가 필요하다는 것이다.

즉 사람이 보기 좋은 방식으로 코드를 쓰면, 그걸 언어에 딸린 컴파일러가 알아서 어셈블리로 변환해 주고, 그걸 어셈블러가 기계어로 변환하여 컴퓨터에게 일을 시키는 것이다.

정말 많은 언어가 등장했고, 사람들이 느끼기에 별로다 싶은 언어는 점점 도태되어 사라져갔다. 이에 따라 요즘에는 C, JAVA, Python 등이 대표적인 언어로 남아 있다. 이중 C는 1960년대, 거의 프로그래밍 언어의 조상 급인데 아직도 쓰이니 정말 굉장히 잘 만들어진 친구이다.

2019년 기준 프로그래밍 언어 순위라는데.. 그냥 재미로 보고 넘기면 될 것 같다. 무슨 기준인지도 잘 모르겠고 그닥 궁금하지 않다.

언어의 예시: C

그럼 조상격인 C를 이용해 코드의 작동 방식을 설명해 보겠다.

누구나 해 봤을 법한 C언어 코드이다. 세상아 안녕!
내장된 컴파일러를 이용하여 이걸 기계어로 바꾸면 다음과 같다.

한 3000줄 언저리 된다. 그러니까 언어가 없었으면 3000줄 짤 거 언어가 있어서 5줄만에 다 짰다는 거다. Terminal(윈도우의 명령 프롬프트 비슷한 거)을 이용해 실행하면 다음과 같이 결과가 잘 나온다.

참고로 이건 C의 예시이고, C와 다르게 동작하는 언어들도 많다. (대표적으로 파이썬) 하지만 결국 "실행 프로그램은 0과 1로 이루어져 있다"는 내용은 언어와 무관하게 동일하다.

IDE

근데 내가 C언어로 공부할 땐 저런 거 본 적 없는데요?!

그렇다, 시니어라면 다 아는 내용이라 중간에 읽다가 껐을 테고, 이 글을 아직도 읽고 있는 주니어라면 아마 IDE만 가지고 코딩해봤을 것이다. 그리고 여태 코딩하며 다음과 같은 화면만 봤을 것이다.

앞에서 내가 실행 프로그램을 만들기 위해 사용한 친구는 gcc라는 C언어 컴파일러인데, C로 짠 코드를 기계어로 변환해주는 프로그램이다. 하지만 이를 쓰는 건, 공부할 때는 꽤나 불필요하다. 만약 주니어라면 어떻게 쓰는지 몰라서 문제고, 시니어라면 굳이 쓸 필요가 없어서 귀찮아서 문제다.

그래서 훌륭한 회사들은 코딩을 도와주는 프로그램을 개발해냈다. Jetbrains나 Microsoft같은 회사가 대표적이고, 대표적인 IDE는 Pycharm, IntelliJ IDEA, Codeblocks, Visual Studio Code 등이 있겠다. 위의 사진은 GDB Online Compiler이다.

이들은 유저가 코드를 작성하고 실행 버튼을 누르면 내장된 컴파일러로 알아서 컴파일하고 알아서 코드를 실행 파일로 바꾼 다음 알아서 콘솔 창에 결과를 출력해 주는 아주 편리한 도구이다.

결론

이 글의 흐름은 다음과 같다.
1. 원래 컴퓨터는 0이랑 1만 알아먹는다.
2. 사람들은 0과 1로 코드를 짤 수 있다.
3. 근데 너무 빡세서 어셈블리라는 언어를 만들고, "어셈블러"라는 프로그램을 만들었다.
4. 근데도 빡세서 여러 언어를 내놓기 시작했다.
5. 그래서 프로그래머가 언어로 코드를 짜면, 그걸 언어에 내장된 무언가를 이용해서 어셈블리어로 변환하고 기계어로 변환하여 실행 파일을 만들 수 있다.
6. 그걸 실행하는 거다.

그러니까 코딩은 무한 소급되는 것이 아니라, 아주 깔끔한 구조로 동작한다.

            컴파일러 
       (기계어로 되어 있음)
코드 ----------------------> 실행 프로그램 (기계어로 되어 있음)

그래서 최초의 프로그램을 돌리는 친구는 누구냐? 뭐 어디까지 내려가야 최초일지 모르겠으니 위에서부터 차근차근 내려가자면,

  1. 코딩해서 프로그램을 짠다.
  2. 내장된 실행프로그램이 그걸 기계어로 변환해 준다.
    • 앞서 말했듯 실행프로그램은 0과 1로만 되어 있다.
  3. 컴퓨터가 기계어를 실행한다.
    • 여기는 하드웨어의 영역이라 여기서 다루지는 않았다.
    • 혹시 여기도 궁금해서, 정말 맨 밑바닥까지 가서 진짜 최초를 보고 싶다면 이 시리즈을 참조하자.

이 정도가 되겠다.

profile
프론트엔드 개발자입니다

0개의 댓글