Linux v6.18.37 · 개념과 코드 읽기

init 프로그램의 인자와 환경을 넘깁니다

이 코드는 어떤 문제를 푸나요?

커널 내부에서 실행되던 init 태스크가 사용자 프로그램으로 바뀌려면 파일 이름, 인자 배열, 환경 배열이 준비되어야 합니다. 이 작은 함수는 그 연결을 보여 줍니다. 성공한 exec는 현재 태스크의 프로그램을 바꾸므로 새 자식 프로세스를 만드는 fork와 다릅니다.

읽을 범위: v6.18.37 · init/main.c · run_init_process 1391–1404행입니다. 아래에 이 범위의 원문과 각 줄의 설명을 실었습니다. 주제 전체의 흐름과 다른 경로는 기존 분석에서 함께 읽으실 수 있습니다.

먼저 알아둘 개념

argv와 envp

각각 프로그램 인자와 환경 문자열의 포인터 배열입니다. 끝은 NULL 포인터로 표시합니다.

exec

현재 프로세스의 실행 이미지를 새 프로그램으로 바꿉니다. PID가 새로 생기는 동작과 구분합니다.

처음 읽을 때

코드를 읽을 때 argv[0]에 실행할 init의 이름을 넣습니다. 그다음 어느 상태가 바뀌는지 각 줄에서 확인하세요.

더 깊이 살펴볼 때

이 경로의 호출 문맥, 잠금·인터럽트 상태, 오류 시 남는 자원을 함께 추적해 보세요. 호출된 함수가 수행하는 작업과 현재 함수가 직접 보장하는 범위를 구분하는 것이 중요합니다.

그림으로 보는 변화

init 프로그램의 인자와 환경을 넘깁니다의 단계별 개념 그림
각 단계에 화살표 의미와 생략 범위를 표시했습니다. 주소·숫자 예제는 실제 장치 값을 뜻하지 않습니다.
1단계 설명

1단계 고정

GIF 원본 열기

1. 파일 이름 준비

argv[0]에 실행할 init의 이름을 넣습니다.

화살표는 인자 준비 순서입니다.

2. 전달 내용 확인

인자와 환경 배열을 NULL까지 순회해 로그로 확인합니다.

문자열 내용 복사가 아니라 포인터 배열 순회입니다.

3. 프로그램 전환

kernel_execve로 새 사용자 이미지를 준비합니다.

성공하면 이후 사용자 공간 실행으로 이어집니다.

run_init_process를 한 줄씩 읽기

줄 번호는 v6.18.37 원문 기준입니다. 주석·빈 줄을 포함한 함수 전체를 먼저 보고, 그 아래에서 각 줄을 설명합니다.

static int run_init_process(const char *init_filename)
{
	const char *const *p;

	argv_init[0] = init_filename;
	pr_info("Run %s as init process\n", init_filename);
	pr_debug("  with arguments:\n");
	for (p = argv_init; *p; p++)
		pr_debug("    %s\n", *p);
	pr_debug("  with environment:\n");
	for (p = envp_init; *p; p++)
		pr_debug("    %s\n", *p);
	return kernel_execve(init_filename, argv_init, envp_init);
}
static int run_init_process(const char *init_filename)

현재 init 태스크에서 init_filename이 지정한 사용자 프로그램 실행을 시도합니다. int 반환값은 kernel_execve의 결과이며 새 PID를 만드는 함수가 아닙니다.

	const char *const *p;

argv나 envp의 문자열 포인터 배열을 한 원소씩 읽을 순회 포인터입니다. *p가 현재 문자열 주소이고 NULL이면 배열 끝입니다. 포인터가 보관하는 것은 객체의 주소입니다. 이 선언만으로 대상 구조체나 문자열의 내용이 복사되지는 않습니다.

	argv_init[0] = init_filename;

사용자 프로그램이 받는 첫 번째 인자 포인터를 실행 파일 이름으로 정합니다. 문자열 전체를 복사하지 않습니다.

	pr_info("Run %s as init process\n", init_filename);

지금 어떤 사용자 init 파일의 실행을 시도하는지 기록합니다. 이 로그만으로 exec 성공까지 확인한 것은 아니며 실행 호출은 아래에 있습니다.

	pr_debug("  with arguments:\n");

뒤의 디버그 출력이 init에 전달할 argv 목록임을 표시합니다. 부팅 실패 때 실제 인자 구성을 확인하기 위한 제목입니다.

	for (p = argv_init; *p; p++)

argv_init의 첫 원소부터 NULL까지 이동합니다. 각 회차에서 문자열 내용이 아니라 문자열 포인터 하나를 골라 다음 줄에서 출력합니다.

		pr_debug("    %s\n", *p);

현재 배열 원소 *p가 가리키는 문자열 하나를 출력합니다. argv 순회 안에서는 인자, envp 순회 안에서는 환경 문자열을 보여 줍니다.

	pr_debug("  with environment:\n");

이후 출력이 argv가 아니라 init의 환경 문자열 envp 목록임을 표시합니다.

	for (p = envp_init; *p; p++)

envp_init의 첫 문자열부터 NULL 종결자 전까지 순회합니다. 환경 문자열을 수정하거나 새로 만드는 루프는 아닙니다.

		pr_debug("    %s\n", *p);

현재 배열 원소 *p가 가리키는 문자열 하나를 출력합니다. argv 순회 안에서는 인자, envp 순회 안에서는 환경 문자열을 보여 줍니다.

	return kernel_execve(init_filename, argv_init, envp_init);

커널이 마련한 파일명·인자·환경으로 현재 태스크의 실행 이미지를 바꿀 공통 exec 경로에 들어갑니다.

함께 생각해 볼 질문

argv[0]과 파일 경로는 항상 같은가요?

일반 exec 규약에서는 다를 수 있습니다. 여기서는 init_filename을 argv[0]에 넣습니다.

for 문의 *p는 무엇인가요?

현재 배열 원소인 문자열 포인터입니다. NULL이면 배열 끝입니다.

이 함수가 PID 1을 새로 생성하나요?

PID 1 태스크는 이미 만들어졌습니다. 여기서는 그 태스크가 실행할 프로그램을 정합니다.

출처와 읽은 범위

Linux stable v6.18.37 · init/main.c

해당 버전 원본 파일 · 기존 코드 분석 · 설명 원고

맨 위로 ↑