메모리 이해하기


주기억장치인 메모리에 대해 작성한 글입니다


주기억장치 관리

  • Main Memory Management

메모리

메모리 역사

  • 메모리 종류
    • Core memory : 반지 모양의 철심에 자석 물질을 바르고 전기를 흘러 파장 생성
    • 진공관 메모리
    • 트랜지스터 메모리
    • 집적회로 메모리: SRAM, DRAM
  • 메모리 용량
    • 1970년대: 8-bit PC, 64KB
    • 1980년: 16-bit IBM-PC, 640KB → 1MB → 4MB
    • 1990년: 수MB → 수십 MB
    • 2000년~현재: 수백 MB → 수 GB
  • 언제나 부족한 메모리
    • 메모리 관리가 issue. 메모리가 계속 커져도 메모리가 부족함(마치 우리의 연봉같이.. 계속….ㅎㅎㅎ)
    • 기계어/어셈블리어 → C언어 → 자바, 객체지향형 언어
    • 숫자 처리 → 문자 처리 → 멀티미디어 처리 → Big Data
  • 메모리 용량 증가 vs 프로그램 크기 증가
    • 언제나 부족한 메모리
  • 어떻게 메모리를 효과적으로 사용할 수 있을까?
    • 메모리 낭비 없애기
    • 가상 메모리 (virtual memory)

메모리 주소

  • 주소(Address) + 데이터(Data)
  • 입력 : 주소(address), 입출력 : 데이터
  • 프로그램을 메모리에 올리기
  • 프로그램 개발
    • 원천파일 (Source file): 고수준 언어 또는 어셈블리 언어, main.c
    • 목적파일 (Object file): 컴파일 또는 어셈블 결과, main.o
    • 실행파일 (Executable file): 링크 결과
  • 개발 도구
    • 컴파일러 compiler : Source file -> Object file
    • 어셈블러 assembler
    • 링커 linker : Object 파일과 라이브러리 연결
    • 로더 loader : 메인 메모리로 올라감
  • 프로그램 실행
    • code(기계어) + data(변수) + stack(함수 호출할 때 돌아오는 주소 저장, 지역변수 저장)
  • 실행파일을 메모리에 올리기
    • 메모리 몇 번지에?
      • OS가 없으면 우리가 고민해야 함. 다행히 OS의 로더가 고민해줌
    • 다중 프로그래밍 환경에서는?
  • MMU 사용
    • CPU와 메모리 사이에 존재
    • 재배치 레지스터 (Relocation register)
      • addresss translation
      • CPU가 만든 값에 relocation register에 있는 값을 주소에 더해서 실제 메모리 주소값을 도출. 주소로 갈 때 베이즈 레지스터에 대해 다시 바인딩
  • 주소 구분
    • 논리주소 (logical address) vs 물리주소 (physical address)

메모리 낭비 방지

  • Dynamic Loading
  • Dynamic Linking
  • Swapping

  • (1) 동적 적재 (Dynamic Loading)
    • 현재 시스템은 Dynamic Loading
    • 프로그램 실행에 반드시 필요한 루틴/데이터만 적재
      • 모든 루틴(routine)(함수)이 다 사용되는 것은 아니다 (예: 오류처리)
      • 모든 데이터(data)가 다 사용되는 것은 아니다 (예: 배열)
      • 자바: 모든 클래스가 다 사용되는 것은 아니다
      • 실행 시 필요하면 그때 해당 부분을 메모리에 올린다
    • cf. 정적 적재 (Static loading)
  • (2) 동적 연결 (Dynamic Linking)
    • 여러 프로그램에 공통 사용되는 라이브러리
      • 공통 라이브러리 루틴(library routine)를 메모리에 중복으로 올리는 것은 낭비
      • 라이브러리 루틴 연결을 실행 시까지 미룬다.
      • 오직 하나의 라이브러리 루틴만 메모리에 적재되고,
      • 다른 애플리케이션 실행시 이 루틴과 연결(link)된다
      • 원래 exe 파일 만들기 전에 링크가 발생하는데(정적 연결), 링크를 메모리에 올리고 실행시 연결 => 하나의 라이브러리만 메모리에 적재
        • cf. 정적 연결 (Static linking)
      • 공유 라이브러리 (shared library) - Linux
      • 동적 연결 라이브러리 (Dynamic Linking Library, DLL) - Windows
  • (3) 스와핑 (Swapping)
    • 메모리에 적재되어 있으나 현재 사용되지 않고 있는 프로세스 이미지
      • 프로세스 이미지를 하드디스크의 특정 부분으로 몰아냄
      • 메모리 활용도 높이기 위해 Backing store (= swap device)로 몰아내기
        • 하드디스크(file system) : banking store, swap device, 메인 메모리 크기정도면 ok
      • swap-out vs. swap-in
      • Relocation register 사용으로 적재 위치는 무관
      • 프로세스 크기가 크면 backing store 입출력에 따른 부담 크다(하드디스크는 느림)

