Study/CS 기초

컴퓨터 구조 전체 보기

김 도경 2024. 10. 15. 18:03

[2024.10.14~15] 필수 온라인 강의 Part1 컴퓨터 구조 CH01~CH06

오리엔테이션

 

컴퓨터 공학을 공부하는 이유 : 성능&용량&비용 때문에 함 < 모든 프로그램은 컴퓨터 부품이 실행한다

컴퓨터 구조를 배운다고 하면 배우는 것

1. 프로그램을 이루는 정보(0과 1로 이루어짐)

    - 명령어 : 컴퓨터를 동작시키는 실질적인 정보

    - 데이터 : 명령어의 대상(재료)

 

2. 컴퓨터의 핵심 부품들


    - CPU : 명령어 해석, 실행(execute) 담당하는 장치 : 인출(fetcch)

                - 레지스터, ALU, 제어장치(제어신호, 명령어 해석)

    - 메인 메모리 & 캐시 메모리 : 실행 중인(프로세스) 프로그램 저장하는 장치 (전원이 꺼지면 삭제) : 휘발성  저장장치
                - 메인 메모리에는 주소라는 게 있음.

    - 보조 기억장치 : 보관할 프로그램 저장하는 장치 (전원이 꺼져도 저장 유지) : 비휘발성 저장장치

    - 입출력 장치 : 컴퓨터 내외부와 정보를 주고받는 장치

        + 메인 보드(마더보드) : 핵심 부품을 연결할 기판

        + (시스템) 버스 : 부품 간 정보를 주고받을 통로

 

명령어
소스코드에서 명령어로

컴퓨터는 명령어를 이해한다.

소스코드는 실행되기 전 명령어(+데이터)로 변환되어 실행한다 : 소스코드는 실행되기 전에 명령어/데이터로 변환!!!!

 

소스코드 : 사람(개발자)가 이해하기 편한 언어 = 고급언어

명령어와 데이터 : 컴퓨터가 이해하기 편한 언어 = 저급 언어

 

즉, 고급 언어로 작성된 소스 코드는 내부적으로 저급 언어로 구성된 명령어와 데이터로 변환된다

 

저급 언어의 두 종류

  • 기계어 (machine code) : 컴퓨터가 직접 이해하는 언어 
  • 어셈블리어 (assembly language) : 사람이 읽기 조금이라도 편하게 읽는 언어

고급 언어에서 저급 언어로 변환되는 대표적 방식

  • 컴파일
     - 소스 코드 전체가 컴파일러*에 의해 검사, 목적 코드로 변환
     - C/C++, Rust
     - 컴파일러 종류 : gcc, clang, Visual Studio, .....
  • 인터프리트
     - 소스 코드 한 줄씩 인터프리터에 의해 검사, 목적 코드로 변환
     - Python, JavaScript

오걔념 주의

  • 소스 코드가 저급 언어로 변환되는 대표적인 방식일 뿐 컴파일 방식과 인터프리트 방식은 칼로 자르듯 구분되는 개념은 아님
  • 컴파일 언어의 특성과 인터프리트 언어의 특성을 모두 갖춘 언어 : Java
  • 이 외에도 다양한 방식들이 있음
컴파일 명령어 관찰 하기 (실습)

- 소스코드가 어떻게 명령이 되는지를 확인 할 수 있는 좋은 프로그램이다.

- 코드도 다양하고, CPU도 종류별로 다 바꿔줄수있음

 

- 한 줄 한줄이 모두 명령어이다.

명령어의 구조

 

명령어 : 컴퓨터를 동작시키는 실질적인 정보

- 무엇을 대상(명령의 대상)으로 무엇을 수행(명령의 동작)해라

 

명령의 동작 / 명령의 수행

  • 오퍼랜드로 연산 코드를 수행하라
        - 오퍼랜드(operand) : 명령어를 수행할 대상 : 피연산자
                                           - 대상(데이터)이 직접 명시되기도 하고, 대상의 위치*가 명시되기도 함
  • 오퍼랜드가 개수가 정해져있지 않음

- 오퍼랜드에 따라서 얼마나 복잡한지에 따라서 개수가 달라질 수는 있다.

 

    - 연산 코드(op-code) 오퍼랜드로 수행할 동작

       - 대표적인 명령어 연산코드는 정해져있다.

 

  • 대표적인 연산코드

 

주소 지정

 

오퍼랜드(operand) : 명령어를 수행할 대상 : 대상(데이터)이 직접 명시되기도 하고, 대상의 위치*가 명시되기도 함

-> 왜 위치가 명시됌? 명령어의 길이는 한정되어 있기 때문

주소지정 학습

- 유효 주소 : 연산 코드에 사용할 데이터가 저장된 위치, 즉 연산의 대상이 되는 데이터가 저장된 위치

 

주소지정

- 유효 주소를 찾는 방법

- CPU 마다 차이가 있다!

 

1. 즉시 주소 지정

   - 연산에 사용할 데이터를 오퍼랜드 필드에 직접 명시

   - 가장 빠른 주소 지정 + 데이터 크기에 제한

 

