ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 실습과 그림으로 배우는 리눅스 구조 5장 정리
    운영체제 2022. 11. 6. 12:56

    메모리 관리


    단순 메모리 할당시 문제점

    • 커널이 프로세스에 메모리를 할당하는 경우는 다음과 같다. 
      • 프로세스가 생성될 때 
      • 프로세스가 시스템 콜을 통해서 동적으로 메모리를 할당 받을 때
        • malloc() 
    • 단순한 메모리 할당 방법의 문제점
      • 메모리 단편화 문제 
      • 다른 용도의 메모리에 접근 가능
      • 여러 프로세스를 다루기 곤란

     

    1. 메모리 단편화 문제
      • 메모리 획득과 해제를 반복하면, 메모리 단편화가 발생한다. 
      • 총 남아있는 메모리 양은 충분하지만, 중간 중간이 비어 있어서 사용할 수 없는 경우가 발생한다. 
    2. 다른 용도의 메모리에 접근 가능
      • 주소를 직접 지정하면 데이터가 오염되거나 파괴될 위험이 있다.  
    3. 여러 프로세스를 다루기 곤란
      • 같은 프로그램을 실행하게 되었을 때, 주소가 겹치면 실패하는 경우도 있다. 

     

    • 이렇게 단순한 메모리 할당은 문제가 있기 때문에 가상 메모리 방식을 사용한다.

    가상 메모리 

    • 프로세스가 물리 메모리에 직접 접근하지 않고, 가상 주소를 이용해서 간접적으로 접근하는 방식
    • 프로세스에 보이는 주소를 가상 주소, 시스템에 존재하는 메모리의 실제 주소를 물리 주소라고 한다.
    • cat /proc/pid/maps 사용시 가상 주소가 출력된다. 
    • 프로세스는 물리 주소에 직접 접근할 수 없다.

     

     

    페이지 테이블

    • 가상 주소를 물리 주소로 변환할 때 페이지 테이블을 사용한다.
    • 가상 메모리는 전체 메모리를 페이지 단위로 나누어 관리하며 변환도 페이지 단위로 이루어진다. 
    • 페이지 사이즈는 아키텍처마다 다르지만, x86_64의 경우 4KB이다.
    • 페이지 테이블 예시 
      • 프로세스 A의 가상 주소 0~100은 실제 물리 주소 500~600에 매핑
      • 프로세스 B의 가상 주소 100~200은 실제 물리 주소 400~500에 매핑되어 있을 수 있다. 

     

    • 페이지 폴트(page fault)
      • 물리 메모리에 매핑되어 있지 않은 가상 메모리 주소를 참조하면 CPU에서 페이지 폴트 인터럽트(page fault interrupt)가 발생한다.
      • 페이지 폴트 Flow 
        1. 페이지 폴트에 의해 현재 실행 중인 명령이 중단되고, 커널안의 페이지 폴트 핸들러가 동작한다. 
        2. 커널은 프로세스로부터 "메모리 접근이 잘못 되었다"는 내용을 페이지 폴트 핸들러에게 알려준다.
        3. SIGSEGV 시그널을 프로세스에게  통지 
        4. SIGSEGV 시그널을 받은 프로세스는 강제 종료된다.   

     

     

    페이지 테이블 생성  

    1. 프로세스를 실행할 실행파일에서 필요한 정보(코드 영역 사이즈, 데이터 영역 사이즈 등)를 읽는다.
    2. 커널 메모리 영역에 프로세스를 위한 페이지 테이블을 생성한다.  
    3. 페이지 테이블에 맞게 가상 주소 공간을 물리 주소 공간에 매핑한다. 

     

     

    추가적인 메모리 할당

    • 프로세스가 메모리를 추가로 요구하는 경우, 커널은 대응하는 페이지 테이블 작성 후 할당된 메모리에 대응하는 가상 주소를 프로세스에게 반환한다.  
    • C언어의 메모리 할당
      • C언어 표준 라이브러리에 있는 malloc() 함수 호출
      • malloc()함수에서 mmap() 시스템 콜을 통해서 메모리를 확보

     

     

    파일 맵

    • mmap()을 특정한 방법으로 호출하면, 파일의 내용을 메모리로 읽어 들여 가상 주소 공간에 매핑할 수 있다.
    • 가상 주소 공간에 매핑하게 되면, read(), write()와 같은 시스템 콜 없이 파일을 수정할 수 있다. 
    • 매핑된 영역에 데이터를 덮어쓰기(memcpy())하여 write() 시스템 콜과 같은 효과를 낼 수 있다. 
    • 직접 메모리에 올려서 사용하기 때문에 빠르다. 

     

     

    디맨드 페이징

    • mmap()을 통해서 모든 가상 주소 공간을 물리 주소 공간에 매핑하게 되면, 메모리가 낭비될 수 있다. 
      • 실행에 사용하지 않는 코드와 데이터가 있을 수 있을 수 있기 때문에 메모리가 낭비된다. 
      • 그래서 사용중인 가상 메모리 영역에만 물리 메모리를 할당하고, 그렇지 않은 경우에는 프로세스가 접근할 때 할당하는 방식을 사용한다.  
    • 디멘드 페이징을 사용하면 다음 3가지 상태를 사용할 수 있다. 
      • 프로세스에 가상 메모리가 할당되지 않음
      • 프로세스에 가상 메모리가 할당되었고, 물리 메모리도 할당되었음
      • 프로세스에 가상 메모리가 할당되었지만, 물리 메모리에는 할당되지 않았음
    • 물리 메모리가 매핑되지 않은 가상메모리에 프로세스가 접근한다면
      1. CPU에 페이지 폴트 인터럽트가 발생한다.
      2.  커널의 페이지 폴트 핸들러가 접근된 페이지에 물리 메모리를 할당하여 페이지 폴트를 지운다. 
      3. 사용자 모드로 돌아와 프로세스가 계속 실행된다.  
    • 프로세스가 메모리를 획득하면 가상 메모리 사용량은 증가하지만, 물리 메모리 사용량은 증가하지 않는다. 
    • 획득한 메모리에 접근하면, 페이지 폴트가 발생하며 물리 메모리 사용량이 증가한다.  
    • 메모리에 한번 접근한 후, 같은 메모리 영역에 한번 더 접근하면 접근한 페이지에 대한 물리 메모리영역이 이미 할당되었기 때문에 페이지 폴트가 발생하지 않는다. 

     

     

    메모리 부족

    • 가상 메모리 부족
      • 물리 메모리와 상관없이, 프로세스가 사용가능한 가상 메모리 공간 이상의 메모리 획득을 요청하면 발생한다. 
      • x86 아키텍처는 4GB의 가상 메모리 공간을 사용하기 때문에 데이터베이스와 같은 큰 메모리 시스템을 사용하게 되면 문제가 발생할 수 있다.
      • x86_64 아키텍처는 가상 주소 공간이 128TB가 있기 때문에 현재로서는 가상 메모리 부족이 발생하지 않는다.  

     

    • 물리 메모리 부족
      • 가상 메모리와 무관하게 시스템에 탑재된 물리 메모리를 모두 사용하게 되면 발생한다. 

     

     

    Copy on Write

    • fork() 시스템 콜이 발생했을 때 부모 프로세스의 메모리를 자식 프로세스에 전부 복사하지 않고, 페이지 테이블만 복사된다. 
    • 그래서 초기에는 부모프로세스와 자식 프로세스의 가상 메모리영역은 동일한 물리 메모리영역과 매핑되어 있다. 
    • 또한 두 프로세스 모두 페이지에 쓰기가 허용되지 않으며, 쓰기 이벤트가 발생되면 다음과 같이 쓰기가 발생한다. 
      1. 페이지에 쓰기는 허용되지 않기 때문에, CPU에 페이지 폴트 발생한다. 
      2. CPU가 커널 모드로 변경되어 페이지 폴트 핸들러가 동작한다. 
      3. 페이지 폴트 핸들러는 접근한 페이지를 다른 메모리 영역에 복사 후, 쓰려고 한 프로세스에 할당 후 내용을 다시 작성한다. 
      4. 부모 프로세스, 자식 프로세스 각각 공유가 해제된 페이지 엔트리에 대해서 페이지 테이블을 업데이트한다. 

     

     

    스왑(swap)

    • 물리 메모리가 부족한 시점, 물리 메모리를 획득해야 할 때 기존의 사용하던 물리 메모리 공간 일부를 스왑 영역에 저장하여 공간을 만들어 낸다. 
    • 스왑 아웃(swap out)
      • 물리 메모리에서 사용하던 일부를 스왑 영역에 임시 보관하는 것
      • 어떤 영역이 스왑 아웃될지는 앞으로 한동안 사용되지 않을 것 같은 영역을 커널이 결정한다. 
      • 스왑 아웃된 메모리 영역은 다른 프로세스가 사용가능하다. 
    • 스왑 인(swap in)
      • 프로세스가 스왑 아웃된 페이지영역에 접근했을 때 페이지 폴트가 발생하고, 스왑 영역에 임시 보관되었던 영역을 물리 메모리에 되돌림
    • 스왑 아웃과 스왑 인을 합쳐서 스와핑이라고 한다.
    • 리눅스에서는 페이지 단위로 이루어지므로 페이징이라고도 한다.
      • 스왑 인 = 페이지 인
      • 스왑 아웃 = 페이지 아웃 
    • 스왑을 사용하게 되면, 실제로 탑재된 [물리 메모리 + 스왑 영역]의 크기만큼 메모리를 사용할 수 있게 된다.
    • 하지만 스왑 영역은 메모리만큼 빠르지 않기 때문에 스왑이 자주 발생하면 좋지 않다.

     

     

    스래싱(Thrashing) 상태

    • 메모리에 접근할 때매다 스왑인, 스왑 아웃이 발생하는 상태
    • 스래싱이 발생하면 시스템은 한동안 사용자의 입력에 반응할 수 없고, 결국에는 컴퓨터가 멈춰버리거나 메모리 부족이 발생 
    • 스래싱이 발생할 수 있으므로 서버에서 스와핑은 발생시키지 않는 것이 좋다. 
    • free 커맨드를 통해서 swap 메모리 확인 가능

    • sar -W 커맨드를 통해서 스와핑이 얼마나 발생하고 있는지 확인할 수 있다. 

     

     

    계층형 페이지 테이블

    • 페이지 테이블은 계층 구조로 이루어져 있다. 
    • 1차원 구조로 되어 있을 경우, x86_64 아키텍처라면 한 페이지 테이블에 256GB의 메모리가 필요할 것이다. 
      • 계층 구조를 이용해서 페이지 테이블에 필요한 메모리 사용량을 절약할 수 있다. 
    • 가상 메모리 사용량이 늘어나면, 계층형 페이지 테이블이 1차 원형 테이블 보다  필요로 하는 메모리 사용량이 많아지지만(페이지 테이블 엔트리가 증가하기 때문에), 그런 일이 생기는 경우는 매우 드물다. 
      • 따라서 전체 프로세스의 계층형 페이지 테이블의 메모리 사용량 합계는 1차 원형 테이블보다 적다.
    •  시스템이 메모리 부족을 겪는 경우
      • 프로세스가 직접 사용하는 메모리 사용량이 많은 경우
      • 프로세스를 너무 많이 만든 경우
      • 가상 메모리를 대량으로 사용하고 있는 프로세스 때문에 페이지 테이블 메모리 사용량의 증가한 경우  

     

     

    Huge Page

    • 가상 메모리 사용량이 증가하면 페이지 테이블에 사용하는 메모리량도 증가한다. 
      • 이에 따라 fork() 시스템 콜도 느려지게 된다. 
      • 그래서 리눅스에서는 Huge page 기능을 사용해서 이를 해결한다. 
    • Huge page는 페이지 사이즈를 늘려서 페이지 테이블의 엔트리 개수를 줄이고, 페이지 테이블에 사용되는 메모리 사용량을 줄이는 방법이다.  
    • 데이터 베이스나 가상 머신 시스템에서는 가상 메모리를 대량으로 사용하는 설정이 들어가 있기 때문에 필요에 따라 Huge page 기능을 사용하여 메모리 사용량을 줄이는 것이 도움이 될수도 있다. 

     

    • Transparent huge page
      • 리눅스에서 제공하는 기능
      • 가상 주소 공간에 연속된 여러개의 4KB 페이지가 특정 조건을 만족하면 묶어서 huge page로 변환해주는 기능
      • Huge page를 풀고 묶는 경우가 잦아지면 성능이 하락하는 경우도 있기 때문에, 시스템 구축시 무효화하는 경우도 있다.
      • 해당 기능은 /sys/kernel/mm/transparent_hugepage/enabled 에서 지정할 수 있다. 

     

     

     

    Reference

    실습과 그림으로 배우는 리눅스 구조 

    반응형

    댓글

Designed by Tistory.