Android 커널 (Android Kernel)

Android GKI 아키텍처, Binder IPC, 메모리 관리(ashmem/lmkd), HAL 인터페이스, SELinux 정책, 부팅 과정, 프로세스 모델, 스토리지, 커널 디버깅과 보안까지 Android 플랫폼의 커널 레벨 종합 가이드.

참고: Android 커널의 공식 문서는 source.android.com에서 확인할 수 있다. 이 페이지는 커널 개발자 관점에서 Android 플랫폼의 커널 레벨 변경사항과 아키텍처를 다룬다.

Android 커널 개요

Android는 Linux 커널을 기반으로 하는 모바일/임베디드 운영체제다. 초기에는 메인라인 커널에서 크게 분기(fork)하여 다수의 out-of-tree 패치를 유지했으나, Google의 지속적인 업스트리밍 노력으로 GKI(Generic Kernel Image) 시대에는 메인라인과의 차이가 크게 줄었다.

업스트리밍 역사

Android가 독자적으로 유지하던 커널 기능들은 점진적으로 메인라인 Linux에 통합되거나 대체되었다.

Android 전용 기능메인라인 대체커널 버전상태
wakelocksPM wakeup sources3.5+완전 통합
ashmemmemfd_create()5.18+ 제거memfd로 전환
ION allocatorDMA-BUF heaps5.6+ION 제거 (5.11)
lowmemorykiller (LMK)lmkd (userspace, PSI 기반)4.12+ 제거유저스페이스 전환
binder(메인라인 통합)3.19+staging → drivers/android
paranoid networking(Android 전용 유지)GKI 유지

Android 버전별 커널 매핑

Android 버전커널 버전GKI 지원주요 커널 변화
Android 115.4GKI 1.0GKI 도입, 모듈화 시작
Android 125.10GKI 2.0KMI 안정성 보장, cgroup v2 필수
Android 135.10 / 5.15GKI 2.0EROFS 시스템 파티션, Virtualization
Android 145.15 / 6.1GKI 2.016KB 페이지 지원, Rust 모듈
Android 156.1 / 6.6GKI 2.0MGLRU 기본 활성화, io_uring 비활성화

GKI (Generic Kernel Image)

GKI는 Android 기기의 커널 파편화 문제를 해결하기 위한 Google의 전략이다. 단일 커널 바이너리(GKI 커널)가 모든 ARM64 디바이스에서 부팅되고, 벤더별 하드웨어 지원은 로드 가능한 모듈로 분리된다.

GKI 아키텍처

Android Framework / HAL (Userspace) AIDL / HIDL / Binder / hwbinder / vndbinder KMI 경계 (Kernel Module Interface) GKI 커널 (Google 빌드) 코어 서브시스템 · 스케줄러 · MM · VFS · Net 벤더 모듈 (.ko) GPU · 카메라 · 모뎀 · 센서 드라이버 KMI Linux 커널 코어 Binder · DMA-BUF Heaps · PSI · cgroup v2 · dm-verity · SELinux 하드웨어 (SoC: Qualcomm / MediaTek / Samsung / Google Tensor)
GKI 2.0 (Android 12+): GKI 2.0부터 벤더는 GKI 커널 소스를 직접 수정할 수 없으며, 모든 벤더 코드는 반드시 로드 가능 모듈 형태로 제공해야 한다. KMI 심볼 리스트에 없는 커널 함수는 모듈에서 호출할 수 없다.

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.6Android 15 GKI 릴리스stable/linux-6.6.y
android14-6.1Android 14 GKI 릴리스stable/linux-6.1.y
android14-5.15Android 14 레거시stable/linux-5.15.y
android13-5.10Android 13 레거시stable/linux-5.10.y

Bazel(Kleaf) 빌드 시스템

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 핵심 특성

Binder 트랜잭션 흐름

Client Process BpBinder (Proxy) IPCThreadState ioctl(BINDER_ WRITE_READ) /dev/binder 커널 Binder 드라이버 binder_transaction() copy_from_user() mmap 버퍼 매핑 wake_up_interruptible() Server Process BBinder (Stub) IPCThreadState ioctl(BINDER_ WRITE_READ) BC_TRANSACTION BR_TRANSACTION BC_REPLY BR_REPLY

핵심 자료구조

