컴퓨터에 관하여

May 24, 2024 (4mo ago)

컴퓨터란 무엇인가?

컴퓨터를 뭐라고 설명하는 것이 가장 적합한 설명일까요? 컴퓨터는 다양한 기능들은 모두 한 가지로 귀결됩니다. 데이터를 명령어를 통해 데이터를 다루고 컴퓨터를 동시키는 기능을 하며 명령어를 통하여 컴퓨터 내부에 있는 부품들을 실행시키고 원하는 결과를 이끌어 냅니다. 명령어에 대해서 알아보기 이전에 컴퓨터를 구성하는 구성품에 대해서 먼저 이야기 해보겠습니다.

1092와 1022를 더해라

위의 명령어를 기준으로 명령어는 어떻게 구성되어 있는지 파악해보겠습니다. 위의 명령어는 크게 더해라 | 1092와 | 1022를 으로 나눌 수 있습니다.

명령어는 어떻게 작성이 될까?

컴퓨터가 이해하는 언어는 0과 1로 구성되어 있는 이진법으로 작성된 수라는 것을 이제는 모두 아실거라고 생각해요. 우리가 흔히 일상에서 사용하는 수는 10진법이고 이외에도 16진법 8진법이 존재하는데 여기서는 2진법이외에 8진법, 16진법이 왜 등장하게 되었는지와 어째서 이것이 유용한지 다루어 보겠습니다.

이진수의 경우 0 1 10 11 100 101 110... 순서대로 1,2,3,4,5,6에 해당하는 수를 이와 같이 표현하죠. 그렇다면 이진수의 경우 음수를 표현하기 위해서는 어떻게 해야 할까요? 흔히 2의 보수를 구해 이 값을 음수로 사용합니다. 여기서 2의 보수는 2진수 내의 모든 0과 1의 순서를 반대로 하는 것입니다. 예를 들어 11의 음수는 01이 되는 것이죠. 하지만 이러한 방법만을 사용한다면 01 이 음수인지 01이 음수인지 구분이 가지 않습니다. 그래서 사용하는 것이 플래그 입니다.

1비트는 01 둘 중 하나로 표현되는 데이터를 이야기하고 1비트로 2개의 데이터를 표현할 수 있습니다.. 그렇다면 2비트는 몇개의 데이터를 표현할 수 있을까요? 00, 01, 10, 11 총 4가지의 데이터를 표현할 수 있습니다. 여기서 눈치채셨을 수도 있지만 n비트당 표현할 수 있는 데이터의 수는 2*n 가지 입니다. 이러한 비트이야기를 먼저 이야기한 이유는 앞서 설명했던 플래그는 해당 비트의 최상위 비트가 1일 경우 음수에 해당하기 때문입니다. 예를 들어 0000 00001 와 같이 8비트의 수가 있을 경우 해당 수의 음수는 1000 0001입니다! 이를 설명하기 위해서 먼저 비트를 이야기 했는데요 이어서 마저 컴퓨터가 이해하는 언어 이야기로 돌아가 보겠습니다.

이진법은 0과 1로 표현하는 것이었는데 16진법은 어떻게 수를 표현하는 것일까요? 16진법은 1~9 까지는 수로 표현하고 10~16의 수는 A~F의 알파벳으로 표현하는 방법입니다. 그리고 수학적으로 16진수와 10진수를 헷갈리지 않게끔 16진수로 표현할 경우 0x15라고 표현하여 십진수와 구분하여 사용합니다. 이는 단순히 이진법으로 표기되어 있는 수는 사람이 이해하기 어렵기 때문에 조금더 사람이 이해할 수 있게끔 등장한 표기 방법입니다.

더해라

더해라는 연산필드에 속한 연산코드에 해당합니다. 연산코드는 위와 같이 간단하게 표현하기는 했지만 이 외에도 아래와 같은 많은 기능을 수행합니다.

  1. 데이터 전송
  2. 산술/논리 연산
  3. 제어 흐름 변경
  4. 입출력 제어

이들 또한 대표적인 기능을 적었을 뿐입니다. 명령어에서 이러한 연산코드를 받은 후 명령어의 또 다른 구성요소인 오퍼랜드와 함께 주어진 명령어를 수행하게 됩니다!

1092와 1022를?

  • 0-주소 명령어 연산코드

  • 1-주소 명령어 연산 코드 | 오퍼랜드

  • 2-주소 명령어 연산 코드 | 오퍼랜드 | 오퍼랜드

  • 3-주소 명령어 연산 코드 | 오퍼랜드 | 오퍼랜드 | 오퍼랜드

오퍼랜드는 명령어의 오퍼랜드 필드에 메모리나 레지스터의 주소를 담는 경우가 많습니다. 그래서 오퍼랜드 필드를 주소 필드라고 부르기도 합니다. 하지만 앞서 설명한 말과 맥락이 일치하지 않습니다. 연산 코드와 함께 오퍼랜드를 통해 주어진 데이터를 통해 명령어를 수행한다고 말하지 않았습니까?

이는 틀린말이 아닙니다. 단지 방법의 차이일 뿐이에요! 데이터 자체를 오퍼랜드 필드에 저장하는 것이 아닌 데이터는 레지스트리나 메모리에 저장이 되어 있습니다. 오퍼랜드에는 이 데이터가 속한 주소에 대한 데이터를 연산 코드에 전달하여 해당 데이터를 바로 사용할 수 있게끔 만들어주는 역할을 합니다.

그렇다면 또 다른 의문점이 들거에요. 왜 데이터를 오퍼랜드에 저장하는 것이 아니라 메모리나 레지스트리에 저장하여 사용을 하는 것일까? 라는 의문 말이죠! 이는 명령어의 길이 때문입니다!

