Process init · Linux 6.18.37 LTS · source + diagram note

fork_init

fork_init()은 태스크를 복제하지 않습니다. start_kernel()의 부팅 초기화에서 task cache와 스레드 한도를 마련하며, PID 초기화와 보조 캐시 초기화는 앞뒤의 별도 함수가 맡습니다.

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

start_kernel()는 pid_idr_init()을 호출하고, 그 사이에 익명 VMA·스택·credential 준비를 거쳐 fork_init(), 이어 proc_caches_init()을 호출한다. 세 함수는 서로를 직접 호출하는 체인이 아니다(main.c L1080–1095).

그림 1. v6.18.37 부팅 순서와 각 함수의 책임
① pid_idr_init()초기 PID namespace의 IDR과 pid cache를 준비한다. CPU 수에 따라 pid_max 범위도 조정한다.pid.c L799–818
② fork_init()task_struct cache를 만들고 메모리 추정치에서 max_threads 및 초기 RLIMIT·ucount 한도를 정한다.fork.c L807–850 · fork.c L766–785
③ proc_caches_init()sighand·signal·files·fs cache를 만들고 mmap/nsproxy 초기화를 호출한다.fork.c L2977–2997
화살표는 start_kernel()의 호출 순서다. ①과 ② 사이에는 anon_vma_init()·thread_stack_cache_init()·cred_init()이 있다. PID IDR 준비, task 수 한도, process 보조 캐시는 서로 다른 작업이다. main.c L1080–1095

표시한 코드와 적용 범위

아래 세 발췌는 고정된 Linux v6.18.37의 main.c L1080–1095, fork.c L807–850, fork.c L2977–2997에서 가져온 연속 행이다. PID 할당기 설명은 같은 태그의 pid.c L799–818, max_threads 계산은 fork.c L766–785를 함께 읽었다.

범위: 이 글은 부팅 중 fork_init()을 중심으로 한다. fork/clone syscall, namespace별 PID 배정, 모든 아키텍처와 설정 조합, 실제 보드의 부팅 로그 전체는 검증하지 않았다. 문C의 fork_init 글과 start_kernel 글은 설명 방식에 대한 역사적 참고다. 두 글의 구형 함수 위치를 이 버전의 증거로 쓰지 않았다.

태그 소스: init/main.c · kernel/fork.c · kernel/pid.c. 표시된 두 파일은 원본 SPDX가 GPL-2.0-only다.

세 함수의 실제 연속 코드

1. start_kernel의 직접 호출 순서

init/main.c:1080–1095 · 중간 호출을 생략하지 않은 실제 부팅 순서다.

	pid_idr_init();
	anon_vma_init();
	thread_stack_cache_init();
	cred_init();
	fork_init();
	proc_caches_init();
	uts_ns_init();
	time_ns_init();
	key_init();
	security_init();
	dbg_late_init();
	net_ns_init();
	vfs_caches_init();
	pagecache_init();
	signals_init();
	seq_file_init();

2. fork_init: task cache와 초기 한도

kernel/fork.c:807–850 · task cache 생성 뒤 set_max_threads와 init_task/user namespace 초기 한도가 이어진다.