연속 메모리 할당

  • Contiguous Memory Allocation
  • 다중 프로그래밍 환경
    • 부팅 직후 메모리 상태: O/S + big single hole, 메모리가 비어있음
    • 프로세스 생성 & 종료 반복 ☞ scattered holes
  • 메모리 단편화 (Memory fragmentation)
    • Hole들이 불연속하게 흩어져 있기 때문에 프로세스 적재 불가
    • 외부 단편화 (external fragmentation) 발생
    • 메모리 관리자 입장에선 매우 억울
    • 외부 단편화를 최소화 하려면?
  • 연속 메모리 할당 방식
    • First-fit (최초 적합) : 순차적으로 찾아 처음으로 발견되는 곳에 할당
    • Best-fit (최적 적합) : 들어갈 수 있는 Hole 중 가장 유사한 사이즈로 할당
    • Worst-fit (최악 적합) : 크기가 제일 안 맞는 곳에 할당
  • 예제
    • Hole: 100 /500 / 600 / 300 / 200 KB
    • 프로세스: 212 417 112 426 KB
  • 할당 방식 성능 비교: 속도 및 메모리 이용률
    • 속도: first-fit
    • 이용률: first-fit, best-fit
  • 외부 단편화로 인한 메모리 낭비
    • 1/3 수준 (사용 불가)
    • Compaction : 최적 알고리즘 없음, 고부담. hole을 한 곳으로 모음
    • 다른 방법은?

페이징

  • Paging
  • 프로세스를 연속적으로 해야된다고 생각했는데, 발상을 돌려서 일정한 단위로 잘라서 메모리로 올림
  • 프로세스를 일정 크기(=페이지)로 잘라서 메모리에!
    • 프로세스는 페이지(page)의 집합
    • 메모리는 프레임(frame)의 집합
    • 페이지를 프레임에 할당
  • MMU 내의 재배치 레지스터 값을 바꿈으로서
    • CPU는 프로세스가 연속된 메모리 공간에 위치한다고 착각(CPU를 속이자)
    • MMU는 페이지 테이블 (page table)이 된다
  • 논리주소 (Logical address)
    • CPU가 내는 주소는 2진수로 표현 (전체 m 비트)
    • 하위 n 비트는 오프셋(offset) 또는 변위(displacement)
    • 상위 m-n 비트는 페이지 번호
  • 물리주소
    • 메모리를 접근할 때 사용되는 주소, 기억장치의 주소 레지스터에 적재
  • 주소변환 (Address translation)
    • 논리주소 → 물리주소 (Physical address)
    • 페이지 번호(p)는 페이지 테이블 인덱스 값
    • p 에 해당되는 테이블 내용이 프레임 번호(f)
    • 변위(d)는 변하지 않음
    • 요약 : 10진수 값을 2진수로 변환한 후, 페이지 사이즈에 따라 p와 d를 정한 후, p를 p에 해당하는 테이블 내용 프레임 번호(f)로 변경하면 됨
  • 예제 (1)
    • Page size = 4 bytes
    • Page Table: 5 6 1 2 (인덱스 0 1 2 3)
    • 논리주소 13 번지는 물리주소 몇 번지?
      • 13 = 1101(2)
      • 4바이트(2^2)니까 페이지 번호(p) : 11, displacement(d) : 01
      • p는 3이니까 index가 3인 페이지 테이블은 2
      • 1001 => 2번째 프레임에서 1만큼 떨어짐
  • 예제 (2)
    • Page Size = 1KB = 1024
    • Page Table: 1 2 5 4 8 3 0 6
    • 논리주소 3000번지는 물리주소 몇 번지?
      • 3000 = 101110111000(2) : 10칸이 d
      • p : 10 (2) => 5, d : 1110111000
      • 1011110111000(2)
    • 물리주소 0x1A53 번지는 논리주소 몇 번지?
      • 0x1A53는 16진수 => 1A53을 2진수로
      • p : 110 (6), d : 1001010011
      • 1111001010011(2)
  • 내부 단편화 (Internal Fragmentation)
    • 프로세스 크기가 페이지 크기의 배수가 아니라면,
    • 마지막 페이지는 한 프레임을 다 채울 수 없다
    • 남은 공간 = 메모리 낭비
    • 내부 단편화로 인한 메모리 낭비는 미미함
  • 페이지 테이블 만들기
    • MMU를 어디에 넣을까!
    • CPU 레지스터로
      • 장점 : 주소 변환이 빠름
      • 단점 : 많이 못들어감
    • 메인 메모리로
      • 장점 : 많이 들어감
      • 단점 : 주소 변환이 느림
    • TLB (Translation Look-aside Buffer)로
      • 요즘 방식
      • SRAM 사용
      • CPU와 메모리의 중간 단계
    • 척도: 테이블 엔트리 개수 vs 변환 속도
    • 연습: TLB 사용 시 유효 메모리 접근 시간
      • Tm(총 걸린 시간) = 100ns, Tb(읽는 시간) = 20ns, hit ratio = 80%
      • Teff = hTb + (1-h)(Tb+Tm) = 140ns
  • 보호 (Protection): 해킹 등 방지
    • 모든 주소는 페이지 테이블을 경유하므로,
    • 페이지 테이블 엔트리마다 r(reader), w(writer), x(executor) 비트를 두고
    • 해당 페이지에 대한 접근 제어 가능
  • 공유 (Sharing): 메모리 낭비 방지
    • 같은 프로그램을 쓰는 복수 개의 프로세스가 있다면,
    • Code + data + stack 에서 code는 공유 가능 (단, 코드는 내용이 달라지면 안됨. non-selfmodifying code = reentrant code = pure code 인 경우)
    • 프로세스의 페이지 테이블 코드 영역이 같은 곳을 가리키도록 설정

