# 태스크 객체를 만들기 위한 저장소와 한도를 준비합니다

v6.6 / kernel/fork.c

프로세스를 실제 생성하기 전에 task_struct를 효율적으로 할당할 캐시와 생성 제한을 준비합니다. 따라서 fork_init의 실행 자체가 새 프로세스 하나를 만드는 것은 아닙니다. 이 초기화 결과를 이후 fork 계열 경로가 사용합니다.

## fork_init

```c

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

```

### 1041행

```c

void __init fork_init(void)

```

나중에 task_struct를 할당할 캐시와 초기 태스크 생성 한도를 준비합니다. 인자가 없는 부팅 초기화 함수이며 이 호출 자체가 자식 프로세스를 만들지는 않습니다.

### 1043행

```c

	int i;

```

초기 user namespace의 자원 종류별 한도를 순회할 인덱스입니다. 아래 UCOUNT_COUNTS 반복에서 사용합니다.

### 1044행

```c

#ifndef CONFIG_ARCH_TASK_STRUCT_ALLOCATOR

```

아키텍처가 task_struct 전용 할당 함수를 제공하는지 나타냅니다. #ifndef이므로 전용 구현이 없을 때만 아래의 공통 slab 캐시를 만들며, 전용 구현을 쓰는 빌드는 그 생성을 건너뜁니다. 이 선택은 전처리 단계에서 이루어지며, CPU가 실행 중 이 줄의 조건을 검사하지 않습니다.

### 1045행

```c

#ifndef ARCH_MIN_TASKALIGN

```

아키텍처가 task_struct의 별도 최소 정렬을 정의하지 않았다면 공통 기본값을 제공하는 구간입니다. 정의가 있는 아키텍처의 요구값을 덮어쓰지 않으려는 조건입니다. 이 선택은 전처리 단계에서 이루어지며, CPU가 실행 중 이 줄의 조건을 검사하지 않습니다.

### 1046행

```c

#define ARCH_MIN_TASKALIGN	0

```

아키텍처별 추가 정렬 요구가 없음을 0으로 표시합니다. 실제 캐시 정렬은 아래에서 L1_CACHE_BYTES와의 최댓값을 사용하므로 객체가 무정렬로 배치되는 것은 아닙니다. 이 이름은 전처리 단계에서 정의한 값으로 치환되며, 실행 중 값을 담을 변수를 만드는 것은 아닙니다.

### 1047행

```c

#endif

```

아키텍처가 정렬 매크로를 제공하지 않은 경우의 기본값 정의를 마칩니다. 이제 공통 식에서 캐시라인 크기와 비교할 수 있습니다.

### 1048행

```c

	int align = max_t(int, L1_CACHE_BYTES, ARCH_MIN_TASKALIGN);

```

캐시라인 크기와 아키텍처의 task_struct 최소 정렬 요구 중 더 큰 값을 선택합니다. max_t의 int는 비교·결과에 사용할 타입이며 이후 객체 캐시가 이 정렬을 지킵니다.

### 1049행

```c

	unsigned long useroffset, usersize;

```

task_struct에서 사용자와 복사해도 되는 영역의 시작 오프셋과 길이를 담습니다. 전체 객체 크기와 구분되는 허용 범위입니다.

### 1052행

```c

	task_struct_whitelist(&useroffset, &usersize);

```

task_struct 안에서 usercopy를 허용할 시작 위치와 크기를 얻습니다.

### 1053행

```c

	task_struct_cachep = kmem_cache_create_usercopy("task_struct",

```

정해진 크기의 객체 캐시를 만들면서 사용자와 복사할 수 있는 바이트 범위를 제한합니다.

### 1054행

```c

			arch_task_struct_size, align,

```

캐시에 넣을 task_struct의 실제 크기와 앞서 계산한 정렬을 넘깁니다. 아키텍처별 추가 상태 때문에 고정 숫자를 쓰지 않습니다. 앞줄에서 시작한 호출이나 식에 이어지는 부분이므로, 이 줄만으로 별도의 함수 호출이 생기지는 않습니다.

### 1055행

```c

			SLAB_PANIC|SLAB_ACCOUNT,

```

필수 캐시 생성이 실패하면 부팅을 중단하고(SLAB_PANIC), 이 캐시에서 할당되는 객체를 메모리 cgroup 회계에 포함할 수 있게(SLAB_ACCOUNT) 지정합니다. 앞줄에서 시작한 호출이나 식에 이어지는 부분이므로, 이 줄만으로 별도의 함수 호출이 생기지는 않습니다.

### 1056행

```c

			useroffset, usersize, NULL);

```

사용자 복사가 허용되는 바이트 범위를 전달하고 객체 생성자 콜백은 등록하지 않습니다. useroffset과 usersize는 앞의 whitelist 호출에서 얻었습니다.

### 1057행

```c

#endif

```

아키텍처 전용 할당기가 없는 경우의 공통 task_struct 캐시 생성을 마칩니다. 뒤의 아키텍처 보조 캐시·태스크 수 한도 준비는 이어서 수행합니다.

### 1060행

```c

	arch_task_cache_init();

```

아키텍처별 태스크 관련 캐시를 준비합니다.

### 1062행

```c

	set_max_threads(MAX_THREADS);

```

메모리 크기와 정책에 맞춰 최대 태스크 수를 제한합니다.

### 1064행

```c

	init_task.signal->rlim[RLIMIT_NPROC].rlim_cur = max_threads/2;

```

