태스크 생성 전에 실행되는 부팅 초기화
start_kernel()는 pid_idr_init()을 호출하고 다른 준비를 거쳐 fork_init(), 이어 proc_caches_init()을 호출한다. 세 함수가 서로를 직접 호출하는 체인은 아니다(main.c L1037–1054).
anon_vma_init()·thread_stack_cache_init()·cred_init() 등이 있다. 화살표는 start_kernel()의 호출 순서이며 각 함수의 내부 호출 체인을 뜻하지 않는다. main.c L1037–1054원문과 적용 범위
아래 세 코드 블록은 Linux v6.6의 main.c L1037–1054, fork.c L1041–1086, fork.c L3277–3302에서 각각 연속된 실제 행을 보여준다. 한도 계산은 fork.c L998–1017, PID 초기화는 pid.c L648–667, 생성 경로의 한도 검사는 fork.c L2365–2380를 따로 확인했다.
범위: 이 글은 부팅 중 초기화 단계에 집중한다. 실제 fork/clone의 전체 경로와 모든 설정 조합은 다루지 않는다. 문C의 fork_init 글은 v4.14, start_kernel 목록은 v4.0/v5.x, do_fork 그림은 구형 런타임 생성 경로다. 설명 구성만 참고했으며 구형 API를 v6.6 근거로 쓰거나 원그림을 복제하지 않았다.
고정 태그: init/main.c · kernel/fork.c · kernel/pid.c. 표시한 두 파일의 SPDX는 GPL-2.0-only다. PID 코드는 별도 링크로만 인용한다.
세 연속 코드 발췌
1. start_kernel()의 직접 호출 순서
init/main.c:1037–1054 · 중간 호출을 보존한 연속 발췌다.
pid_idr_init();
anon_vma_init();
#ifdef CONFIG_X86
if (efi_enabled(EFI_RUNTIME_SERVICES))
efi_enter_virtual_mode();
#endif
thread_stack_cache_init();
cred_init();
fork_init();
proc_caches_init();
uts_ns_init();
key_init();
security_init();
dbg_late_init();
net_ns_init();
vfs_caches_init();
pagecache_init();
signals_init();2. fork_init()의 조건부 task cache와 초기 한도
kernel/fork.c:1041–1086 · 함수 전체이며 설정 조건을 보존했다.
void __init fork_init(void)
{
int i;
#ifndef CONFIG_ARCH_TASK_STRUCT_ALLOCATOR
#ifndef ARCH_MIN_TASKALIGN
#define ARCH_MIN_TASKALIGN 0
#endif
int align = max_t(int, L1_CACHE_BYTES, ARCH_MIN_TASKALIGN);
unsigned long useroffset, usersize;
/* create a slab on which task_structs can be allocated */
task_struct_whitelist(&useroffset, &usersize);
task_struct_cachep = kmem_cache_create_usercopy("task_struct",
arch_task_struct_size, align,
SLAB_PANIC|SLAB_ACCOUNT,
useroffset, usersize, NULL);
#endif
/* do the arch specific task caches init */
arch_task_cache_init();
set_max_threads(MAX_THREADS);
init_task.signal->rlim[RLIMIT_NPROC].rlim_cur = max_threads/2;
init_task.signal->rlim[RLIMIT_NPROC].rlim_max = max_threads/2;
init_task.signal->rlim[RLIMIT_SIGPENDING] =
init_task.signal->rlim[RLIMIT_NPROC];
for (i = 0; i < UCOUNT_COUNTS; i++)
init_user_ns.ucount_max[i] = max_threads/2;
set_userns_rlimit_max(&init_user_ns, UCOUNT_RLIMIT_NPROC, RLIM_INFINITY);
set_userns_rlimit_max(&init_user_ns, UCOUNT_RLIMIT_MSGQUEUE, RLIM_INFINITY);
set_userns_rlimit_max(&init_user_ns, UCOUNT_RLIMIT_SIGPENDING, RLIM_INFINITY);
set_userns_rlimit_max(&init_user_ns, UCOUNT_RLIMIT_MEMLOCK, RLIM_INFINITY);
#ifdef CONFIG_VMAP_STACK
cpuhp_setup_state(CPUHP_BP_PREPARE_DYN, "fork:vm_stack_cache",
NULL, free_vm_stack_cache);
#endif
scs_init();
lockdep_init_task(&init_task);
uprobes_init();
}3. proc_caches_init()의 보조 cache
kernel/fork.c:3277–3302 · 함수 전체이며 VMA 조건을 보존했다.
void __init proc_caches_init(void)
{
sighand_cachep = kmem_cache_create("sighand_cache",
sizeof(struct sighand_struct), 0,
SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_TYPESAFE_BY_RCU|
SLAB_ACCOUNT, sighand_ctor);
signal_cachep = kmem_cache_create("signal_cache",
sizeof(struct signal_struct), 0,
SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_ACCOUNT,
NULL);
files_cachep = kmem_cache_create("files_cache",
sizeof(struct files_struct), 0,
SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_ACCOUNT,
NULL);
fs_cachep = kmem_cache_create("fs_cache",
sizeof(struct fs_struct), 0,
SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_ACCOUNT,
NULL);
vm_area_cachep = KMEM_CACHE(vm_area_struct, SLAB_PANIC|SLAB_ACCOUNT);
#ifdef CONFIG_PER_VMA_LOCK
vma_lock_cachep = KMEM_CACHE(vma_lock, SLAB_PANIC|SLAB_ACCOUNT);
#endif
mmap_init();
nsproxy_cache_init();
}어떤 객체와 한도를 누가 준비하나
pid_idr_init()은 초기 PID namespace의 IDR·pid cache를 준비한다(pid.c L648–667). fork_init()의 task_struct_cachep 생성은 !CONFIG_ARCH_TASK_STRUCT_ALLOCATOR 분기 안에 있고, arch_task_cache_init()과 set_max_threads(MAX_THREADS)는 그 바깥에서 실행된다(fork.c L1041–1086).
set_max_threads()는 totalram_pages()의 페이지 수, PAGE_SIZE, THREAD_SIZE로 후보값을 계산하고 overflow 방지·제안 상한·MIN_THREADS/MAX_THREADS clamp를 적용한다(fork.c L998–1017). fork_init()은 init_task.signal의 NPROC·SIGPENDING 초기 rlimit과 init_user_ns.ucount_max[]에 max_threads/2를 사용한다. 별도 set_userns_rlimit_max()는 네 UCOUNT_RLIMIT_*의 namespace별 최대치를 RLIM_INFINITY로 지정한다(fork.c L1041–1086).
| 호출 | 표시 소스에서 확인되는 일 | 별도 단계 | 근거 |
|---|---|---|---|
pid_idr_init() | PID IDR·pid cache·pid_max 기초값 | task_struct cache | pid.c L648–667 |
fork_init() | 조건부 task cache, arch hook, max_threads·초기 한도 | 실제 자식 태스크 복제 | fork.c L1041–1086 · fork.c L998–1017 |
proc_caches_init() | sighand·signal·files·fs·VMA cache, 조건부 VMA lock cache, mmap/nsproxy 초기화 | PID IDR·max_threads 계산 | fork.c L3277–3302 |
task 본체와 주변 cache를 구분하기
task slab 생성은 아키텍처 설정에 따른 분기 안에 있다. 주변 객체의 cache는 뒤의 proc_caches_init()이 별도로 만들며, VMA lock cache는 CONFIG_PER_VMA_LOCK일 때만 추가한다(fork.c L1041–1086 · fork.c L3277–3302).
!CONFIG_ARCH_TASK_STRUCT_ALLOCATOR에서 task_struct_cachep를 생성한다. arch hook과 초기 한도는 조건문 밖이다.fork.c L1041–1086CONFIG_PER_VMA_LOCK이면 VMA lock cache도 만든다. 이후 mmap/nsproxy를 초기화한다.fork.c L3277–3302초기화와 태스크 생성은 다른 동작
fork_init()은 cache와 한도를 준비한다. copy_process()가 NPROC와 max_threads를 검사하는 일은 새 태스크 생성 경로의 별도 코드다(fork.c L1041–1086 · fork.c L2365–2380). 이 초기화 호출 이후에도 부팅은 이어지므로 ‘이후 생성’을 곧 ‘부팅 완료 뒤’로 읽으면 안 된다(main.c L1066–1068).
max_threads를 검사한다. 전체 복제·해제 경로는 이 글의 세 발췌 밖이다.fork.c L2365–2380설정 조건과 해석의 한계
두 초기화 함수는 void __init이다. task slab 생성은 !CONFIG_ARCH_TASK_STRUCT_ALLOCATOR, VMA lock cache는 CONFIG_PER_VMA_LOCK, VMAP stack cache CPU hotplug 등록은 CONFIG_VMAP_STACK 조건에 의존한다(fork.c L1041–1086 · fork.c L3277–3302). 따라서 모든 설정에서 같은 cache가 생성된다고 단정하지 않는다.
표시된 cache 생성에는 SLAB_PANIC이 포함된다. 세 발췌에는 일반적인 오류 레이블·역순 rollback 코드가 보이지 않는다. PID 번호 관리와 task 수 제한, 부팅 준비와 태스크 생성은 각각 다른 근거를 가진다(pid.c L648–667 · fork.c L998–1017 · fork.c L2365–2380).
무엇을 확인할 수 있나
| 방법 | 확인할 수 있는 것 | 그 방법만으로 알 수 없는 것 | 근거 |
|---|---|---|---|
| v6.6 태그 소스 읽기 | 직접 호출 순서와 설정 분기 | 특정 보드에서 걸린 시간 | main.c L1037–1054 · fork.c L1041–1086 · fork.c L3277–3302 |
cat /proc/sys/kernel/threads-max | 실행 중 노출된 현재 thread 상한 | 초기화 중 어느 문장에서 멈췄는지 | fork.c L998–1017 · fork.c L3533–3554 |
| 부팅 로그·별도 계측 | 명시적으로 계측한 지점의 도달 여부 | 로그만으로 모든 cache·limit의 내부 상태 | fork.c L1041–1086 · fork.c L3277–3302 |
fork_init()과 proc_caches_init()은 start_kernel()에서 직접 호출된다. initcall_debug나 일반 런타임 tracepoint를 두 함수 내부 단계의 자동 완료 증거로 제시하지 않는다(main.c L1037–1054).
이어서 읽기
totalram_pages()와THREAD_SIZE에 따라 계산한 한도는 어떻게 달라지는가? fork.c L998–1017CONFIG_ARCH_TASK_STRUCT_ALLOCATOR가 설정된 아키텍처는 task cache를 어디서 준비하는가? fork.c L1041–1086CONFIG_PER_VMA_LOCK에 따른 VMA lock cache 분기는 어떻게 달라지는가? fork.c L3277–3302- 실제 복제 경로는 v6.6 copy_process 글과 fork.c L2365–2380에서 이어서 확인한다.