운영체제(OSTEP)

OSTEP 14장

woohap 2024. 10. 19. 00:00
이번 장에서는 UNIX 메모리 관리 인터페이스에 대해 논의
여기서 메모리란 사용자 주소 공간을 의미

메모리 공간의 종류

C 프로그램이 실행되면, 두 가지 유형의 메모리 공간이 할당됨

  1. 스택 메모리 - 할당과 반환이 컴파일러에 의해 암묵적으로 이루어짐 스택 메모리를 자동 메모리라고도 부름
  2. // func()가 호출되면 func를 위한 스택 프레임이 생성되고 프레임 내부에 x를 위한 공간을 할당 // func() 함수가 종료되면 스택 메모리에 있던 할당된 스택 프레임 전체가 반환됨 void func() { int x; }
  3. 힙 메모리 - 오랫동안 값이 유지되어야 하는 변수를 위한 메모리 모든 할당과 반환이 프로그래머에 의해 명시적으로 처리 됨
  4. // 스택 메모리에 func 스택 프레임과 px를 위한 메모리 공간이 확보 됨 // malloc() 호출로 인해 힙 영역에 int 크기 만큼의 메모리가 할당됨 // 만약 이 상태로 함수가 종료되면 스택 메모리는 반환되지만 힙 메모리는 반환되지 않아 누수 발생 void func() { int *px = (int*) malloc(sizeof(int)); // free(px); }

malloc() 함수

힙에 요청할 공간의 크기를 넘겨주면, 성공할 경우 새로 할당도니 공간에 대한 포인터를 사용자에게 반환

실패 시 NULL을 반환

malloc 함수는 void\*를 반환한다. → 타입 변환(형변환)을 통해 포인터에 저장 
→ 포인터 타입을 통해 읽어오는 데이터 크기가 결정되므로 중요

malloc 함수의 인자는 size\_t 타입이며, 필요 공간의 크기를 바이트 단위로 표시한 것

sizeof() 연산자

컴파일 시간의 연산자→ 인자의 실제 크기가 컴파일 시간에 결정됨
해당 연산자는 인자의 실제 크기를 숫자로 변환

// px는 포인터이므로 8byte 변수임 
int *px = malloc(10 * sizeof(int)); // 40byte 힙 메모리 확보

// sizeof(arr)을 하면 40byte를 반환, 배열임을 명시적으로 알려주고 있기 때문 
int arr[10];

//strlen() 함수는 널 문자열을 제외한 길이를 반환하므로 malloc으로 할당 받을 때는 +1을 해준다
malloc( strlen(s) + 1 );

free() 함수

더 이상 사용되지 않는 힙 메모리를 해제하기 위해 사용하는 함수이다.
free() 함수를 이용해서 힙 메모리를 해제하지 않으면 해당 
힙 메모리는 사용되지 않아도 계속 존재하게 됨 즉, 메모리 누수가 발생한다.

<<주의사항>>

미리 할당되었다고 가정

대부분의 함수는 자신이 호출되기 전에 필요한 메모리가 미리 할당되었다고 가정한다.
즉 strcpy(dst, src) 함수의 경우 dst와 src 포인터 모두 할당되었다는 전제로 사용되어야 한다.

char * src = "hello"'
char * dst;
strcpy(dst, src); // dst가 할당되지 않았기 때문에 segfault 발생 후 죽음 

버퍼 오버 플로우

메모리를 부족하게 할당받은 것을 의미
문자열 복사할 때, 많이 발생

-   문제가 없어 보일 수도 있음  
    실제로 문자열 복사를 진행했을 때, 할당된 메모리 공간보다 더 많은 공간에 데이터를 저장하게 되었지만 
    그 공간이 사용중이지 않기 때문에 문제가 발생하지 않을 수 있음 

프로그램이 한 번 올바르게 실행된다고 하더라도, 프로그램이 올바르다는 것을 의미하는 것은 아니다.

할당 받은 메모리 초기화하지 않기

즉, malloc() 함수를 이용해 힙 메모리를 제대로 할당 받았으나 
해당 메모리 공간에 값을 저장하지 않은 경우를 의미 (uninitialized read) 
이 경우 힙으로부터 알 수 없는 값을 읽게 되어 문제가 발생할 수 있다.

메모리 해제하지 않기

메모리 누수를 의미한다.
즉, malloc() 함수를 이용해 힙 메모리를 할당 받아 사용한 뒤 
사용하지 않음에도 불구하고 힙 메모리를 해제하지 않는 경우를 의미한다. 
이 경우 메모리 누수가 누적되어 시스템 메모리가 부족해지고 결국 시스템을 재시작하는 문제를 일으킬 수 있다.

