Process init · Linux v6.6 · source + diagram note

fork_init

fork_init()은 태스크를 복제하지 않습니다. 부팅 중 설정에 따라 task cache를 만들고 스레드 한도를 준비하며, PID 초기화와 보조 cache 준비는 앞뒤의 별도 함수가 맡습니다.

태스크 생성 전에 실행되는 부팅 초기화

start_kernel()는 pid_idr_init()을 호출하고 다른 준비를 거쳐 fork_init(), 이어 proc_caches_init()을 호출한다. 세 함수가 서로를 직접 호출하는 체인은 아니다(main.c L1037–1054).

그림 1. v6.6 부팅 호출 순서와 서로 다른 책임
① pid_idr_init()초기 PID namespace의 IDR·pid cache와 CPU 수를 반영한 pid_max 범위를 준비한다.pid.c L648–667
② fork_init()아키텍처 자체 할당기가 없을 때 task_struct 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를 준비한다.fork.c L3277–3302
①과 ② 사이에는 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 cachepid.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).

그림 2. 설정 조건이 드러나는 두 cache 계열
fork_init() · task 본체!CONFIG_ARCH_TASK_STRUCT_ALLOCATOR에서 task_struct_cachep를 생성한다. arch hook과 초기 한도는 조건문 밖이다.fork.c L1041–1086
proc_caches_init() · 주변 객체sighand / signal / files / fs / VMA cache. CONFIG_PER_VMA_LOCK이면 VMA lock cache도 만든다. 이후 mmap/nsproxy를 초기화한다.fork.c L3277–3302
PID IDR과 pid cache는 두 상자에 포함하지 않는다. 앞선 pid_idr_init()의 일이다. main.c L1037–1054 · pid.c L648–667

초기화와 태스크 생성은 다른 동작

fork_init()은 cache와 한도를 준비한다. copy_process()가 NPROC와 max_threads를 검사하는 일은 새 태스크 생성 경로의 별도 코드다(fork.c L1041–1086 · fork.c L2365–2380). 이 초기화 호출 이후에도 부팅은 이어지므로 ‘이후 생성’을 곧 ‘부팅 완료 뒤’로 읽으면 안 된다(main.c L1066–1068).

그림 3. 준비 단계와 생성 경로의 경계
준비 · start_kernel()PID 관리 구조 → 조건부 task cache·한도 → 보조 cache. 여기에는 자식 태스크 생성 호출 체인이 없다.main.c L1037–1054 · fork.c L1041–1086 · fork.c L3277–3302
생성 경로 · copy_process()새 태스크 생성 중 NPROC와 max_threads를 검사한다. 전체 복제·해제 경로는 이 글의 세 발췌 밖이다.fork.c L2365–2380
문C do_fork 그림은 구형 런타임 경로의 구성 참고다. 이 그림은 v6.6 소스에 맞춰 독립적으로 그렸으며 원본 이미지를 복제하지 않았다.

설정 조건과 해석의 한계

두 초기화 함수는 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).

이어서 읽기