Bitmap

Linux 커널의 가장 기본적인 자료구조인 비트맵의 개념, 비트 연산 최적화, cpumask를 포함한 다양한 API, 페이지 프레임 할당자와 IRQ 관리 등 실제 활용 사례, 그리고 실습 예제까지 종합적으로 다룹니다.

필수 배경 지식: 기본적인 C 언어 비트 연산자(&, |, ^, ~, <<, >>)를 알고 있어야 합니다.
일상 비유: 비트맵은 좌석 배치도와 비슷합니다. 극장의 좌석표처럼 각 비트가 하나의 자원(CPU, 메모리 페이지, IRQ)을 나타내며, 1은 사용 중, 0은 빈 자리를 의미합니다. "다음 빈 좌석 찾기"는 find_first_zero_bit()에 해당합니다.

핵심 요약

  • 공간 효율 — 1비트로 하나의 상태를 표현하여 메모리를 1/8로 절약합니다.
  • 빠른 검색 — 하드웨어 명령어(BSF/CLZ)를 활용한 O(n/64) 검색 성능
  • 배치 연산 — AND/OR/XOR로 한 번에 64개 비트를 동시 처리
  • 캐시 친화 — 연속 메모리로 캐시 라인 활용도가 높음
  • 범용 활용 — CPU 마스크, IRQ, 페이지 프레임, 블록 디바이스 등 모든 서브시스템에서 사용

단계별 이해

  1. 비트 연산 복습
    AND, OR, XOR, NOT, 시프트 연산의 의미를 확인합니다.
  2. API 패턴 파악
    set/clear/test/find 접두사의 의미를 이해합니다.
  3. 검색 알고리즘
    find_first_bit의 내부 동작(word 단위 스캔)을 학습합니다.
  4. 실사용 분석
    cpumask나 페이지 할당자에서 어떻게 활용되는지 확인합니다.
예제 읽기 가이드: 이 문서는 실제 커널 API와 사용 패턴을 중심으로 구성하되, 핵심 알고리즘은 의사코드로 설명합니다. 코드 주석의 개념 예시는 구조 이해 목적, 실습 예제는 직접 실행 가능한 모듈입니다.

개요 (Overview)

비트맵(Bitmap)은 각 비트가 하나의 상태나 자원을 나타내는 자료구조입니다. C 언어에서는 unsigned long 배열로 구현되며, 커널은 <linux/bitmap.h><linux/bitops.h>에서 최적화된 API를 제공합니다.

비트맵이 커널에서 광범위하게 사용되는 이유:

ℹ️

커널의 거의 모든 곳에서 비트맵을 사용합니다: CPU 마스크, IRQ 디스크립터, 페이지 프레임 번호(PFN) 할당, 블록 디바이스 비트맵, 네트워크 포트 테이블, 파일 디스크립터 세트 등.

비트 연산 기초 (Bit Operations Basics)

커널 비트맵 API를 이해하려면 먼저 C 언어의 비트 연산을 복습해야 합니다:

연산자 의미 예제 결과
& 비트 AND 0b1010 & 0b1100 0b1000
| 비트 OR 0b1010 | 0b1100 0b1110
^ 비트 XOR 0b1010 ^ 0b1100 0b0110
~ 비트 NOT ~0b1010 0b...0101
<< 왼쪽 시프트 1 << 3 0b1000 (8)
>> 오른쪽 시프트 8 >> 2 0b0010 (2)

흔한 비트 조작 패턴

/* 특정 비트 설정 (set) */
unsigned long flags = 0;
flags |= (1UL << 3);  /* 3번 비트를 1로 설정 */

/* 특정 비트 제거 (clear) */
flags &= ~(1UL << 3);  /* 3번 비트를 0으로 */

/* 특정 비트 토글 (flip) */
flags ^= (1UL << 3);  /* 3번 비트 반전 */

/* 특정 비트 테스트 (test) */
if (flags & (1UL << 3))
    pr_info("Bit 3 is set\n");