명령어의 길이가 n비트로 구성되어 있고 그 중 연산 코드 필드가 m 비트이고 1-주소 명령어이며 데이터를 오퍼랜드에 직접 저장하였다고 가정하였을 경우 사용할 수 있는 데이터의 크기는 n - m비트로 2^n-m가지의 데이터를 저장할 수 있습니다. 이는 굉장히 작은 크기에 해당합니다. 예를 들어 총 길이가 16비트인 명령어에서 연산 코드가 4비트이며 2-주소 명령어를 사용한다고 가정할 경우 각각의 오퍼랜드에 저장할 수 있는 데이터의 갯수는 2^6, 64개에 불과합니다.

이렇듯 많은 데이터를 사용할 수 없다는 단점을 해결하기 위해 사용하는 것이 주소 지정 방식입니다. 메모리의 주소를 사용한다면 사용가능한 데이터의 크기는 하나의 메모리 주소에 저장 가능한 크기만큼 커지고 레지스트리 또한 마찬가지 입니다. 이렇듯 연산의 대상이 되는 데이터가 저장된 위치를 유효 주소, effective address라고 합니다.

주소 지정 방식은 굉장히 유용한 방식이며 이러한 지정 방법도 다양한 종류로 구분이 됩니다. 하지만 여기서는 중요한 두 가지의 방법만 다루겠습니다.

  1. 레지스터 주소 지정 방식

데이터를 저장한 레지스터를 오퍼랜드에 직접 명시하는 방법입니다. 레지스터를 사용하는 이유가 무엇일까요? 일반적으로 CPU가 데이터에 접근하는 속도는 레지스트리를 이용하는 것이 메모리에 저장된 데이터를 읽어오는 속도보다 빠르기 때문입니다. 하지만 이렇게 주소를 직접 지정하는 방법은 표현할 수 있는 방법이 제한된다는 문제를 갖고 있습니다.

  1. 레지스터 간접 주소 지정 방식

이 방법은 연산 작업에 사용할 데이터를 메모리에 저장하고 레지스트리에는 메모리의 주소를 저장합니다. 그리고 명령어에는 유효 주소를 저장한 레스트리를 가르키는 주소를 저장합니다. 여기서는 명령어에 대해 다루기 위해서 주소 지정 방식에 대해서 자세하게 다루지 않겠습니다. ~에서 조금더 자세하게 설명하였으니 참조해주세요!

명령어 사이클과 인터럽트

우리가 수행하는 작업들은 명령어를 통해서 수행이 이루어지고 CPU는 이 명령을 수행하기 위해서 각각의 명령어를 특정 주기로 반복하여 실행합니다. 이러한 수행 방법을 우리는 명령어 사이클이라고 합니다. 명령어 사이클은 어떻게 이루어질 까요?

명령어 사이클

명렁어 사이클의 경우 위의 그림과 같이 인출 사이클과 명령어 사이클을 반복하며 이루어집니다. 메모리에서 다음에 실행해야할 데이터에 대한 정보를 인출해오고 해당 데이터를 제어 장치에 전달하여 제어장치는 해당 데이터를 실행 사이클을 이용하여 제어 신호를 만들고 해당 신호를 제어 버스로 전달하며 이루어지는 작업을 명령어 사이클이라고 합니다.

하지만 여기서

우리가 컴퓨터를 사용하면 모든 작업이 순서대로 이루어진다고 느껴지셨나요? 예를 들어 인터넷을 통해서 음악을 다운 받고 있었는데 갑자기 중요한 일이 생각나서 다른 작업을 수행하기 위해 워드를 이용하여 타이핑을 할 경우 해당 작업들은 어떻게 명령을 수행할 까요? 음악 다운로드 -> 메일 도착 -> 워드 실행 -> 타이핑 순으로 작업이 이루어질까요? 그렇지 않습니다! 우리가 느끼지 컴퓨터를 이용할 때 느끼지 못할 뿐 인터럽트가 계속해서 발생하고 있습니다.

인터럽트를 처리하는 것은 굉장히 중요한 사항입니다. 왜 일까요? 그냥 새롭게 생긴 작업을 명령어 사이클 마지막에 추가하면 안될까요? 인터럽트는 작업을 수행하는 중 CPU가 다른 작업들 보다 우선시 해야 작업이 발생 했을 경우 발생합니다. 그렇기 때문에 인터럽트를 먼저 처리해야 하므로 이러한 처리 방법이 굉장히 중요합니다. 인터럽트의 처리 방법은 크게 동기 인터럽트비동기 인터럽트로 구분이 됩니다.

동기 인터럽트는 CPU에 의해서 발생하는 인터럽트를 이야기 합니다. 이러한 인터럽트는 예외라는 용어를 사용해서 설명합니다. 이러한 이유는 동기 인터럽트는 CPU가 작업을 수행하던 중 예상치 못한 오류와 같은 예외적인 상황을 마주했을 경우 발생하기 때문입니다. 굉장히 자주 접한 상황이죠?

비동기 인터럽트는 주로 입출력 장치에 의해서 발생하는 인터럽트를 이야기 합니다. 비동기 인터럽트는 굉장히 빈번하게 이루어지는 인터럽트입니다. 비동기 인터럽트는 입출력 장치가 특정 작업을 수행하고 CPU 또한 다른 작업을 수행합니다. 그러던 중 입출력 장치가 작업을 끝냈을 경우 CPU에서 작업을 끝냈다는 신호를 전달하고 CPU는 이러한 신호를 전달 받았을 경우 해당 작업을 마무리하는 수행합니다.

비동기 인터럽트의 간단한 설명은 위와 같지만 사실 비동기 인터럽트는 이렇게 단순하게 처리할 수 있는 작업이 아닙니다. 이에 대해서는 블로그 포스트를 통해서 자세하게 다루어 보겠습니다.