2. 직접 주소 지정  : cpu가 레지스터에 접근하는 속도보다 메모리에 접근속도가 휠씬 더 느림.

   - 오퍼랜드 필드에 유효 주소(연산에 사용될 데이터가 저장된 메모리 주소) 명시

   - 오퍼랜드 필드로 표현 가능한 메모리 주소 크기에 제한

 

3. 간접 주소 지정

   - 오퍼랜드 필드에 연산코드에 사용될 데이터를 가르키고 있는 유효 주소의 주소 명시

   - 유효 주소 크기에 제한은 없으나, 속도가 비교적 느림

 

4. 레지스터 주소 지정

   - 연산에 사용할 데이터를 저장한 레지스터를 오퍼랜드 필드에 직접 명시

   - 레지스터 접근은 메모리보다 빠르다!

 

5. 레지스터 간접 주소 지정

   -  연산에 사용할 데이터를 메모리에 저장하고, 그 주소(유효 주소)를 저장한 레지스터를 오퍼랜드 필드에 명시

   -  메모리 접근은 한 번 : 간접보다 메모리주소지정이 더 크다

 

데이터

 

이진수와 2의 보수

 

  • 컴퓨터는 0과 1만을 이해한다
    -  모든 양수를 0과 1로 표현하는 방법? : 이진수
    -  모든 음수를 0과 1로 표현하는 방법? : 2의 보수
    - 모든 소수를 0과 1로 표현하는 방법? : 부동 소수점
    - 모든 문자를 0과 1로 표현하는 방법? : 문자 집합 & 인코딩

  • 이진수(이진법)
    - 이진법: 0과 1로 모든 수를 표기하는 방법
    - 이진수: 0과 1만으로 표현된 수
    - 이진수로 표현할 때는, 10진수랑 헷갈릴수도 있어서 (2) 아래첨자를 사용하거나, 0b를 붙인다.
    - 1을 넘어가는 시점에 자리올림!
    - 단점 : 숫자가 너무 길어진다!

  • 십육진법(십육진수)
    - 십육진법: 1~9과 A~F로 모든 수를 표기하는 방법
            (A=10, B=11, C=12, D=13, E=14, F=15)
    - 이진수: 1~9과 A~F로 표현된 수
    - 15(F)을 넘어가는 시점에 자리올림

  • 2의 보수법 (2'd complement )
    - 0과 1만으로 음수를 표현하는 방법 중 하나
    - 어떤 수 n을 그보다 큰 2^n에서 뺀 값
                                : 11(2)의 보수 = 100(2)-11(2) = 01(2)
    - 모든 0과 1을 뒤집은 다음 1을 더해주면 됨
    - 0과 1만으로 음수를 표현하면 양수와 음수를 구분하기 어렵지 않을까?
           -  그래서 사용하는 CPU 내부의 정보, 플래그(flag)!
                     - 플래그: CPU가 명령어를 실행하는 과정에서 참고할 정보의 모음
                     - 음수 플래그가 세팅 되어 있을 경우: 음수  / 음수 플래그가 세팅 되어 있지 않을 경우: 양수
  • 부동소숫점(floating point)
    - 소숫점이 움직일 수 있다. <> 고정소숫점
    - 컴퓨터에서는 대부분 부동 소숫점이라, 부동소숫점만 이용

    - 컴퓨터 내부의 소수 표현 방식(부동 소수점 표현 방식)
            1.23123 (가수) X 10^2(지수)   
           - 이진수를 m X 2^n 꼴
           - 가수 부분은 1.XXX 꼴을 띄고 있음, XXX 부분(소수 부분)만 저장하면 됨

    - 이와 같은 표현의 문제 : 십진수 소수를 이진수로 표현할 때 십진수 소수와 이진수 소수 표현이 딱 맞아 떨어지지 않을 수 있다
               - 1/10이라는 분수 m X 10^n 꼴로 표현하면 딱 떨어진다
               - 1/10이라는 분수 m X 2^n 꼴로 표현하면 무한히 많은 가수가 필요하다

  • 문자 인코딩과 디코딩
    - 문자 집합(character set) : 표현 가능한 문자들의 집합
    - 문자 인코딩: 문자를 0과 1로 이루어진 문자 코드로 변환
    - 문자 디코딩: 0과 1로 이루어진 문자 코드를 문자로 변환

    - 아스키 문자 집합: 초창기 문자 집합, 알파벳 + 아라비아 숫자 + 일부 특수 문자 + 제어 문자

- 7bit, 2^7 : 128개의 표현으로 인코딩 할 수 있다.

- 대응이 곧 인코딩이다. 

- 유니코드 문자 집합 : 대부분 언어, 특수 문자, 이모티콘, 화살표 등 모두 가능
   - 특수문자, 모든 언어 가능(한국어 가능)
   - utf-8, utf-16 등등

- 유니코드 코드 포인트 : 유니코드 문자에 부여된 고유한 수
- https://home.unicode.org/ : 유니코드 홈페이지

- 유니코드 코드 포인트 : 유니코드 문자에 부여된 고유한 수
- 코드 포인트를 인코딩하는 방식에 따라 utf-8, utf-16, utf-32 등으로 나뉨
                   - 즉, 유니코드 문자 집합을 인코딩하는 다양한 방법들이 있음

- 인코딩 대상은 코드포인트이다. : https://github.com/kangtegong/fastcampus-cs/tree/main/arch

 

 

CPU
CPU 구성요소

 

CPU의 핵심 구성 요소 세 가지

  • ALU (산술논리연산장치) : 연산을 수행하는 장치 (계산을 담당하는 회로)
    - 레지스터로부터 피연산자(연산의 대상)을 받아들이고
    - 제어 장치로부터 제어 신호(연산할 작업)를 받아들인다
    - 연산의 결과를 레지스터, 플래그 레지스터에 저장한다
             - 플래그 래지스터 : 연산의 결과에 대한 부가 정보
  • 제어 장치: 명령어를 해석하고 제어 신호를 내보내는 장치
    - 명령어 레지스터 : 해석해야할 명령어

    - 클럭(Clock) 신호 : 부품을 일사분란하게 움직일 수 있게 하는 시간 단위
        - 부품이 움직이는 ‘박자’
        -  클럭 신호가 빠르게 반복된다면? : 빠르게 반복
        -  클럭 신호가 느리게 반복된다면? : 느리게 반복

  • 레지스터(들): 명령어 처리 전후로 값을 임시 저장하는 장치 : 가장 중요한 장치
    - 프로그램의 실행 전후로 값을 임시 저장하는 작은 저장장치
    - 레지스터에 어떤 값이 저장되는지만 관찰해도 프로그램의 저수준의 흐름을 볼 수 있다
    - CPU마다 레지스터의 이름, 크기, 종류가 다양하다
    1. 프로그램 카운터 : 메모리에서 가져올 명령어 주소 (메모리에서 읽어들일 주소)
           - 프로그램 카운터는 일반적으로 1씩 증가되며 메모리의 프로그램이 순차적으로 실행된다
           - 일반적이지 않은(특별한) 경우 : Jump 명령어가 발생, 인터럽트가 발생한 경우 등등 : 빈번하게 발생
    2. 명령어 레지스터 : 해석할 명령어 (메모리에서 읽어들인 주소)
    3. 메모리 주소 레지스터 : 메모리의 주소 (읽어들일 주소 값)
    4. 메모리 버퍼 레지스터 : 메모리와 주고받을 명령어와 데이터
    5. 플래그 레지스터 : 연산 결과에 대한 부가 정보 저장
    6. 범용 레지스터 : 범용적으로 사용 가능한 레지스터 (여러 개 있음)
    7. 스택 포인터 : 스택 주소 지정 방식에서 사용되는, ‘스택의 꼭대기’를 가리키는 레지스터
         - pop, stack
    8. 베이스 레지스터 : 변위 주소 지정 방식에서 사용되는, ‘떨어진 거리’를 가리키는 레지스터

  • 변위 주소 지정 방식 : 오퍼랜드 필드의 값을 변위 삼아, 특정 레지스터 값을 더해 유효 주소를 얻는 주소 지정
    - 상대 주소 지정 : 오퍼랜드 + 프로그램 카운터 == 유효 주소
    - 베이스 레지스터 주소지정 : 오퍼랜드 + 베이스 레지스터(기준 주소) == 유효 주소

  • CS107 x86-64 Reference Sheet
    - https://web.stanford.edu/class/cs107/resources/x86-64-reference.pdf
    - https://web.stanford.edu/class/archive/cs/cs107/cs107.1202/guide/x86-64.html

 

명령어 사이클과 인터럽트

 

CPU가 명령어를 순차적으로 처리하는 양상

  • 명령어 사이클: CPU가 명령어를 처리하는 정형화된 주기, 흐름
  • 인출 사이클 : 메모리에서 명령어를 가져오고(=인출하고)
  • 실행 사이클 : 가져온 명령어를 실행하고

  • 실제 연산에 사용될 데이터를 얻기 위해 한 번 더 메모리에 접근해야 할 경우가 있다
  • 명령어를 인출했더라도 바로 실행이 불가능하다면? (간접 주소 지정) : 간접 사이클 추가

 

인터럽트(interrupt) - exception, fault

  • 인터럽트(interrupt): 방해하다, 중단시키다
  • CPU의 정상적인 실행 흐름을 방해하는 신호
  • 인터럽트의 종류는 공식이 없음- 동기/비동기는 intel 공문
  • 동기 인터럽트 (= 예외, Exception)
           - 주로 CPU에 의해 발생
           - 명령어 처리 도중 비정상적인 상황에 마주했을 경우 발생
  • 비동기 인터럽트 (하드웨어 인터럽트)
           - 주로 입출력장치에 의해 발생
           - 세탁기 완료 알림, 전자레인지 조리 완료 알림과 같은 ‘알림’ 역할 수행
  • 하드웨어 인터럽트 처리 순서
    1. 입출력장치는 CPU에게 인터럽트 요청 신호를 보냄
    2. CPU는 실행 사이클 이후 인출 전 인터럽트 여부 확인
    3. CPU는 인터럽트 요청 확인 후, 인터럽트 플래그를 통해 인터럽트 수용 여부 확인
    4. 인터럽트가 가능하다면 지금까지의 작업 백업 : 메모리 내 스택 영역
    5. 인터럽트 벡터를 참고하여 인터럽트 서비스 루틴(인터럽트 핸들러 실행)
    6. 인터럽트 서비스 루틴 실행 후 백업한 작업 복구, 실행 재개
  • 인터럽트 요청 신호: CPU 작업을 방해하는 인터럽트에 대한 요청
  • 인터럽트 벡터: 인터럽트 서비스 루틴의 시작 주소를 포함하는 인터럽트 서비스 루틴의 식별 정보
  • 인터럽트 서비스 루틴: 인터럽트를 처리하는 프로그램

멀티 코어와 멀티 프로세서
  • 클럭
    - 컴퓨터 부품은 클럭 신호에 맞춰 일사 분란하게 움직인다
    - CPU는 클럭 신호에 따라 명령어 사이클에 맞춰 명령어들을 실행한다
    - 클럭 속도가 높은 CPU는 일반적으로 성능이 좋다
    - 클럭 속도 (Hz): 1초에 반복된 클럭의 횟수로 측정   ( 1GHz = 10^9Hz)
  • CPU 오버클럭킹(오버(Over) + 클럭(Clock))
    - 임의로 클럭 속도를 끌어올리는 기술
    - 부팅시 BIOS에서 설정 가능
  • 클럭과 발열의 관계
    - 그저 무지막지하게 클럭 수를 높이면 성능이 비례하여 상승할까? NO
                         : 클럭 수가 높아질 수록 발열 문제가 심각해지기 때문
  • 클럭 수를 높이는 방법 이외에 성능을 높일 수 있는 방법은?
    1. 코어(Core) 수 늘리기!  : 멀티 코어 프로세서
       - 코어: 명령어를 인출하고, 해석하고, 실행하는 CPU 내 부품
             -> 코어가 여러 개 있다면? 한 번에 여러 명령어 인출, 해석, 실행 가능
    2. 스레드 수를 늘리기! : 멀티 스레드 프로세서
        - ‘스레드’ 용어에 대한 혼동 바로잡기
                     - CPU의 스레드 : 하드웨어적 스레드: 하나의 코어가 동시에 처리하는 명령어 단위
                     - 소프트웨어 스레드: 하나의 프로그램을 독립적으로 실행하는 단위
                     - 멀티 스레드 : CPU 여러 개의 하드웨어적 스레드로 한 코어로 여러 명령어를 실행 가능한 CPU
         - 레지스터 세트 : 하나의 명령어를 실행하기 위해 꼭 필요한 레지스터 집합

     - 메모리 + 메모리에 저장된 프로그램 입장에서 하드웨어 스레드와 코어를 구분할 수 있을까? NO
               : 각 하드웨어를 마치 하나의 단일 스레드/코어 프로세서로 인식
     - 이런 점에서 하드웨어 스레드를 논리 프로세서라고 부르기도 함

명령어 병렬 처리

 

CPU의 성능을 높일 수 있는 설계 : 높은 클럭 수,  멀티 코어, 멀티 프로세서

-> 이 외의 방법 : 명령어 병렬 처리 기법

  • 명령어 파이프라이닝 (pipline+ing) : 공장의 생산라인처럼 돌아가는 것
     - 공장의 생산 라인과 같이 명령어가 처리되는 서로 다른 단계를 동시에 처리하는 기법
명령어 파이프라이닝 명령어 파이프라이닝을 안 한 경우

 

  • 명령어 파이프라이닝에 실패하는 시나리오 : 파이프라인 위험(pipeline hazard)
    1. 데이터 위험(data hazard) : 명령어 간 의존성에 의해 발생 겹쳐 실행하기 어려운 명령어도 있다!
        - 명령어 간의 의존성 : 명령어 A가 저장되어야 명령어 B가 실행가능한 경우
          ① 데이터가 쓰여진 직후 그 데이터를 읽어들이는 경우 (RAW, read after write) 
          ② 데이터를 쓴 직후 그 데이터에 새 내용을 쓰는 경우(WAW, write after write ) 
          ③ 데이터를 읽어들인 직후 그 데이터에 새 내용을 쓰는 경우 (WAR, write after read)

    2. 제어 위험(control hazard)
         - 프로그램 카운터의 갑작스러운 변화에 의해 발생 (분기 : 분기 예측(branch prediction)
                : jump의 경우

    3. 구조적 위험(structural hazard)
         - 서로 다른 명령어가 같은 자원을 사용하려 할 경우 발생
         - 같은 부분 사용, 같은 메모리 사용 등등

  • 파이프라이닝의 발전: 슈퍼스칼라 -> 최근 쓰는 파이프라이닝
    - 다수의 명령어 파이프라인을 두는 방식
    - 여러 명령어 동시 인출/해석/실행/저장이 가능한 CPU

- 속도가 2배 증가 X -> 늘어날수록 명령어 의존도가 늘어나기에 조금 다름

  • 파이프라이닝을 십분 활용하기 위한 CPU 구조
    - CISC : Complex Instruction Set Computer(CPU) (Intel x86 CPU)
                     - 복잡하고 다양한 기능의 명령어 제공
                     -  다양한 주소 지정 방식 제공
                     -  적은 명령어 수로 명령어 실행 가능 : 메모리를 절약을 할 수 있음
                     -  단점 : 하나의 명령어 실행에 일정하지 않은 클록 수 -> 명령어 파이프라이닝에 불리
                     -  단점 : 대부분의 명령어는 사용되지 않는다!
               : “CISC 명령어 중 20% 정도의 명령어가 전체 명령어의 80%를 차지” : 어차피 쓰인것만 쓰인다.

    - RISC : Reduced Instruction Set Computer (ARM CPU) : 최근 주목받기 시작
                   -  짧고 규격화된 명령어 -> 명령어 파이프라이닝에 유리
                   -  적은 수의 명령어 제공
                   -  메모리 접근 최소화 (레지스터 활용)
                   -  단점 : CISC에 비해 더 많은 명령어로 실행 -> 컴파일러의 역할이 중요

    - RISC 같은 CISC: 마이크로 명령어
    - CISC 같은 RISC: 최근 실제 RISC 명령어는 단순하지 않다

    • 비 순차적 명령어 처리( Out-of-Order-Execution)
      - 파이프라이닝 내의 의존 관계가 없는 명령어를 순차적으로 처리하지 않는 방법
      - 명령어를 순차적으로만 실행해서는 성능 향상에 실패하는 경우가 있음
         -> 순서를 바꾸어 실행해도 프로그램 실행에 없는 명령어 순서를 바꿈 : 파이프라이닝의 성능을 높이는 기능


메인메모리와 캐시 메모리

 

RAM & ROM

  • RAM (Random Access Memory) : 임의 접근 가능
     - "(메인) 메모리”라는 용어는 RAM을 지칭하는 경우가 많다
     - 휘발성 저장장치 
     - 많은 프로그램을 동시에, 빠르게 실행하는 데에 유리
     - RAM의 종류
         -  DRAM(Dynamic RAM): 시간이 지나면 점차 저장된 데이터가 사라지는 RAM
                  : 메인 메모리에서 주로 사용되는 RAM
         -   SRAM(Static RAM): 시간이 지나도 저장된 데이터가 사라지지 않는 RAM
                  :  캐시 메모리에서 주로 사용되는 RAM
         - SDRAM(Synchromous Dynamic RAM) : 클럭과 동기화된 DRAM
               : 클럭의 타이밍에 맞춰 CPU와 정보를 주고받을 수 있는 RAM
         - DDR SDRAM(Double Data Rate SDRAM): 대역폭을 넓혀 속도를 높인 DRAM
               : SDRAM (SDR;Single Data Rate RAM)
        -  DDR2 SDRAM: DDR SDRAM보다 대역폭 두 배 넓은 RAM
              :  DDR SDRAM보다 대역폭이 두 배 넓은 SDRAM
        -   DDR3 SDRAM : 대역폭을 넓혀 속도를 높인 RAM
              :  DDR2 SDRAM보다 대역폭이 두 배 넓은 SDRAM

  • ROM (Read Only Memory) : 읽기 전문
    - 비석과도 같은 저장 장치 ; 한번 저장하면 읽기만 가능
    - 냉장고, 전자제인지, 게임기, 텔레비젼과 같은 가전제품에서도 많이 사용
     - ROM의 종류
         -  Mask ROM : 가장 기본적인 형태의 ROM
                  : 제조 과정에서 저장할 내용을 미리 기록( (옛날) 냉장고, 전자레인지, 텔레비전, 게임기)
         -  PROM(Programmable ROM): 데이터를 한 번 새길 수 있는 ROM
                  :  한 번에 한해 사용자가 직접 원하는 데이터를 써넣을 수 있는 ROM
                      (비어 있는 PROM을 얻어 안에 원하는 내용을 한 번 새기기)
         -  EPROM (Erasable PROM): 지우고 다시 저장 가능한 PROM
                  : 자외선 혹은 전기를 이용해 지우기 가능
                  - 자외선을 이용해 데이터를 지울 수 있는 ROM: UVEPROM (Ultra-Violet PROM)
                  - 고전압의 전기 신호를 이용해 데이터를 지울 수 있는 ROM: EEPROM(Electrically Erasable PROM)
         - 플래시 메모리: EEPROM의 발전된, 저렴한 형태, 반도체 기반의 저장장치
                  :  메인 메모리 범주에 속한다기보다는 ‘범용성 넓은 저장장치’
                 - 사실상 대부분의 가전제품에서 활용
                      ( 보조기억장치로도 사용 (USB Memory, SD 카드, SSD) )
리틀 엔디안과 빅 엔디안
  • 메모리에 데이터를 밀어넣는 순서
       - 일반적으로 메모리는 바이트 단위로 저장
       - 하지만 CPU로부터 메모리가 받아들이는 데이터는 4 바이트(32 비트), 혹은 8 바이트 (64 비트) 워드(Word) 단위
       - 1 바이트씩 저장하는 메모리의 경우 : 4바이트 데이터는 네 개의 주소에 걸쳐 저장
         ( 1A2B 3C4D 는 1A, 2B, 3C, 4D로 나누어 4 개의 주소에 걸쳐 저장)
       - 1 바이트씩 저장하는 메모리의 경우 : 8바이트 데이터는 여덟 개의 주소에 걸쳐 저장
         ( 1A2B 3C4D 5A6B 7C8D는 1A, 2B, 3C, 4D, 5A, 6B, 7C, 8D로 8개의 주소에 걸쳐 나누어 저장)

  • 엔디안
       -  연속해서 저장해야 하는 바이트를 저장하는 순서
       - 나라마다 글을 읽는 순서가 다르듯, 메모리에 바이트를 밀어넣는 순서도 다르다
       - 리틀 엔디안, 빅 엔디안
  • 빅 엔디안
       -  낮은 번지 주소부터 상위 바이트부터 저장하는 방식 
       -  여기서 상위 바이트란 수를 이루는 가장 큰 값(MSB) : 가장 처음 접근해야하는 글자
           (e.g. 1A2B 3C4D 에서 최상위 바이트는 1A)
           (1A2B 3C4D 를 빅 엔디안 방식으로 저장 -> 낮은 주소번지부터 1A, 2B, 3C, 4D 순으로 저장)
       -  일상적으로 읽고 쓰는 숫자 체계를 읽는 순서와 동일하기 때문에 메모리 값을 직접 읽기 편리 (디버깅)
       -  빅 엔디안 방식으로 주소에 1A, 2D, 3C, 4D로 저장된 값은 그대로 1A2D3C4D 로 읽으면 그만
  • 리틀 엔디안
       -  낮은 번지 주소부터 하위 바이트부터 저장하는 방식
       -  여기서 하위 바이트란 수를 이루는 가장 작은 값(LSB)
           ( e.g. 1A2B 3C4D 에서 최하위 바이트는 4D)
           ( 1A2B 3C4D 를 리틀 엔디안 방식으로 저장 -> 낮은 주소번지부터 4D ,3C ,2B, 1A 순으로 저장)
       -  수치 계산(자리 올림 등)이 편리

  • 서로 다른 시스템 간에 데이터를 전송할 때에는 엔디안을 고려해야 한다
  • NUXI Problem: 엔디안을 고려하지 않아 ”UNIX”가 “NUXI” 꼴로 보인 문제
  • 오늘날 데이터 송수신 간에 사용자가 엔디안을 고려하지 않는 이유
                   : 네트워크 전송시 엔디안이 빅 엔디안으로 통일되었기 때문
주소공간 

  • 물리 주소: 실제 메모리의 하드웨서 상의 주소

  • 논리 주소: CPU와 실행 중인 프로그램이 사용하는 주소 (0번지부터 시작)
     - 모든 프로그램은 0번지부터 시작하는 각자의 논리 주소를 사용한다
     - CPU는 0번지부터 시작하는 각 프로그램의 논리 주소를 인출/해석/실행

  • CPU/프로그램이 사용하는 주소 체계(논리 주소)와 메모리가 사용하는 주소 체계(물리 주소)가 다르다면 어떻게 문제없이 부품 간 통신이 가능할까? : CPU와 메모리 사이의 주소 변환이 이루어지면 된다!
     - 논리 주소와 물리 주소 간의 변환을 담당하는 장치: MMU(Memory Management Unit)
        - MMU의 기본 동작
           - 베이스 레지스터를 활용한 주소 변환
           - 베이스 레지스터 = 기준 주소
           - 논리 주소 = 기준 주소로부터 떨어진 거리
  • 한계 레지스터도 MMU에 영향을 주는 레지스터 : 한계 레지스터는 프로그램의 크기가 담김
      - 그 한계를 넘으면 안 됨
저장장치 계층 구조와 캐시 메모리

 

  • 저장장치 계층구조
    - CPU와 멀어질수록 달라지는 저장장치의 특성
          - 레지스터 vs 메인 메모리
          - 메인 메모리 vs 보조기억장치
          - 보조기억장치 vs 클라우드 저장 장치
    저장장치 계층구조
          - CPU와 멀어질수록 달라지는 저장장치의 특성 ->빠른 접근 속도와 큰 용량이 양립하기 어려운 이유
                1. CPU와 가까운 저장 장치는 빠르고, 멀리 있는 저장장치는 느리다.
                2. 속도가 빠른 저장 장치는 용량이 작고, 가격이 비싸다

    - CPU가 아무리 빨리 정보를 처리해도 메모리가 발맞춰주지 않으면 말짱 헛수고!

  • 그래서 등장한 캐시 메모리!
         - CPU와 메모리 간의 속도 차를 극복하기 위해 탄생
         -  CPU와 메모리 사이에 위치, 레지스터보다 용량이 크고 메모리보다 빠른 SRAM 기반
         -  CPU에서 사용할 법한 정보를 미리 가져와 저장
         - 메모리가 대형 마트라면 캐시 메모리는 편의점

         -  여러 단계의 캐시 메모리
               - L1(Level 1) 캐시 메모리 CPU와 가깝고 용량 작음
               - L2(Level 2) 캐시 메모리
               - L3(Level 3) 캐시 메모리 CPU와 멀고 용량 큼

 

                - 분리형 캐시: L1D(데이터 저장) + L1I(명령어 저장)

 

  • 캐시 메모리에 데이터가 있을 때와 없을 때
       - 캐시 메모리는 CPU에서 사용할 법한 내용을 미리 가져와 저장한다
       - 캐시 메모리가 CPU가 요구하는 정보를 저장할 경우: 캐시 히트 (메모리 접근 X)
       - 캐시 메모리가 CPU가 요구하는 정보를 저장할 경우 : 캐시 미스 (메모리 접근)
       - 캐시 히트율: 캐시 히트 횟수 / (캐시 히트 횟수 + 캐시 미스 횟수)

  • 캐시 메모리는 어떤 데이터를 저장할까? -> CPU가 자주 사용할 법한 내용
       - 그렇다면 CPU가 자주 사용할 법한 내용은 어떻게 예측할까? -> 참조 지역성의 원리 (locality of reference)
           * 참고 지역성의 원리 : 경향성
                1. 시간 지역성: CPU는 최근에 접근했던 메모리 공간에 다시 접근하려는 경향이 있다.
                2. 공간 지역성: CPU는 접근한 메모리 공간 근처를 접근하려는 경향이 있다.

캐시 친화적 코드
  • 캐시 친화적 코드: 캐시 미스가 최소화되는 코드
       - 시간 지역성 / 공간 지역성을 준수하는 코드

  • 캐시 친화적인 코드
    - matrix 변수는 10,000 x 10,000 크기의 2차원 리스트로 초기화
    - 중첩된 for 루프를 사용하여 매트릭스의 각 요소를 순차적으로 접근
    - 각 요소 matrix[i][j]의 값을 1로 변경
    - 메모리 접근 패턴이 연속적이고 공간적 지역성(spatial locality)을 잘 활용하고 있기 때문에 캐쉬 친화적
# matrix라는 변수 생성
# 10000x10000 크기의 2차원 리스트(또는 배열) 저장
# 이 리스트의 모든 요소를 0으로 초기화
matrix = [[0] * 10000 for _ in range(10000)]

for i in range(10000):
    for j in range(10000):
        matrix[i][j] = 1

 

  • 캐시친화적이지 않은 코드
    - matrix 변수는 10,000 x 10,000 크기의 2차원 리스트로 초기화
    - for 루프에서 i가 고정된 상태에서 j를 먼저 순회하며, matrix[j][i] 값을 1로 변경
          ( **열(column)**을 먼저 순회한 후 **행(row)**을 순회하는 구조)
    - 메모리 접근이 불연속적으로 이루어지며, 캐시 메모리가 효과적으로 작동하지 않기 때문에 캐시 미스가 빈번하게 발생
# matrix라는 변수 생성
# 10000x10000 크기의 2차원 리스트(또는 배열) 저장
# 이 리스트의 모든 요소를 0으로 초기화
matrix = [[0] * 10000 for _ in range(10000)]

for i in range(10000):
    for j in range(10000):
        matrix[j][i] = 1

 

보조기억장치와 입출력 장치 
(주변 장치)
하드 디스크와 플래시 메모리

 

  • 대표적인 보조기억장치
    - 하드 디스크 드라이브 (하드 디스크, HDD)
    - 플래시 메모리 ( 솔리드 스테이트 드라이브(solid-state drive, SSD), USB 메모리, SD 카드)

  • 하드 디스크
     - LP, CD/DVD 플레이어와 유사
    - 구성 요소
           - 플래터: 하드 디스크 상에서 실질적으로 데이터가 저장되는 부분
           - 스핀들: 플래터를 회전시키는 부분
           - 헤드: 플래터의 데이터를 읽고 쓰는 부분
           - 디스크 암: 헤드를 옮기는 부분
    - 복수의 헤드와 플래터가 있는 경우가 많음
    - 플래터의 양면을 사용하는 경우가 많음
    - 하드 디스크의 데이터 단위
           -  트랙: 플래터 상의 동심원
           -  섹터: 트랙을 나눈 단위 (가장 작은 단위)
           -  실린더: 여러 개의 트랙을 모은 단위
           -  블록: 실제 입출력이 수행되는 단위 : 실제로 가장 작은 단위
    - 하드 디스크의 지연 시간
           -   탐색 시간: 헤드를 원하는 섹터까지 이동시키는 시간
           -   회전 지연: 원하는 섹터를 헤더까지 회전시키는 시간
           -   전송 시간: 데이터를 송수신하는 시간
  • 플래시 메모리 : 반도체 기반의 저장 장치
     - 매우 범용성 넓은 저장 장치 : 오늘날의 가전제품에 모두 들어있다고 생각.
     - NAND 플래시 메모리와 NOR 플래시 메모리가 있음
              - 일반적으로 사용되는 것은 NAND 플래시 메모리
     -  셀(cell): 플래시 메모리의 가장 작은 저장 단위
              -  한 셀에 몇 비트까지 저장이 가능한지에 따라 플래시 메모리의 종류가 나뉨
                    -  SLC 타입: 한 셀에 한 비트 저장 가능
                    -   MLC 타입: 한 셀에 두 비트 저장 가능
                    -   TLC 타입: 한 셀에 세 비트 저장 가능
                    -   QLC 타입: 한 셀에 네 비트 저장 가능
     -  플래시 메모리의 저장 단위
         셀 < 페이지(읽기와 쓰기 단위) < 블록(삭제단위) < 플레인 < 다이
RAID

 

  • 보조기억장치를 더 안전하게, 안정적으로 저장하는 방법
  • 데이터의 안전성, 높은 성능을 위해 여러 보조기억장치를 하나처럼 사용 가능한 기술
  • RAID 레벨
       - RAID를 구성하는 방법
       - RAID 0, RAID 1, RAID 2, RAID 3, RAID 4, RAID 5, RAID 6, RAID 10, RAID 50, ...

    - RAID 0 = 스트라이핑(분산하여 저장)
          - 데이터를 단순히 보조기억장치에 나누어 저장하는 구성 방식: 성능 개선 / 신뢰성은 감소
          - 마치 줄무늬처럼 저장된 데이터: 스트라입
          - 빠른 입출력 속도, 성능 개선 가능
          - 신뢰성 감소 : 1개가 고장나면 모든 게 쓸모 없어 질 수 있음

    - RAID 1 = 미러링(복사본을 만드는 방식)
          -  쓰기 성능의 감소, 저장 공간 감소, 신뢰성 증가(복구 용이)
    - RAID 4
         -  패리티 비트(parity bit)라는 오류 검출용 비트를 저장하는 장치를 따로 두는 방식
                   - 단, 패리티 비트를 저장한 디스크에 병목 현상이 증가
         -   RAID 1에 비해 적은 하드 디스크로도 신뢰성 증가 가능
          * 패리티 비트?
                  • 홀수 패리티: 전체 1의 개수가 홀수가 되도록 패리티 비트를 정하는 방식
                  • 짝수 패리티: 전체 1의 개수가 짝수가 되도록 패리티 비트를 정하는 방식
                  • 두 개 이상의 비트에 문제가 생길 경우 오류 검출 불가능 : 
                    본래 패리티 비트는 오류 검출용 비트일 뿐, 복구는 불가능, RAID에서는 어느 정도의 복구가 가능

    - RAID 5
         - 패리티 비트를 분산하여 저장하는 방식
         - RAID 4의 병목을 해소하는 방식

    - RAID 6
         -  패리티를 두 개 두는 방식
         -  RAID 4 혹은 5보다 더욱 신뢰성이 높아진 방식 (단, 쓰기 성능은 감소)

    - nested RAID
         - 여러 RAID 구성 방식을 합친 방식
         - RAID 10(RAID 1 + RAID 0), RAID 50 (RAID 5 + RAID 0)
입출력 기법

 

보조기억장치도 입출력장치의 일부로 볼 수 있다 (주변 장치)
그저 특별한 입출력장치일 뿐
보조기억장치의 입출력 과정은 타 입출력장치와 유사하다

 

입출력장치와 CPU는 어떻게 정보를 주고 받을까?

 - 입출력 장치는 CPU 입장에서 메모리, 레지스터에 비해 다루기 까다로움
        1. 매우 다양한 입출력장치의 종류 (모니터, 마이크, 키보드, 등등 모두 입출력장치임)
        2. CPU와 입출력장치 간의 전송률 차

  • 장치 컨트롤러( 입출력 제어기, 입출력 모듈)
             - CPU와 입출력장치 간의 통신 중개
             - 오류 검출
             - 데이터 버퍼링을 통한 전송률 차이 완화

  • 장치 드라이버
             -  장치 컨트롤러를 동작시키기 위한 특별한 프로그램
             -  운영체제의 일부로 포함되어 있는 경우가 많음
             -  입출력이란 CPU가 장치 컨트롤러와 정보를 주고받는 것  -> 그를 가능케 하는 프로그램 : 장치 드라이버

  • CPU와 장치 컨트롤러가 정보를 주고받는 방법
    1. 프로그램 입출력 : 명령어를 기반으로 입출력을 수행하는 방법
    2. 인터럽트 기반 입출력 : 인터럽트를 기반으로 입출력을 수행하는 방법
    3. DMA 기반 입출력 : DMA(직접 메모리에 접근) 를 기반으로 입출력을 수행하는 방법
            DMA (직접 메모리 접근)
                 -  입출력장치와 메모리가 CPU를 거치지 않고도 상호작용할 수 있는 입출력 방식
                                      * 입출력 작업 전달 (e.g. 메모리의 내용을 하드디스크에 백업)
                 -  직접 메모리에 접근 가능한 입출력 기능
                 - 메모리로부터 백업할 정보를 읽어오고, 이를 하드 디스크의 장치 컨트롤러에 내보내기
                 - 입출력 작업이 끝났다면 인터럽트를 통해 작업이 끝났음을 알림
                 -  DMA 컨트롤러가 필요
                 - 사이클 스틸링: CPU 입장에서는 시스템 버스를 사용할 사이클을 도둑맞음
                                - > 입출력 버스(PCI, PCI Express (PCIe) 버스) 를 사용하여 다른 버스를 쓸 이유가 없음