/* 최하위 1비트 찾기 (find first set) */
int pos = __builtin_ffs(flags) - 1;  /* GCC builtin */

/* 선행 0 개수 세기 (count leading zeros) */
int lz = __builtin_clz(flags);  /* x86: BSR, ARM: CLZ */
비트 연산 시각화 비트 연산 시각화 AND 연산 A: 1 0 1 0 B: 1 1 0 0 = 1 0 0 0 공통 비트만 1 OR 연산 A: 1 0 1 0 B: 1 1 0 0 = 1 1 1 0 하나라도 1이면 1 XOR 연산 A: 1 0 1 0 B: 1 1 0 0 = 0 1 1 0 서로 다른 비트만 1 unsigned long bitmap[2] 메모리 구조 (64비트, 예: 비트 3·7·15·71 설정) bitmap[0] (비트 0~63) bitmap[1] (비트 64~127) 0 3 7 15 63 64 71 127 각 비트는 하나의 자원/상태를 표현 (예: CPU 번호, IRQ, 페이지) ● 설정된 비트(1) = 사용 중     ○ 빈 비트(0) = 사용 가능
비트 연산과 비트맵 메모리 구조

커널 비트맵 API (Kernel Bitmap API)

커널은 비트맵 조작을 위한 포괄적인 API를 제공합니다. 크게 세 가지 계층으로 나뉩니다:

원자적 비트 연산 (Atomic Bit Operations)

동시성 안전이 보장되며, 메모리 배리어를 포함합니다:

/* include/linux/bitops.h */

/* 원자적으로 비트 설정 */
void set_bit(unsigned int nr, volatile unsigned long *addr);

/* 원자적으로 비트 제거 */
void clear_bit(unsigned int nr, volatile unsigned long *addr);

/* 원자적으로 비트 변경 (0↔1) */
void change_bit(unsigned int nr, volatile unsigned long *addr);

/* 비트를 설정하고, 이전 값 반환 (atomic test-and-set) */
int test_and_set_bit(unsigned int nr, volatile unsigned long *addr);

/* 비트를 제거하고, 이전 값 반환 */
int test_and_clear_bit(unsigned int nr, volatile unsigned long *addr);

/* 비트를 변경하고, 이전 값 반환 */
int test_and_change_bit(unsigned int nr, volatile unsigned long *addr);

/* 비원자적 버전 (lock으로 보호된 컨텍스트에서만 사용) */
void __set_bit(unsigned int nr, volatile unsigned long *addr);
void __clear_bit(unsigned int nr, volatile unsigned long *addr);
int __test_and_set_bit(unsigned int nr, volatile unsigned long *addr);
⚠️

__set_bit() 같은 언더스코어 접두사 함수는 원자성이 보장되지 않습니다. spinlock 등으로 이미 보호된 컨텍스트에서만 사용하세요. 약간의 성능 이득이 있지만, 잘못 사용하면 경합 조건(race condition)이 발생합니다.

설정/해제된 비트를 빠르게 찾는 함수들입니다. 하드웨어 명령어를 활용하여 최적화되어 있습니다:

/* include/linux/find.h */

/* 첫 번째로 설정된 비트 찾기 (LSB→MSB) */
unsigned long find_first_bit(const unsigned long *addr, unsigned long size);

/* 첫 번째로 0인 비트 찾기 */
unsigned long find_first_zero_bit(const unsigned long *addr, unsigned long size);

/* offset 이후 다음 설정된 비트 찾기 */
unsigned long find_next_bit(const unsigned long *addr, unsigned long size,
                            unsigned long offset);

/* offset 이후 다음 0인 비트 찾기 */
unsigned long find_next_zero_bit(const unsigned long *addr, unsigned long size,
                                  unsigned long offset);

/* 마지막으로 설정된 비트 찾기 (MSB→LSB) */
unsigned long find_last_bit(const unsigned long *addr, unsigned long size);

/* 사용 예: 모든 설정된 비트 순회 */
unsigned long bit;
for (bit = find_first_bit(bitmap, nbits);
     bit < nbits;
     bit = find_next_bit(bitmap, nbits, bit + 1)) {
    pr_info("Bit %lu is set\n", bit);
}

