fops > driver call back function
지금까지 한 것 : 모듈을 만들고, printk, gpio etc
드라이버 제작을 위해서 알아야하는 기본적인 사항들 :
메모리 할당 : 힙(큰 용량 - 큰 변수들 할당, 메모리 할당은 여기를 이야기함)과 스택(작음 - 작은 변수들 할당, 아껴야 함)
glibc(유저 영역에서 할당자, malloc), slab 할당자(kernel, data < page, kmeme_cache에 정의한 cache 단위로 할당, 페이지 보다 작음, kmalloc(<4mb), kfree, vmalloc(물리적 비연속 공간 메모리 할당, kmalloc보다 느리다.)), buddy 할당자(kernel,
data > page, 2^N 단위로 페이지 할당), 프로세스 컨택스트에서 사용
페이지(page) : 메모리 매핑의 최소 단위, physical(frame) > virtual(page), usually 4KB
PAGEOFFSET : 유저 영역과 커널 영역을 분리하는 경계
피지컬 영역에서의 할당(비연속적)과 다르게 가상 영역에서 할당은 연속적
buddy system
정의
buddy 시스템은 연속된 페이지 프레임을 할당/해제하기 위한 메모리 할당 방식입니다.
리눅스에서는 **페이지 단위(보통 4KB)**로 관리하는 물리 메모리 할당을 위해 사용됩니다.
동작 원리
기본 단위 | 2^n 크기의 블록 (예: 1 페이지, 2 페이지, 4 페이지, 8 페이지, ...) |
할당 방식 | 요청 크기에 맞는 최소한의 블록을 찾아 할당, 없으면 상위 블록을 절반으로 나눔 |
병합 (해제 시) | 해제된 블록의 buddy(짝) 블록이 같은 상태(free)라면 자동으로 병합하여 상위 블록으로 올라감 |
목적 | 큰 메모리 블록을 빠르게 할당, 조각화(fragmentation) 문제 완화 |
예시 | 페이지 할당 (alloc_pages()), kmalloc()에서 페이지 단위 메모리 확보 시 내부적으로 사용 |
예제 동작
총 1024KB(1MB) 메모리 → 1KB 요청:
- 1MB → 512KB → 256KB → 128KB → 64KB → 32KB → 16KB → 8KB → 4KB → 2KB → 1KB
요청 크기에 맞게 재귀적으로 쪼갬.
1KB 블록 해제:
- buddy 블록도 free 상태라면 2KB로 병합 → 반복적으로 상위까지 병합
장점 / 단점
빠른 분할 및 병합 | 2^n 단위로만 할당 → 내부 단편화 발생 |
큰 블록 할당에 적합 | 작은 크기 요청 시 과도한 낭비 가능 (예: 1바이트 요청에도 최소 1 페이지 할당) |
메모리 상태 관리가 비교적 단순 | 작은 객체 관리에는 부적합 |
slab
정의
slab 할당자는 작은 크기의 커널 객체들을 효율적으로 관리하기 위한 메모리 관리 기법입니다.
특히:
- struct inode
- struct dentry
- task_struct
같은 자주 할당되고 해제되는 작은 객체들을 대상으로 최적화되었습니다.
동작 원리
slab | 동일한 크기의 객체들이 미리 할당되어 들어가는 메모리 덩어리(캐시 풀) |
cache | 특정 타입의 slab 풀 (예: inode 캐시, dentry 캐시) |
object | slab 안에 들어가는 실제 커널 객체 |
slab page | slab은 결국 페이지 단위로 할당되며, 내부적으로 buddy 시스템에서 확보한 연속 페이지를 사용 |
작동 과정
커널은 자주 쓰이는 구조체 타입별로 slab 캐시(kmem_cache) 생성
요청이 들어오면:
- 기존 slab에 free object가 있으면 할당
- 없으면 새 slab(page 단위)를 buddy system에서 확보 후 채워 넣음
객체가 해제되면 slab 내에서 free 상태로 표시 (메모리는 slab에 남음)
장점 / 단점
작은 객체 반복 할당/해제에서 빠른 성능 | slab 관리용 메타데이터 필요 |
캐싱/미리 초기화로 캐시 적중 시 오버헤드 최소화 | slab 사용량이 늘어나면 페이지 단편화 발생 가능 |
내부 프리 리스트로 락 경쟁 최소화 (per-CPU 캐시 활용 등) | slab 사이즈는 고정, 가변 크기 객체 관리에는 부적합 |
객체 정렬, 캐시 라인 최적화, 버퍼 오버플로 탐지 같은 추가 기능 지원 | 관리 복잡성 증가 |
buddy system vs slab
주요 용도 | 페이지 단위 큰 메모리 블록 관리 | 커널 객체/작은 크기 메모리 관리 |
최소 단위 | 1 페이지 (보통 4KB) | 객체 크기에 맞춘 캐시 풀 |
내부 동작 | alloc_pages(), __get_free_pages() 등 | kmalloc(), kmem_cache_alloc() 등 |
기반 관계 | slab이 페이지 단위 메모리를 확보할 때 내부적으로 buddy system 사용 | slab은 buddy system에서 가져온 페이지를 작은 객체 관리용으로 쪼개서 사용 |
커널과 DMA(Direct Memory Access - CPU와 독립되어 메모리 입출력 담당, 메모리 전송 완료시 인터럽트 발생, INTReq와 DMAreq로 구분됨, DMA는 단순함 - 어디에서, 무엇을(바이트 수), 어디로만 알면됨, DMA 전송을 위해서는 메모리가 비연속적이면 안됨, 만약 비연속적이면 DMA 기능을 사용하지 못한다.)
memory pool(상비 메모리)
1. reserve memory : 예약되지 않은 메모리를 사용하다가, kmalloc이 not ok면 reserved된 메모리를 꺼내서 사용한다.
인터럽트 처리 : 메모리 동기화 이슈, 인터럽트 처리는 대기 불가(GFP_ATOMIC), 유저 프로세스는 대기 가능(GFP_KERNEL)
인터럽트 처리는 아키텍처마다 다를 수 있다. => 특정 버전 ARM 기준이다.
핀 설정 => 인터럽트 활성화 => 인터럽트 벡터에 함수 등록 => 사용 => 인터럽트 해제
인터럽트 처리 : top half, bottom half(인터럽트 내부에서도 우선 순위가 높은 작업만 우선 수행, 타 인터럽트 수행)
softirq(비추, 쓰지는 말아야 하지만 기존의 네트워크들이 쓰고 있음, 커널 소스 수정 필요), tasklet, workqueue
하드웨어 통신, 커널과 쓰레드, 세마포어 경쟁, 블로킹 입출력, 작업의 딜레이
'VEDA 복습 > 리눅스, 리눅스 프로그래밍' 카테고리의 다른 글
VEDA 54일차 - 리눅스 커널 프로그래밍 (0) | 2025.06.04 |
---|---|
VEDA 53일차 - 임베디드 파일시스템, 디바이스 드라이버 (1) | 2025.06.02 |
VEDA 49일차 - 리눅스 드라이버 (0) | 2025.05.27 |
VEDA 48일차 - 리눅스 드라이버 (0) | 2025.05.27 |
VEDA 44일차 - 하드웨어 제어 + TCP/IP 서버 -클라이언트 프로그래밍 (0) | 2025.05.20 |