728x90
10. 막간 : 메모리 관리 API
10.1 메모리 공간의 종류
- C 프로그램이 실행되면, 두 가지 유형의 메모리 공간이 할당된다. 첫번째는 스택(Stack) 메모리라고 불리며 할당과 반환은 프로그래머를 위해 컴파일러에 의해 암묵적으로 이루어진다. 이러한 이유 때문에 때로는 자동 C automatic() 메모리라고 불린다.
- 함수를 선언하면 C프로그램에서 스택에 메모리를 할당한다. 함수에서 리턴하면 컴파일러는 프로그래머 대신에 메모리를 반환한다. 함수 리턴 이후에도 유지되어야 하는 정보는 스택에 저장하지 않는 것이 좋다.
- 오랫동안 값이 유지되어야 하는 변수를 위해 힙(heap) 메모리라고 불리는 두 번째 유형의 메모리가 필요하다. 모든 할당과 반환이 프로그래머에 의해 명시적으로 처리된다.
void func()
{
int* x = (int*)malloc(sizeof(int));
...
}
- 이 코드의 경우 한 행에 스택과 힙 할당이 모두 발생한다. 포인터 변수의 선언을 만나면 정수 포인터를 위한 공간을 할당하고 malloc을 호출해서 정수를 위한 공간을 heap으로 부터 요구한다.
10.2 malloc() 함수
- malloc() 호출은 간단하다. 힙에 요청할 공간의 크기를 넘겨주면, 성공했을 경우 새로 할당된 공간에 대한 포인터를 사용자에게 반환하고 실패했을 경우 NULL을 반환한다.
#include<stdlib.h>
...
void malloc(size_t);
- malloc ()의 인자는 size_t 타입의 변수이고 이 변수는 필요 공간의 크기를 바이트 단위로 표시한 것이다. 대부분의 프로그래머는 malloc(10) 처럼 숫자를 직접 입력하지 않는다. 이 malloc() 호출에서는 정확한 크기의 공간을 요청하기 위하여 sizeof() 연산자를 사용한다. C언어에서 이 SIZEOF()는 통상 컴파일 시간 연산자이다. 인자의 실제 크기가 컴파일 시간에 결정된다. sizeof() 는 숫자로 대체되어 malloc()에 전달된다. 이러한 이유로 sizeof()는 연산자로 간주되는게 맞으며 함수 호출이 아니다.(함수 호출은 실행 시간에 일어난다.)
- 데이터 타입 뿐만 아니라 변수의 이름도 sizeof()의 인자로 전달할 수 있다.
- malloc()은 void 타입의 포인터를 반환한다. 그렇게 하는 것은 주소만 넘겨주고 해당 주소공간에 어떤 타입의 자료를 저장할 지는 프로그래머가 결정하게 하는 전형적인 C의 방법이다. 프로그래머는 타입변환(type casting)을 통해 공간활용을 결정한다.
10.3 free() 함수
- 더 이상 사용되지 않는 힙 메모리를 해제하기 위해 프로그래머는 free() 를 호출한다.
free(x);
- 한 개의 인자, malloc()에 의해 반환된 포인터를 받는다. 할당된 영역의 크기는 전달되지 않는다. 할당된 메모리의 크기는 메모리 할당 라이브러리가 알고 있어야 한다.
10.4 흔한 오류
- 많은 새로운 언어들이 자동 메모리관리를 지원한다. 그러한 언어들에서는 메모리를 할당하기 위하여 malloc()과 유사한, 보통 new 또는 새 객체를 할당하기 위한 비슷한 루틴을 호출하는 반면에, 공간을 해제하기 위해서는 아무것도 호출하지 않는다. 쓰레기 수집기가 실행되어 참조되지 않는 메모리가 찾아 알아서 해제한다.
메모리 할당 잊어버리기
- Segmentation fault
- 많은 루틴은 자신이 호출되기 전에 필요한 메모리가 미리 할당되었다고 가정한다.
메모리를 부족하게 할당받기
- Buffer over-flow
- 목적지 버퍼 공간을 약간 부족하게 할당받는 것. malloc() 구현 방식에 따라 제대로 동작하는 것처럼 보이는 경우가 종종 있다. 어떤 경우 할당된 공간의 마지막을 지나쳐 한 바이트 만큼 공간을 더 사용한다. 이 경우 malloc함수 라이브러리가 여분의 공간을 할당하고 프로그램은 다른 변수의 값을 덮어쓰지 않고 잘 동작한다. 또 다른 경우에는 프로그램은 고장을 일으키고 크래시된다.
할당받은 메모리를 초기화하지 않기
- Uninitialized read
- 초기화하지 않으면 알수없는 값을 읽는다.
메모리 해제하지 않기
- Memory leak
- 메모리 해제를 잊었을 때 발생한다. 장시간 실행되는 응용프로그램 또는 운영체제 자체와 같은 시스템 프로그램에서는 큰 문제다. 메모리가 천천히 누수되면 결국 메모리가 부족하게 되고 시스템을 재시작할 수 밖에 없다.
- 메모리 청크의 사용이 끝나면 반드시 해제해야한다. 쓰레기 수집 기능이 있는 언어도 이 문제에는 도움이 되지 않는다. 메모리 청크에 대한 참조가 존재하면, 어느 쓰레기 수집기도 그 청크를 해제하지 않을 것이고, 따라서 이런 현대적인 언어에서도 메모리 누수는 여전히 문제가 된다.
메모리 사용이 끝나기 전에 메모리 해제하기
- Dangling pointer
- 메모리 사용이 끝나기 전에 메모리를 해제하는 것이다. 차후 그 포인터를 사용한다면 프로그램을 크래시 시키거나 유효 메모리 영역을 덮어 쓸 수 있다.
반복적으로 메모리 해제하기
- Doubble free
- 메모리를 한번 이상 해제하는 경우이다. 결과는 예측하기 어렵다. 가장 흔히 일어나는 결과는 크래시다.
free() 잘못 호출하기
- Invalid free
- free()를 잘못 호출하는 것이다. free()는 malloc() 받은 포인터만 전달될 것으로 예상한다. 그 의외의 값을 전달하면 문제가 발생한다.
10.5 운영체제의 지원
- malloc() 과 free()는 시스템 콜이 아니라 라이브러리 함수이다. malloc() 라이브러리가 프로세스 가상 주소공간 안의 공간을 관리하지만 라이브러리 자체는 시스템에게 더 많은 메모리를 요구하고 반환하는 시스템 콜을 기반으로 구축된다.
- 그런 시스템 콜 중 하나가 brk라고 불리는 시스템 콜로서, 프로그램의 break 위치를 변경하는데 사용된다 break는 힙의 마지막 위치를 나타낸다. brk는 새로운 break 주소를 나타내는 한 개의 인자를 받는다. 새로운 break가 현재 break보다 큰지 작은지에 따라 힙의 크기를 증가시키거나 감소시킨다. sbrk는 증가량을 받아들이는 것을 제외하고 비슷한 용도로 사용된다.
- brk 또는 sbrk를 직접 호출해서는 안된다는 것에 주의해라. 이들은 메모리 할당 라이브러리에 의해 사용된다. 직접 사용하면 감당할 수 없는 결과가 발생한다.
- 마지막으로, mmap() 함수를 사용하며 운영체제로부터 메모리를 얻을 수 있다. 올바른 인자를 전달하면 mmap() 은 프로그램에 anoymous의 메모리 영역을 만든다. annouymous영역은 특정 파일과 연결되어 있지 않고 스왑공간에 연결된 영역을 말한다.
728x90
'책 > 운영체제' 카테고리의 다른 글
운영체제 12. 세그멘테이션 (0) | 2024.03.09 |
---|---|
운영체제 11. 주소변환의 원리 (0) | 2024.03.09 |
운영체제 9. 주소공간의 개념 (0) | 2024.03.05 |
운영체제 8. 멀티 프로세서 스케줄링(Multi Processor Scheduling) (1) | 2024.03.04 |
운영체제 7. 스케줄링 : 비례 배분 (Proportional Share) 스케줄러, 공정 배분(fair share) 스케줄러 (0) | 2024.03.03 |