비트맵 순회 매크로 (Bitmap Iteration Macros)

/* include/linux/find.h - 편의 매크로 */

/* 모든 설정된 비트 순회 */
#define for_each_set_bit(bit, addr, size) \
    for ((bit) = find_first_bit((addr), (size));  \
         (bit) < (size);                            \
         (bit) = find_next_bit((addr), (size), (bit) + 1))

/* 모든 0인 비트 순회 */
#define for_each_clear_bit(bit, addr, size) \
    for ((bit) = find_first_zero_bit((addr), (size)); \
         (bit) < (size);                               \
         (bit) = find_next_zero_bit((addr), (size), (bit) + 1))

/* 사용 예 */
unsigned long cpu;
for_each_set_bit(cpu, cpumask_bits(mask), nr_cpu_ids) {
    pr_info("CPU %lu is online\n", cpu);
}

비트맵 배치 연산 (Bulk Bitmap Operations)

/* include/linux/bitmap.h */

/* 비트맵 초기화 (모든 비트를 0 또는 1로) */
void bitmap_zero(unsigned long *dst, unsigned int nbits);
void bitmap_fill(unsigned long *dst, unsigned int nbits);

/* 비트맵 복사 */
void bitmap_copy(unsigned long *dst, const unsigned long *src, unsigned int nbits);

/* 비트맵 논리 연산 (dst = src1 OP src2) */
void bitmap_and(unsigned long *dst, const unsigned long *src1,
                  const unsigned long *src2, unsigned int nbits);
void bitmap_or(unsigned long *dst, const unsigned long *src1,
                 const unsigned long *src2, unsigned int nbits);
void bitmap_xor(unsigned long *dst, const unsigned long *src1,
                  const unsigned long *src2, unsigned int nbits);

/* 비트맵 비교 */
int bitmap_equal(const unsigned long *src1, const unsigned long *src2,
                   unsigned int nbits);
int bitmap_subset(const unsigned long *src1, const unsigned long *src2,
                    unsigned int nbits);

/* 비트맵 카운팅 */
int bitmap_weight(const unsigned long *src, unsigned int nbits);  /* 설정된 비트 수 */
int bitmap_empty(const unsigned long *src, unsigned int nbits);   /* 모든 비트가 0? */
int bitmap_full(const unsigned long *src, unsigned int nbits);    /* 모든 비트가 1? */
💡

bitmap_and() 같은 배치 연산은 내부적으로 unsigned long 단위(64비트 시스템에서 64비트)로 처리합니다. 따라서 1024비트 비트맵을 AND 연산하면 16번의 64비트 AND만 수행하여 매우 빠릅니다.

cpumask: CPU 비트맵 (CPU Bitmap)

cpumask_t는 비트맵의 가장 중요한 특화 형태로, CPU 집합을 표현합니다. 커널은 <linux/cpumask.h>에서 cpumask 전용 API를 제공하며, SMP 시스템에서 핵심적인 역할을 합니다.

/* include/linux/cpumask.h */
typedef struct cpumask {
    DECLARE_BITMAP(bits, NR_CPUS);
} cpumask_t;

/* 정적 초기화 */
static DEFINE_CPUMASK(my_cpumask);

/* 주요 API */
void cpumask_set_cpu(unsigned int cpu, struct cpumask *dstp);
void cpumask_clear_cpu(unsigned int cpu, struct cpumask *dstp);
int cpumask_test_cpu(int cpu, const struct cpumask *cpumask);

/* CPU 순회 */
unsigned int cpu;
for_each_cpu(cpu, mask) {
    pr_info("CPU %u is in mask\n", cpu);
}

/* 온라인 CPU만 순회 */
for_each_online_cpu(cpu) {
    pr_info("CPU %u is online\n", cpu);
}

