[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
- 이 외에도 다양한 방식들이 있음
컴파일 명령어 관찰 하기 (실습)
- https://godbolt.org/ 이 프로그램에서 실습을 진행함
- 소스코드가 어떻게 명령이 되는지를 확인 할 수 있는 좋은 프로그램이다.
- 코드도 다양하고, 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)
- 파이프라이닝 내의 의존 관계가 없는 명령어를 순차적으로 처리하지 않는 방법
- 명령어를 순차적으로만 실행해서는 성능 향상에 실패하는 경우가 있음
-> 순서를 바꾸어 실행해도 프로그램 실행에 없는 명령어 순서를 바꿈 : 파이프라이닝의 성능을 높이는 기능
- 비 순차적 명령어 처리( 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) 버스) 를 사용하여 다른 버스를 쓸 이유가 없음
'Study > CS 기초' 카테고리의 다른 글
운영체제 - 가상 메모리 관리 (3) | 2024.10.21 |
---|---|
운영체제 - 동기화와 교착상태 (2) | 2024.10.21 |
운영체제 - CPU 스케줄링 (2) | 2024.10.19 |
운영체제 - 프로세스와 스레드 (5) | 2024.10.19 |
운영체제 거시적으로 보기 (7) | 2024.10.19 |