본문 바로가기
Back-end

2023.08.20 TIL

by 신재권 2023. 8. 20.

Computer Structure

0과 1로 숫자를 표현하는 법

컴퓨터는 0과 1로 모든 정보를 표현하고, 0과 1로 표현된 정보만 이해할 수 있다.

정보 단위

컴퓨터가 이해하는 가장 작은 정보단위인 0, 1을 비트라고 한다. 즉, 비트로 2가지 정보를 표현할 수 있다.

즉 n개의 비트로 2^n 가지 정보를 표현할 수 있다.

바이트는 비트를 8개 묶은 단위이고, 1킬로바이트는 1000바이트이고, 1메가 바이트는 1000킬로바이트, 1기가 바이트는 1000메가 바이트, 1테라바이트는 1000기가 바이트 이다.

1024 단위는 KiB, MiB 등 이다. 1000 개 기준으로 나눈다.

  • 워드(Word) : 워드는 CPU가 한 번에 처리할 수 있는 데이터 크기를 의미한다. CPU가 한 번에 16비트를 처리할 수 있다면 1워드는 16비트가 되고, 한 번에 32비트를 처리하면 1워드는 32비트가 된다.
    • 이렇게 정의된 워드의 절반 크기를 하프 워드라고, 하고 1배의 크기를 풀 워드, 2배의 크기를 더블 워드라고 한다.
    • 현대 컴퓨터의 워드 크기는 대부분 32 비트, 64비트이다.

이진법

수학에서 0과 1만으로 모든 숫자를 표현하는 방법을 이진법이라 한다.

이진법으로 표현한 수를 이진수라고 한다.

이진수를 음수로 표현하는 방법은 2의 보수를 구해 음수로 간주하는 방법이 있다.

2의 보수의 사전적 의미는 ‘어떤 수를 그보다 큰 2^n에서 뺀 값’을 의미한다.

2의 보수를 쉽게 표현하자면, 모든 0과 1을 뒤집고, 거기에 1을 더하면 된다.

이진수만 봐서 양수, 음수 구분이 어렵기 때문에 플래그를 사용한다.

2의 보수 표현은 한계가 있다. n비트로 -2^n과 2^n 이라는 수를 동시에 표현할 수 없다.

십육진법

이진법은 0과1로만 모든 숫자를 표현하니, 숫자의 길이가 길어진다는 단점이 있다.

십육진법은 수가 15를 넘어가는 시점에 자리 올림을 하는 숫자 표현 방식이다.

0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F 로 표기한다.

십육진수는 한 글자당 열여섯 종류의 숫자를 표현할 수 있다. 십육진수를 이루는 숫자 하나를 이진수로 표현하려면 4비트가 필요하다. 즉, 각 숫자 자리를 2진수로 표현이 가능하여 변환이 쉽다.

문자 집합

컴퓨터가 인식하고 표현할 수 있는 문자의 모음을 문자 집합이라 한다.

컴퓨터는 문자 집합에 속해 있는 문자를 이해할 수 있고, 반대로 문자 집합에 속해 있지 않은 문자는 이해할 수 없다.

문자 집합에 속한다고 해도, 문자를 0과 1로 변환해야 컴퓨터가 이해할 수 있다. 이 변환 과정을 문자 인코딩이라 한다.

인코딩을 하면 0과 1로이루어진 결과값이 문자코드가 된다.

0과 1로 이루어진 문자 코드를 사람이 이해할 수 있는 문자로 변환하는 것을 문자 디코딩이라 한다.

아스키 코드

아스키는 영어 알파벳, 숫자, 특수문자 등을 포함합니다. 아스키 문자 집합에 속한 문자들은 각각 7비트로 표현되는데, 7비트로 표현할 수 있는 정보의 가짓수는 2^7 개로, 총 128개의 문자를 표현할 수 있다.

실제로는 8비트인데 1개의 비트는 패리티 비트로, 오류 검출을 위해 사용하는 비트이다.

자주 쓰는 건 아스키코드 48번 - 숫자 0, 65번 - A, 97 - a 이다. 이것만 알고 있음 모든 값을 구할 수 있기 때문이다.