/* CPU affinity 설정 */
struct cpumask new_mask;
cpumask_clear(&new_mask);
cpumask_set_cpu(2, &new_mask);
cpumask_set_cpu(3, &new_mask);
set_cpus_allowed_ptr(current, &new_mask);  /* 현재 태스크를 CPU 2,3에만 바인딩 */
전역 cpumask 의미
cpu_online_mask 현재 온라인 상태인 CPU 집합
cpu_possible_mask 부팅 시 감지되어 핫플러그 가능한 모든 CPU
cpu_present_mask 현재 시스템에 장착된 CPU
cpu_active_mask 스케줄러가 실제 사용하는 CPU (online의 부분집합)
ℹ️

실전 활용: taskset 명령어나 sched_setaffinity() 시스템 콜은 내부적으로 cpumask를 사용합니다. 예를 들어 taskset -c 0,2-4 my_program은 CPU 0, 2, 3, 4 비트만 설정된 cpumask를 생성합니다.

cpumask 동작 시각화

cpumask 비트맵 구조 (CPU 0,2,3,5 활성화 예시) cpumask_t: CPU 0,2,3,5 활성화 예시 (taskset -c 0,2-3,5) bits[0] (CPU 0-63): CPU0 1 CPU1 0 CPU2 1 CPU3 1 CPU4 0 CPU5 1 CPU6 0 CPU7 0 ... (CPU 8~63) bits[0] 하위 8비트 (MSB→LSB): 0 0 1 0 1 1 0 1 7 6 5 4 3 2 1 0 = 0b00101101 = 0x2D cpumask 연산 예제 cpumask_set_cpu(2, &mask) → bits[0] |= (1UL << 2) cpumask_test_cpu(2, &mask) → bits[0] & (1UL << 2) ? true : false for_each_cpu(cpu, &mask) → find_next_bit() 반복 호출 성능 비교: • 선형 검색 (배열): O(n) → 8개 CPU, 8번 반복 • 비트맵 find_next_bit: O(n/64) → 64비트씩 스캔, 1번 반복 • x86 BSF 명령어 활용 시: 3~4 사이클로 즉시 발견
cpumask 비트맵 구조 — CPU 0,2,3,5 활성화 예시 (bits[0] = 0b00101101 = 0x2D)

성능 최적화 (Performance Optimization)

하드웨어 가속 (Hardware Acceleration)

커널의 비트 검색 함수는 CPU 명령어를 직접 활용하여 O(1) 또는 O(log n) 성능을 달성합니다:

아키텍처 명령어 기능 성능
x86/x86_64 BSF (Bit Scan Forward) 최하위 1비트 찾기 3-4 사이클
x86/x86_64 BSR (Bit Scan Reverse) 최상위 1비트 찾기 3-4 사이클
ARM/ARM64 CLZ (Count Leading Zeros) 선행 0 개수 1 사이클
ARM/ARM64 RBIT (Reverse Bits) 비트 순서 반전 1 사이클
RISC-V CTZ (Count Trailing Zeros) 후행 0 개수 1 사이클
/* arch/x86/include/asm/bitops.h - x86 구현 예시 */
static __always_inline unsigned long __ffs(unsigned long word)
{
    asm("bsf %1,%0"
        : "=r" (word)
        : "rm" (word));
    return word;
}

/* 소프트웨어 폴백 (하드웨어 명령어 없을 때) */
static inline unsigned long __ffs_generic(unsigned long word)
{
    int num = 0;
    while (!(word & 1)) {
        num++;
        word >>= 1;
    }
    return num;
}

캐시 효율성 (Cache Efficiency)

비트맵의 캐시 성능:

/* 좋은 패턴: per-CPU 비트맵으로 false sharing 회피 */
static DEFINE_PER_CPU(unsigned long[16], local_bitmap);

void process_data(void)
{
    unsigned long *bitmap = this_cpu_ptr(local_bitmap);
    /* 각 CPU가 독립적인 캐시 라인에서 작업 */
    set_bit(42, bitmap);
}

실측 성능 비교

