임베디드 스터디 - 버퍼 관리와 메모리 할당
임베디드 스터디 - 버퍼 관리와 메모리 할당
copy_to_user() / copy_from_user()
- 드라이버가 커널 버퍼 데이터를 사용자 공간으로 전달할 때
memcpy()대신 반드시 사용해야 함 - 사용자 공간 포인터는 신뢰할 수 없는 값 — 잘못된 주소, 커널 영역 주소, 널 포인터일 수 있음
1
2
3
4
// 사용자가 넘긴 포인터가 이런 값이라면?
void *user_ptr = (void *)0xFFFF0000; // 커널 영역 주소
void *user_ptr = NULL; // 널 포인터
void *user_ptr = (void *)0xDEADBEEF; // 완전히 잘못된 주소
copy_to_user()내부 동작
1
2
3
4
5
6
copy_to_user(user_ptr, kernel_buf, size)
↓
1. user_ptr이 실제 사용자 공간 주소인지 검증
2. user_ptr + size 범위가 유효한지 검증
3. 검증 통과 시 → 데이터 복사
4. 검증 실패 시 → EFAULT 반환 (복사 안 함)
| 함수 | 방향 | 사용 시점 |
|---|---|---|
copy_to_user() | 커널 → 사용자 | read() 구현 시 |
copy_from_user() | 사용자 → 커널 | write() 구현 시 |
kmalloc() / kfree()
- 커널 공간에서 동적 메모리를 할당/해제하는 함수
- 사용자 공간의
malloc()/free()에 대응
1
2
void *buf = kmalloc(size, flags); // 할당
kfree(buf); // 해제
- 물리적으로 연속된 메모리를 할당 → 일반적으로 수십 KB 이하 권장
GFP_KERNEL vs GFP_ATOMIC
- 메모리 부족 시 SLEEP 가능 여부가 핵심 차이
| 플래그 | 메모리 부족 시 동작 | 사용 컨텍스트 |
|---|---|---|
GFP_KERNEL | SLEEP 하며 대기 | 일반 커널 코드, work_queue |
GFP_ATOMIC | 즉시 실패 반환 | 인터럽트 핸들러, tasklet |
1
2
3
어디서 할당하는가?
├── 일반 커널 코드, work_queue → GFP_KERNEL (sleep 가능)
└── 인터럽트 핸들러, tasklet → GFP_ATOMIC (sleep 불가)
vmalloc()
kmalloc()은 물리적으로 연속된 메모리를 할당 → 크기가 클수록 메모리 파편화 문제로 연속 공간 찾기 어려움vmalloc()은 가상 주소 공간에서 연속, 물리는 불연속 허용 → 대용량 메모리 할당 가능
1
2
3
kmalloc() 물리 메모리: [████████] 연속 필요
vmalloc() 물리 메모리: [████][ ][██] 불연속 허용
가상 메모리: [██████████] 연속으로 보임
kmalloc() | vmalloc() | |
|---|---|---|
| 물리 메모리 | 물리적 연속 | 불연속 허용 |
| 적합한 크기 | 수십 KB 이하 | 수 MB 이상 |
| DMA 사용 | ✅ 가능 | ❌ 불가 |
| 오버헤드 | 낮음 | 가상-물리 매핑 오버헤드 있음 |
| 주요 용도 | 드라이버 내부 버퍼, DMA | 대용량 버퍼 |
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.