Android 커널 (Android Kernel)
Android GKI 아키텍처, Binder IPC, 메모리 관리(ashmem/lmkd), HAL 인터페이스, SELinux 정책, 부팅 과정(Boot Process), 프로세스(Process) 모델, 스토리지, 커널 디버깅(Debugging)과 보안까지 Android 플랫폼의 커널 레벨 종합 가이드.
핵심 요약
- GKI(Generic Kernel Image) — 공통 커널 이미지와 벤더 모듈을 분리하여 Android 기기 간 커널 파편화를 해소합니다.
- Binder IPC — Android의 핵심 프로세스 간 통신 메커니즘으로, 커널 드라이버(
/dev/binder)가 직렬화·트랜잭션을 처리합니다. - HAL(Hardware Abstraction Layer) — 커널 드라이버와 Android 프레임워크 사이의 인터페이스 계층으로, HIDL/AIDL을 통해 안정적 ABI를 보장합니다.
- Android 부팅 과정 — 부트로더(Bootloader) → 커널 → init → Zygote 순서로 진행되며, dm-verity/AVB가 무결성을 검증합니다.
- Android SELinux — 모든 프로세스와 파일에 보안 레이블을 부여하는 강제 접근 제어(MAC) 정책으로, Android 보안의 핵심입니다.
- LMKD/메모리 관리 — PSI(Pressure Stall Information) 기반 유저스페이스 메모리 킬러와 DMA-BUF 힙으로 Android 메모리를 관리합니다.
- pKVM(Protected KVM) — Android 가상화 프레임워크로, 호스트 커널로부터도 게스트 메모리를 보호하는 기밀 컴퓨팅을 제공합니다.
단계별 이해
- 메인라인과 Android 커널 차이 파악 — Android가 Linux 메인라인에서 분기한 역사와 GKI 통합 과정을 먼저 이해합니다.
wakelocks, ashmem, ION 등 Android 전용 기능이 어떻게 메인라인에 통합되거나 대체되었는지 흐름을 따라가면 현재 구조를 파악할 수 있습니다.
- GKI 아키텍처와 모듈 구조 학습 — 공통 커널 이미지(GKI)와 벤더 모듈 분리 구조, KMI(Kernel Module Interface) 안정성 보장 메커니즘을 학습합니다.
gki_defconfig로 공통 설정을 빌드하고, 벤더 훅(vendor hook)으로 SoC별 기능을 주입하는 방식을 실습합니다. - Binder IPC 동작 원리 분석 —
ioctl()기반 트랜잭션 흐름, BC_/BR_ 프로토콜, 공유 메모리 매핑 구조를 코드 수준에서 추적합니다.adb shell service list와/sys/kernel/debug/binder/를 통해 실제 바인더 통신을 관찰합니다. - 부팅·보안·메모리 서브시스템 연결 — dm-verity/AVB 부팅 검증, SELinux 정책 적용, LMKD 메모리 관리가 커널 수준에서 어떻게 연동되는지 통합 관점으로 분석합니다.
각 서브시스템의 커널 로그(
dmesg)와 sysfs 인터페이스를 교차 확인하며 전체 그림을 완성합니다. - VTS/CTS로 호환성 검증 — GKI 호환성 테스트(VTS)와 Android 호환성 테스트(CTS)를 실행하여 커널 변경이 Android 플랫폼 요구사항을 충족하는지 확인합니다.
Android CDD(Compatibility Definition Document)를 기준으로 커널 설정과 ABI 안정성을 점검합니다.
Android 커널 개요
Android는 Linux 커널을 기반으로 하는 모바일/임베디드 운영체제입니다. 초기에는 메인라인 커널에서 크게 분기(fork)하여 다수의 out-of-tree 패치(Patch)를 유지했으나, Google의 지속적인 업스트리밍 노력으로 GKI(Generic Kernel Image) 시대에는 메인라인과의 차이가 크게 줄었습니다.
업스트리밍 역사
Android가 독자적으로 유지하던 커널 기능들은 점진적으로 메인라인 Linux에 통합되거나 대체되었습니다.
| Android 전용 기능 | 메인라인 대체 | 커널 버전 | 상태 |
|---|---|---|---|
| wakelocks | PM wakeup sources | 3.5+ | 완전 통합 |
| ashmem | memfd_create() | 5.18+ 제거 | memfd로 전환 |
| ION allocator | DMA-BUF heaps | 5.6+ | ION 제거 (5.11) |
| lowmemorykiller (LMK) | lmkd (userspace, PSI 기반) | 4.12+ 제거 | 유저스페이스 전환 |
| binder | (메인라인 통합) | 3.19+ | staging → drivers/android |
| paranoid networking | (Android 전용 유지) | — | GKI 유지 |
Android 버전별 커널 매핑(Mapping)
| Android 버전 | 커널 버전 | GKI 지원 | 주요 커널 변화 |
|---|---|---|---|
| Android 11 | 5.4 | GKI 1.0 | GKI 도입, 모듈화 시작 |
| Android 12 | 5.10 | GKI 2.0 | KMI 안정성 보장, cgroup v2 필수 |
| Android 13 | 5.10 / 5.15 | GKI 2.0 | EROFS 시스템 파티션, Virtualization |
| Android 14 | 5.15 / 6.1 | GKI 2.0 | 16KB 페이지 지원, Rust 모듈 |
| Android 15 | 6.1 / 6.6 | GKI 2.0 | MGLRU 기본 활성화, io_uring 비활성화 |
GKI (Generic Kernel Image)
GKI는 Android 기기의 커널 파편화 문제를 해결하기 위한 Google의 전략입니다. 단일 커널 바이너리(GKI 커널)가 모든 ARM64 디바이스에서 부팅되고, 벤더별 하드웨어 지원은 로드 가능한 모듈로 분리됩니다.
GKI 아키텍처
KMI (Kernel Module Interface)
KMI는 GKI 커널이 벤더 모듈에 제공하는 안정적인 심볼 집합입니다. LTS 커널의 수명 동안 KMI ABI가 유지되어, 벤더 모듈을 GKI 커널과 독립적으로 업데이트할 수 있습니다.
/* KMI 심볼 리스트 예시 (abi_gki_aarch64) */
/* 이 파일에 등록된 심볼만 벤더 모듈에서 사용 가능 */
[abi_symbol_list]
alloc_chrdev_region
__alloc_pages
cdev_add
class_create
dma_buf_export
device_register
module_layout
...
/* ABI 모니터링: abigail 도구로 변경 감지 */
$ stgdiff --abi abi_prev.xml abi_curr.xml
커널 브랜치 구조
| 브랜치 | 용도 | 업스트림 소스 |
|---|---|---|
android-mainline | 최신 개발 (다음 GKI 버전) | Linus torvalds/linux |
android15-6.6 | Android 15 GKI 릴리스 | stable/linux-6.6.y |
android14-6.1 | Android 14 GKI 릴리스 | stable/linux-6.1.y |
android14-5.15 | Android 14 레거시 | stable/linux-5.15.y |
android13-5.10 | Android 13 레거시 | stable/linux-5.10.y |
Bazel(Kleaf) 빌드 시스템(Build System)
GKI 커널은 전통적인 make 대신 Bazel(Kleaf)로 빌드합니다. Kleaf는 재현 가능한 빌드를 보장하고, 벤더 모듈과 GKI 커널을 별도로 빌드·결합할 수 있게 합니다.
# GKI 커널 빌드 (Bazel/Kleaf)
$ tools/bazel run //common:kernel_aarch64_dist -- --dist_dir=out/dist
# 벤더 모듈 빌드 (예: Pixel)
$ tools/bazel run //private/google-modules/soc/gs:slider_dist -- --dist_dir=out/dist
# gki_defconfig 기반 설정
$ tools/bazel run //common:kernel_aarch64_config -- menuconfig
Binder IPC
Binder는 Android의 핵심 IPC 메커니즘으로, 프로세스 간 단일 복사(single-copy) 데이터 전송, 호출자 PID/UID 추적, 참조 카운팅 기반 객체 수명 관리, 사망 통지(death notification) 기능을 제공합니다. 커널 드라이버 drivers/android/binder.c가 핵심 구현입니다.
Binder 핵심 특성
- 단일 복사 전송: 송신 측 데이터를
copy_from_user()로 커널 버퍼(Buffer)에 복사한 뒤, 수신 측의mmap()된 영역에 직접 매핑 — 일반 IPC(pipe, socket)의 2회 복사 대비 효율적 - 프로세스 ID 추적: 커널이 트랜잭션(Transaction)에 송신자의 PID/UID를 자동 첨부 — 위변조 불가능한 인증
- 참조 카운팅:
binder_ref/binder_node로 원격 객체의 수명을 자동 관리 - 사망 통지: 서버 프로세스가 종료되면
BR_DEAD_BINDER로 클라이언트에 즉시 통지
Binder 트랜잭션 흐름
핵심 자료구조
/* drivers/android/binder_internal.h */
/* 각 프로세스가 /dev/binder를 open()하면 생성 */
struct binder_proc {
struct hlist_node proc_node; /* 전역 프로세스 리스트 */
struct rb_root threads; /* binder_thread RB-tree */
struct rb_root nodes; /* binder_node RB-tree (서버 객체) */
struct rb_root refs_by_desc; /* binder_ref (핸들→노드 매핑) */
struct rb_root refs_by_node;
struct list_head todo; /* 처리 대기 작업 큐 */
struct binder_alloc alloc; /* mmap 버퍼 관리 */
pid_t pid;
struct task_struct *tsk;
...
};
/* 단일 Binder 트랜잭션 */
struct binder_transaction {
struct binder_work work;
struct binder_thread *from; /* 송신 스레드 */
struct binder_proc *to_proc; /* 수신 프로세스 */
struct binder_thread *to_thread; /* 수신 스레드 */
struct binder_buffer *buffer; /* 데이터 버퍼 */
unsigned int code; /* 트랜잭션 코드 */
unsigned int flags;
struct binder_priority priority; /* 우선순위 상속 */
kuid_t sender_euid; /* 송신자 UID */
...
};
/* Binder 서비스 객체 (서버 측) */
struct binder_node {
struct binder_work work;
struct rb_node rb_node; /* proc->nodes 트리 */
struct hlist_head refs; /* 이 노드를 참조하는 binder_ref 목록 */
binder_uintptr_t ptr; /* 유저스페이스 객체 포인터 */
binder_uintptr_t cookie;
bool has_strong_ref;
bool has_weak_ref;
...
};
Binder 프로토콜
Binder는 ioctl(BINDER_WRITE_READ)를 통해 커널과 유저스페이스 간 명령/응답을 교환합니다. 명령은 BC_*(유저→커널), 응답은 BR_*(커널→유저) 접두사를 사용합니다.
| 명령 (BC_*) | 방향 | 설명 |
|---|---|---|
BC_TRANSACTION | 유저 → 커널 | 원격 호출 요청 전송 |
BC_REPLY | 유저 → 커널 | 트랜잭션 응답 전송 |
BC_ACQUIRE | 유저 → 커널 | 원격 객체 강참조 획득 |
BC_RELEASE | 유저 → 커널 | 원격 객체 강참조 해제 |
BC_INCREFS | 유저 → 커널 | 원격 객체 약참조 증가 |
BC_REQUEST_DEATH_NOTIFICATION | 유저 → 커널 | 사망 통지 등록 |
BC_ENTER_LOOPER | 유저 → 커널 | 스레드(Thread)가 Binder 루프 진입 |
| 응답 (BR_*) | 방향 | 설명 |
|---|---|---|
BR_TRANSACTION | 커널 → 유저 | 수신 측에 트랜잭션 전달 |
BR_REPLY | 커널 → 유저 | 송신 측에 응답 전달 |
BR_DEAD_BINDER | 커널 → 유저 | 원격 객체 사망 통지 |
BR_SPAWN_LOOPER | 커널 → 유저 | 추가 Binder 스레드 생성 요청 |
BR_TRANSACTION_COMPLETE | 커널 → 유저 | 트랜잭션 전송 완료 확인 |
mmap 버퍼와 단일 복사
ashmem/memfd를 파일 디스크립터(File Descriptor)로 전달하여 별도 공유 메모리를 사용해야 합니다.
/* Binder mmap — 수신 프로세스가 /dev/binder를 mmap() */
/* ProcessState.cpp (Android libbinder) */
#define BINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2)
void *vm = mmap(NULL, BINDER_VM_SIZE,
PROT_READ, MAP_PRIVATE | MAP_NORESERVE,
binder_fd, 0);
/* 단일 복사 메커니즘:
* 1. 송신: copy_from_user()로 유저 데이터 → 커널 binder_buffer
* 2. 커널이 binder_buffer의 물리 페이지를 수신 측 mmap 영역에 매핑
* 3. 수신: mmap된 가상 주소에서 직접 데이터 접근 (추가 복사 없음) */
binder_ioctl() 핵심 흐름
Binder 드라이버의 모든 통신은 binder_ioctl() 함수를 통해 이루어집니다. 가장 중요한 BINDER_WRITE_READ 명령의 처리 흐름을 살펴봅니다.
/* drivers/android/binder.c — binder_ioctl() 핵심 흐름 */
static long binder_ioctl(
struct file *filp,
unsigned int cmd,
unsigned long arg)
{
struct binder_proc *proc = filp->private_data;
struct binder_thread *thread;
/* 현재 스레드의 binder_thread 가져오기/생성 */
thread = binder_get_thread(proc);
switch (cmd) {
case BINDER_WRITE_READ:
/* 쓰기 버퍼 처리: BC_* 명령 순차 실행 */
if (bwr.write_size > 0)
binder_thread_write(proc, thread,
bwr.write_buffer, bwr.write_size,
&bwr.write_consumed);
/* 읽기 버퍼 처리: BR_* 응답 대기/수신 */
if (bwr.read_size > 0)
binder_thread_read(proc, thread,
bwr.read_buffer, bwr.read_size,
&bwr.read_consumed,
filp->f_flags & O_NONBLOCK);
break;
case BINDER_SET_MAX_THREADS:
/* Binder 스레드 풀 최대 크기 설정 */
proc->max_threads = arg;
break;
case BINDER_VERSION:
/* 드라이버 프로토콜 버전 반환 */
break;
}
return 0;
}
/* binder_thread_write(): BC_TRANSACTION 처리 시 */
/* 1. binder_transaction() 호출 */
/* 2. 대상 프로세스의 binder_buffer 할당 (mmap 영역) */
/* 3. copy_from_user()로 데이터 복사 */
/* 4. flat_binder_object 변환 (fd/binder 참조) */
/* 5. 대상 스레드/프로세스 todo 리스트에 작업 추가 */
/* 6. wake_up_interruptible()로 대상 깨우기 */
BINDER_SET_MAX_THREADS로 설정한 수만큼의 Binder 스레드를 유지합니다. 모든 스레드가 트랜잭션 처리 중이면 커널이 BR_SPAWN_LOOPER를 보내 추가 스레드 생성을 요청합니다. Android의 기본 최대 스레드 수는 15입니다 (ProcessState.cpp에서 설정).
Binder 디바이스 노드
Android는 보안 도메인 분리를 위해 3개의 Binder 디바이스를 사용합니다. Android 10+에서는 binderfs를 통해 동적으로 디바이스를 생성할 수 있습니다.
| 디바이스 | 용도 | 접근 도메인 |
|---|---|---|
/dev/binder | Framework ↔ App IPC | 앱, 시스템 서비스 |
/dev/hwbinder | Framework ↔ HAL (HIDL) | HAL 프로세스 |
/dev/vndbinder | 벤더 내부 IPC | 벤더 프로세스 |
/* binderfs 마운트 (Android 10+) */
# /dev/binderfs에 마운트하고 디바이스 동적 생성
$ mount -t binder binder /dev/binderfs
$ ls /dev/binderfs/
binder hwbinder vndbinder binder-control
/* 커널 설정 */
CONFIG_ANDROID_BINDERFS=y
Android 메모리 관리 커널 기법
ashmem → memfd_create 전환
ashmem(Anonymous Shared Memory)은 Android 초기부터 사용된 공유 메모리 메커니즘으로, 파일 디스크립터 기반 공유와 ASHMEM_SET_PROT_MASK로 권한 축소가 가능했습니다. 메인라인에서는 주로 memfd_create() 기반 경로(필요 시 sealing 조합)로 전환되었습니다.
lowmemorykiller → lmkd
초기 Android는 커널 내부 lowmemorykiller 드라이버로 메모리 부족 시 프로세스를 강제 종료했다. 이는 OOM killer와 충돌하고 정책 유연성이 부족하여, Android 10부터 유저스페이스 lmkd 데몬으로 전환되었습니다.
/* 기존 커널 LMK (drivers/staging/android/lowmemorykiller.c, 제거됨)
* /sys/module/lowmemorykiller/parameters/ 를 통해 임계값 설정 */
/* 현재: lmkd (userspace) — PSI(Pressure Stall Information) 기반 */
/* lmkd가 /proc/pressure/memory를 모니터링 */
# PSI 메모리 압력 트리거 예시:
# some 70000 1000000 → 1초 동안 70ms 이상 stall 시 이벤트
/* lmkd.rc 설정 */
service lmkd /system/bin/lmkd
class core
group root readproc
capabilities DAC_OVERRIDE KILL IPC_LOCK SYS_NICE SYS_RESOURCE
writepid /dev/cpuset/system-background/tasks
DMA-BUF Heaps (ION 대체)
초기 Android는 ION allocator(drivers/staging/android/ion/)로 GPU·카메라·디스플레이 등 하드웨어 간 공유 버퍼를 할당했다. ION은 메인라인에 머물지 못하고 커널 5.6에서 DMA-BUF heaps(drivers/dma-buf/dma-heap.c)로 대체되었으며, ION 코드는 커널 5.11에서 완전 제거되었습니다.
/* drivers/dma-buf/dma-heap.c — DMA-BUF Heap 코어 API */
/* Heap 등록 (벤더 모듈이 호출) */
struct dma_heap *dma_heap_add(
const struct dma_heap_export_info *exp_info);
/* 유저스페이스 할당 인터페이스 */
/* /dev/dma_heap/<heap_name> 을 open() 후 ioctl()로 버퍼 할당 */
struct dma_heap_allocation_data {
__u64 len; /* 요청 크기 */
__u32 fd; /* 반환: dma-buf fd */
__u32 fd_flags; /* O_CLOEXEC | O_RDWR */
__u64 heap_flags; /* 예약 */
};
/* ioctl 호출 */
int fd = open("/dev/dma_heap/system", O_RDWR);
struct dma_heap_allocation_data alloc = {
.len = 4096,
.fd_flags = O_CLOEXEC | O_RDWR,
};
ioctl(fd, DMA_HEAP_IOCTL_ALLOC, &alloc);
/* alloc.fd에 dma-buf 파일 디스크립터 반환 */
| Heap 이름 | 할당 소스 | 용도 |
|---|---|---|
system | 페이지 할당자 (non-contiguous) | 일반 공유 버퍼, GPU 텍스처 |
system-uncached | 페이지 할당자 (캐시 비활성) | HW 직접 접근 (DMA 일관성 보장) |
cma | CMA 영역 (contiguous) | 카메라, 디스플레이 (IOMMU 미지원 HW) |
| (벤더 커스텀) | SoC 전용 메모리 영역 | 보안 버퍼, 코덱 전용 영역 |
/dev/ion 디바이스에서 heap ID로 구분했으나, DMA-BUF heaps는 /dev/dma_heap/<name>으로 각 heap이 독립 디바이스 노드를 가집니다. 이 설계는 SELinux 정책으로 heap별 접근 제어(Access Control)를 가능하게 하고, 벤더 커스텀 heap을 GKI 모듈로 깔끔하게 분리할 수 있습니다.
MGLRU와 Android
Android 부팅 과정 (커널 관점)
파티션 구조
Android 기기의 스토리지는 여러 전용 파티션으로 나뉘며, GKI 시대에는 커널 이미지와 벤더 모듈이 별도 파티션으로 분리됩니다.
| 파티션 | 내용 | 파일시스템(Filesystem) | 비고 |
|---|---|---|---|
boot | GKI 커널 + generic ramdisk | 부트 이미지 (v4) | Android 13+: 커널만 |
init_boot | generic ramdisk (init) | 부트 이미지 | Android 13+: boot에서 분리 |
vendor_boot | 벤더 ramdisk + DTB | 부트 이미지 (v4) | 벤더 모듈, fstab |
system | Android Framework | EROFS / ext4 | dm-verity 보호 |
vendor | 벤더 HAL, 라이브러리 | EROFS / ext4 | dm-verity 보호 |
userdata | 사용자 데이터, 앱 | f2fs / ext4 | FBE(File-Based Encryption) |
vbmeta | AVB 검증 메타데이터 | VBMeta 구조체(Struct) | 해시(Hash) 트리 루트 |
부팅 흐름
AVB (Android Verified Boot)
AVB는 부팅 체인의 무결성(Integrity)을 보장합니다. 부트로더가 vbmeta 파티션의 서명을 검증하고, 각 파티션의 해시/해시 트리 루트를 확인합니다. 커널 레벨에서는 dm-verity가 런타임 시 블록 단위 검증을 수행합니다.
관련 내용은 Secure Boot, Device Mapper — dm-verity 섹션을 참고하십시오.
HAL과 커널 인터페이스
Project Treble — Framework/Vendor 분리
Android 8.0(Oreo)에서 도입된 Project Treble은 Android Framework과 벤더 구현을 명확히 분리합니다. 이 아키텍처 덕분에 Google이 Framework을 업데이트하더라도 벤더 HAL과 커널 드라이버를 수정할 필요가 없습니다.
- VNDK(Vendor NDK): Framework이 벤더에 제공하는 안정적 네이티브 라이브러리 집합
- VINTF(Vendor Interface): HAL 버전 매니페스트로 호환성 보장
- /dev/hwbinder: Framework ↔ HAL 간 전용 Binder 도메인
HIDL → AIDL 전환
Android 13부터 새로운 HAL은 반드시 Stable AIDL을 사용해야 합니다. HIDL(HAL Interface Definition Language)은 deprecated 상태이며, 기존 HIDL HAL도 점진적으로 AIDL로 마이그레이션 중입니다.
| 특성 | HIDL | AIDL (Stable) |
|---|---|---|
| 도입 버전 | Android 8.0 | Android 11+ (HAL용) |
| 전송 계층 | hwbinder | binder / hwbinder |
| 언어 지원 | C++, Java | C++, Java, Rust, NDK |
| 버전 관리 | 메이저.마이너 | 단일 정수 버전 |
| 상태 | Deprecated (Android 13+) | 현재 표준 |
HAL ↔ 커널 드라이버 매핑
| Android HAL | 커널 서브시스템 | 주요 인터페이스 |
|---|---|---|
| Audio HAL | ALSA (ASoC) | /dev/snd/*, tinyalsa |
| Camera HAL | V4L2 / Media | /dev/video*, /dev/media* |
| Display/Composer HAL | DRM/KMS | /dev/dri/*, modesetting |
| Sensors HAL | IIO (Industrial I/O) | /sys/bus/iio/ |
| GNSS HAL | TTY / USB serial | /dev/ttyACM*, NMEA |
| USB HAL | USB Gadget / ConfigFS | /config/usb_gadget/ |
| Power HAL | cpufreq / devfreq / PM | /sys/devices/system/cpu/ |
| Thermal HAL | Thermal framework | /sys/class/thermal/ |
| Health HAL | power_supply | /sys/class/power_supply/ |
| Vibrator HAL | FF (Force Feedback) / input | /dev/input/event* |
Android SELinux 정책 (커널 관점)
Android는 SELinux Enforcing 모드를 필수로 요구한다 (Android 5.0+). 커널의 SELinux LSM이 모든 시스템 콜(System Call)에서 접근 제어를 강제하며, Android 전용 정책으로 앱/서비스/HAL 간의 세밀한 격리(Isolation)를 구현합니다.
커널 SELinux 초기화
/* Android init의 SELinux 초기화 흐름 */
/* system/core/init/selinux.cpp */
/* 1. 커널 커맨드라인: androidboot.selinux=enforcing */
/* 2. 1st stage init: /system/etc/selinux/precompiled_sepolicy 로드 */
selinux_android_load_policy();
/* 3. Enforcing 모드 강제 설정 */
security_setenforce(1);
/* 4. 파일 시스템 레이블링 확인 */
# restorecon -R /dev /sys /proc
/* CTS(Compatibility Test Suite)가 Enforcing 모드 검증 */
Binder LSM 훅
SELinux는 Binder 전용 LSM 훅을 통해 프로세스 간 Binder 통신을 제어합니다.
/* security/selinux/hooks.c — Binder 관련 LSM 훅 */
/* Binder 트랜잭션 권한 검사 */
static int selinux_binder_transaction(
const struct cred *from,
const struct cred *to)
{
return avc_has_perm(from_sid, to_sid,
SECCLASS_BINDER, BINDER__CALL, NULL);
}
/* Binder 파일 디스크립터 전송 권한 검사 */
static int selinux_binder_transfer_file(
const struct cred *from,
const struct cred *to,
const struct file *file)
{
/* fd 전달 시 수신자에 대한 파일 접근 권한 검사 */
...
}
/* SELinux 정책 규칙 예시 (*.te 파일) */
# system_server가 surfaceflinger에 binder call 허용
# allow system_server surfaceflinger:binder { call transfer };
# neverallow: untrusted_app이 hal_camera_server에 직접 binder call 차단
# neverallow untrusted_app hal_camera_server:binder call;
컨텍스트 파일
Android SELinux 정책은 여러 컨텍스트 파일로 관리됩니다.
| 파일 | 용도 |
|---|---|
file_contexts | 파일/디렉토리 → SELinux 레이블 매핑 |
service_contexts | Binder 서비스 이름 → SELinux 레이블 |
hwservice_contexts | HIDL/AIDL HAL 서비스 → SELinux 레이블 |
property_contexts | 시스템 프로퍼티 → SELinux 레이블 |
genfs_contexts | 가상 파일시스템(proc, sysfs) 레이블 |
관련 내용은 LSM/Seccomp 섹션을 참고하십시오.
Android 전용 커널 설정
gki_defconfig에 정의되어 있습니다. 아래 표는 Android 플랫폼에서 필수 또는 강력히 권장하는 커널 옵션들입니다.
| CONFIG 옵션 | 필수 여부 | 설명 |
|---|---|---|
CONFIG_ANDROID | 필수 | Android 서브시스템 활성화 |
CONFIG_ANDROID_BINDER_IPC | 필수 | Binder IPC 드라이버 |
CONFIG_ANDROID_BINDERFS | 필수 | binderfs 동적 디바이스 생성 |
CONFIG_MEMCG | 필수 | 메모리 cgroup (lmkd 의존) |
CONFIG_PSI | 필수 | Pressure Stall Information (lmkd) |
CONFIG_DM_VERITY | 필수 | dm-verity (AVB 런타임 검증) |
CONFIG_SECURITY_SELINUX | 필수 | SELinux MAC 필수 |
CONFIG_CGROUPS | 필수 | cgroup v2 (프로세스 관리) |
CONFIG_FUSE_FS | 필수 | FUSE (외부 스토리지, MediaProvider) |
CONFIG_EROFS_FS | 권장 | EROFS (읽기 전용(Read-Only) 시스템 파티션, Android 13+) |
CONFIG_F2FS_FS | 권장 | F2FS (userdata 파티션) |
CONFIG_CRYPTO_AES | 필수 | FBE(파일 기반 암호화(Encryption)) |
CONFIG_BLK_INLINE_ENCRYPTION | 권장 | 인라인 암호화 엔진 (UFS/eMMC HW 암호화) |
CONFIG_LRU_GEN | 권장 | MGLRU (Android 15+ 기본) |
CONFIG_SCHED_TUNE | 권장 | UCLAMP 스케줄러(Scheduler) 튜닝 |
Android 프로세스 모델 (커널 관점)
Android init 프로세스
Android의 PID 1 프로세스인 init은 일반 Linux의 systemd/SysVinit과 다르게 .rc 파일(Android Init Language)을 파싱하여 서비스를 시작합니다. Property 시스템(__system_property_*)은 공유 메모리 기반으로, /dev/__properties__를 통해 전 프로세스에 읽기 전용으로 노출됩니다.
# init.rc 예시 (Android Init Language)
service surfaceflinger /system/bin/surfaceflinger
class core animation
user system
group graphics drmrpc readproc
capabilities SYS_NICE
onrestart restart --only-if-running zygote
task_profiles HighPerformance
socket pdx/system/vr/display/client stream 0666 system graphics u:object_r:pdx_display_client_endpoint_socket:s0
# Property 트리거
on property:sys.boot_completed=1
write /proc/sys/vm/dirty_expire_centisecs 200
write /proc/sys/vm/dirty_background_ratio 5
Zygote — fork() 기반 앱 생성
Zygote는 Android 앱 프로세스의 템플릿 프로세스입니다. ART(Android Runtime)와 공통 클래스를 미리 로드한 뒤, 앱 시작 요청 시 fork()로 빠르게 자식 프로세스를 생성합니다. 커널의 COW(Copy-On-Write) 메커니즘 덕분에 공유 가능한 페이지는 즉시 복사하지 않아 메모리 효율적입니다.
/* Zygote fork 흐름 (커널 관점) */
/* 1. Zygote가 ART, 프레임워크 클래스, 리소스를 미리 로드 */
/* 2. ActivityManagerService가 앱 시작 요청 */
/* 3. Zygote가 fork() 호출 */
pid = fork(); /* 커널: copy_process() → COW 페이지 테이블 복사 */
if (pid == 0) {
/* 자식: 앱 프로세스 */
setuid(app_uid); /* 앱별 고유 UID 설정 */
setgid(app_gid);
prctl(PR_SET_SECCOMP); /* seccomp 필터 적용 */
selinux_android_setcontext(uid, is_system, se_info, se_name);
/* ART에서 앱 코드 실행 */
}
/* USAP (Unspecialized App Process) Pool — Android 10+ */
/* Zygote가 미리 fork()한 프로세스 풀을 유지 */
/* 앱 시작 시 fork() 비용 절약 → cold start 시간 단축 */
Android cgroup 구성
task_profiles.json이 cgroup 설정을 추상화하며, init이 .rc 파일의 task_profiles 지시어를 통해 서비스를 적절한 cgroup에 배치합니다.
| cgroup 컨트롤러 | 마운트(Mount) 경로 | Android 용도 |
|---|---|---|
cpuctl | /dev/cpuctl/ | foreground/background CPU 할당 |
cpuset | /dev/cpuset/ | 빅/리틀 코어 핀닝 |
memory | /dev/memcg/ | 앱별 메모리 제한, lmkd 연동 |
freezer | /sys/fs/cgroup/ | 백그라운드 앱 동결 (cgroup v2 freezer) |
io | /sys/fs/cgroup/ | I/O 대역폭(Bandwidth) 제한 |
# Android cgroup 계층 구조 (task_profiles.json 기반)
# foreground 앱: big 코어 + 높은 CPU 가중치
/dev/cpuset/foreground → cpus: 0-7
/dev/cpuctl/foreground → cpu.weight: 1024
# background 앱: little 코어 + 낮은 CPU 가중치
/dev/cpuset/background → cpus: 0-3
/dev/cpuctl/background → cpu.weight: 50
# UCLAMP: 스케줄러에 utilization 힌트 전달 (EAS 연동)
# foreground 앱의 최소 util을 설정하여 성능 보장
/dev/cpuctl/foreground/cpu.uclamp.min: 20
/dev/cpuctl/foreground/cpu.uclamp.max: 1024
관련 내용은 cgroups v1/v2, 프로세스 스케줄러 섹션을 참고하십시오.
Android 스토리지 (커널 관점)
SDCardFS → FUSE passthrough
Android의 외부 스토리지(/sdcard)는 과거 커널 내 SDCardFS로 권한 에뮬레이션을 수행했으나, 유지보수 문제로 Android 11부터 FUSE(Filesystem in Userspace)로 전환되었습니다. FUSE 오버헤드(Overhead)를 줄이기 위해 FUSE passthrough 모드가 도입되어, 읽기/쓰기 경로에서 유저스페이스 우회가 가능합니다.
/* FUSE passthrough 원리 */
/* 1. MediaProvider(Java)가 FUSE 마운트 제공 */
/* 2. 첫 open() 시 MediaProvider가 권한 검사 */
/* 3. passthrough 등록: 이후 read/write는 커널 내에서 직접 하위 FS로 전달 */
/* 커널 설정 */
CONFIG_FUSE_FS=y
CONFIG_FUSE_PASSTHROUGH=y /* 커널 6.6+ 메인라인 통합 */
F2FS (Flash-Friendly File System)
Samsung이 개발한 F2FS는 NAND 플래시 특성에 최적화된 로그 구조 파일시스템으로, 대부분의 Android 기기에서 userdata 파티션에 사용됩니다. Append-only 쓰기, 멀티 헤드 로깅, 인라인 데이터 등으로 플래시 수명과 성능을 개선합니다.
# F2FS 주요 마운트 옵션 (Android)
# fstab 예시:
/dev/block/by-name/userdata /data f2fs \
noatime,nosuid,nodev,discard,reserve_root=32768, \
fsync_mode=nobarrier,compress_algorithm=lz4, \
inlinecrypt,atgc,gc_merge
# 주요 옵션 설명:
# inlinecrypt → 인라인 암호화 엔진 사용 (FBE)
# atgc → 비동기 GC (Age-Threshold GC)
# gc_merge → idle 시에만 GC 수행
# compress_algorithm=lz4 → 투명 파일 압축
EROFS (Enhanced Read-Only File System)
Android 13+에서 system, vendor 파티션은 EROFS를 사용합니다. ext4 읽기 전용보다 이미지 크기가 작고(LZ4/LZMA 압축), 마운트 시간이 빠르며, 임의 읽기 성능이 우수합니다.
# EROFS 이미지 생성
$ mkfs.erofs -zlz4hc -T 1230768000 -U clear \
--mount-point /system system.img system/
# 커널 설정
CONFIG_EROFS_FS=y
CONFIG_EROFS_FS_ZIP=y /* 압축 지원 */
CONFIG_EROFS_FS_ZIP_LZMA=y /* LZMA 알고리즘 */
FBE (File-Based Encryption)
Android 7.0부터 도입된 FBE(File-Based Encryption)는 파일 단위로 서로 다른 키로 암호화하여, 잠금(Lock) 화면 해제 전에도 알람·전화 등 DE(Device Encrypted) 영역의 데이터에 접근할 수 있게 한다. 커널의 fscrypt 서브시스템(fs/crypto/)이 핵심 구현이며, 최신 SoC는 인라인 암호화 엔진(ICE)으로 소프트웨어 오버헤드 없이 블록 레벨 암호화를 수행합니다.
/* fs/crypto/keysetup.c — fscrypt 키 설정 핵심 흐름 */
/* 1. 유저스페이스(vold)가 fscrypt 정책 설정 */
/* ioctl(FS_IOC_SET_ENCRYPTION_POLICY) */
struct fscrypt_policy_v2 {
__u8 version; /* FSCRYPT_POLICY_V2 */
__u8 contents_encryption_mode; /* FSCRYPT_MODE_AES_256_XTS */
__u8 filenames_encryption_mode; /* FSCRYPT_MODE_AES_256_CTS */
__u8 flags; /* FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64 */
__u8 master_key_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE];
};
/* 2. 인라인 암호화 엔진 사용 여부 결정 */
/* blk-crypto가 HW 지원 확인 → ICE 사용 / SW 폴백 */
static int fscrypt_select_encryption_impl(
struct fscrypt_info *ci)
{
if (fscrypt_using_inline_encryption(ci)) {
/* HW 인라인 암호화 경로 */
return blk_crypto_start_using_key(
ci->ci_enc_key.blk_key,
bdev_get_queue(sb->s_bdev));
}
/* SW crypto-API 폴백 */
return fscrypt_prepare_key(&ci->ci_enc_key, raw_key, ci);
}
dm-crypt로 파티션 전체를 암호화하여 Direct Boot가 불가능했으나, FBE는 fscrypt로 디렉토리 단위 키를 적용하여 DE/CE 영역을 구분합니다. 관련 내용은 커널 보안 섹션을 참고하십시오.
Android 가상화 (pKVM)
Android 13부터 pKVM(protected KVM)이 도입되어, 기밀 컴퓨팅(Confidential Computing)과 보안 격리를 하드웨어 수준에서 지원합니다. pKVM은 EL2(하이퍼바이저(Hypervisor) 레벨)에서 경량 하이퍼바이저를 실행하며, 호스트 커널(Android)조차 게스트 VM의 메모리에 접근할 수 없도록 Stage-2 페이지 테이블(Page Table)을 제어합니다.
pKVM 아키텍처
/* arch/arm64/kvm/hyp/nvhe/ — pKVM EL2 하이퍼바이저 핵심 */
/* 메모리 소유권 전환: 호스트 → pVM 기부(donate) */
/* 호스트 커널이 pVM에 메모리를 넘기면 Stage-2 매핑에서 */
/* 호스트 측 매핑이 제거되어 접근 불가 */
enum pkvm_page_state {
PKVM_PAGE_OWNED, /* 호스트 소유 */
PKVM_PAGE_SHARED_OWNED, /* 호스트 소유, 게스트 공유 */
PKVM_PAGE_SHARED_BORROWED, /* 게스트가 빌린 페이지 */
PKVM_PAGE_DONATED, /* pVM에 완전 이전 */
};
/* HVC 호출로 EL2에 메모리 소유권 변경 요청 */
/* arch/arm64/kvm/hyp/nvhe/mem_protect.c */
static int __pkvm_host_donate_hyp(
u64 pfn, u64 nr_pages)
{
/* Stage-2 페이지 테이블에서 호스트 매핑 제거 */
host_stage2_set_owner_locked(
phys, size, PKVM_ID_HYP);
/* EL2 매핑에 추가 */
hyp_stage1_map_locked(phys, size);
return 0;
}
Microdroid와 pVM 활용
Microdroid는 pVM 내부에서 실행되는 경량 Android 기반 OS입니다. 최소한의 init + 단일 앱 페이로드(Payload)로 구성되며, Virtio 디바이스(vsock, block, console)를 통해 호스트와 통신합니다.
| 활용 사례 | 설명 | 보호 대상 |
|---|---|---|
| Keystore (StrongBox) | 키 관리를 격리된 VM에서 수행 | 암호화 키 |
| DRM 복호화(Decryption) | 미디어 콘텐츠 복호화를 pVM 내에서 처리 | DRM 키, 평문 콘텐츠 |
| on-device AI | ML 모델 추론을 격리 환경에서 실행 | 모델 가중치, 입력 데이터 |
| 생체 인증 | 지문/얼굴 매칭을 pVM에서 수행 | 생체 템플릿 |
# pVM 생성 및 실행 (crosvm 기반)
# Android Virtualization Framework (AVF)
$ adb shell /apex/com.android.virt/bin/vm run \
--daemonize \
--config /path/to/vm_config.json
# vm_config.json 주요 필드
# {
# "kernel": "/path/to/kernel",
# "initrd": "/path/to/initrd",
# "protected": true, ← pKVM 보호 모드
# "memory_mib": 256,
# "cpus": 2
# }
# 커널 설정
CONFIG_KVM=y
CONFIG_KVM_ARM_HOST=y /* pKVM 하이퍼바이저 */
Energy Aware Scheduling (EAS)
모바일 SoC는 빅·미들·리틀 코어로 구성된 비대칭 멀티프로세싱(AMP) 아키텍처를 사용합니다. 커널의 EAS(Energy Aware Scheduling)는 성능 요구와 에너지 소비를 동시에 최적화하며, Android에서 배터리 수명과 성능 균형의 핵심입니다.
에너지 모델과 스케줄링
/* kernel/sched/fair.c — EAS 핵심: find_energy_efficient_cpu() */
/* EAS는 태스크 배치 시 에너지 소비를 계산하여 */
/* 에너지 절약이 가능한 CPU를 선택한다 */
static int find_energy_efficient_cpu(
struct task_struct *p, int prev_cpu)
{
struct perf_domain *pd;
unsigned long best_delta = ULONG_MAX;
int best_cpu = -1;
/* 각 성능 도메인(빅/미들/리틀)을 순회 */
for_each_pd(pd, root_domain) {
unsigned long cur_energy, new_energy;
/* 에너지 모델(EM)로 현재 에너지 계산 */
cur_energy = compute_energy(p, -1, pd);
/* 태스크를 이 도메인에 배치했을 때 에너지 */
new_energy = compute_energy(p, cpu, pd);
if (new_energy - cur_energy < best_delta) {
best_delta = new_energy - cur_energy;
best_cpu = cpu;
}
}
return best_cpu;
}
/* 에너지 모델 등록 (SoC DT/ACPI에서) */
/* include/linux/energy_model.h */
struct em_perf_state {
unsigned long frequency; /* kHz */
unsigned long power; /* mW */
unsigned long cost; /* 정규화된 비용 */
};
UCLAMP (Utilization Clamping)
UCLAMP은 태스크(Task)의 utilization 값에 최솟값(uclamp.min)과 최댓값(uclamp.max)을 설정하여, EAS와 cpufreq governor에 성능 힌트를 전달합니다. Android는 이를 통해 foreground 앱에는 높은 성능을, background 앱에는 전력 절약을 적용합니다.
| Task Profile | uclamp.min | uclamp.max | 효과 |
|---|---|---|---|
| HighPerformance | 512 | 1024 | 빅 코어 우선, 최고 주파수 |
| Foreground (기본) | 20 | 1024 | 필요 시 빅 코어 활용 |
| Background | 0 | 256 | 리틀 코어, 저주파수 제한 |
| Frozen (cgroup freezer) | 0 | 0 | CPU 사용 완전 제한 |
/* Android Power HAL이 UCLAMP 설정 */
/* cgroup v2 경로로 uclamp 제어 */
# foreground 앱 부스트 (터치 이벤트 시)
write /dev/cpuctl/foreground/cpu.uclamp.min 256
# 부스트 해제 (터치 종료 후)
write /dev/cpuctl/foreground/cpu.uclamp.min 20
/* 커널 내부: 스케줄러가 uclamp 값 적용 */
/* kernel/sched/core.c */
static inline unsigned long uclamp_eff_value(
struct task_struct *p,
enum uclamp_id clamp_id)
{
/* 태스크 고유값 + cgroup 제한값 중 더 제한적인 값 */
struct uclamp_se uc_max = uclamp_tg_restrict(p, clamp_id);
struct uclamp_se uc_eff = uclamp_eff_get(p, clamp_id);
return min(uc_eff.value, uc_max.value);
}
Thermal 관리
Android 기기의 Thermal HAL과 커널 thermal framework이 협력하여 발열을 제어합니다. 커널이 온도 센서를 모니터링하고 쓰로틀링 정책을 실행하며, Thermal HAL은 유저스페이스 수준의 추가 제어(프레임 레이트 감소, 밝기 조절 등)를 담당합니다.
/* drivers/thermal/thermal_core.c — 커널 thermal 프레임워크 */
/* thermal zone 등록 (SoC 드라이버) */
struct thermal_zone_device *thermal_zone_device_register_with_trips(
const char *type,
const struct thermal_trip *trips,
int num_trips,
void *devdata,
const struct thermal_zone_device_ops *ops,
...);
/* Thermal trip 포인트 — 온도 임계값별 동작 정의 */
struct thermal_trip {
int temperature; /* 밀리도(m°C) 단위 */
int hysteresis; /* 해제 히스테리시스 */
enum thermal_trip_type type; /* PASSIVE / ACTIVE / HOT / CRITICAL */
};
/* DT 예시: SoC thermal zone */
/*
thermal-zones {
cpu-thermal {
polling-delay-passive = <100>;
polling-delay = <1000>;
thermal-sensors = <&tsens 0>;
trips {
cpu_warm: cpu-warm { temperature = <65000>; type = "passive"; };
cpu_hot: cpu-hot { temperature = <75000>; type = "passive"; };
cpu_crit: cpu-crit { temperature = <95000>; type = "critical"; };
};
cooling-maps {
map0 { trip = <&cpu_warm>; cooling-device = <&cpu0 1 3>; };
map1 { trip = <&cpu_hot>; cooling-device = <&cpu0 4 6>; };
};
};
};
*/
관련 내용은 프로세스 스케줄러, 전력 관리 섹션을 참고하십시오.
Android 커널 디버깅
tracing: atrace/systrace → perfetto
Android의 시스템 트레이싱은 커널 ftrace 인프라를 기반으로 합니다. atrace가 ftrace의 trace_marker에 이벤트를 기록하고, 커널 tracepoint도 함께 수집합니다. 현재 표준 도구는 Perfetto이며, 유저스페이스 + 커널 트레이스를 통합 수집합니다.
# Perfetto로 커널 + 유저스페이스 통합 트레이스 수집
$ adb shell perfetto -o /data/misc/perfetto-traces/trace.pb -t 10s \
-c - <<EOF
buffers: { size_kb: 65536 }
data_sources: {
config {
name: "linux.ftrace"
ftrace_config {
ftrace_events: "sched/sched_switch"
ftrace_events: "sched/sched_wakeup"
ftrace_events: "power/cpu_frequency"
ftrace_events: "binder/binder_transaction"
ftrace_events: "block/block_rq_issue"
atrace_categories: "am" # ActivityManager
atrace_categories: "gfx" # Graphics
}
}
}
EOF
# ftrace Binder 트레이스포인트 (커널 내장)
# /sys/kernel/debug/tracing/events/binder/
# binder_transaction, binder_transaction_received,
# binder_lock, binder_locked, binder_unlock
binder_transaction과 binder_transaction_received 이벤트의 시간차를 측정하면 커널 내 처리 지연과 대기 시간(Latency)을 구분할 수 있습니다. Perfetto UI에서 시각적으로 확인 가능합니다.
관련 내용은 ftrace/Tracepoints, procfs/sysfs 섹션을 참고하십시오.
Tombstone과 커널 크래시 분석
Android의 tombstone은 네이티브 프로세스가 SIGSEGV, SIGABRT 등으로 크래시할 때 debuggerd가 생성하는 상세 덤프(Dump)다. 커널의 ptrace와 /proc/[pid]/maps를 통해 레지스터(Register), 백트레이스, 메모리 맵(Memory Map) 정보를 수집합니다.
# tombstone 파일 확인
$ adb shell ls /data/tombstones/
tombstone_00 tombstone_01 tombstone_02
# tombstone 내용 분석 (핵심 필드)
$ adb shell cat /data/tombstones/tombstone_00
# pid: 12345, tid: 12345, name: my_native_app
# uid: 10123
# signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
# x0 0000000000000000 x1 0000007fc3a2b8e0 ...
# backtrace:
# #00 pc 0x00001234 /system/lib64/libc.so (strlen+16)
# #01 pc 0x0000abcd /data/app/.../lib/arm64/libnative.so
# addr2line으로 소스 위치 역추적
$ llvm-addr2line -e libnative.so 0x0000abcd
# bugreport 수집 (커널 로그 + ANR + tombstone 통합)
$ adb bugreport bugreport.zip
# bugreport에 포함되는 커널 정보:
# - dmesg (커널 링 버퍼)
# - /proc/meminfo, /proc/slabinfo
# - /proc/pressure/{cpu,memory,io}
# - SELinux avc 거부 로그
# - Binder 통계 (/sys/kernel/debug/binder/)
SEGV_MTESERR 또는 SEGV_MTEAERR 시그널(Signal)이 표시됩니다. 이는 use-after-free나 buffer overflow를 하드웨어 레벨에서 감지한 것으로, tag mismatch 정보와 함께 정확한 메모리 오류 위치를 제공합니다.
ramoops/pstore — 커널 패닉(Kernel Panic) 분석
Android 기기는 커널 패닉 시 로그를 보존하기 위해 ramoops(RAM 기반 Oops/panic 로깅)를 사용합니다. 재부팅 후 pstore 파일시스템에서 이전 패닉 로그를 읽을 수 있습니다.
/* Device Tree에서 ramoops 영역 예약 */
reserved-memory {
ramoops@0x9FF00000 {
compatible = "ramoops";
reg = <0x0 0x9FF00000 0x0 0x100000>; /* 1MB */
console-size = <0x40000>; /* 256KB 콘솔 로그 */
pmsg-size = <0x40000>; /* 256KB pmsg */
record-size = <0x40000>; /* 256KB oops 레코드 */
ftrace-size = <0x40000>; /* 256KB ftrace 덤프 */
};
};
# 재부팅 후 pstore에서 패닉 로그 읽기
$ adb shell ls /sys/fs/pstore/
console-ramoops-0 dmesg-ramoops-0 pmsg-ramoops-0
$ adb shell cat /sys/fs/pstore/dmesg-ramoops-0
# 커널 설정
CONFIG_PSTORE=y
CONFIG_PSTORE_RAM=y
CONFIG_PSTORE_CONSOLE=y
CONFIG_PSTORE_PMSG=y
CONFIG_PSTORE_FTRACE=y
Android 커널 보안
주요 커널 CVE 사례
| CVE | 이름 | 영향 | 커널 대응 |
|---|---|---|---|
| CVE-2016-5195 | Dirty COW | COW race로 권한 상승 | mm: get_user_pages 수정 |
| CVE-2014-3153 | Towelroot | futex requeue 취약점(Vulnerability) | futex: requeue PI 수정 |
| CVE-2019-2215 | Bad Binder | Binder UAF로 권한 상승 | binder: epoll 참조 수정 |
| CVE-2021-22555 | Netfilter UAF | heap out-of-bounds 쓰기 | netfilter: 메모리 관리 수정 |
| 다수 | io_uring 취약점 | 복잡한 상태 머신 취약점 | Android: io_uring 완전 비활성화 (Android 15) |
커널 하드닝 기법
Android GKI 커널은 메인라인 대비 추가적인 보안 강화를 적용합니다.
| 기법 | CONFIG 옵션 | 설명 |
|---|---|---|
| CFI (Control-Flow Integrity) | CONFIG_CFI_CLANG | 간접 호출 대상을 컴파일 시점에 제한 — ROP/JOP 공격 차단 |
| SCS (Shadow Call Stack) | CONFIG_SHADOW_CALL_STACK | 리턴 주소를 별도 스택에 저장 — 스택 버퍼 오버플로(Buffer Overflow)우 완화 |
| MTE (Memory Tagging Extension) | CONFIG_ARM64_MTE | ARMv8.5-A 하드웨어 메모리 태깅 — UAF/overflow 런타임 감지 |
| PAN (Privileged Access Never) | CONFIG_ARM64_PAN | 커널이 유저 메모리 직접 접근 차단 |
| KASAN | CONFIG_KASAN_HW_TAGS | MTE 기반 하드웨어 KASAN — 프로덕션 빌드에서도 사용 가능 |
| KASLR | CONFIG_RANDOMIZE_BASE | 커널 베이스 주소 랜덤화 |
| KPTI | CONFIG_UNMAP_KERNEL_AT_EL0 | Meltdown 완화 — 유저 모드에서 커널 매핑 제거 |
| io_uring 비활성화 | CONFIG_IO_URING=n | 공격 표면 축소 (Android 15+) |
/* GKI 보안 설정 확인 */
$ adb shell zcat /proc/config.gz | grep -E "CFI|SHADOW_CALL|MTE|KASAN|KASLR"
CONFIG_CFI_CLANG=y
CONFIG_SHADOW_CALL_STACK=y
CONFIG_ARM64_MTE=y
CONFIG_KASAN_HW_TAGS=y
CONFIG_RANDOMIZE_BASE=y
Android 커널 운영 플레이북
Android 커널은 GKI 제약, 벤더 모듈 분리, 보안 정책(SELinux/Verified Boot)을 동시에 고려해야 합니다. 기능 수정 전에는 ABI/호환성/보안 영향 평가를 먼저 수행하는 것이 필수입니다.
| 점검 축 | 질문 | 확인 포인트 |
|---|---|---|
| GKI 호환성 | 변경이 커널 ABI를 깨는가? | 심볼 export/구조체 변경 영향 점검 |
| 부팅 체인 | boot/vendor_boot/init_boot 이미지 연계가 맞는가? | 부팅 이미지 헤더/ramdisk/DTB 포함 상태 확인 |
| 보안 정책 | SELinux/Verified Boot 정책과 충돌하는가? | avc 로그, dm-verity 상태, vbmeta 확인 |
| 성능/안정성 | LMKD, Binder, 스케줄링 지표가 악화되는가? | logcat, kernel dmesg, perfetto/ftrace 분석 |
# Android 커널 상태 점검
adb shell uname -a
adb shell cat /proc/version
adb shell getenforce
adb shell dmesg | tail -n 200
# 부팅 상태/검증 체인 확인
adb shell getprop ro.boot.verifiedbootstate
adb shell getprop ro.boot.vbmeta.device_state
adb shell getprop ro.boot.flash.locked
# Binder/메모리/압력 관련 지표 예시
adb shell cat /proc/meminfo | head
adb shell cat /proc/pressure/memory
adb shell ls /sys/kernel/debug/binder 2>/dev/null
Binder ioctl/BC_/BR_ 프로토콜
Binder IPC의 실제 커널 인터페이스는 ioctl(fd, BINDER_WRITE_READ, &bwr) 단일 시스템 콜로 수렴합니다. 유저 공간은 BC_(Binder Command) 프로토콜로 요청을 보내고, 커널은 BR_(Binder Return) 프로토콜로 응답을 돌려줍니다. 이 양방향 프로토콜이 Binder의 전체 동작을 결정합니다.
ioctl 명령어 일람
| ioctl 명령 | 방향 | 설명 |
|---|---|---|
BINDER_WRITE_READ | 양방향 | BC/BR 프로토콜 데이터 송수신 — 가장 핵심 |
BINDER_SET_MAX_THREADS | 유저→커널 | Binder 스레드 풀 최대 크기 설정 |
BINDER_SET_CONTEXT_MGR | 유저→커널 | servicemanager 등록 (handle 0) |
BINDER_THREAD_EXIT | 유저→커널 | Binder 스레드 종료 통지 |
BINDER_VERSION | 커널→유저 | 프로토콜 버전 조회 |
BINDER_GET_NODE_INFO_FOR_REF | 커널→유저 | 참조 핸들에 대한 노드 정보 조회 |
BINDER_GET_NODE_DEBUG_INFO | 커널→유저 | 디버깅용 노드 상세 정보 |
BINDER_FREEZE | 유저→커널 | 프로세스 Binder 동결 (Android 11+) |
BINDER_ENABLE_ONEWAY_SPAM_DETECTION | 유저→커널 | oneway 스팸 감지 활성화 |
/* include/uapi/linux/android/binder.h — 주요 ioctl 정의 */
#define BINDER_WRITE_READ _IOWR('b', 1, struct binder_write_read)
#define BINDER_SET_MAX_THREADS _IOW('b', 5, __u32)
#define BINDER_SET_CONTEXT_MGR _IOW('b', 7, __s32)
#define BINDER_THREAD_EXIT _IOW('b', 8, __s32)
#define BINDER_VERSION _IOWR('b', 9, struct binder_version)
#define BINDER_FREEZE _IOW('b', 14, struct binder_freeze_info)
/* binder_write_read — BINDER_WRITE_READ의 데이터 구조 */
struct binder_write_read {
binder_size_t write_size; /* BC 데이터 크기 */
binder_size_t write_consumed; /* 커널이 소비한 바이트 */
binder_uintptr_t write_buffer; /* BC 명령 버퍼 포인터 */
binder_size_t read_size; /* BR 데이터 크기 */
binder_size_t read_consumed; /* 커널이 채운 바이트 */
binder_uintptr_t read_buffer; /* BR 응답 버퍼 포인터 */
};
BC_ (Binder Command) 프로토콜
유저 프로세스가 커널에 보내는 명령입니다. write_buffer에 직렬화(Serialization)하여 전달합니다.
| BC 명령 | 용도 |
|---|---|
BC_TRANSACTION | 동기/비동기 트랜잭션 전송 |
BC_REPLY | 트랜잭션 응답 전송 |
BC_ACQUIRE_RESULT | 참조 획득 결과 (미사용) |
BC_FREE_BUFFER | 수신 버퍼 해제 통지 |
BC_INCREFS / BC_ACQUIRE | 원격 객체 참조 증가 (약한/강한) |
BC_DECREFS / BC_RELEASE | 원격 객체 참조 감소 (약한/강한) |
BC_REGISTER_LOOPER | 스레드가 루퍼로 등록 |
BC_ENTER_LOOPER | 메인 스레드 루퍼 진입 |
BC_EXIT_LOOPER | 루퍼 종료 |
BC_REQUEST_DEATH_NOTIFICATION | 사망 통지 등록 |
BC_DEAD_BINDER_DONE | 사망 통지 처리 완료 |
BC_TRANSACTION_SG | scatter-gather 트랜잭션 (Android 8+) |
BC_REPLY_SG | scatter-gather 응답 |
/* BC_TRANSACTION / BC_REPLY에 사용되는 트랜잭션 데이터 */
struct binder_transaction_data {
union {
__u32 handle; /* BC_TRANSACTION: 대상 핸들 */
binder_uintptr_t ptr; /* BC_REPLY: 대상 포인터 */
} target;
binder_uintptr_t cookie; /* 로컬 BBinder 포인터 */
__u32 code; /* 메서드 ID (onTransact 코드) */
__u32 flags; /* TF_ONE_WAY 등 */
pid_t sender_pid; /* 커널이 채움 — 위변조 불가 */
uid_t sender_euid;
binder_size_t data_size; /* Parcel 데이터 크기 */
binder_size_t offsets_size; /* flat_binder_object 오프셋 */
union {
struct {
binder_uintptr_t buffer; /* 데이터 버퍼 */
binder_uintptr_t offsets; /* 오프셋 배열 */
} ptr;
__u8 buf[8];
} data;
};
BR_ (Binder Return) 프로토콜
커널이 유저 프로세스에게 돌려주는 응답입니다. read_buffer를 통해 전달됩니다.
| BR 응답 | 용도 |
|---|---|
BR_TRANSACTION | 수신: 트랜잭션 도착 |
BR_REPLY | 수신: 트랜잭션 응답 도착 |
BR_ERROR | 오류 코드 반환 |
BR_OK | 명령 정상 처리 |
BR_DEAD_REPLY | 대상 프로세스 사망으로 응답 불가 |
BR_TRANSACTION_COMPLETE | 트랜잭션 전송 완료 확인 |
BR_INCREFS / BR_ACQUIRE | 로컬 객체 참조 증가 요청 |
BR_DECREFS / BR_RELEASE | 로컬 객체 참조 감소 요청 |
BR_DEAD_BINDER | 등록한 원격 객체 사망 통지 |
BR_SPAWN_LOOPER | 스레드 풀 확장 요청 |
BR_FROZEN_REPLY | 대상이 동결 상태로 응답 불가 (Android 11+) |
BR_ONEWAY_SPAM_SUSPECT | oneway 스팸 의심 통지 |
트랜잭션 처리 상세 흐름
binder_proc / binder_thread / binder_node 구조
/* drivers/android/binder.c — binder_ioctl 핵심 분기 */
static long binder_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
struct binder_proc *proc = filp->private_data;
struct binder_thread *thread;
thread = binder_get_thread(proc);
switch (cmd) {
case BINDER_WRITE_READ:
ret = binder_ioctl_write_read(filp, cmd, arg, thread);
break;
case BINDER_SET_MAX_THREADS: {
__u32 max_threads;
if (copy_from_user(&max_threads, ubuf, sizeof(max_threads)))
return -EFAULT;
binder_inner_proc_lock(proc);
proc->max_threads = max_threads;
binder_inner_proc_unlock(proc);
break;
}
case BINDER_SET_CONTEXT_MGR:
ret = binder_ioctl_set_ctx_mgr(filp, &new_node);
break;
case BINDER_FREEZE:
ret = binder_ioctl_freeze(&info, proc);
break;
...
}
return ret;
}
Binder 디버그 /proc 인터페이스
# /proc/binder 디버그 정보 조회
$ adb shell cat /proc/binder/state
# 전체 Binder 시스템 상태: 프로세스별 스레드/노드/참조 수
$ adb shell cat /proc/binder/stats
# 통계: BC/BR 명령별 호출 횟수, 트랜잭션 수
# BC_TRANSACTION: 1234567
# BR_TRANSACTION: 1234567
# BC_REPLY: 987654
$ adb shell cat /proc/binder/transactions
# 현재 진행 중인 트랜잭션 목록
$ adb shell cat /proc/binder/proc/<PID>
# 특정 프로세스의 Binder 상태 상세
# thread 1234: l 12 need_return 0 tr 0
# node 5678: u00007f... c00007f... pri 0:139 hs 1 hw 1 ls 0 lw 0
# ref 9012: desc 0 node 5678 s 1 w 1 d (null)
# Binder 트랜잭션 추적 (ftrace)
$ adb shell "echo 1 > /sys/kernel/debug/tracing/events/binder/enable"
$ adb shell cat /sys/kernel/debug/tracing/trace | head -50
BINDER_FREEZE는 Android 11+에서 앱 동결(frozen) 상태 지원을 위해 추가되었다. 동결된 프로세스의 Binder 트랜잭션은 BR_FROZEN_REPLY로 거부되며, 이는 cgroup freezer와 연동하여 백그라운드 앱의 리소스 소비를 최소화합니다.
GKI 모듈 개발 실전
GKI(Generic Kernel Image) 환경에서는 벤더별 코드를 로드 가능한 커널 모듈(LKM)로 분리해야 한다. Google이 배포하는 GKI 커널 바이너리는 변경할 수 없으며, 벤더/OEM은 KMI(Kernel Module Interface)를 통해서만 커널과 상호작용합니다. GKI 2.0(Android 12+)부터 이 구조가 강제됩니다.
GKI/KMI 아키텍처
GKI 모듈 Makefile
# 벤더 커널 모듈 Makefile 예시
# GKI 커널 소스 트리 외부에서 빌드
KERNEL_SRC ?= /path/to/gki-kernel
M ?= $(shell pwd)
obj-m += my_vendor_driver.o
my_vendor_driver-objs := core.o platform.o dma_ops.o
# 추가 CFLAGS (KMI 호환성 유지)
ccflags-y += -DANDROID_GKI_COMPATIBLE
ccflags-y += -Wno-unused-function
all:
$(MAKE) -C $(KERNEL_SRC) M=$(M) \
ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- \
LLVM=1 modules
clean:
$(MAKE) -C $(KERNEL_SRC) M=$(M) clean
install:
$(MAKE) -C $(KERNEL_SRC) M=$(M) \
INSTALL_MOD_PATH=$(DIST_DIR) modules_install
KMI 심볼 익스포트
/* 벤더 모듈에서 KMI 심볼 사용 */
/* abi_gki_aarch64.stg에 등록된 심볼만 사용 가능 */
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
/* KMI 심볼 사용 예: 등록된 exported 심볼만 링크 가능 */
static int my_driver_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
void *buf;
/* dma_alloc_coherent — KMI 심볼 */
buf = dma_alloc_coherent(dev, SZ_4K, &dma_addr, GFP_KERNEL);
if (!buf)
return -ENOMEM;
/* devm_* API도 KMI 심볼 */
dev_info(dev, "vendor driver probed\n");
return 0;
}
/* 벤더 모듈이 자체 심볼을 추가 export할 경우 */
/* android/abi_gki_aarch64_* 파일에 등록 필요 */
EXPORT_SYMBOL_GPL(my_vendor_helper_func);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Vendor SoC Driver for GKI");
Android.bp 통합
// vendor/my_vendor/my_driver/Android.bp
// Android 빌드 시스템에서 커널 모듈 통합
kernel_module {
name: "my_vendor_driver",
srcs: [
"core.c",
"platform.c",
"dma_ops.c",
],
kernel_src: "//common",
// vendor_dlkm 파티션에 설치
vendor: true,
// 부팅 시 자동 로드
install_in_vendor_dlkm: true,
}
모듈 서명
# GKI 모듈 서명 — CONFIG_MODULE_SIG_FORCE=y 환경
# 서명되지 않은 모듈은 로딩 거부
# 1. 서명 키 생성 (벤더 빌드 시스템에서)
$ openssl req -new -x509 -newkey rsa:4096 \
-keyout vendor_signing_key.pem \
-out vendor_signing_cert.pem \
-days 3650 -nodes \
-subj "/CN=Vendor Module Signing Key/"
# 2. 모듈 서명
$ scripts/sign-file sha256 \
vendor_signing_key.pem \
vendor_signing_cert.pem \
my_vendor_driver.ko
# 3. 서명 검증
$ modinfo my_vendor_driver.ko | grep sig
sig_id: PKCS#7
signer: Vendor Module Signing Key
sig_hashalgo: sha256
# 4. ABI 모니터링 — KMI 변경 감지
$ tools/abi/abidiff \
--leaf-changes-only \
--impacted-interfaces \
abi_gki_aarch64.stg \
abi_gki_aarch64_new.stg
# ABI 변경이 감지되면 GKI 호환성 위반 → 빌드 실패
abidiff 검증을 CI에 포함하는 것이 필수입니다.
pKVM 하이퍼바이저 내부
pKVM(protected KVM)은 Android 13+에서 도입된 경량 Type-1 하이퍼바이저로, ARM EL2에서 실행됩니다. 기존 KVM과 달리 pKVM은 호스트 커널을 신뢰하지 않는(deprivileged host) 보안 모델을 채택합니다. 호스트 Linux 커널은 EL1에서 게스트처럼 동작하며, pKVM이 Stage-2 페이지 테이블을 통해 메모리 접근을 통제합니다.
Deprivileged Host 모델
전통적 KVM에서는 호스트 커널이 모든 물리 메모리(Physical Memory)에 접근할 수 있지만, pKVM에서는 게스트 VM에 기부(donate)되거나 공유(share)된 메모리를 호스트가 접근할 수 없다. 이 모델은 호스트 커널이 침해당해도 게스트 데이터가 보호되는 것을 보장합니다.
Stage-2 페이지 테이블 격리
pKVM Hypercall 인터페이스
/* arch/arm64/kvm/hyp/nvhe/hyp-main.c — pKVM HVC 핸들러 */
/* EL1(호스트) → EL2(pKVM) 호출 경로 */
static void handle_host_hcall(struct kvm_cpu_context *host_ctxt)
{
u64 func_id = smccc_get_function(host_ctxt);
switch (func_id) {
case KVM_HOST_SMCCC_FUNC(__pkvm_host_share_hyp):
/* 호스트 → pKVM: 페이지 공유 */
ret = __pkvm_host_share_hyp(pfn);
break;
case KVM_HOST_SMCCC_FUNC(__pkvm_host_unshare_hyp):
/* 호스트 → pKVM: 공유 해제 */
ret = __pkvm_host_unshare_hyp(pfn);
break;
case KVM_HOST_SMCCC_FUNC(__pkvm_host_donate_hyp):
/* 호스트 → pKVM: 메모리 기부 (호스트 매핑 제거) */
ret = __pkvm_host_donate_hyp(pfn, nr_pages);
break;
case KVM_HOST_SMCCC_FUNC(__pkvm_init_vm):
/* pVM 생성 — Stage-2 페이지 테이블 할당 */
ret = __pkvm_init_vm(host_kvm, pgd_hva, last_ran_hva);
break;
case KVM_HOST_SMCCC_FUNC(__pkvm_init_vcpu):
/* vCPU 초기화 */
ret = __pkvm_init_vcpu(handle, host_vcpu);
break;
case KVM_HOST_SMCCC_FUNC(__pkvm_teardown_vm):
/* pVM 파괴 — 기부된 메모리 반환 */
ret = __pkvm_teardown_vm(handle);
break;
...
}
}
메모리 소유권 전환
/* pKVM 메모리 소유권 전환 상태 머신 */
/*
* donate: 호스트 → 게스트 (호스트 S2에서 매핑 제거)
* share: 양측 S2에 매핑 유지 (Virtio 링 등)
* lend: 일시적 대여 (반환 보장)
*/
/* 호스트가 pVM에 메모리를 기부 */
int pkvm_host_donate_guest(u64 pfn, u64 nr_pages,
pkvm_handle_t handle)
{
/* 1. 소유권 검증: 호스트 소유인지 확인 */
if (hyp_page_owner(pfn) != PKVM_ID_HOST)
return -EPERM;
/* 2. 호스트 Stage-2에서 매핑 제거 */
kvm_pgtable_stage2_unmap(&host_mmu, pfn << PAGE_SHIFT,
nr_pages << PAGE_SHIFT);
/* 3. 게스트 Stage-2에 매핑 추가 */
kvm_pgtable_stage2_map(&guest_mmu, ipa, pfn << PAGE_SHIFT,
nr_pages << PAGE_SHIFT, prot);
/* 4. 소유권 추적 업데이트 */
hyp_page_set_owner(pfn, nr_pages, handle);
return 0;
}
Protected VM 생명주기
# pVM 생명주기 관리 (Android 14+)
# 1. Virtualization APEX 확인
$ adb shell pm path com.android.virt
package:/apex/com.android.virt/app/...
# 2. VirtManager를 통한 pVM 생성
# VirtualizationService → crosvm → KVM ioctl → pKVM HVC
# 3. pVM 인스턴스 이미지 생성
$ adb shell /apex/com.android.virt/bin/vm run-microdroid \
--protected \
--payload-binary-name libmy_payload.so \
--mem 256
# 4. pVM 상태 확인
$ adb shell dumpsys android.system.virtualizationservice
# VM count: 1, Protected: true
# Memory donated: 262144 KB
# 5. pVM 디버깅 (개발 빌드 전용)
$ adb shell cat /sys/kernel/debug/kvm/*/vcpu0/guest_debug
$ adb shell dmesg | grep -i pkvm
CONFIG_KVM=y와 CONFIG_PROTECTED_NVHE_STACKTRACE=y가 GKI defconfig에 활성화되어 있어야 합니다. 프로덕션 빌드에서는 pVM 디버그 인터페이스가 비활성화됩니다.
ION/DMA-BUF 힙 관리
Android의 멀티미디어/그래픽 파이프라인(Pipeline)에서 프로세스 간 제로 카피(zero-copy) 버퍼 공유는 필수입니다. Android 12 이전에는 ION 드라이버가 이 역할을 담당했으나, Android 12+에서는 메인라인 dma-buf heaps 프레임워크로 전환되었습니다. 양쪽 모두 dma_buf 파일 디스크립터를 통해 GPU, 디스플레이, 카메라, 코덱 간 버퍼를 공유합니다.
DMA-BUF Heap 아키텍처
dma_buf_ops 구현
/* drivers/dma-buf/heaps/system_heap.c — System Heap 구현 */
static const struct dma_buf_ops system_heap_buf_ops = {
.attach = system_heap_attach,
.detach = system_heap_detach,
.map_dma_buf = system_heap_map_dma_buf,
.unmap_dma_buf = system_heap_unmap_dma_buf,
.begin_cpu_access = system_heap_dma_buf_begin_cpu_access,
.end_cpu_access = system_heap_dma_buf_end_cpu_access,
.mmap = system_heap_mmap,
.vmap = system_heap_vmap,
.vunmap = system_heap_vunmap,
.release = system_heap_dma_buf_release,
};
/* 할당 — 고차 페이지 우선, 실패 시 fallback */
static struct dma_buf *system_heap_allocate(
struct dma_heap *heap, unsigned long len,
unsigned long fd_flags, unsigned long heap_flags)
{
struct system_heap_buffer *buffer;
struct sg_table *table;
struct page *page;
buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
/* 큰 order 부터 시도하여 TLB 효율 극대화 */
for (order = orders; order >= 0; order--) {
page = alloc_pages(GFP_HIGHUSER | __GFP_COMP, order);
if (page)
sg_set_page(sg, page, PAGE_SIZE << order, 0);
}
/* dma_buf 생성 및 fd 반환 */
dmabuf = dma_buf_export(&exp_info);
return dmabuf;
}
ION → dma-buf heaps 마이그레이션
/* ION (레거시) vs dma-buf heaps (신규) 비교 */
/* === ION (Android 11 이전) === */
int ion_fd = open("/dev/ion", O_RDONLY);
struct ion_allocation_data alloc = {
.len = size,
.heap_id_mask = ION_HEAP_SYSTEM,
.flags = ION_FLAG_CACHED,
};
ioctl(ion_fd, ION_IOC_ALLOC, &alloc);
int dmabuf_fd = alloc.fd;
/* === dma-buf heaps (Android 12+) === */
int heap_fd = open("/dev/dma_heap/system", O_RDONLY);
struct dma_heap_allocation_data alloc = {
.len = size,
.fd_flags = O_RDWR | O_CLOEXEC,
};
ioctl(heap_fd, DMA_HEAP_IOCTL_ALLOC, &alloc);
int dmabuf_fd = alloc.fd;
/* 이후 사용법 동일: mmap / import / attach */
void *ptr = mmap(NULL, size, PROT_READ | PROT_WRITE,
MAP_SHARED, dmabuf_fd, 0);
/dev/dma_heap/*)는 Android의 BufferAllocator NDK 클래스에 래핑되어 있습니다. Gralloc 4.0+에서 이를 사용하며, 카메라/코덱 HAL은 AHardwareBuffer API를 통해 간접 사용합니다. ION에서 전환 시 헤더와 ioctl 번호만 변경하면 되므로 마이그레이션 난이도는 낮습니다.
Android Thermal Mitigation
모바일 기기에서 열 관리(Thermal Management)는 성능과 사용자 경험에 직접 영향을 미친다. Android의 Thermal Mitigation은 커널 thermal_zone_device 프레임워크를 기반으로, Thermal HAL이 유저 공간 정책을 결정하고, 커널이 실제 주파수/전력 제한을 적용하는 2계층 구조입니다.
Thermal HAL → 커널 프레임워크 흐름
Thermal Zone 설정
/* DT 기반 Thermal Zone 설정 예시 */
thermal-zones {
cpu-thermal {
polling-delay-passive = <100>; /* 수동 폴링 100ms */
polling-delay = <1000>; /* 능동 폴링 1초 */
thermal-sensors = <&tsens 0>;
trips {
cpu_alert0: trip-alert0 {
temperature = <75000>; /* 75°C — 경고 */
hysteresis = <2000>;
type = "passive";
};
cpu_alert1: trip-alert1 {
temperature = <85000>; /* 85°C — 스로틀링 */
hysteresis = <2000>;
type = "passive";
};
cpu_crit: trip-critical {
temperature = <110000>; /* 110°C — 강제 셧다운 */
hysteresis = <1000>;
type = "critical";
};
};
cooling-maps {
map0 {
trip = <&cpu_alert0>;
cooling-device = <&cpu0 0 3>; /* 쿨링 상태 0~3 */
};
map1 {
trip = <&cpu_alert1>;
cooling-device = <&cpu0 4 THERMAL_NO_LIMIT>;
};
};
};
};
Thermal Mitigation 정책
# Android thermal 상태 확인
$ adb shell dumpsys thermalservice
# 현재 온도, 스로틀링 상태, 쿨링 디바이스 상태
# 커널 thermal zone 직접 확인
$ adb shell cat /sys/class/thermal/thermal_zone0/temp
72000 # 72.0°C (millidegree)
$ adb shell cat /sys/class/thermal/thermal_zone0/policy
step_wise
# 쿨링 디바이스 상태 (현재 스로틀링 레벨)
$ adb shell cat /sys/class/thermal/cooling_device0/cur_state
3 # 레벨 3 스로틀링 적용 중
$ adb shell cat /sys/class/thermal/cooling_device0/max_state
15 # 최대 15단계
# Thermal HAL AIDL 인터페이스 정보
$ adb shell dumpsys android.hardware.thermal.IThermal/default
# Temperature{type=CPU, name=cpu-0-0, value=72.3, status=THROTTLING}
# CoolingDevice{type=CPU, name=thermal-cpufreq-0, value=3}
Treble/VNDK 커널 인터페이스
Android Treble은 프레임워크(Google)와 벤더(SoC/OEM) 코드를 명확히 분리하여, OS 업데이트를 벤더 종속 없이 가능하게 하는 아키텍처입니다. 커널 관점에서 Treble의 핵심은 HAL 인터페이스의 안정성과 커널 uAPI의 불변성을 보장하는 것입니다.
Treble 아키텍처 상세
AIDL HAL 정의
// hardware/interfaces/power/aidl/android/hardware/power/IPower.aidl
// AIDL HAL 인터페이스 정의 (Android 12+)
package android.hardware.power;
import android.hardware.power.Mode;
import android.hardware.power.Boost;
interface IPower {
/**
* 성능 모드 설정 — 커널 cpufreq/schedutil 파라미터 변경
* @param type 모드 유형 (SUSTAINED_PERFORMANCE, VR, LAUNCH 등)
* @param enabled 활성화 여부
*/
void setMode(in Mode type, in boolean enabled);
/**
* 성능 부스트 힌트 — 커널 EAS/uclamp 튜닝
* @param type 부스트 유형 (INTERACTION, DISPLAY_UPDATE_IMMINENT 등)
* @param durationMs 부스트 지속 시간 (ms)
*/
void setBoost(in Boost type, in int durationMs);
/**
* Power Hint Session (Android 12+)
* — 스레드 그룹의 목표 작업 시간을 커널 스케줄러에 전달
*/
IPowerHintSession createHintSession(
in int tgid,
in int uid,
in int[] threadIds,
in long durationNanos);
}
커널 uAPI 안정성
/* 커널 uAPI 안정성 — Treble의 커널 계약 */
/*
* uAPI 헤더 (include/uapi/linux/*) 는 절대 깨뜨리면 안 됨
* — 기존 ioctl 번호 변경 금지
* — 기존 구조체 필드 제거/재배치 금지
* — 새 필드는 구조체 끝에만 추가 (크기 검증)
* — 새 ioctl/sysfs는 추가만 가능
*/
/* 예: sysfs 인터페이스 안정성 */
/* /sys/class/thermal/thermal_zone*/temp — ABI 보장 */
/* /sys/devices/system/cpu/cpu*/cpufreq/scaling_* — ABI 보장 */
/* 벤더 HAL → 커널 접근 경로 */
/* 1. sysfs: /sys/... (가장 일반적) */
/* 2. ioctl: /dev/ion, /dev/binder, /dev/dma_heap/* */
/* 3. netlink: NETLINK_GENERIC, NETLINK_KOBJECT_UEVENT */
/* 4. procfs: /proc/meminfo, /proc/pressure/* */
/* Android 전용 sysfs 노출 예 */
/* /sys/class/leds/ — LED HAL */
/* /sys/class/power_supply/ — Battery HAL */
/* /sys/class/thermal/ — Thermal HAL */
/* uAPI 위반 감지: VTS 테스트가 자동 검증 */
/* VtsTrebleVintfTest — VINTF manifest ↔ 커널 일치 검사 */
/dev/binder (또는 /dev/hwbinder)를 사용합니다. VNDK(Vendor Native Development Kit)는 프레임워크 공유 라이브러리(Shared Library)의 벤더 사용 가능 서브셋을 정의합니다.
dm-verity/AVB 체인
Android Verified Boot(AVB)는 부팅 체인 전체의 무결성을 암호학적으로 검증합니다. 부트로더가 vbmeta 서명을 검증하고, 커널이 dm-verity를 통해 system/vendor 파티션의 블록 단위 무결성을 런타임에 보장합니다. 변조가 감지되면 부팅이 거부되거나 에러 복구 모드로 전환됩니다.
AVB 검증 체인
avbtool 명령
# AVB 이미지 생성 및 검증 명령
# 1. vbmeta 이미지 생성 (RSA4096 서명)
$ avbtool make_vbmeta_image \
--algorithm SHA256_RSA4096 \
--key oem_key.pem \
--include_descriptors_from_image boot.img \
--include_descriptors_from_image vendor_boot.img \
--include_descriptors_from_image dtbo.img \
--chain_partition system:1:system_key.avbpubkey \
--chain_partition vendor:2:vendor_key.avbpubkey \
--output vbmeta.img
# 2. boot.img에 해시 디스크립터 추가
$ avbtool add_hash_footer \
--image boot.img \
--partition_name boot \
--partition_size $((64 * 1024 * 1024)) \
--algorithm SHA256_RSA4096 \
--key boot_key.pem
# 3. system.img에 hashtree 디스크립터 추가 (dm-verity용)
$ avbtool add_hashtree_footer \
--image system.img \
--partition_name system \
--partition_size $((4 * 1024 * 1024 * 1024)) \
--algorithm SHA256_RSA4096 \
--key system_key.pem
# 4. 이미지 검증
$ avbtool verify_image --image vbmeta.img --expected_chain_partition system:1:system_key.avbpubkey
dm-verity 테이블 설정
# dm-verity 테이블 구조 (커널 dm 타겟)
# init에서 mount 시 dm-verity 타겟으로 설정
# dm-verity 파라미터:
# version dev hash_dev data_block_size hash_block_size
# num_data_blocks hash_start_block algorithm digest salt
# 실제 예시 (adb shell에서)
$ adb shell cat /sys/block/dm-0/dm/table
0 8388608 verity 1 /dev/sda17 /dev/sda17 4096 4096 \
1048576 1048577 sha256 \
a1b2c3d4e5f6... \
0123456789ab...
# dm-verity 에러 모드 확인
$ adb shell getprop ro.boot.veritymode
enforcing # enforcing | logging | eio
# hashtree 생성 (veritysetup 사용)
$ veritysetup format /dev/sda17 /dev/sda17 \
--hash-offset=$((1048577 * 4096)) \
--data-block-size=4096 \
--hash-block-size=4096
enforcing 모드에서는 I/O 에러(EIO)를 반환하고 restart 트리거로 재부팅될 수 있다. 개발 중에는 adb disable-verity로 비활성화할 수 있으나, 프로덕션에서는 절대 비활성화하면 안 됩니다. 관련 상세는 Device Mapper 문서를 참고하십시오.
FUSE/Passthrough 스토리지
Android는 외부 저장소 접근 제어를 위해 FUSE(Filesystem in Userspace)를 사용합니다. 앱이 /storage/emulated/0 경로에 접근하면, FUSE 데몬(MediaProvider)이 권한 검사를 수행한 뒤 하위 파일시스템(ext4/f2fs)에 실제 I/O를 위임합니다. Android 12+에서는 FUSE passthrough로 성능 오버헤드를 대폭 줄였습니다.
FUSE Passthrough vs 일반 FUSE
FUSE Passthrough 설정
/* fs/fuse/passthrough.c — FUSE passthrough 핵심 구현 */
/* Android 12+: FUSE_PASSTHROUGH 기능 */
/* 1. FUSE 데몬이 파일 open 시 lower file fd를 커널에 등록 */
int fuse_passthrough_open(struct fuse_dev *fud,
u32 lower_fd)
{
struct file *passthrough_filp;
/* lower_fd → struct file 변환 */
passthrough_filp = fget(lower_fd);
/* 유효성 검증: regular file인지, 같은 마운트인지 */
if (!S_ISREG(file_inode(passthrough_filp)->i_mode))
return -EINVAL;
/* passthrough entry 등록 */
fuse_pt->filp = passthrough_filp;
return 0;
}
/* 2. 이후 read/write는 lower file에 직접 전달 */
ssize_t fuse_passthrough_read_iter(
struct kiocb *iocb_fuse,
struct iov_iter *iter)
{
struct file *passthrough_filp = ff->passthrough.filp;
struct kiocb iocb_passthrough;
/* lower file의 f_op->read_iter 직접 호출 */
kiocb_clone(&iocb_passthrough, iocb_fuse, passthrough_filp);
return vfs_iter_read(passthrough_filp, iter,
&iocb_passthrough.ki_pos, 0);
}
sdcardfs → FUSE 마이그레이션
# Android 외부 저장소 변천사
# Android 4~7: sdcardfs (stacked filesystem, 커널 내부)
# — 성능 좋음, 하지만 메인라인 거부됨
# Android 8~10: sdcardfs 유지 (deprecation 시작)
# Android 11: FUSE 기반 MediaProvider 전환
# — Scoped Storage 강제, 성능 저하 문제
# Android 12+: FUSE passthrough로 성능 회복
# FUSE 마운트 확인
$ adb shell mount | grep fuse
/dev/fuse on /storage/emulated type fuse \
(rw,lazytime,nosuid,nodev,noexec,noatime, \
user_id=0,group_id=0,allow_other)
# FUSE passthrough 활성 여부 확인
$ adb shell cat /sys/fs/fuse/features/passthrough
1 # 1 = 활성
# 성능 비교 (sequential read, 1GB file)
# 일반 FUSE: ~200 MB/s
# FUSE passthrough: ~800 MB/s (ext4 직접 수준)
# 네이티브 ext4: ~850 MB/s
# CONFIG 확인
$ adb shell zcat /proc/config.gz | grep FUSE
CONFIG_FUSE_FS=y
CONFIG_FUSE_PASSTHROUGH=y
open() 시에만 FUSE 데몬을 거쳐 권한 검사를 수행하고, 이후 read/write는 커널 내에서 lower filesystem으로 직접 전달됩니다. 이로써 Scoped Storage의 보안 모델을 유지하면서도 네이티브에 근접한 I/O 성능을 달성합니다.
Android 성능 디버깅 실전
Android 커널 성능 문제를 진단할 때는 simpleperf(CPU 프로파일링(Profiling)), Perfetto(시스템 트레이싱), atrace(Android 트레이스), ftrace(커널 트레이스) 네 가지 도구를 상황에 맞게 조합합니다. 이 섹션에서는 각 도구의 커널 레벨 활용법을 다룹니다.
simpleperf — CPU 프로파일링
# simpleperf: Android 전용 perf 래퍼 (NDK 포함)
# 커널 PMU(Performance Monitoring Unit) 이벤트 사용
# 1. 시스템 전체 CPU 프로파일링 (10초)
$ adb shell simpleperf record -a --duration 10 -o /data/local/tmp/perf.data
# -a: all CPUs, --duration: 초 단위
# 2. 특정 프로세스 프로파일링
$ adb shell simpleperf record -p $(pidof system_server) \
--call-graph dwarf --duration 5
# 3. 결과 분석 — 핫 함수
$ adb shell simpleperf report -i /data/local/tmp/perf.data \
--sort comm,dso,symbol | head -30
# Overhead Command Shared Object Symbol
# 8.23% system_server [kernel.kallsyms] __schedule
# 5.12% system_server [kernel.kallsyms] binder_ioctl
# 3.45% surfaceflinger libgui.so ...
# 4. 하드웨어 이벤트 프로파일링
$ adb shell simpleperf stat -a --duration 5 \
-e cache-misses,cache-references,instructions,cycles
# Performance counter statistics:
# 123,456,789 cache-misses # 12.3% of cache-references
# 1,002,345,678 cache-references
# 5. Flamegraph 생성 (호스트에서)
$ adb pull /data/local/tmp/perf.data
$ simpleperf report-sample --protobuf -i perf.data -o perf.proto
$ pprof -flame perf.proto
atrace — Android 트레이스
# atrace: Android 시스템 트레이스 수집
# 커널 ftrace 기반 — systrace와 동일한 백엔드
# 1. 사용 가능한 트레이스 카테고리
$ adb shell atrace --list_categories
gfx - Graphics
input - Input
view - View System
sched - CPU Scheduling
freq - CPU Frequency
binder_driver - Binder Kernel driver
...
# 2. 5초간 스케줄링/Binder/GPU 트레이스 수집
$ adb shell atrace -a -t 5 sched freq binder_driver gfx \
-o /data/local/tmp/atrace.txt
# 3. 압축 형태 수집 (Perfetto로 분석)
$ adb shell atrace --async_start -b 32768 sched freq binder_driver
# ... 시나리오 수행 ...
$ adb shell atrace --async_stop -o /data/local/tmp/trace.txt
$ adb pull /data/local/tmp/trace.txt
# 4. systrace (Python 래퍼, 호스트에서 실행)
$ python systrace.py -t 10 sched freq binder_driver gfx -o trace.html
Perfetto 트레이스 설정
# Perfetto 트레이스 설정 (text proto format)
# ui.perfetto.dev에서 시각화 가능
buffers {
size_kb: 65536
fill_policy: RING_BUFFER
}
data_sources {
config {
name: "linux.ftrace"
ftrace_config {
# 스케줄링 이벤트
ftrace_events: "sched/sched_switch"
ftrace_events: "sched/sched_wakeup"
ftrace_events: "sched/sched_blocked_reason"
# CPU 주파수
ftrace_events: "power/cpu_frequency"
ftrace_events: "power/suspend_resume"
# Binder
ftrace_events: "binder/binder_transaction"
ftrace_events: "binder/binder_transaction_received"
ftrace_events: "binder/binder_lock"
# 메모리
ftrace_events: "vmscan/mm_vmscan_direct_reclaim_begin"
ftrace_events: "oom/oom_score_adj_update"
# I/O
ftrace_events: "block/block_rq_issue"
ftrace_events: "block/block_rq_complete"
buffer_size_kb: 16384
drain_period_ms: 250
}
}
}
# 프로세스 정보 (심볼 해석용)
data_sources {
config {
name: "linux.process_stats"
process_stats_config {
scan_all_processes_on_start: true
proc_stats_poll_ms: 1000
}
}
}
duration_ms: 10000 # 10초간 수집
ftrace Android 설정
# Android에서 ftrace 직접 사용
# debugfs가 마운트되어 있어야 함 (userdebug/eng 빌드)
# 1. 사용 가능한 트레이서 확인
$ adb shell cat /sys/kernel/debug/tracing/available_tracers
function_graph function nop
# 2. 스케줄러 이벤트 트레이스
$ adb shell "echo 1 > /sys/kernel/debug/tracing/events/sched/sched_switch/enable"
$ adb shell "echo 1 > /sys/kernel/debug/tracing/events/sched/sched_wakeup/enable"
$ adb shell "echo 1 > /sys/kernel/debug/tracing/tracing_on"
# 3. Binder 트레이스
$ adb shell "echo 1 > /sys/kernel/debug/tracing/events/binder/enable"
# 4. 특정 함수 트레이스 (function_graph)
$ adb shell "echo function_graph > /sys/kernel/debug/tracing/current_tracer"
$ adb shell "echo binder_ioctl > /sys/kernel/debug/tracing/set_graph_function"
# 5. 트레이스 읽기
$ adb shell cat /sys/kernel/debug/tracing/trace | head -50
# system_server-1234 [002] .... 12345.678: binder_ioctl <- ...
# system_server-1234 [002] .... 12345.679: | binder_ioctl_write_read()
# system_server-1234 [002] .... 12345.680: | binder_thread_write()
# 6. 트레이스 중지 및 정리
$ adb shell "echo 0 > /sys/kernel/debug/tracing/tracing_on"
$ adb shell "echo nop > /sys/kernel/debug/tracing/current_tracer"
$ adb shell "echo > /sys/kernel/debug/tracing/trace"
Systrace 분석 핵심 패턴
# Perfetto/systrace에서 확인할 Android 커널 성능 패턴
# 패턴 1: Binder 병목
# — binder_transaction 시간이 16ms 초과 → UI 프레임 드롭
# — binder thread exhaustion: BR_SPAWN_LOOPER 빈발
# 패턴 2: Direct Reclaim 지연
# — mm_vmscan_direct_reclaim_begin → _end 구간이 수 ms
# — 해결: watermark_scale_factor 조정, MGLRU 활성화
$ adb shell "echo 150 > /proc/sys/vm/watermark_scale_factor"
# 패턴 3: CPU 주파수 부족
# — cpu_frequency 이벤트에서 고정된 낮은 주파수
# — thermal throttling 또는 EAS 문제
$ adb shell cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_cur_freq
# 패턴 4: I/O 지연
# — block_rq_issue → block_rq_complete 구간 분석
# — f2fs GC 간섭, FUSE 오버헤드
# 패턴 5: Lock Contention
# — sched_blocked_reason 이벤트에서 binder_lock 빈발
$ adb shell simpleperf record -e sched:sched_stat_blocked -a --duration 5
ui.perfetto.dev 웹 UI에서 SQL 쿼리를 사용하면 대규모 트레이스에서도 빠르게 병목(Bottleneck)을 찾을 수 있다. 예: SELECT ts, dur, name FROM slice WHERE name LIKE 'binder%' AND dur > 16000000 (16ms 초과 Binder 트랜잭션 검색). 관련 커널 트레이싱 상세는 ftrace/Tracepoints 문서를 참고하십시오.
GKI 호환성 테스트 VTS/CTS
Android GKI 커널의 품질과 호환성을 보장하기 위해 Google은 VTS(Vendor Test Suite)와 CTS(Compatibility Test Suite)를 필수 통과 요구사항으로 지정합니다. VTS는 커널/HAL 인터페이스를, CTS는 프레임워크 API를 검증합니다. GKI 모듈 개발자는 반드시 이 테스트를 통과해야 합니다.
VTS/CTS 테스트 흐름
VTS 커널 테스트 실행
# VTS 커널 테스트 실행 (Tradefed 기반)
# 1. VTS 환경 설정
$ source build/envsetup.sh
$ lunch aosp_arm64-userdebug
# 2. VTS 빌드
$ make vts -j$(nproc)
# 3. VTS 커널 테스트 실행
$ vts-tradefed run commandAndExit vts \
--module VtsKernelLinuxKselftest \
--test "timers:posix_timers" \
--serial $DEVICE_SERIAL
# 4. 커널 설정 검증 테스트
$ vts-tradefed run commandAndExit vts \
--module VtsKernelConfig
# 5. VINTF (Vendor Interface) 매니페스트 검증
$ vts-tradefed run commandAndExit vts \
--module VtsTrebleVintfTest
# — HAL manifest ↔ 실제 HAL 서비스 일치 확인
# — 커널 버전 ↔ compatibility matrix 일치 확인
# 6. procfs/sysfs 인터페이스 검증
$ vts-tradefed run commandAndExit vts \
--module VtsKernelProcFileApi
# — /proc/meminfo, /proc/version, /proc/cpuinfo 포맷 검증
CTS 테스트 실행
# CTS (Compatibility Test Suite) 실행
# 1. CTS 다운로드 (android.googlesource.com)
$ unzip android-cts-*.zip
$ cd android-cts
# 2. CTS 보안 테스트 (커널 취약점 관련)
$ ./tools/cts-tradefed run commandAndExit cts \
--module CtsSecurityTestCases \
--serial $DEVICE_SERIAL
# — SELinux 정책 검증
# — seccomp 필터 동작 확인
# — 알려진 CVE 패치 여부 검증
# 3. 파일시스템 관련 테스트
$ ./tools/cts-tradefed run commandAndExit cts \
--module CtsFileSystemTestCases
# 4. 네트워크 관련 테스트
$ ./tools/cts-tradefed run commandAndExit cts \
--module CtsNetTestCases
# 5. 특정 테스트만 실행
$ ./tools/cts-tradefed run commandAndExit cts \
--module CtsSecurityTestCases \
--test android.security.cts.KernelConfigTest
# 6. 결과 확인
$ cat results/latest/test_result.xml | grep "fail"
KernelCI 통합
# .kernelci.yaml — GKI 커널 CI 설정 예시
# https://kernelci.org 연동
build_configs:
android-gki:
tree: android-mainline
branch: android14-6.1
arch: arm64
build_environment: gcc-12
defconfig: gki_defconfig
fragments:
- android/gki_defconfig.fragment
test_configs:
vts-kernel:
test_suite: vts
test_plans:
- VtsKernelLinuxKselftest
- VtsKernelConfig
- VtsKernelProcFileApi
abi-check:
test_suite: abi
reference: abi_gki_aarch64.stg
tool: abidiff
# ABI 변경 시 자동 실패 + 리뷰어 알림
boot-test:
test_suite: boot
targets:
- qemu-arm64
- pixel-6 # 실제 기기 테스트
timeout: 300 # 5분 부팅 타임아웃
VtsTrebleVintfTest는 커널 버전, HAL manifest, compatibility matrix의 3자 일치를 검증하므로, 커널 버전 업그레이드 시 반드시 VINTF manifest도 함께 업데이트해야 합니다.
참고 자료
- Android 공식 커널 문서 — Android Kernel Overview (source.android.com)
- GKI 공식 문서 — Generic Kernel Image (GKI) overview
- HIDL/AIDL HAL 아키텍처 — Android HAL Interface Definition
- Android SELinux 정책 문서 — Security-Enhanced Linux in Android
- Android 부트로더 가이드 — Android Bootloader documentation
- Android Verified Boot 문서 — Verified Boot (AVB) documentation
- Android 공통 커널 저장소 — Android Common Kernel (ACK) Git repository
- 커널 소스 — drivers/android (Binder 등) (Bootlin Elixir)
- 커널 소스 — drivers/android/binder.c Binder IPC 구현 (Bootlin Elixir)
- LWN.net — GKI: Android's Generic Kernel Image
- LWN.net — Binder: the Android IPC mechanism
- LWN.net — Protected KVM (pKVM) for Android
- 커널 공식 문서 — Multi-Gen LRU (MGLRU) documentation
- Perfetto 트레이싱 UI — Perfetto Web UI (ui.perfetto.dev)
관련 문서
Android 커널과 관련된 다른 문서를 더 깊이 이해하고 싶다면 다음을 참고하세요.