아스키코드 표에는 제어 문자도 포함되어 있다.

하지만, 영어로만 이루어져있기 때문에, 자신들의 언어를 0과 1로 표현할 수 있는 고유한 문자 집합 및 인코딩 방식이 필요했다.

EUC-KR

EUC-KR은 한글 인코딩 방식이다.

한글 인코딩에는 완성형, 조합형 인코딩이 존재한다.

완성형 인코딩 방식은 초성, 중성, 종성의 조합으로 이루어진 완성된 하나의 글자에 고유한 코드를 부여하는 방식이다. 즉 가는 1, 나는 2 처럼이다.

조합형 인코딩 방식은 초상을 위한 비트열, 중성을 위한 비트열, 종성을 위한 비트열을 할당하여 그것들의 조합으로 하나의 글자 코드를 완성시키는 방식이다.

한글 한 글자에 2바이트 코드가 부여되어 EUC-KR로 인코딩된 한글 한 글자를 표현하려면 16비트가 필요하다. 16 비트는 4자리 십육진수로 표현이 가능하다.

유니코드

유니코드는 대부분의 나라의 문자, 특수문자, 화살표 등을 코드로 표현할수 있는 통일된 문자 집합이다.

아스키 코드나 EUC-KR은 글자에 부여된 값을 그대로 인코딩 값으로 사용하지만, 유니코드는 글자에 부여된 값 자체를 인코딩된 값으로 삼지 않고, 이 값을 다양한 방법으로 인코딩한다.

UTF-8, 16, 32는 유니코드 문자에 부여된 값을 인코딩 하는 방식이다.

UTF-8은 통상 1~4바이트까지의 인코딩 결과를 만들어 낸다.

소스코드와 명령어

컴퓨터는 명령어를 처리하는 기계이다. C/C++, Java 등 프로그래밍 언어로 만든 소스코드는 컴퓨터 내부에서 명령어로 변환된다.

고급 언어와 저급 언어

컴퓨터는 C/C++, Java 와 같은 프로그래밍 언어를 이해할 수 없다.

즉, 우리가 사용하는 프로그래밍 언어는 컴퓨터가 이해하는 언어가 아닌 사람이 이해하고 작성하기 쉽게 만들어진 언어이다.

컴퓨터는 이 언어를 이해하지 못한다. 이렇게 사람을 위한 언어를 고급 언어라고 한다.

컴퓨터가 직접 이해하고 실행할 수 있는 언어를 저급 언어라고 한다.

저급 언어는 명령어로 이루어져있다.

즉, 고급 언어로 작성된 소스코드가 실행되려면 저급 언어, 명령어로 변환되어야 한다.

저급 언어는 기계어와 어셈블리어 2종류가 있다.

기계어는 0과 1의 명령어 비트로 이루어진 언어이다.

어셈블리어는 0과 1로 표현된 명령어를 사람이 읽기 편한 형태로 번역한 언어이다.

컴파일 언어와 인터프리트 언어

개발자들이 고급 언어로 작성한 소스 코드는 결국 저급 언어로 변환되어 실행된다.

컴파일 방식과 인터프리트 방식이 있다.

컴파일 언어는 컴파일러에 의해 소스 코드 전체가 저급 언어로 변환되어 실행되는 고급 언어이다.

대표적으로 C 언어가 존재한다.

컴파일 언어로 작성된 소스 코드는 코드 전체가 저급 언어로 변환되는 과정을 거치는데 이것을 컴파일이라고 한다. 이 컴파일을 수행해 주는 도구를 컴파일러라고 한다.

컴파일러는 개발자가 작성한 소스 코드 전체를 쭉 훑어보며 소스 코드에 문법적인 오류는 없는지, 실행 가능한 코드인지, 실행하는데 불필요한 코드는 없는지 등을 따지며 소스 코드를 처음부터 끝까지 저급언어로 컴파일 한다.

만약 컴파일러가 소스 코드 내에서 오류를 하나라도 발견하면 해당 소스 코드는 컴파일에 실패한다.

컴파일이 성공하면, 개발자가 작성한 코드는 컴퓨터가 이해할 수 있는 저급 언어로 변환되는데, 이를 목적 코드라고 한다.

