포스트

brk(), sbrk()

본 글은 제 개인적인 공부를 위해 작성한 글입니다. 틀린 내용이 있다면 언제든지 피드백을 주시면 감사하겠습니다. 참고로만 활용해주시길 바랍니다.

데이터, BSS, 힙 영역을 통칭하여 ‘데이터 세그먼트’라고 부르기도 한다. (구 버전 위키 피셜)
이 글에서도 데이터, BSS, 힙 영역을 통칭하여 데이터 세그먼트라고 부르겠다.

Program Break


image 메모리 구조

Program Break는 프로세스의 데이터 세그먼트의 끝을 넘어서는 첫 번째 위치의 주소이다.

Program Break를 증가시키는 것은 프로세스에 메모리를 할당하는 효과를 가져오고,

Program Break를 감소시키면 메모리 할당이 해제된다.

brk(), sbrk()


brk()sbrk()데이터 세그먼트에 할당된 메모리 양을 제어하기 위해 Unix 및 Unix-like 운영 체제에서 사용되는 기본 메모리 관리 System Call이다.

이 함수들은 일반적으로 malloc()과 같은 상위 수준의 메모리 관리 라이브러리 함수에서 호출된다.

원래 Unix 시스템에서는 애플리케이션이 추가적인 데이터 공간을 확보할 수 있는 유일한 방법은 brk()와 sbkr()뿐 이었지만 이후 버전에서는 mmap()을 통해서도 가능하다.

brk()와 sbrk()는 호출 프로세스의 데이터 세그먼트의 끝을 정의하는 Program Break의 위치를 재설정하여 데이터 세그먼트에 할당된 공간의 양을 동적으로 변경한다.

1
2
3
4
5
6
7
8
9
10
11
// brk()와 sbrk()의 원형
#include <unistd.h>
/* Set the end of accessible data space (aka "the break") to ADDR.
	Returns zero on success and -1 for errors (with errno set).  */
int brk(void *addr);

/* Increase or decrease the end of accessible data space by DELTA bytes.
	If successful, returns the address the previous end of data space
	(i.e. the beginning of the new space, if DELTA > 0);
	returns (void *) -1 for errors (with errno set).  */
void *sbrk(intptr_t delta);

intptr_t 타입은 포인터의 주소를 저장하는 자료형
포인터와 정수 사이의 변환을 수행하기 적합하여, 포인터를 안전하게 저장할 수 있는 정수 타입이라고 생각하자.

brk()

  1. 기능

    인자로 들어온 주소로 Program Break를 설정

  2. 매개변수

    설정하고자 하는 Program Break의 주소 값

  3. 반환 값

    성공 시 → 0

    실패 시 → -1을 반환하고 errno 전역변수를 ENOMEM으로 설정하여할당이 실패한 이유를 표시

sbrk()

  1. 기능

    인자로 들어온 delta만큼 Program Break를 조정 (양/음수 모두 가능)

    sbrk(0)을 호출하면 현재 Program Break의 주소 값을 가져올 수 있다.

  2. 매개변수

    바이트 단위의 정수

    e.g.
    sbrk(0xf) → 15바이트만큼 Program Break 증가
    sbrk(5) → 5바이트만큼 Program Break 증가

  3. 반환 값

    성공 시 → 이전 Program Break의 주소 값

    실패 시 → (void *)-1를 반환하고 errno 전역변수를 ENOMEM으로 설정하여 할당이 실패한 이유를 표시

sbrk() 예시

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <unistd.h>
#include <stdio.h>

int main()
{
    // printf가 버퍼 메모리를 선언하기에 첫 printf 후에 Program Break가 증가한다.
    // sbrk()의 반환 값은 이전의 Program Break의 위치인 것을 기억하자.
    void *currentBrk = sbrk(0);
    printf("First  %p\n", currentBrk);

    currentBrk = sbrk(0);
    printf("Second %p\n", currentBrk);

    // printf가 생성한 버퍼가 이미 존재하므로 더 이상 Program Break가 증가하지 않는다.
    currentBrk = sbrk(0);
    printf("Third  %p\n", currentBrk);

    // 이제 Program Break를 증가시키고 확인해보자.
    currentBrk = sbrk(0x5);
    printf("Fourth %p\n", currentBrk);

    currentBrk = sbrk(0);
    printf("Fifth  %p\n", currentBrk);
    /*
        First  0x55f4b1657000
        Second 0x55f4b1678000
        Third  0x55f4b1678000
        Fourth 0x55f4b1678000
        Fifth  0x55f4b1678005 -> 5만큼 증가하였다.
    */

    return 0;
}



Q) malloc()이 실패하는 경우는 sbrk()로 힙 세그먼트 크기를 최대로 늘렸음에도 불구하고 부족해서 발생하는 것인가요?

A) 그렇다. sbrk()malloc() 내부적으로 자동 수행된다. 더 이상 할당 가능한 공간이 없으면 할당기가 sbrk()를 호출해 힙 영역을 늘리고, 늘어난 영역에 대해 새롭게 공간을 할당해서 반환하는 것이다.

malloc()을 끊임없이 반복한다면 언젠가는 더 이상 sbrk()를 호출해서 영역을 늘릴 수 없는 순간이 올테고, 보통 그 실패 지점은 OS Kernel이 필요로 하는 공간 크기가 침범될 위험이 발견될 때이다.

즉, sbrk()가 실패하는 순간이 곧 malloc()이 실패하는 순간이 되는 것이다.

참고

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.