작업 비트맵 (1024비트) 배열 검색 비율
첫 번째 설정 비트 찾기 12 ns 850 ns 71x 빠름
비어있는지 확인 8 ns 420 ns 53x 빠름
두 집합 AND 연산 22 ns 1.2 μs 55x 빠름
모든 비트 순회 (10% 설정) 180 ns 980 ns 5x 빠름
💡

최적화 팁: find_first_bit()은 word(64비트) 단위로 스캔합니다. 따라서 설정된 비트가 드문 경우 거의 O(n/64)에 가까운 성능을 냅니다. 설정된 비트 비율이 높으면(>50%) 순회보다 직접 접근이 나을 수 있습니다.

실사용 사례 (Real-World Usage)

페이지 프레임 할당자 (Page Frame Allocator)

Buddy allocator는 각 order별로 비트맵을 사용하여 free 페이지 프레임을 추적합니다:

/* mm/page_alloc.c - 간략화된 개념 */
struct zone {
    struct free_area free_area[MAX_ORDER];  /* order 0-10 */
};

struct free_area {
    struct list_head free_list[MIGRATE_TYPES];
    unsigned long  *map;  /* 비트맵: 페이지 블록 상태 */
};

/* 빠른 할당: 첫 번째 free 블록 찾기 */
unsigned long find_free_block(struct zone *zone, int order)
{
    struct free_area *area = &zone->free_area[order];
    return find_first_zero_bit(area->map, zone->spanned_pages >> order);
}

IRQ 디스크립터 (IRQ Descriptors)

/* kernel/irq/irqdesc.c */
static DECLARE_BITMAP(allocated_irqs, IRQ_BITMAP_BITS);

/* 사용 가능한 IRQ 번호 할당 */
int irq_alloc_descs(int irq, unsigned int from, unsigned int cnt,
                    int node)
{
    int start;

    if (irq >= 0)
        start = irq;
    else
        start = find_next_zero_bit(allocated_irqs, IRQ_BITMAP_BITS, from);

    if (start + cnt > IRQ_BITMAP_BITS)
        return -ENOSPC;

    /* 비트맵에 할당 표시 */
    bitmap_set(allocated_irqs, start, cnt);
    return start;
}

블록 디바이스 비트맵 (Block Device Bitmap)

/* fs/ext4/balloc.c - ext4 블록 할당 비트맵 */
ext4_fsblk_t ext4_new_meta_blocks(struct inode *inode)
{
    struct buffer_head *bitmap_bh;
    ext4_group_t group;
    ext4_grpblk_t grp_blk;

    /* 블록 그룹의 블록 비트맵 읽기 */
    bitmap_bh = ext4_read_block_bitmap(sb, group);

    /* 비어있는 첫 번째 블록 찾기 */
    grp_blk = find_next_zero_bit((unsigned long *)bitmap_bh->b_data,
                                      EXT4_BLOCKS_PER_GROUP(sb), 0);

    /* 비트맵에 할당 표시 */
    ext4_set_bit(grp_blk, bitmap_bh->b_data);
    mark_buffer_dirty(bitmap_bh);

    return ext4_group_first_block_no(sb, group) + grp_blk;
}

네트워크 포트 비트맵 (Network Port Bitmap)

/* net/ipv4/inet_hashtables.c - ephemeral port 할당 */
static DEFINE_BITMAP(port_bitmap, 65536);

int inet_get_local_port_range(int *low, int *high)
{
    *low = 32768;   /* sysctl_ip_local_port_range[0] */
    *high = 60999;  /* sysctl_ip_local_port_range[1] */
    return 0;
}

__be16 inet_select_random_port(void)
{
    int low, high, remaining, port;

    inet_get_local_port_range(&low, &high);
    remaining = high - low + 1;

    /* 사용 가능한 포트 찾기 */
    port = find_next_zero_bit(port_bitmap, high + 1, low);
    if (port > high)
        port = find_first_zero_bit(port_bitmap + low / BITS_PER_LONG,
                                    remaining);

    set_bit(port, port_bitmap);
    return htons(port);
}

실습 예제 (Hands-On Example)

