Dev/CS

[컴퓨터 구조] 데이터, 명령어

ragabys 2023. 9. 20. 10:32

데이터 단위

컴퓨터는 기본적으로 0과 1로만 이루어진 이진법 데이터를 이해하고, 이 때의 0 또는 1로 이루어진 데이터는 비트라고 한다.

비트 8개를 사용하여 표현하는 데이터는 바이트라 하며, 1KB는 1,000바이트, 1MB는 1,000,000 바이트를 의미한다.

1KB 가 1024byte라는 것은 잘못된 관습이며, 1024byte = 1KiB 라 표현하는 것이 옳은 표현 방식이다.

워드(Word)는 하나의 기계어 명령어나 연산을 통해 저장된 장치에서 레지스터로 데이터를 옮길 수 있는 데이터 단위이며,

현대 사회의 워드의 크기는 대부분 32비트(4바이트) 아니면 64비트(8바이트) 이다.

 

이진법

일상생활에서 많이 사용하는 0~9까지의 숫자로 수를 표현하는 방식은 십진법이라 하며,

0과 1만을 이용하여 숫자를 표현하는 방식은 이진법이라고 한다.

컴퓨터는 0과 1을 이용한 데이터만을 이해하기 때문에, 기존에 음수를 전달할 때처럼 마이너스 부호를 붙여 전달하는 방식은

컴퓨터가 이해할 수 없다.

-1011 이런 형태의 이진수는 있을수가 없다

그래서 이진법의 음수 데이터를 표현하기 위해 여러 방법들이 구상되었다.

  1. MSB 데이터를 활용한 음수 표현
  2. 1의 보수
  3. 2의 보수

순서대로 각각의 방식에 대해 자세히 확인해보자.

1) MSB 데이터를 활용한 음수 표현

우선 MSB 데이터가 무엇인지에 대해 짚고 넘어가자면, Most Significant Bit의 약자로, 직역하자면 가장 중요한 비트라는 뜻이다.

비트의 제일 최상위 비트를 의미하며 이 비트를 활용하여 MSB 비트가 0이면 양수,

1이면 음수의 형태를 나타내는 것으로 정하는 것이다.

Ex) 십진수 4 = 이진수 0100 / 십진수 -4 = 이진수 1100

단순히 맨 앞 비트를 0또는 1로 표시하여 부호를 나타내는 방식이기때문에 직관적이라는 장점이 있지만,

그에 비해  치명적인 단점이 많아 잘 쓰이지 않는 방식이다.

우선, 연산에 있어서 부정확한 결과값을 낸다는 단점이 있다.

1은 이진수로 0001 이고, -1은 이진수로 1001인데, 1+(-1)의 값 0이 이진수 합연산 결과 값인 1010(십진수 -2)과 다르기 때문이다.

또한 0000은 +0을, 1000은 -0을 나타내어 혼란을 가져오기도 한다.

MSB의 반대 격인 LSB도 있으며 Least Significant Bit의 약자로 직역하자면
가장 중요하지 않은 비트라는 뜻으로, 최하위 비트인 2^0 비트를 의미한다.

LSB는 네트워크 송수신 시 비트 에러 검사를 위한 체크섬, 해쉬 코드 등에
주로 사용되며 이 부분은 추후에 자세히 알아보도록 하자.

 

2) 1의 보수

이진수의 모든 비트를 NOT 연산하여 0은 1로, 1은 0으로 바꾸는 방식이다.

예를 들어 10진수 12는 이진법으로 표현하면 1100이 된다. -12는 비트를 반전시킨 0011이 된다.

밑에서 서술할 2의 보수 방식에 비해 편하다는 장점은 있지만, 연산과정이 불편하다.

예를 들어 5+(-1) 연산시, 이진수 5는 0101이고, 이진수 -1은 0001에 비트 반전시킨 1110이 된다.

5+(-1) 연산을 진행하면 10011이 되고, 원래 4비트 연산이었는데 5비트 결과값이 되었고 이런 경우 맨 앞 비트를 Carry 비트라 하며,

Carry 비트 1을 결과값 나머지 부분인 0011에 합 연산해주면 0100 = 10진수 4가 된다.

 

3) 2의 보수

1의 보수 방식에서 한 단계 더 진행하여, 1의 보수로 얻은 결과 값에 +1을 해준다.