세그멘테이션

  • Segmentation
  • 페이징이 더 보편적으로 사용
  • 페이징에선 일정 크기(페이지)로 잘랐음
  • 세그멘테이션은 프로세스를 논리적 내용(=세그멘트)으로 잘라서 메모리에 배치!
    • 프로세스는 세그멘트(segment)의 집합
    • 세그멘트의 크기는 일반적으로 같지 않다
  • 세그멘트를 메모리에 할당
    • MMU 내의 재배치 레지스터 값을 바꿈으로서
    • CPU는 프로세스가 연속된 메모리 공간에 위치한다고 착각
    • MMU는 세그멘트 테이블(segment table)이 된다
  • 논리주소 (Logical address)
    • CPU 가 내는 주소는 segment 번호(s) + 변위(d)
  • 주소변환
    • 논리주소 → 물리주소 (Physical address)
    • 세그멘트 테이블 내용: base + limit
    • 세그멘트 번호(s)는 세그멘트 테이블 인덱스 값
    • s에 해당되는 테이블 내용으로 시작 위치 및 한계값 파악
    • 한계(limit)를 넘어서면 segment violation 예외 상황 처리
    • 물리주소 = base[s] + d
  • 예제

    LimitBase
    10001400
    4006300
    4004300
    11003200
    10004700
    • 논리주소 (2, 100)는 물리주소 무엇인가?
      • index=2 => 4300 + 100
    • 논리주소 (1, 500)은 물리주소?
      • 해당 주소 없음. limit을 넘어섬(400)
  • 보호 (Protection): 해킹 등 방지
    • 모든 주소는 세그멘트 테이블을 경유하므로,
    • 세그멘트 테이블 엔트리마다 r, w, x 비트 두어
    • 해당 세그멘트에 대한 접근 제어 가능
    • 페이징보다 우월!
      • 프로세스는 일정 크기로 자르기 때문에 code, data, stack이 혼재할 수 있음
  • 공유 (Sharing): 메모리 낭비 방지
    • 같은 프로그램을 쓰는 복수 개의 프로세스가 있다면,
    • Code + data + stack 에서 code 는 공유 가능 (단, non-selfmodifying code = reentrant code = pure code 인 경우)
    • 프로세스의 세그멘트 테이블 코드 영역이 같은 곳을 가리키게
    • 페이징보다 우월!
  • 외부 단편화 (External Fragmentation)
    • 세그멘트 크기는 고정이 아니라 가변적
    • 크기가 다른 각 세그멘트를 메모리에 두려면 = 동적 메모리 할당
    • First-, best-, worst-fit, compaction 등 문제
  • 세그멘테이션 + 페이징
    • 세그멘테이션은 보호와 공유면에서 효과적
    • 페이징은 외부 단편화 문제를 해결
    • 따라서 세그멘트를 페이징하자! ☞ Paged segmentation
    • 예: Intel 80x86

Reference


카일스쿨 유튜브 채널을 만들었습니다. 데이터 사이언스, 성장, 리더십, BigQuery 등을 이야기할 예정이니, 관심 있으시면 구독 부탁드립니다 :)

PM을 위한 데이터 리터러시 강의를 만들었습니다. 문제 정의, 지표, 실험 설계, 문화 만들기, 로그 설계, 회고 등을 담은 강의입니다

이 글이 도움이 되셨거나 다양한 의견이 있다면 댓글 부탁드립니다 :)

Buy me a coffeeBuy me a coffee





© 2017. by Seongyun Byeon

Powered by zzsza