Embedded : : Linux/: : Linux Kernel

do_fork() 함수, 그리고 5.x 이후

Jay.P Morgan 2024. 10. 12. 01:03

 

  1.  Intro

 

 

  1.1  do_fork() 함수 소개

  리눅스에서 구동 중인 모든 프로세스 _do_fork() 함수가 실행할 생성됩니다(kernel version 4.x까지). 프로세스는 누가 생성할까요? 리눅스 시스템에서 프로세스 생성을 전담하는 프로세스가 있습니다. 주인공은 init kthreadd 프로세스입니다.

 

  유저레벨프로세스 init 프로세스, 커널 레벨 프로세스(커널 스레드) kthreadd 프로세스가 생성합니다. 그런데 프로세스는 생성이 아니라 복제된다고 말할 있습니다

 

 

  1.2  프로세스 생성

 

     "그러면 프로세스를 생성할 부모 프로세스를 복제하는 이유는 무엇일까?" 

 

  프로세스를 생성할 프로세스에게 필요한 리소스를 각각 할당 받으면 시간이 오래 걸립니다. 이미 생성된 프로세스에서 리소스를 물려 받는 것입니다

 

  리눅스 커널에서는 '코드의 성능이나 속도를 개선'하기 위한 많은 기법이 적용되고 있습니다.

대표적인 방법을 가지 소개하겠습니다.

 

     "'이미 만들어놓은 리소스' 가져다 쓴다."

 

  리눅스 커널에서는 속도 개선을 위해 반복해서 실행하는 코드를 줄이려는 노력을 흔적을 있습니다. 커널 메모리 할당자인 슬럽 메모리 할당자(Slub Memory Allocator) 유사한 기법을 적용하고 있습니다. 드라이버에서 메모리 할당 요청할 쓰는 구조체를 정의해서 해당 구조체에 대한 메모리를 미리 확보해 놓습니다. 메모리 할당을 요청하면 이미 확보한 메모리 주소를 알려줍니다. 과정으로 메모리를 할당하는 속도가 빨라집니다.

 

  프로세스 생성 과정도 마찬가지입니다. 프로세스를 생성할 부모 프로세스가 쓰고 있는 자료구조를 복사합니다. 프로세스를 생성할 이미 생성된 프로세스에서 복제하는 것이 속도 면에서 효율적이기 때문입니다

 

 

 

  2.  kernel_clone

 

 

  2.1  _do_fork()  →  kernel_clone()

  이번에도 저번과 같이 ftrace에 대한 오류가 나왔다.

  아래와 같은 오류다.

root@raspberrypi:/home/ejun0/user_procs# ./clone_process_debug.sh 
tracing_off
events disabled
set_ftrace_filter init
function tracer enabled
./clone_process_debug.sh: 28 줄: echo: 쓰기 오류: 부적절한 인수
event enabled
set_ftrace_filter enabled
function stack trace enabled
tracing_on

  clone_process_debug.sh의 28번째 줄은 아래와 같다.

echo _do_fork copy_process* >> /sys/kernel/debug/tracing/set_ftrace_filter

 

_do_fork나 copy_process중에 함수가 없다는 것이다.

사용자 정의 함수도 아니고 원래 리눅스 커널 함수인데 없다니..!

좀 의아했다.

 

아무튼 아래와 같은 명령어로 _do_fork가 없다는 사실을 알게 되었다.

cat /sys/kernel/debug/tracing/available_filter_functions | grep _do_fork

cat /sys/kernel/debug/tracing/available_filter_functions | grep copy_process
copy_process

 

그럼 함수 이름이 바뀐 것일까?

kernel/fork.c 파일에 정의된 clone 함수를 찾아보았더니 아래와 같이 함수가 바뀐 것을 알 수 있었다.

#ifdef CONFIG_CLONE_BACKWARDS
SYSCALL_DEFINE5(clone, unsigned long, clone_flags, unsigned long, newsp,
		 int __user *, parent_tidptr,
		 unsigned long, tls,
		 int __user *, child_tidptr)
#elif defined(CONFIG_CLONE_BACKWARDS2)
SYSCALL_DEFINE5(clone, unsigned long, newsp, unsigned long, clone_flags,
		 int __user *, parent_tidptr,
		 int __user *, child_tidptr,
		 unsigned long, tls)
#elif defined(CONFIG_CLONE_BACKWARDS3)
SYSCALL_DEFINE6(clone, unsigned long, clone_flags, unsigned long, newsp,
		int, stack_size,
		int __user *, parent_tidptr,
		int __user *, child_tidptr,
		unsigned long, tls)
#else
SYSCALL_DEFINE5(clone, unsigned long, clone_flags, unsigned long, newsp,
		 int __user *, parent_tidptr,
		 int __user *, child_tidptr,
		 unsigned long, tls)
#endif
{
	struct kernel_clone_args args = {
		.flags		= (lower_32_bits(clone_flags) & ~CSIGNAL),
		.pidfd		= parent_tidptr,
		.child_tid	= child_tidptr,
		.parent_tid	= parent_tidptr,
		.exit_signal	= (lower_32_bits(clone_flags) & CSIGNAL),
		.stack		= newsp,
		.tls		= tls,
	};

	return kernel_clone(&args);
}

마지막을 보면 함수가 _do_fork가 아닌 kernel_clone으로 바뀌어 있는게 보인다.

 

 

  3.  결론 (Conclude)

 

clone_process_debug.sh의 28번째 줄을 아래와 같이 바꾸자

echo kernel_clone copy_process* >> /sys/kernel/debug/tracing/set_ftrace_filter

 

 

출처: Austin Kim 블로그,  _do_fork의 행방 — 2jun0의 블로그 (tistory.com)