Memory API: malloc(), free(), brk(), mmap() ..

Jin Hur·2021년 9월 28일
0

Two types of memory

  • Static: Code(also called as text), Data
  • Dynamic: Heap, Stack

Stack

  • 컴파일러에 의해 암묵적으로 생성된다. 때떄로 automatic memory라고도 불린다.
  • short-lived memory, 함수가 반환되는 시점에서 사라진다.
void func(){
	int x;
    // 지역변수 x는 함수가 반환되면서 스택 메모리에서 사라진다.
    // 지역변수 뿐 아니라 매개변수, 리턴 주소도 포함.
    // 함수가 호출되면, 호출된 함수(callee)의 스택 프레임(매개변수, 지역변수, 리턴 주소 포함)이
    // 호출한 함수(caller)의 스택 프레임 위에 쌓인다. 
    ...
}

Heap

  • 개발자가 명시적으로 할당받는다.
  • (relatively) long-lived memory, free() 전까지 상대적으로 긴 수명으로 (힙)메모리에서 보존된다.
void func(){
	int *x = (int *)malloc(sizeof(int));
    // x는 int 사이즈만큼 할당받은 힙공간을 가리키는 포인터 
    ...
}
  • malloc() call
    • input: memory size(how many bytes you need)
    • output: 할당된 공간을 가리키는 포인터(실패시 널 반환)
    • macro나 routine을 사용하는 것이 좋다.
      시스템에 따라 같은 long 자료형이 4B의 사이즈를 가질 수 있거나, 8B의 사이즈를 가질 수 있다. 매크로를 사용하면서 이식성 높은 프로그래밍을 할 수 있다.
      ex) malloc(sizeof(int));
      ex) malloc(strlen(s) + 1);

char str = "hello";
char
dist = (char *)malloc(strlen(str) + 1);
strcpy(dist, str);

malloc(4) (x, 좋지 않은 방법)
sizeof(long) -> (32bit)4B, (64bit)8B : 이식성이 좋다.

  • free() call
    • input: 반환할 공간을 가리키는 포인터
     int *x = (int *)malloc(10*sizeof(int));
      ...
      free(x);	// 할당 공간 반환

흔히 하는 실수

1. Forgetting to allocate memory

char * src = "hello";
char * dist;		// 포인터 변수만 선언, 이 포인터가 가리키는 공간 X
strcpy(dist, src); 	// "세그멘테이션 폴트" 발생 and die

strcpy 함수는 메모리 공간이 확보되어야 한다. 위 코드에서 dist 포인터가 가리키고 있는 공간은 없다. 아니 없다라기 보단 쓰레기 값을 바탕으로 잘못된 공간을 가리키기에 '세그멘테이션 폴트' 에러가 발생할 것이다.

C언어에서 전역변수 또는 초기화 없이 정적으로 할당된 변수는 0 또는 NULL로 초기화된다.
따라서 포인터 변수가 스택 또는 힙에 할당되면 NULL이 아닌 쓰레기값을 가지게 될 것이고, 이는 곳 잘못된(허가받지 않은) 공간을 참조하는 것과 같다.

올바른 구현은 아래와 같다.

char * dist = (char *)malloc(strlen(src) + 1);

2. Not allocating enough Memory

이 문제는 메모리를 할당하지 않아 발생하는 1번 에러보다 더 안좋을 수 있다.
1번 에러는 바로 죽지만, 지금과 같은 에러는 '버퍼 오버플로우(buffer overflow)'와 같은 보안상 위험 에러를 남긴다.

char * src = "hello";
char * dst = (char *)malloc(strlen(src));	// too small, 널 문자를 위한 공간이 없음
strcpy(dst, stc);	// 작동은 정상적으로 한다. 

마지막 널 문자가 어디에 dst가 가리키는 버퍼를 넘어 어느 주소에 들어갈 지 모른다. 이러한 상황은 보안에 큰 문제가 될 수 있다.


3. Fogetting to initiallize allocated memory

Heap 공간은 초기화하지 않으면 garbage 값이 있다.(unknown value)
-> 초기화가 필요하다.


4. Forgetting to free memory

메모리를 할당받고 사용하지 않음에도 free()와 같은 함수로 할당을 해제시키지 않으면 memory leak(메모리 누수) 상황이 발생한다.

참고로 자바와 같은 언어는 '가비지 컬렉션' 메카니즘을 지원한다. 명시적인 free() 호출없이 해당 공간을 가리키는 변수가 없으면 할당을 해제해 준다.

5. Freeing memory before you are done with it

포인터 변수가 할당받은 공간을 가리키고 있는데, 해당 공간을 해제시키면서 발생할 수 있는 상황이 있다. 이를 dangling pointer라 한다.

포인터 변수는 잘못된 주소를 가리킬 수 있으며, 이를 사용할 시 세그멘테이션 폴트와 같은 에러가 나올 수 있다.


6. Freeing memory repeatedly

같은 공간에 대해 중복 free()하지 않게 주의해야 한다.

7. Calling free() incorrectly

할당받은 공간이 없는 포인터 변수에 대해 free()를 호출하지 않도록 주의한다.

메모리 관련 문제를 돕는 툴(tool)들

  • Purify
  • Valgrind
  • ..

Underly OS support / Other Calls

Underlying OS support

  • malloc()/free()는 유저 레벨의 라이브러리에서 제공한다. (c언어 표준 라이브러리)

  • 라이브러리가 내부적으로 sys_brk()나 sys_mmap() 시스템 콜을 사용하여 몇 몇 페이지를 할당을 받고, malloc()이나 free() 함수를 통해 관리한다.

  • 만약 공간이 부족하다면, OS에 페이지를 추가 공간을 요구한다. 마찬가지로 sys_brk()나 sys_mmap() 시스템 콜 사용

Other Calls

  • calloc(): allocate and zero space: 힙 영역 메모리를 할당하고 0으로 초기화해준다.
  • realloc(): 더 넓은 공간을 위해 새로운 큰 영역을 할당하고, 기존의 old region 값들을 복사해준다. 이 후 새로운 공간의 포인터 반환

0개의 댓글