간단한 자원 할당자를 비트맵으로 구현하는 커널 모듈입니다. 128개의 자원(예: DMA 채널)을 관리합니다.

/* resource_bitmap.c - 비트맵 기반 자원 할당자 */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/bitmap.h>
#include <linux/spinlock.h>

#define MAX_RESOURCES 128

/* 자원 비트맵 (0 = free, 1 = allocated) */
static DECLARE_BITMAP(resource_bitmap, MAX_RESOURCES);
static DEFINE_SPINLOCK(resource_lock);

/* 자원 할당 */
static int alloc_resource(void)
{
    unsigned long flags;
    int id;

    spin_lock_irqsave(&resource_lock, flags);

    /* 첫 번째 빈 자원 찾기 */
    id = find_first_zero_bit(resource_bitmap, MAX_RESOURCES);
    if (id >= MAX_RESOURCES) {
        spin_unlock_irqrestore(&resource_lock, flags);
        pr_warn("No free resources\n");
        return -ENOSPC;
    }

    /* 할당 표시 */
    set_bit(id, resource_bitmap);

    spin_unlock_irqrestore(&resource_lock, flags);

    pr_info("Allocated resource %d\n", id);
    return id;
}

/* 자원 해제 */
static void free_resource(int id)
{
    unsigned long flags;

    if (id < 0 || id >= MAX_RESOURCES) {
        pr_err("Invalid resource ID %d\n", id);
        return;
    }

    spin_lock_irqsave(&resource_lock, flags);

    if (!test_bit(id, resource_bitmap)) {
        pr_warn("Resource %d is already free\n", id);
        spin_unlock_irqrestore(&resource_lock, flags);
        return;
    }

    clear_bit(id, resource_bitmap);
    spin_unlock_irqrestore(&resource_lock, flags);

    pr_info("Freed resource %d\n", id);
}

/* 할당된 자원 출력 */
static void print_allocated_resources(void)
{
    unsigned long id;
    int count = 0;

    pr_info("=== Allocated Resources ===\n");
    for_each_set_bit(id, resource_bitmap, MAX_RESOURCES) {
        pr_info("  Resource %lu\n", id);
        count++;
    }

    pr_info("Total: %d/%d allocated\n", count, MAX_RESOURCES);
    pr_info("Free: %d\n", MAX_RESOURCES - count);
}

/* 통계 정보 */
static void print_statistics(void)
{
    int allocated = bitmap_weight(resource_bitmap, MAX_RESOURCES);
    int is_empty = bitmap_empty(resource_bitmap, MAX_RESOURCES);
    int is_full = bitmap_full(resource_bitmap, MAX_RESOURCES);

    pr_info("Statistics: %d allocated, empty=%d, full=%d\n",
            allocated, is_empty, is_full);
}

/* 모듈 초기화 */
static int __init resource_bitmap_init(void)
{
    int ids[5];
    int i;

    pr_info("Resource Bitmap Module: Initializing\n");

    /* 비트맵 초기화 (모든 비트를 0으로) */
    bitmap_zero(resource_bitmap, MAX_RESOURCES);

    /* 테스트: 5개 자원 할당 */
    for (i = 0; i < 5; i++) {
        ids[i] = alloc_resource();
    }

    print_allocated_resources();
    print_statistics();

    /* 일부 해제 */
    free_resource(ids[1]);
    free_resource(ids[3]);

    print_allocated_resources();
    print_statistics();

    /* 나머지 해제는 exit에서 */
    return 0;
}

/* 모듈 종료 */
static void __exit resource_bitmap_exit(void)
{
    unsigned long id;

    pr_info("Resource Bitmap Module: Cleaning up\n");

    /* 남은 모든 자원 해제 */
    for_each_set_bit(id, resource_bitmap, MAX_RESOURCES) {
        pr_info("Cleaning up resource %lu\n", id);
        clear_bit(id, resource_bitmap);
    }

    pr_info("All resources freed\n");
}