void __init fork_init(void)
{
	int i;
#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);

	/* 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: 보조 캐시와 후속 초기화

kernel/fork.c:2977–2997 · 이 함수의 본문에는 PID IDR 생성이 없다.

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);
	mmap_init();
	nsproxy_cache_init();
}

각 함수는 다른 준비를 맡는다

pid_idr_init()은 init_pid_ns.idr을 초기화하고 pid cache를 만든다(pid.c L799–818). 뒤에 부르는 fork_init()은 task_struct_cachep를 만들고 아키텍처별 task cache 준비를 호출한 다음, set_max_threads(MAX_THREADS)를 실행한다(fork.c L807–850). 이 값은 부팅 시 가용 페이지 추정과 THREAD_SIZE를 이용해 계산하고 범위에 맞춘다(fork.c L766–785).

fork_init()은 그 결과로 init_task.signal의 RLIMIT_NPROC·RLIMIT_SIGPENDING, 초기 사용자 namespace의 ucount_max[]를 설정한다. init_task.signal의 NPROC cur/max와 SIGPENDING은 max_threads/2로 두지만, 별도 set_userns_rlimit_max()는 init_user_ns의 네 UCOUNT_RLIMIT_* 상한을 RLIM_INFINITY로 지정한다. 여기서 max_threads/2를 전체 시스템의 단일한 런타임 허용량으로 읽으면 안 된다(fork.c L807–850).

호출여기서 준비하는 것이 함수의 일이 아닌 것근거
pid_idr_init초기 PID IDR·pid cache·pid_max 기초값task_struct cache와 max_threadspid.c L799–818
fork_inittask cache·max_threads·초기 rlimit/ucount·후속 내부 초기화PID IDR 생성, 실제 자식 태스크 복제fork.c L807–850 · fork.c L766–785
proc_caches_initsighand·signal·files·fs cache, mmap/nsproxy 초기화 호출PID IDR 생성, max_threads 산정fork.c L2977–2997

task cache와 보조 cache를 분리하기

task_struct_cachep는 fork_init()에서 kmem_cache_create_usercopy()로 설정되는 cache 포인터다. proc_caches_init()의 네 cache는 별도 객체에 대한 것이다. 둘을 한 cache 또는 하나의 구조체 필드 묶음으로 그리면 할당 대상을 잘못 읽게 된다(fork.c L807–850 · fork.c L2977–2997).

그림 2. 두 초기화 함수가 만드는 cache 계열
fork_init() · task 본체task_struct_cachep → task_struct용 cache. 같은 함수가 arch_task_cache_init()과 초기 한도 설정을 잇는다.fork.c L816–839
proc_caches_init() · 주변 객체sighand_cachep / signal_cachep / files_cachep / fs_cachep; 마지막에 mmap_init()과 nsproxy_cache_init().fork.c L2977–2997
PID의 IDR·cache는 이 두 상자 밖에서 먼저 pid_idr_init()이 준비한다. pid.c L799–818

초기화 단계와 태스크 생성 단계를 구분하기

부팅 초기에 start_kernel()이 초기화 함수를 순서대로 실행한 뒤에도 부팅은 이어진다. 이어지는 rest_init()에서 init·kthreadd 태스크 생성이 시작되므로, 이 초기화 단계와 새 태스크를 만드는 단계는 구분해야 한다(main.c L1109–1110 · main.c L711–735). copy_process()의 NPROC와 max_threads 검사 위치는 fork.c L2053–2068다. 그 함수의 전체 생성·해제 경로는 copy_process 글에서 읽는다.

그림 3. 초기화 단계와 태스크 생성 단계의 구분
부팅 초기화 · start_kernel()PID IDR → task cache·초기 한도 → process 보조 cache. 이 단계는 자식 태스크를 생성하는 그림이 아니다.main.c L1080–1095
초기화 뒤 생성 경로 · copy_process()새 태스크 생성 시 NPROC와 max_threads 등 한도를 별도로 검사한다. 생성은 부팅 후반에도 시작된다. 이 글에서 전체 호출 체인을 단정하지 않는다.fork.c L2053–2068
문C의 do_fork 글의 그림은 구형 런타임 생성 경로를 보여준다. 원본 그림을 복제하지 않았고, 이 부팅 초기화 도식에 구형 함수명을 이식하지 않았다.

코드에서 확인되는 조건만 남기기

두 초기화 함수는 void __init이고, 표시된 cache 생성은 SLAB_PANIC을 사용한다. 이 본문에 일반적인 오류 레이블·역순 rollback 경로가 있다고 서술할 근거는 없다(fork.c L807–850 · fork.c L2977–2997). CONFIG_VMAP_STACK에 따른 CPU hotplug 등록은 조건부이며, cache 생성과 별도로 읽어야 한다(fork.c L841–849).

해석상 중요한 세 구분은 PID 번호 관리와 task 수 제한, task_struct cache와 주변 객체 cache, 부팅 준비와 실제 태스크 생성이다. 각 구분은 위 소스 위치를 다시 확인할 수 있게 표기했다.

무엇을 확인할 수 있나

방법알 수 있는 것알 수 없는 것근거
태그 소스 순서 읽기start_kernel()의 직접 호출 순서특정 보드에서 각 단계가 끝난 시간main.c L1080–1095
cat /proc/sys/kernel/threads-max부팅 후 노출된 현재 thread 상한 값부팅 중 어느 함수에서 멈췄는지, 초기 계산식의 모든 입력fork.c L766–785 · fork.c L3230–3269
초기 부팅 로그·명시적 계측계측한 지점의 실제 도달 여부로그만으로 cache와 limit의 모든 내부 상태fork.c L807–850 · fork.c L2977–2997

initcall_debug는 initcall 경로를 보는 설정이다. 여기의 fork_init()·proc_caches_init()은 start_kernel()에서 직접 호출되므로 그 설정 하나를 두 함수의 단계별 추적법으로 제시하지 않는다(main.c L1080–1095).

이어서 읽기

  • set_max_threads()의 가용 페이지 추정치와 MIN_THREADS/MAX_THREADS clamp는 보드 메모리 조건에 따라 어떤 값이 되는가? fork.c L766–785
  • pid_max와 max_threads가 서로 다른 제한이라는 점을 생성 경로에서 어떻게 확인하는가? pid.c L191–245 · fork.c L2053–2068
  • task_struct를 실제 할당하고 자원을 복제하는 과정은 copy_process에서 어디서 시작되는가?