메모리 이해하기


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


주기억장치 관리

  • 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


카일스쿨 유튜브 채널을 만들었습니다. 데이터 분석, 커리어에 대한 내용을 공유드릴 예정입니다.

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

이 글이 도움이 되셨거나 의견이 있으시면 댓글 남겨주셔요.

Buy me a coffeeBuy me a coffee





© 2017. by Seongyun Byeon

Powered by zzsza