Notice
Recent Posts
Recent Comments
Today
Total
04-30 00:02
Archives
관리 메뉴

Jeongchul Kim

메모리 관리 본문

Linux

메모리 관리

김 정출 2015. 12. 19. 01:54

제6장 메모리 관리



리눅스 운영체제의 메모리 관리는 기본적으로 페이징을 기반으로 한 가상 메모리 시스템(Virtual Memeory System)을 지원한다.

가상 메모리 시스템의 특징


실제 물리적 메모리의 크기에 관계없이 CPU의 형태에 따른 주소 공간을 사용할 수 있다. 예를 들면 32bit 주소 공간을

  제공하는 CPU의 경우 4GB의 공간에서 커널이 가상적으로 차지하는 1Gb의 공간을 제외한 3GB의 공간에 해당하는 크기의   

  프로그램을 실행시킬 수 있다.


CPU의 명령어 수행 시마다 계산되는 가상 주소MMU(Memory Management Unit) 하드웨어와 커널의 기능으로 실제

             물리 메모리 주소로 변환된다.

▪ 프로그램들의 크기가 크고 또한, 여러 실행 중인 프로세스의 공간은 실제 메모리의 크기를 초과할 수 있으므로 실행 중

 필요한 프로그램의 부분 부분 만을 메모리에 적재하여 실행하고 나머지 부분은 디스크에 존재하게 된다. 필요한 부분에

 대한 메모리 적재 및 디스크로의 퇴출은 일정 크기의 페이지 단위로 일어나는데 이를 페이징(Paging)이라 한다.


▪ 실행 가능 형태의 프로그램은 해당 프로세스가 실행되어도 디스크 파일에 남아 있으며 필요한 페이지가 메모리에 없을 때

 (Page Fault), 페이지 단위 적재를 수행한다. 이를 요구 페이징(demand paging)이라 한다. 메모리의 부족으로 적재된

 페이지를 디스크로 교체(page replacement)할 때, text는 변화하지 않고 실행 파일 내에 있으므로 그대로 다른 페이지를

 위해 활용하고, data나 stack에 속한 페이지와 같이 메모리 적재 후 변화하는 페이지는 디스크의 특별 공간이 swap 장치에

 일시 저장한다.


▪ 리눅스 운영체제는 메모리 병목 현상을 피하려고 항상 일정량 이상의 물리적 free 페이지를 유지한다.


▪ Page fault 시에 매번 디스크 파일이나 swap 공간에서 페이지를 읽어오는 disk IO 오버헤드를 완화하기 위해 리눅스는

  여러 가지 페이징 버퍼링을 사용한다. 즉 교체되어 퇴출 당하는 페이지나 페이지 공유 시에 이들을 SW 적인 폐이지 캐시에

  유지하여 재사용을 시도한다.


▪ 커널이 일단 부팅되면 초기 단계에 가상 메모리 시스템이 활성화되어 커널 자신도 가상 메모리 시스템 하에서 실행된다.


▪ 모든 실행 중인 가상 페이지들의 주소를 실제 메모리 주소로 변환하기 위해 커널 및 각 프로세스마다 페이지 테이블이

 유지된다. 페이지 테이블은 매 가상 페이지마다 PTE(Page Table Enty)들로 구성되는데 각 PTE는 가상 페이지의 메모리

 적재 여부와 메모리에 적재된 페이지의 경우, 실제 메모리의 주소를 가진다.














6.1 프로세스의 가상 주소 공간

가상 메모리 시스템 하에서 사용자는 실제 메모리의 레이아웃에 관계없이 연속된 프로세스의 가상 주소 공간을 사용하고 접근하게 된다. 예를 들면 정수 변수 ix의 주소인 &ix를 프린트해 보면 이는 가상 주소 공간에서의 값이며 실제 프로세스의 물리적 메모리 레이아웃에 따른 변화가 없을 알 수 있다.