/* 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유저 → 커널스레드가 Binder 루프 진입
응답 (BR_*)방향설명
BR_TRANSACTION커널 → 유저수신 측에 트랜잭션 전달
BR_REPLY커널 → 유저송신 측에 응답 전달
BR_DEAD_BINDER커널 → 유저원격 객체 사망 통지
BR_SPAWN_LOOPER커널 → 유저추가 Binder 스레드 생성 요청
BR_TRANSACTION_COMPLETE커널 → 유저트랜잭션 전송 완료 확인

mmap 버퍼와 단일 복사

주의 — 1MB 버퍼 제한: Binder 드라이버는 프로세스당 최대 약 1MB(1048576 - 4096 × 2 바이트)의 mmap 버퍼를 할당한다. 대용량 데이터 전송 시에는 ashmem/memfd를 파일 디스크립터로 전달하여 별도 공유 메모리를 사용해야 한다.
/* 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 디바이스 노드

Android는 보안 도메인 분리를 위해 3개의 Binder 디바이스를 사용한다. Android 10+에서는 binderfs를 통해 동적으로 디바이스를 생성할 수 있다.

디바이스용도접근 도메인
/dev/binderFramework ↔ App IPC앱, 시스템 서비스
/dev/hwbinderFramework ↔ 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로 권한 축소가 가능했다. 커널 5.18에서 제거되었으며, 메인라인의 memfd_create() + memfd_secret()으로 대체되었다.

/* 기존 ashmem (deprecated) */
int fd = open("/dev/ashmem", O_RDWR);
ioctl(fd, ASHMEM_SET_NAME, "my_region");
ioctl(fd, ASHMEM_SET_SIZE, 4096);
void *ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

/* 메인라인 대체: memfd_create (커널 3.17+) */
int fd = memfd_create("my_region", MFD_CLOEXEC | MFD_ALLOW_SEALING);
ftruncate(fd, 4096);
void *ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
/* F_SEAL_SHRINK | F_SEAL_GROW로 크기 변경 방지 (ashmem pin/unpin 대체) */
fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW);

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

MGLRU와 Android

MGLRU + Android Go: Multi-Gen LRU(MGLRU, 커널 6.1+)는 전통적 active/inactive LRU 대비 페이지 에이징 정확도를 크게 개선한다. Android Go(저사양) 디바이스에서 앱 kill 빈도가 18% 감소하고, 차가운 페이지 회수 속도가 향상되었다. Android 15부터 GKI에서 기본 활성화된다. 관련 내용은 Page Cache — MGLRU 섹션을 참고하라.

Android 부팅 과정 (커널 관점)

파티션 구조

Android 기기의 스토리지는 여러 전용 파티션으로 나뉘며, GKI 시대에는 커널 이미지와 벤더 모듈이 별도 파티션으로 분리된다.

파티션내용파일시스템비고
bootGKI 커널 + generic ramdisk부트 이미지 (v4)Android 13+: 커널만
init_bootgeneric ramdisk (init)부트 이미지Android 13+: boot에서 분리
vendor_boot벤더 ramdisk + DTB부트 이미지 (v4)벤더 모듈, fstab
systemAndroid FrameworkEROFS / ext4dm-verity 보호
vendor벤더 HAL, 라이브러리EROFS / ext4dm-verity 보호
userdata사용자 데이터, 앱f2fs / ext4FBE(File-Based Encryption)
vbmetaAVB 검증 메타데이터VBMeta 구조체해시 트리 루트

부팅 흐름

부트로더 ABL / U-Boot AVB 검증 vbmeta chain Linux 커널 start_kernel() 1st stage init 마운트, SELinux dm-verity 설정 2nd stage init .rc 파싱 서비스 시작 부트로더 단계 1. SoC ROM이 ABL 로드 2. vbmeta 파티션 읽기 3. boot 이미지 해시 검증 4. vendor_boot DTB 로드 5. 커널 + ramdisk → 메모리 6. 롤백 인덱스 확인 커널 단계 1. start_kernel() 실행 2. DTB → device_node 변환 3. GKI 모듈 로드 4. 벤더 모듈 로드 5. ramdisk 마운트 → /init 6. SELinux 초기화 init 단계 1. 1st: /system dm-verity 마운트 2. 1st: SELinux Enforcing 3. 2nd: init.rc 파싱 4. 2nd: Zygote 시작 5. 2nd: SurfaceFlinger 시작 6. 2nd: SystemServer 시작

AVB (Android Verified Boot)

AVB는 부팅 체인의 무결성을 보장한다. 부트로더가 vbmeta 파티션의 서명을 검증하고, 각 파티션의 해시/해시 트리 루트를 확인한다. 커널 레벨에서는 dm-verity가 런타임 시 블록 단위 검증을 수행한다.

/* VBMeta 검증 체인 */
vbmeta (RSA 서명)
 ├── boot (hash descriptor)        → GKI 커널 검증
 ├── vendor_boot (hash descriptor) → 벤더 ramdisk/DTB 검증
 ├── system (hashtree descriptor)  → dm-verity 해시 트리 루트
 ├── vendor (hashtree descriptor)  → dm-verity 해시 트리 루트
 └── vbmeta_system (chained)       → 추가 파티션 검증

/* dm-verity 커널 명령줄 (init에 의해 설정) */
# androidboot.veritymode=enforcing
# dm-verity FEC(Forward Error Correction)으로 일부 손상 복구 가능