인터프리터 언어는 인터프리터에 의해 소스 코드가 한 줄씩 실행되는 고급 언어이다.

대표적으로 Python이 있다.

컴파일 언어와 달리 인터프리터 언어는 소스 코드를 한 줄 씩 한 줄씩 차례로 실행한다. 그리고 소스 코드를 한 줄씩 저급 언어로 변환하여 실행해주는 도구를 인터프리터라고 한다.

인터프리터 언어는 컴퓨터와 대화하듯 소스 코드를 한 줄씩 실행하기 때문에 소스 코드 전체를 저급 언어로 변환하는 시간을 기다릴 필요가 없다.

소스 코드 내에 오류가 있으면 컴파일이 불가한 컴파일 언어와 달리, 인터프리터 언어는 소스 코드를 한 줄씩 실행하기 때문에 소스 코드 N번째 줄에 오류가 있어도 N-1 번째 줄까지는 올바르게 수행한다.

컴파일 언어는 컴파일을 통해 목적 코드라 불리는 저급 언어를 만들지만, 인터프리터는 소스 코드를 한 줄 씩 저급 언어로 해석해야 실행하기 때문에 컴파일 언어에 비해 느리다.

목적 파일 vs 실행 파일

이미자로 이루어진 파일을 이미지 파일이라 부르고, 텍스트로 이루어진 파일을 텍스트 파일이라 부른다.

목적 코드로 이루어진 파일을 목적 파일이라 부른다.

실행 코드로 이루어진 파일을 실행 파일이라 부른다. 윈도우의 exe 확장자를 가진 파일이 대표적인 실행 파일이다.

목적 코드가 실행 파일이 되기 위해선 링킹이라는 작업을 거쳐야 한다.

목적 코드가 여러 파일에 나눠져 있을 수 있기 때문에, 여러 파일을 묶는 과정을 링킹이라 한다.

링킹을 마치면 실행 파일이 나온다.

명령어 구조

기계어나 어셈블리어를 이루는 하나하나가 명령어이다.

연산 코드와 오퍼랜드

명령어는 ‘무엇을 대상으로, 어떤 작동을 수행하라’는 구조로 되어 있다.

명령어는 연산 코드와 오퍼랜드로 구성되어 있다.

연산 코드는 명령어가 수행할 연산이고, 연산에 사용할 데이터 또는 데이터가 저장된 위치를 오퍼랜드라고 한다.

연산 코드는 연산자, 피연산자라고도 부른다.

연산 코드가 담기는 영역을 연산 코드 필드라고 부르고, 오퍼랜드가 담기는 부분을 오퍼랜드 필드라고 부른다.

기계어와 어셈블리어 또한 명령어 이기 때문에 연산 코드와 오퍼랜드로 구성되어 있다.

오퍼랜드는 연산에 사용할 데이터 또는 연산에 사용할 데이터가 저장된 위치를 의미한다.

오퍼랜드 필드에는 숫자와 문자 등을 나타내는 데이터 또는 메모리나 레지스터 주소가 올 수있다.

오퍼랜드 필드에는 숫자나 문자 같이 연산에 사용할 데이터를 직접 명시하기보다는, 연산에 사용할 데이터가 저장된 위치, 메모리 주소나 레지스터 이름이 담긴다. 그래서 오퍼랜드 필드를 주소 필드라고도 부른다.

오퍼랜드는 명령어 안에 하나도 없을 수 있고, 한개만 있을 수도 있고, 여러개가 있을 수도 있다.

오퍼랜드가 하나도 없는 명령어를 0-주소 명령어, 하나인 명령어를 1-주소 명령어, 2-주소 명령어 등 갯수에 따라 다르게 부른다.