<그림> 프로세스 및 커널의 가상 공간 구성


Text Area

  CPU가 실행하는 명령어로 구성되는 영역으로 읽기 전용으로만 접근이 허용되므로 프로세스의 실행 중 변화가 없다.

  메모리 퇴출 시에도 디스크 파일에 존재한다.

Date Area

  초기화된 데이터 영역과 초기화되지 않은 데이터 영역으로 구분되며 메모리 적재 후 변화된 부분은 메모리 퇴출 시에

   swap 공간으로 가게 된다.

Heap Area

  프로세스 실행 중에 malloc, new 등 동적 메모리 할당 요구를 수용하는 데이터 영역 공간이다. break 값에 의해 그 끝을

  나타내며 공간 부족 시에 주소가 커지는 방향으로 확장된다.

Stack Area

   함수 내에 할당되는 automatic 변수 들이나 함수 호출 시에 run-time stack frame(함수가 호출 될 때의 반환 주소, 환경 정보 등)

   할당되는 공간이다. 주소가 작아지는 방향으로 확장된다.



그 외에도 프로세스를 위한 가상 주소 공간은 memory mapped file, shared memory 공간 등 여러 형태가 있다. 위와 같은 특성별 프로세스 주소 공간은 세그먼트(segment), 리전(region) 등으로 부르기도 한다.


다음은 프로세스 주소 공간을 알아보기 위한 커널 정의 변수 및 함수

brk 함수는 프로세스의 가상 메모리 공간에서 힙 영역의 끝인 break 값을 설정하는 기능을 수행한다.

sbrk는 세그먼트의 증자분을 반영한다. 이러한 증가한 공간은 heap 영역으로 사용되는데 이러한 데이터 세그먼트의 증가는

일반적으로 동적 메모리 할당인 malloc에 의해 필요할 때 일어나므로 사용자가 직접 brk나 sbrk를 사용하는 것은 권장되지 않는다.





BRK(2)

#include <unistd.h>


int brk(void *end_dat_segment);


입력 값

    end_data_segment : 설정하고자 하는 break 값

반환 값

    정상 : 0, 에러: -1

BRK(2)

#include <unistd.h>


int *sbrk(ptrdiff_t increment);

입력 값

    increment : 현재의 break 값에 더해지는 값

출력 값

    정상 : 이전의 break 값,   에러 : -1



프로세스의 각 공간을 알아보는 프로그램

#include <unistd.h>

#include <stdio.h>

extern etext, edata, end;

int main(void)

{

          char *break_point;

          printf(“exext value : %x\n”, &etext);

          printf*”edata value : %x\n”, &edata);

          printf(“end value : %x\n”, &end);

          break_point = sbrk(0);

          printf(“current break value -> %x\n”,break_point);

}


6.2 동적 메모리 할당

전역 변수를 위한 공간이 선언되었다면 이 변수들을 위한 공간의 레이아웃은 컴파일 시간에 결정되는 것이다.

또한, 함수 내의 automatic 변수들은 실행 시간의 스택(stack)에 할당된다.

이와 같은 형태와는 달리 프로세스의 실행 시간에 사용자가 동적으로 메모리 공간을 할당 받을 수 있는데 이의 대표적인 예는

C 언어의 malloc/calloc 함수 종류와 C++의 new와 같은 객체 할당 연산자를 들 수 있다. 이러한 동적 메모리의 할당 및 반납은 힙(Heap) 공간에서 이루어지는데 일반적으로 커널과 관련 라이브러리는 요구 공간의 적절한 크기 별 할당과 반환을 쉽게 하려고 적절한 동적 메모리 관리 알고리즘을 사용한다.



MALLOC(3)

#include <stdlib.h>


void *malloc(size_t size);

입력 값

       size : 메모리 할당 요구 바이트 크기

반환 값

      정상 : 메모리 할당 요구 크기 바이트 , 에러 : NULL