메모리 사용이 끝나기 전에 메모리 해제하기

메모리 사용이 끝나기 전에 메모리를 해제하게 되면 힙 메모리의 주소를 가지고 있던 포인터 변수 
즉, dangling pointer는 잘못된 메모리 영역을 가리키게 된다. 
이 포인터를 사용할 경우 충돌이 발생하거나 유효 메모리 영역을 덮어 쓸 수 있다.

반복적으로 메모리 해제하기

프로그램은 가끔씩 메모리를 한 번 이상 해제하여 이중 해제하게 됨
결과는 예측하기 어려움

free() 잘못 호출

동적으로 할당 받은 메모리가 아닌 다른 메모리 주소를 인자로 전달하는 경우 문제가 발생

프로세스 종료 시 메모리 누수가 발생하지 않는 이유

메모리 관리는 운영체제에 의해서 수행된다.

프로세스가 실행될 때, 운영체제는 필요한 만큼 프로세스에게 전달하고 
프로세스가 종료되거나 갑작스럽게 죽었을 때 메모리를 돌려받는다. 
즉, 운영체제는 프로세스의 모든 메모리를 회수한다. 그러므로 메모리 누수가 발생하지 않는다.

웹 서버나 데이터베이스 관리 시스템 같이 장시간 실행되는 서버는 메모리 누수에 민감함

기타

코드르에서 메모리 관련 오류를 찾아내는 도구 ‘purify’와 ‘valgrind’가 있다.

운영체제 지원

malloc()과 free()는 시스템 콜이 아니라 라이브러리 함수이다.

malloc 라이브러리가 프로세스 가상 주소 공간 안의 공간을 관리하지만

라이브러리 자체는 시스템에게 더 많은 메모리를 요구하고 반환하는 시스템 콜을 기반으로 구축된다.

brk 시스템 콜

프로그램의 break 위치를 변경하는데 사용 // break란 힙의 마지막 위치를 나타냄
brk 시스템 콜은 새로운 break 주소를 나타내는 한 개의 인자를 받는다. 
새로운 break가 현재 break 보다 큰지 작은지에 따라 힙의 크기를 증가시키거나 감소시킨다.
sbrk 시스템 콜은 증가량만을 받아들이는 것을 제외하고 비슷한 용도로 사용된다.

mmap() 시스템 콜

mmap() 시스템 콜은 가상 메모리 주소를 할당하고 페이지테이블에 매핑을 해주는 시스템 콜

1.  파일 매핑인 경우 
mmap() 시스템 콜 호출 시 필요한 가상 메모리 주소를 할당하고 하드디스크 파일과 매핑 
이후 가상 메모리 주소에 접근하는 경우 페이지 폴트가 발생하여, 
하드디스크 파일을 프레임 단위로 물리 메모리에 가져와 적재한 후 가상 메모리 주소와 물리 메모리 주소를 매핑한다.

File Backed Page 디스크 상의 특정 파일과 직접 연관된 메모리 페이지

2.  익명 매핑인 경우 mmap() 
시스템 콜 호출 시 필요한 가상 메모리 주소를 할당만한다. 
이후 가상 메모리 주소에 접근하는 경우 페이지 폴트가 발생하여, 
물리 메모리 공간을 할당한다. 이후 가상 메모리 주소와 물리 메모리 주소를 매핑한다.

Anonymous Page 특정 파일과 연관되지 않은 메모리 페이지 스택, 힙, 공유 메모리 세그먼트 등등

**
스택의 경우 mmap() 시스템 콜을 호출하여 매핑하지 않음
→ 운영체제가 자동으로 매핑
힙의 경우 운영체제가 자동으로 매핑해주지만, 대규모 메모리 할당을 할 경우 mmap()을 사용하기도 한다.

기타 함수

calloc() 함수

메모리 할당 영역을 0으로 채워서 반환, 즉 초기화를 잊어버리는 오류를 방지

realloc() 함수

이미 할당도니 공간에 대해 추가 공간이 필요할 때 사용
더 큰 새로운 영역을 확보하고 옛 영역의 내용을 복사한 후 새 영역에 대한 포인터로 변환

'운영체제(OSTEP)' 카테고리의 다른 글

빈 공간 관리  (1) 2024.11.09
OSTEP 16장  (0) 2024.10.31
OSTEP 8장  (0) 2024.10.11
OSTEP 6장  (0) 2024.10.10
OSTEP 5장  (0) 2024.10.09