위의 예시에서 10진수 -12는 0100이 된다. 이 때의 결과값을 10진수 5(이진수 0100)과 구분짓는 방법은

부호를 나타내는 Flag를 활용한다. 1의 보수와 또 다른 점은 연산 시 기존 비트의 크기를 초과해

Carry비트가 발생하는 경우, Carry비트의 값은 그냥 버려 연산이 비교적 간단하다.

하지만 2의 보수 또한 단점이 있는데, 2^n 형태, 즉 2의 제곱수는 2의 보수로 표현하더라도 양수와 음수의 표현값이 같다.

 

소스 코드

프로그래밍 언어는 크게 사람이 이해 할 수 있도록 작성된 고급 언어와, 저급 언어로 나뉜다.

고급 언어는 C,C++,Python 등이 해당되고 저급 언어는 기계어, 어셈블리어가 해당된다.

 

고급 언어를 저급 언어로 변환하는 방식에는 컴파일러를 이용한 방식과, 인터프리터를 이용한 방식이 있다.

컴파일 언어는 컴파일러에 의해 소스 코드 전체가 저급 언어로 변환된 후 실행하며,

소스 코드 중간에 에러 발생 시 컴파일이 불가능하며 컴파일러에 변환된 코드는 목적 코드라고 불린다.

인터프리터 언어는 인터프리터에 의해 소스 코드를 한 줄 단위로 읽어 변환하며,

줄 단위의 실행이 이뤄지기 때문에 컴파일 언어에 비해 실행시간이 짧다.

 

컴파일 언어와 인터프리터 언어는 겉보기엔 명확히 구분지어질 것 같지만,

Python과 Java의 경우 인터프리터와 컴파일의 과정을 둘 다 하는 언어이다.

Java의 경우 사람이 작성한 자바 코드를 JVM이 이해할 수 있는 자바 바이트 코드로 자바 컴파일러가 컴파일해주고, 

JVM은 자바 바이트 코드를 자바 인터프리터를 이용해 한 줄씩 목적 코드로 변환한다.

 

목적 코드가 저장된 파일은 목적 파일이라 불리며, 컴파일을 하고 난뒤 .obj 형태로 저장된다.

Object File Format

목적 파일은 목적 파일 헤더, 텍스트 세그먼트, 데이터 세그먼트,

심볼 테이블, 재할당 정보,  디버깅 정보를 담고 있다.

헤더는 파일 조각들의 크기와 위치를 나타낸다.

텍스트 세그먼트에는 코드 데이터가 들어가고,

데이터 세그먼트에는 전역 변수 및

static 변수의 값들이 저장된다.

 

심볼은 변수, 함수, 클래스 등의 식별자(identifier)를 말하며, 심볼 테이블은 이러한 식별자와 관련된 정보를 기록하고 유지한다. 

재할당 정보는 심볼 정보와 PC의 상대 주소를 매칭하는 매칭 테이블 정보이다.

 

목적 파일은 컴파일되기 전 원래 코드가 참조하던 모든 코드들의 목적 파일들과 링킹(linking)의 과정을 거쳐야 하며,

링킹의 과정을 거친 뒤 비로소 알려진 실행파일인 .exe 형태로 저장이 이뤄진다.

링킹의 방식으로는 정적 링킹(Static linking)과 동적 링킹(Dynamic linking)이 있다.

우선 정적 링킹은, 링킹 과정에서 라이브러리를 포함하여 .exe 실행파일을 형성하는 것을 의미한다.

하지만 실행과정에서 필요한 라이브러리(Ex - Java의 String Class file, C++의 cin, cout Class 라이브러리)를

전부 포함하는 방식이기 때문에, 실행 파일의 크기가 커지고 실행 파일을 여러 곳에서 사용할때마다 라이브러리 파일이

메모리에 로딩이 되기 때문에 비효율적이다.

정적 링킹의 단점을 보완하기 위해 나온 것이 동적 링킹이다. 기존의 정적 링킹 방식에서 라이브러리가 메모리에 중복으로

로딩되는 것을 막기 위해 동적 링크 라이브러리(DLL)을 사용하는데, 실행파일에 필요한 라이브러리를 메모리에 올려둔 뒤

실행 중 필요할때마다 미리 메모리에 올려둔 라이브러리를 참조하는 식으로 링킹하는 방식이다.