malloc은 원하는 byte 크기의 연속된 공간을 가상 메모리에서 할당하여 그 주소를 반환한다.




MALLOC(3)

#include <stdlib.h>


void *calloc(size_t nmemb, size_t size);

입력 값

      nmemb : 할당을 원하는 단위 수

      size : 메모리 구성 요소의 단위 크기

반환 값 : 없음

calloc 함수는 일정 크기의 element를 원하는 개수만큼 할당된다.

위와 같은 함수들을 사용하여 할당된 공간은 사용 완료 시에 재사용을 위해 시스템에 반환하여야 하며, 이때 사용되는 함수가 free

시스템에의 반환 없이 이러한 동적 메모리 할당을 계속하면 결국에는 가상 메모리 공간이 부족하게 되어 메모리 부족의 오류가 발생하게 된다.



MALLOC(3)

#include <stdlib.h>


void free(void *ptr);

입력 값

      ptr : 반환을 원하는 메모리 블럭 의 포인터

반환 값: 없음


다음은 간단한 malloc과 free 사용 예제 프로그램

#include <stdio.h>

#include <string.h>

#include <stdlib.h>


struct record {

        int no;

        char name[20];

};


int main(void)

{

      struct record *ptr;

      ptr = (struct record*)malloc(sizeof (struct record))

      if(ptr) {

             ptr->no = 1;

             stcpy(ptr->name, “RTDCS”);

             printf(“record id %d\n”, ptr->no);

             printf(“record name %s\n”, ptr->name);

      }


      free(ptr);

      return 0;

}  



이미 할당된 동적 메모리 공간을 다른 크기의 공간으로 재활용하고자 할 때 realloc을 사용한다. malloc 등의 할당 함수들에 의해

할당된 연속 메모리 공간은 커널이 정하는 정해진 여러 크기의 블록 단위로 할당되기 때문에 반드시 원했던 크기만큼 할당 되는 것이 아니라 더 큰 크기로 할당되는 것이 대부분이다. 따라서 realloc은 이미 할당된 크기의 공간이 새로운 요구 크기를 수용할 수 있으면 이미 할당된 공간을 그대로 재활용되고 그렇지 않을 때에는 예전 공간은 반환되고 새로운 공간이 할당된다. 재활용되는 공간에는 예전의 값들이 보존된다

MALLOC(3)

#include <stdlib.h>


void *realloc(void *ptr, size_t size); // 기존 할당 부분 재 사용

입력 값

      ptr : 메모리 블록으로의 포인터

      size : 재 할당하려는 전체 바이트 크기

반환 값

      정상 : 할당된 블록으로의 포인터 ,  에러 : NULL


malloc 종류의 함수들에 할당된 공간은 메모리 공간의 고갈을 방지하기 위해 free를 통해 반납하는 것이 안전하다. 그러나 통산 매우 큰 프로그램에서는 할당된 메모리의 반환을 하지 않는 실수를 범하는 경우가 많으며 이 경우 수행 기간이 길어지면 문제가 발생하게 된다. alloca 함수는 함수의 stack frame에 요구된 공간을 할당하므로 함수에서의 return 시에 공간이 자동 해제되는 장점이 있다. 단 이러한 공간은 같은 함수 내에서만 사용할 수 있다.


ALLOCA(3)

#include <alloca.h>


void alloca(size_t size); // stack으로 부터 할당  function return 시 자동 해제

입력 값

       size : 할당을 받으려는 바이트 크기

반환 값

       정상 : 할당된 블록으로의 포인터 ,  에러 : NULL





'Linux' 카테고리의 다른 글

Virtual Box 설치 및 Linux Ubuntu 설치  (0) 2016.02.23
ch02 시스템 구조  (0) 2016.02.04
파일 시스템  (1) 2015.12.19
리눅스 스케줄링  (0) 2015.12.19
프로세스와 쓰레드  (0) 2015.12.19
Comments