관련 내용은 Secure Boot, Device Mapper — dm-verity 섹션을 참고하라.

HAL과 커널 인터페이스

Project Treble — Framework/Vendor 분리

Android 8.0(Oreo)에서 도입된 Project Treble은 Android Framework과 벤더 구현을 명확히 분리한다. 이 아키텍처 덕분에 Google이 Framework을 업데이트하더라도 벤더 HAL과 커널 드라이버를 수정할 필요가 없다.

HIDL → AIDL 전환

Android 13부터 새로운 HAL은 반드시 Stable AIDL을 사용해야 한다. HIDL(HAL Interface Definition Language)은 deprecated 상태이며, 기존 HIDL HAL도 점진적으로 AIDL로 마이그레이션 중이다.

특성HIDLAIDL (Stable)
도입 버전Android 8.0Android 11+ (HAL용)
전송 계층hwbinderbinder / hwbinder
언어 지원C++, JavaC++, Java, Rust, NDK
버전 관리메이저.마이너단일 정수 버전
상태Deprecated (Android 13+)현재 표준

HAL ↔ 커널 드라이버 매핑

Android HAL커널 서브시스템주요 인터페이스
Audio HALALSA (ASoC)/dev/snd/*, tinyalsa
Camera HALV4L2 / Media/dev/video*, /dev/media*
Display/Composer HALDRM/KMS/dev/dri/*, modesetting
Sensors HALIIO (Industrial I/O)/sys/bus/iio/
GNSS HALTTY / USB serial/dev/ttyACM*, NMEA
USB HALUSB Gadget / ConfigFS/config/usb_gadget/
Power HALcpufreq / devfreq / PM/sys/devices/system/cpu/
Thermal HALThermal framework/sys/class/thermal/
Health HALpower_supply/sys/class/power_supply/
Vibrator HALFF (Force Feedback) / input/dev/input/event*

Android SELinux 정책 (커널 관점)

Android는 SELinux Enforcing 모드를 필수로 요구한다 (Android 5.0+). 커널의 SELinux LSM이 모든 시스템 콜에서 접근 제어를 강제하며, Android 전용 정책으로 앱/서비스/HAL 간의 세밀한 격리를 구현한다.

커널 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_contextsBinder 서비스 이름 → SELinux 레이블
hwservice_contextsHIDL/AIDL HAL 서비스 → SELinux 레이블
property_contexts시스템 프로퍼티 → SELinux 레이블
genfs_contexts가상 파일시스템(proc, sysfs) 레이블

관련 내용은 LSM/Seccomp 심화 섹션을 참고하라.

Android 전용 커널 설정

참고: GKI 커널의 기본 설정은 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 (읽기 전용 시스템 파티션, Android 13+)
CONFIG_F2FS_FS권장F2FS (userdata 파티션)
CONFIG_CRYPTO_AES필수FBE(파일 기반 암호화)
CONFIG_BLK_INLINE_ENCRYPTION권장인라인 암호화 엔진 (UFS/eMMC HW 암호화)
CONFIG_LRU_GEN권장MGLRU (Android 15+ 기본)
CONFIG_SCHED_TUNE권장UCLAMP 스케줄러 튜닝

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 구성

cgroup v2 (Android 12+): Android 12부터 cgroup v2가 필수다. task_profiles.json이 cgroup 설정을 추상화하며, init이 .rc 파일의 task_profiles 지시어를 통해 서비스를 적절한 cgroup에 배치한다.
cgroup 컨트롤러마운트 경로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 대역폭 제한
# 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 오버헤드를 줄이기 위해 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 알고리즘 */

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

관련 내용은 ftrace/Tracepoints, procfs/sysfs 섹션을 참고하라.

ramoops/pstore — 커널 패닉 분석

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-5195Dirty COWCOW race로 권한 상승mm: get_user_pages 수정
CVE-2014-3153Towelrootfutex requeue 취약점futex: requeue PI 수정
CVE-2019-2215Bad BinderBinder UAF로 권한 상승binder: epoll 참조 수정
CVE-2021-22555Netfilter UAFheap 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리턴 주소를 별도 스택에 저장 — 스택 버퍼 오버플로우 완화
MTE (Memory Tagging Extension)CONFIG_ARM64_MTEARMv8.5-A 하드웨어 메모리 태깅 — UAF/overflow 런타임 감지
PAN (Privileged Access Never)CONFIG_ARM64_PAN커널이 유저 메모리 직접 접근 차단
KASANCONFIG_KASAN_HW_TAGSMTE 기반 하드웨어 KASAN — 프로덕션 빌드에서도 사용 가능
KASLRCONFIG_RANDOMIZE_BASE커널 베이스 주소 랜덤화
KPTICONFIG_UNMAP_KERNEL_AT_EL0Meltdown 완화 — 유저 모드에서 커널 매핑 제거
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