연산 코드는 종류가 매우 많지만 대표적으로 4개로 나눌 수 있다.

  1. 데이터 전송
    • MOVE : 데이터를 옮겨라
    • STORE : 메모리에 저장하라
    • LOAD(FETCH) : 메모리에서 CPU로 데이터를 가져와라
    • PUSH : 스택에 데이터를 저장하라
    • POP : 스택의 최상단 데이터를 가져와라
  2. 산술/논리 연산
    • ADD/SUBTRACT/MULTIPLY/DIVIDE : 덧셈/뺄셈/곱셈/나눗셈을 수행하라
    • INCREMENT/DECREMENT : 오퍼랜드에 1을 더하라/오퍼랜드에 1을 빼라
    • AND/OR/NOT : AND/OR/NOT 연산을 수행하라
    • COMPARE : 두개의 숫자 또는 TRUE/FALSE 값을 비교하라
  3. 제어 흐름 변경
    • JUMP : 특정 주소로 실행 순서를 옮겨라
    • CONDITIONAL JUMP : 조건에 부합될 때 특정 주소로 실행 순서를 옮겨라
    • HALT : 프로그램의 실행을 멈춰라
    • CALL : 되돌아올 주소를 저장한 채 특정 주소로 실행 순서를 옮겨라
    • RETURN : CALL을 호출할 때 저장했던 주소로 돌아가라
  4. 입출력 제어
    • READ(INPUT) : 특정 입출력 장치로부터 데이터를 읽어라
    • WRITE(OUTPUT) : 특정 입출력 장치로 데이터를 써라
    • START IO : 입출력 장치를 시작하라
    • TEST IO : 입출력 장치의 상태를 확인하라

주소 지정 방식

명령어의 오퍼랜드 필드에 메모리나 레지스터의 주소를 담는 경우가 많다. 그래서 오퍼랜드 필드를 주소 필드라고 부르기도 한다.

명령어 내부에 오퍼랜드 영역은 오퍼랜드 갯수에 따라 달라지는데, 많을 수록 각 오퍼랜드 별 영역이 좁아진다.

그래서 오퍼랜드 영역에 직접 값을 저장하는 것보다 주소 필드를 저장하는 것이 효율적이다.

주소 필드 공간이 가리키는 메모리만큼 더 효율적으로 사용할 수 있기 때문이다.

레지스터의 주소를 저장해도, 레지스터가 저장할 수 있는 만큼 공간이 더 커진다.

연산 코드에 사용할 데이터가 저장된 위치, 즉 연산의 대상이 되는 데이터가 저장된 위치를 유효 주소라고 한다.

오퍼랜드 필드에 데이터가 저장된 위치를 명시할 때 연산에 사용할 데이터 위치를 찾는 방법을 주소 지정 방식이라 한다.

주소 지정 방식은 유효 주소를 찾는 방법이다.

  • 즉시 주소 지정 방식 : 연산에 사용할 데이터를 오퍼랜드 필드에 직접 명시하는 방식, 메모리나 레지스터로부터 데이터를 찾는 과정이 없기 때문에 빠르다.
  • 직접 주소 지정 방식 : 오퍼랜드 필드에 유효주소를 직접적으로 명시하는 방식
  • 간접 주소 방식 : 유효 주소의 주소를 오퍼랜드 필드에 명시
  • 레지스터 주소 지정 방식 : 연산에 사용할 데이터를 저장한 레지스터의 주소를 명시
  • 레지스터 간접 주소 지정 방식 : 연산에 사용할 데이터를 메모리에 저장하고, 그 주소를 레지스터에 저장하고, 레지스터의 주소를 오퍼랜드 필드에 명시하는 방법

즉, 연산에 사용할 데이터를 찾는 방법을 주소 지정 방식이라 하고, 연산에 사용할 데이터가 저장된 위치를 유효 주소라고 한다.

스택

스택이란 한 족 끝이 막혀있는 저장공간이다. 후입선출 구조이다.

새로운 데이터를 저장하는 명령어가 PUSH, 꺼내는 명령어가 POP 이다.

스택과 달리 양쪽이 뚫려있는 저장공간이다. 선입선출 구조이다.

한쪽으로 데이터를 저장하고, 다른 한쪽으로는 저장한 순서대로 데이터를 빼낸다.

'Back-end' 카테고리의 다른 글

2023.08.21 TIL  (0) 2023.08.21
2023.08.20 TIL  (0) 2023.08.21
2023.08.19 TIL  (0) 2023.08.19
static lazy 로딩  (0) 2023.08.19
정적 vs 동적 in Java  (0) 2023.08.18