Windows 환경에는 .dll 형식으로, Linux와 Unix 환경에서는 shared library로 불리기 때문에 .so 또는 .sa 형태의 파일로 저장된다.

하지만 동적 링킹 또한 단점은 존재하는데, 동적 링킹을 위한 코드를 추가로 작성해야한다는 것과

라이브러리 참조를 하는 과정에서 메모리 내 동적 링크 라이브러리 주소를 찾아가야하기 때문에 오버헤드가 발생한다.

그런 이유로 성능 상으로는 정적링킹이 좋고, 메모리 관리 차원 상으로는 동적링킹이 좋기 때문에 현재는 둘 다 사용한다.

 

명령어의 구조

컴퓨터가 수행하는 명령어는 연산 코드와 오퍼랜드로 구성되어 있으며,

연산 코드에는 컴퓨터가 수행할 연산 정보를 담고 있으며 연산자로도 불린다.

오퍼랜드는 연산에 사용할 데이터 혹은 연산에 사용할 데이터 위치를 저장하고 있으며 피연산자로도 불린다.

이 때 오퍼랜드가 연산에 필요한 데이터를 처리하는 방식에 따라 여러 주소 지정방식이 존재한다.

  1. 묵시적 주소 지정 방식
  2. 즉시 주소 지정 방식
  3. 직접 주소 지정 방식
  4. 간접 주소 지정 방식
  5. 레지스터 주소 지정 방식
  6. 레지스터 간접 주소 지정 방식
  7. 상대 주소 지정 방식
  8. 인덱스 주소 지정 방식

순서대로 간략히 확인해보자.

1) 묵시적 주소 지정 방식

오퍼랜드에 주소 정보를 저장하지 않는 방식으로, 묵시적으로 데이터 저장 위치를 지정한다. 그로 인해 명령어의 길이가

짧아진다는 장점이 있지만, 사용할 수 있는 명령어 종류가 제한된다는 단점이 있다.

2) 즉시 주소 지정 방식

가장 간단한 주소 지정 방식이다. 오퍼랜드에 피연산 데이터를 직접 넣는 방식으로, 담을 수 있는 데이터의 크기는 작아지지만

주소 접근 과정이 없기 때문에 가장 빠르다.

3) 직접 주소 지정 방식

오퍼랜드에 피연산 데이터가 담긴 메모리 주소를 저장해두는 방식이다.즉시 주소 지정 방식에 비해

담을 수 있는 데이터의 크기 약간 증가한다. 

4) 간접 주소 지정 방식

오퍼랜드에 저장하는 주소에 있는 값이 메모리 내에 주소이며,

해당 주소는 메모리 내에 피연산자가 저장된 위치를 가리키는 값이다.직접 주소 지정 방식에 비해 오퍼랜드 내에 적을 수 있는 메모리 범위가 넓어졌지만,

접근을 두번을 거쳐하므로 소요 시간이 늘어난다.

5) 레지스터 주소 지정 방식

레지스터에 피연산자를 저장하고 해당 레지스터 주소를 오퍼랜드에 저장하는 방식으로, 메모리에 접근하는 직접 주소

지정 방식에 비해 속도가 빠르다.

6) 레지스터 간접 주소 지정 방식

피연사자가 저장된 메모리의 주소를 레지스터에 저장하고, 해당 레지스터의 주소를 오퍼랜드에 저장하는 방식으로

메모리에 접근하는 횟수가 총 한 번이기 때문에, 간접 주소 지정 방식에 비해 속도가 빠르다.

7) 상대 주소 지정 방식

유효주소를 계산하기 위해 특정 레지스터의 내용에 명령어 주소필드 값을 더하는 방식으로

주로 레지스터로 PC를 사용하고 적은 비트수로 표현할 수 있기 때문에 명령어 구성이 간결하다.

8) 인덱스 주소 지정 방식

인덱스 주소 지정 방식은 유효 주소를 계산하기 위해 인덱스를 사용하는 방식이다.

유효주소는 명령어 오퍼랜드와 인덱스 레지스터의 내용를 더한 값이다.

인덱스 레지스터는 프로그램 실행 중 피연산자 주소를 가리키는데 사용되는 프로세서 레지스터이며,

문자열과 배열 단계별로 접근하는 데 유용하며, 루프 반복과 카운터를 유지하는 데도 유용하다.

 

 

참고서적

혼자 공부하는 컴퓨터 구조+운영체제(강민철 저, 한빛미디어)