[TIL/크래프톤 정글] DAY 49

배재준·2025년 4월 27일

크래프톤 정글 - TIL

목록 보기
42/93
post-thumbnail

2025.04.27

TIL(TODAY I LEARN)


  • 오늘한 내용 : C - 가용 리스트 조작을 위한 기본 상수 및 매크로 정의

  • WEEK07: 시스템 콜, 데이터 세그먼트, 메모리 단편화, sbrk/mmap


가용 리스트 조작을 위한 기본 상수 및 매크로 정의

// 워드 및 더블 워드 크기 정의
#define WSIZE       4           // 워드 크기(바이트). 헤더와 풋터의 크기 단위
#define DSIZE       8           // 더블 워드 크기. 블록의 최소 크기 및 정렬 기준
#define CHUNKSIZE   (1 << 12)   // 힙 확장 시 요청할 기본 크기(4KB)

// 유틸리티 매크로
#define MAX(x, y)      ((x) > (y) ? (x) : (y))      // 두 값 중 최대값 반환
#define PACK(size, alloc)  ((size) | (alloc))       // 크기와 할당 비트를 하나의 워드로 결합

// 메모리 접근 매크로
#define GET(p)         (*(unsigned int *)(p))       // 주소 p에서 4바이트 워드 읽기
#define PUT(p, val)    (*(unsigned int *)(p) = (val))// 주소 p에 4바이트 워드 저장

// 헤더/푸터 워드에서 크기 및 할당 정보 추출
#define GET_SIZE(p)    (GET(p) & ~0x7)               // 워드에서 하위 3비트를 제외한 크기 정보
#define GET_ALLOC(p)   (GET(p) & 0x1)               // 워드에서 최하위 비트(할당 여부) 추출

// 블록 포인터(bp)를 기준으로 헤더와 풋터 위치 계산
#define HDRP(bp)       ((char *)(bp) - WSIZE)       // 블록 유효 영역 시작(bp)에서 헤더 시작 위치
#define FTRP(bp)       ((char *)(bp) + GET_SIZE(HDRP(bp)) - DSIZE)  
                                                     // 블록 크기만큼 이동한 후 풋터 시작 위치

// 다음·이전 블록의 블록 포인터 계산
#define NEXT_BLKP(bp)  ((char *)(bp) + GET_SIZE(((char *)(bp) - WSIZE)))
                                                     // 현재 블록 끝에서 다음 블록의 유효 영역 시작 위치
#define PREV_BLKP(bp)  ((char *)(bp) - GET_SIZE(((char *)(bp) - DSIZE)))
                                                     // 현재 블록 헤더 바로 전에서 이전 블록의 유효 영역 시작 위치
  1. CHUNKSIZE (1<<12) 에서의 비트 연산

    • 1 << 12 는 1을 왼쪽으로 12비트 이동시킨 결과(2ⁱ²)로, 4096(4 KB)를 의미합니다.
    • 힙을 한 번에 늘릴 때 기본 단위를 4 KB(=페이지 크기)로 잡기 위해 사용합니다.
    • 비트 시프트 연산은 산술 곱셈보다 연산 비용이 낮고, “2의 거듭제곱” 값을 표현할 때 가독성도 좋습니다.
  2. PACK(size, alloc) ((size) | (alloc)) 의 비트 OR 연산

    • 블록size는 항상 짝수(8의 배수) → size 가장 오른쪽 비트는 항상 0 → or 연산을 통해 할당 여부 확인 가능
    // size 예: 32 (0b0010_0000), alloc 예: 1 (0b0000_0001)
    PACK(32, 1)0b0010_0000 | 0b0000_0001 = 0b0010_0001 (33)
    • 헤더/풋터 워드 하나에 블록 크기(size)와 할당 여부 비트(alloc: 0 또는 1) 를 동시에 저장
    • 예) size = 32(0x20), alloc = 10x20 | 0x1 = 0x21
    • 하위 비트(LSB)를 할당 플래그로, 상위 비트들을 블록 크기로 재해석합니다.
  3. GET(p)·PUT(p,val) 에서 unsigned int 사용 이유

    • unsigned int 는 보통 4 바이트(32비트) 이므로, 헤더/풋터 정보를 한 번에 읽고 쓸 때 적당한 크기
    • GET(p) (*(unsigned int *)(p))
      • 포인터 punsigned int* 로 보고 4 바이트를 한 번에 메모리에서 읽음
    • PUT(p,val) (*(unsigned int *)(p) = (val))
      • 같은 방식으로 4 바이트를 메모리에 저장
  4. GET_SIZE(p)·GET_ALLOC(p) 에서의 16진수 마스크 연산

    • GET(p) 가 반환한 32비트 워드에서
      • 크기만 추출: GET(p) & ~0x7
        • 0x7 은 하위 3비트(000...0111)를 의미
        • ~0x7111...1000 이므로, 하위 3비트를 0으로 만들고(정렬 정보 제거) 나머지 상위 비트를 크기로 해석
      • 할당 플래그만 추출: GET(p) & 0x1
        • LSB(최하위 비트) 가 1이면 “할당(allocated)”, 0이면 “free”
  5. 블록 포인터 연산에서 char* 캐스팅을 하는 이유

    • C 표준에서 포인터 산술 연산은 “타입 크기 × n 바이트” 만큼 주소가 이동
      • 예) int* p; p + 1sizeof(int) 만큼(보통 4 바이트) 이동
    • 헤더/풋터 위치나 다음 블록 시작 위치를 바이트 단위 로 정확하게 조작하려면,
      • 일단 (char*)bp 로 캐스팅해 “1바이트 단위” 로 계산
    • 예)
      HDRP(bp)     = (char*)bp - WSIZE;                           // bp 기준으로 4 바이트 뒤(앞) 로 헤더
      NEXT_BLKP(bp) = (char*)bp + GET_SIZE((char*)bp - WSIZE);    // bp에서 블록 크기만큼 더 가면 다음 bp
    • 이렇게 하면 “정렬, 헤더·풋터 오프셋, 블록 크기” 등을 오차 없이 바이트 단위로 계산할 수 있습니다.

0개의 댓글