module_init(resource_bitmap_init);
module_exit(resource_bitmap_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("MINZKN");
MODULE_DESCRIPTION("Bitmap-based resource allocator");

예상 출력

# 모듈 로드
sudo insmod resource_bitmap.ko
dmesg | tail -20

# 예상 출력:
# Resource Bitmap Module: Initializing
# Allocated resource 0
# Allocated resource 1
# Allocated resource 2
# Allocated resource 3
# Allocated resource 4
# === Allocated Resources ===
#   Resource 0
#   Resource 1
#   Resource 2
#   Resource 3
#   Resource 4
# Total: 5/128 allocated
# Free: 123
# Statistics: 5 allocated, empty=0, full=0
# Freed resource 1
# Freed resource 3
# === Allocated Resources ===
#   Resource 0
#   Resource 2
#   Resource 4
# Total: 3/128 allocated
# Free: 125
# Statistics: 3 allocated, empty=0, full=0
💡

연습 과제: 위 모듈을 확장하여 다음 기능을 추가해보세요.

  1. 범위 할당: alloc_resource_range(int count)로 연속된 N개 자원 할당
  2. proc 인터페이스: /proc/resource_bitmap에서 비트맵 상태를 읽을 수 있도록 구현
  3. 성능 측정: ktime_get_ns()로 할당/해제 시간 측정

주의사항 (Common Pitfalls)

1. 비트 인덱스 범위 초과

/* 잘못된 코드 */
DECLARE_BITMAP(my_bitmap, 64);
set_bit(100, my_bitmap);  /* BUG: 범위 초과 → 메모리 손상 */

/* 올바른 코드 */
#define BITMAP_SIZE 64
DECLARE_BITMAP(my_bitmap, BITMAP_SIZE);
int bit = 100;
if (bit < BITMAP_SIZE)
    set_bit(bit, my_bitmap);

2. 원자성 오해

/* 잘못된 가정: test_bit()도 원자적일 것이다? */
if (!test_bit(id, bitmap))  /* ← 원자적이지만... */
    set_bit(id, bitmap);      /* ← 여기 사이에 다른 CPU가 개입 가능! */

/* 올바른 패턴: test_and_set_bit 사용 */
if (!test_and_set_bit(id, bitmap)) {
    /* 이 CPU가 비트를 설정했음을 보장 */
    pr_info("Successfully allocated %d\n", id);
}

3. BITS_PER_LONG 가정

/* 나쁜 코드: 64비트를 가정 */
unsigned long bitmap[2];  /* 128비트? 32비트에서는 64비트! */

/* 좋은 코드: BITS_TO_LONGS 사용 */
#define NBITS 128
unsigned long bitmap[BITS_TO_LONGS(NBITS)];  /* 아키텍처 독립 */

4. 초기화 누락

/* BUG: 초기화 없이 사용 */
unsigned long bitmap[4];
if (find_first_zero_bit(bitmap, 256))  /* 쓰레기 값! */

/* 올바른 코드 */
unsigned long bitmap[4];
bitmap_zero(bitmap, 256);
int bit = find_first_zero_bit(bitmap, 256);  /* OK: 0 반환 */

Bitmap 운영 플레이북

비트맵은 빠르지만 경계/원자성 실수에 취약합니다. 특히 멀티코어 환경에서는 test-then-set 패턴을 반드시 원자 연산으로 묶어야 합니다.

# 비트맵 관련 핵심 점검 패턴
git grep -n "test_bit(.*)\\s*.*set_bit" -- "*.c"
git grep -n "find_first_zero_bit" -- "*.c"
git grep -n "BITS_TO_LONGS\\|DECLARE_BITMAP" -- "*.c" "*.h"
실수영향대응
범위 초과 비트 접근메모리 손상상한 체크 + 정적 크기 매크로 사용
test/set 분리경합 시 중복 할당test_and_set_bit 사용
초기화 누락랜덤 상태bitmap_zero 또는 정적 초기화

Bitmap과 관련된 다른 주제를 더 깊이 이해하고 싶다면 다음 문서를 참고하세요.