초기 태스크의 프로세스·스레드 수 soft limit을 시스템 최대 스레드 수의 절반으로 설정합니다. 이후 태스크가 물려받는 자원 제한의 출발점입니다.

### 1065행

```c

	init_task.signal->rlim[RLIMIT_NPROC].rlim_max = max_threads/2;

```

같은 자원의 hard limit도 절반으로 설정합니다. soft limit의 현재 적용값과 일반 사용자가 올릴 수 있는 상한을 별도 필드에 둡니다.

### 1066행

```c

	init_task.signal->rlim[RLIMIT_SIGPENDING] =

```

대기 중인 시그널 수 제한의 초기값을 프로세스 수 제한과 같게 설정하기 시작합니다. 다음 줄에서 soft·hard limit을 함께 가진 구조체를 복사합니다.

### 1067행

```c

		init_task.signal->rlim[RLIMIT_NPROC];

```

앞줄의 대기 시그널 한도에 RLIMIT_NPROC의 soft·hard limit 쌍을 넘깁니다. 태스크나 주소 공간 자체를 복제하는 대입이 아닙니다.

### 1069행

```c

	for (i = 0; i < UCOUNT_COUNTS; i++)

```

user namespace가 추적하는 각 자원 계수 종류를 순회합니다. 같은 기본 상한을 빠짐없이 정하기 위한 반복입니다.

### 1070행

```c

		init_user_ns.ucount_max[i] = max_threads/2;

```

초기 user namespace에서 i번째 자원 계수의 최대값을 설정합니다. 아래 rlimit 계열별 상한 설정과는 별도로 관리되는 배열입니다.

### 1072행

```c

	set_userns_rlimit_max(&init_user_ns, UCOUNT_RLIMIT_NPROC,      RLIM_INFINITY);

```

초기 user namespace의 사용자별 프로세스·스레드 수에 대한 집계 상한을 설정합니다. RLIM_INFINITY를 넘겨도 저장 값은 표현 가능한 LONG_MAX로 제한됩니다. 이 namespace 상한과 각 태스크의 rlimit은 별개이므로 앞에서 설정한 태스크 한도까지 없애는 코드는 아닙니다.

### 1073행

```c

	set_userns_rlimit_max(&init_user_ns, UCOUNT_RLIMIT_MSGQUEUE,   RLIM_INFINITY);

```

초기 user namespace의 POSIX 메시지 큐가 차지하는 바이트 수에 대한 집계 상한을 설정합니다. RLIM_INFINITY를 넘겨도 저장 값은 표현 가능한 LONG_MAX로 제한됩니다. 이 namespace 상한과 각 태스크의 rlimit은 별개이므로 앞에서 설정한 태스크 한도까지 없애는 코드는 아닙니다.

### 1074행

```c

	set_userns_rlimit_max(&init_user_ns, UCOUNT_RLIMIT_SIGPENDING, RLIM_INFINITY);

```

초기 user namespace의 대기 중인 시그널 수에 대한 집계 상한을 설정합니다. RLIM_INFINITY를 넘겨도 저장 값은 표현 가능한 LONG_MAX로 제한됩니다. 이 namespace 상한과 각 태스크의 rlimit은 별개이므로 앞에서 설정한 태스크 한도까지 없애는 코드는 아닙니다.

### 1075행

```c

	set_userns_rlimit_max(&init_user_ns, UCOUNT_RLIMIT_MEMLOCK,    RLIM_INFINITY);

```

초기 user namespace의 메모리에 고정해 두는 페이지의 바이트 수에 대한 집계 상한을 설정합니다. RLIM_INFINITY를 넘겨도 저장 값은 표현 가능한 LONG_MAX로 제한됩니다. 이 namespace 상한과 각 태스크의 rlimit은 별개이므로 앞에서 설정한 태스크 한도까지 없애는 코드는 아닙니다.

### 1077행

```c

#ifdef CONFIG_VMAP_STACK

```

커널 스택을 가상 주소에 매핑하고 guard page로 스택 넘침을 잡는 구성입니다. 이 구성에서만 CPU가 내려갈 때 캐시에 남은 가상 스택을 정리할 콜백을 등록합니다. 이 선택은 전처리 단계에서 이루어지며, CPU가 실행 중 이 줄의 조건을 검사하지 않습니다.

### 1078행

```c

	cpuhp_setup_state(CPUHP_BP_PREPARE_DYN, "fork:vm_stack_cache",

```

CPU 상태 전환 시 호출할 준비·정리 함수를 등록합니다.

### 1079행

```c

			  NULL, free_vm_stack_cache);

```

CPU 준비 시 실행할 별도 콜백은 없고, CPU가 내려갈 때 캐시된 가상 커널 스택을 정리할 free_vm_stack_cache를 등록합니다. 앞줄에서 시작한 호출이나 식에 이어지는 부분이므로, 이 줄만으로 별도의 함수 호출이 생기지는 않습니다.

### 1080행

```c

#endif

```

가상 커널 스택 캐시의 CPU 종료 콜백 등록 부분을 마칩니다. 이 조건은 스택 캐시 정리에 대한 것이며 뒤의 공통 태스크 추적 초기화까지 제외하지 않습니다.

### 1082행

```c

	scs_init();

```

반환 주소를 별도로 보호하는 shadow call stack 체계의 초기화를 수행합니다.

### 1084행

```c

	lockdep_init_task(&init_task);

```

초기 태스크가 사용할 잠금 의존성 추적 상태를 준비합니다.

### 1085행

```c

	uprobes_init();

```

사용자 프로그램의 probe 추적 기능을 준비합니다.

