IT/Linux Kernel

디버깅을 통해 배우는 리눅스 커널의 구조와 원리 1, 도서 공부하기 11 - _do_fork() 흐름 파악과 ftrace 메시지 추출

변화의 물결1 2025. 3. 20. 00:09

 

 

 

 안녕하세요.

 

  _do_fork() 함수를 알아보기 위한 사전 정보가 조금 필요에 _do_fork() 마무리하지 못해서 추가 부분을 정리해 봅니다.

 


 

1. 유저 레벨 프로세스 생성 시 _do_fork() 함수처리 흐름

 

 유저 레벨 프로세서 생성할 흐름은 아래와 같이 fork() 함수 호출하면 glibc 통해 시스템콜을 발생하고 이에 대응하는 sys_clone() 함수 호출합니다. 이후 호출을 따라가면 _do_fork() 함수를 호출합니다.

 

 

1) sys_clone 함수

 

 sys_clone() 함수 내부를 보면 return (PID) 받기 위해 _do_fork() 함수를 호출합니다.

 SYSCALL_DEFINE5 매크로와 함께 커널 빌드하는 과정에서 sys_ 접두사가 붙여 sys_clone으로 심벌을 생성합니다.

 

rpi_kernel_src/linux $ vim kernel/fork.c

 

 

 

 최근 커널 버전에서는 sys_fork sys_vfork 호출하면 sys_clone() 함수를 하며, sys_clone() 프로세스 생성에 필요한 다양한 플래그를 제공합니다. 플래그들을 조합하여 fork() vfork() 동작을 완벽하게 재현할 있기 때문입니다.

 

 

2. 커널 프로세스 생성 시 _do_fork() 함수 흐름

 

 커널 프로세스란, 시스템 없이 커널 함수로 생성되어 커널 공간에서만 실행되는 프로세스입니다. 유저 프로세스와 같이 마지막 단에서는 _do_fork() 함수를 호출합니다.

 커널 프로세스 커널 스레드가 되고 커널 공간에서 시스템 리소스관리를 수행합니다.

 

 커널 스레드가 생성되는 과정은 2단계로 kthreadd 프로세스에게 커널 프로세스 생성을 요청하는 것과 커널 프로세스 생성 단계로 나눠집니다.

 

 

 

 커널 스레드는 대부분 부팅과정에서 커널 스레드를 생성하며, ps 명령어로 하면 나타나는 PPID 2 프로세스들이 있습니다.

 

 

 

 모든 커널 프로세스가 부팅 시에만 생성되는 것이 아니라, 리눅스 드라이버에서 많은 워커를 워크큐에 큐잉할 경우, 커널에서 메모리가 부족하면 페이지를 확보하는 일을 하는 kswapd 스레드를 깨워 실행합니다. , 커널 시스템이 많을 일을 해야 커널 스레드를 생성합니다.

 

 

3. 유저레벨 프로세스 실행 실습

 

ps 명령어로 프로세스 목록에서 grep으로 bash 이름의 프로세스를 확인합니다.

 

linux $ ps -ely | grep bash

 

 

 

 터미널을 하나 열고 위와 같이 명령어를 실행하면, PID 8512인 프로세스가 하나 생성된 것을 있습니다. 하나 창을 열고 한번 실행하면 PID 8633인 프로세스가 생성되는 것을 있습니다.

 이것으로 새로운 프로그램을 실행하면 이에 해당하는 프로세스가 생성된다는 사실을 있습니다.

 

 

 

 추가로 하나 더 실험해 보겠습니다. 새로 만든 2개의 터미널 창에 geany라는 에디터 프로그램을 실행합니다. 그리고 ps와 grep 명령어를 입력합니다.

 

  PID 8790, 8800 프로세스인 geany 프로세스가 생성되었습니다. 하나 PPID 위에서 터미널의 PID 되어 있는 것을 있습니다. 여기서 있는 것은 대부분의 유저 레벨 프로세스는 셀이나 다른 프로세스를 통해 시작하고, 스스로 실행하지 못한다는 것입니다.

 

linux $ ps -ely | grep geany

 

 

 

 터미널, geany 프로그램 같은 프로그램을 실행하면 메모리에 로딩돼 동작하는 것이 프로세스이고, 유저 레벨에서 실행하는 프로세스는 유저의 액션에 의해 대부분 생성된다는 것입니다.

 

 

4. 유저 프로세스 실습 코드

 

1) 유저 프로세스 소스 코드

 

 ftrace 실습을 위해서 리눅스 시스템 프로그램 만들어 프로세스를 생성합니다.

 그러기 위해서 c언어로 간단한 소스 코드를 하나 작성합니다.

 소스 코드를 위한 디렉터리를 하나 만들어 c파일 하나를 생성합니다.

 

rpi_kernel_src# mkdir app_src; cd app_src
rpi_kernel_src/app_src# vim 4_4_user_process.c

 

 

--- 4_4_user_process.c 내용

 

#include <stdio.h>
#include <unistd.h>

#define PROC_TIMES 500
#define SLEEP_DURATION 3 //second unit

int main()
{
       int proc_times = 0;

        for(proc_times=0; proc_times < PROC_TIMES;proc_times++)
        {
                printf("Raspberry Pi OS tracing \n");
                sleep(SLEEP_DURATION);
        }//for
        return 0;
} //main

 

 소스코드의 내용은, for 루프문에서 "Raspberry Pi OS tracing" 문자열을 출력하고 3 동안 휴면 상태로 진입한 다시 깨는 작업을 500 반복을 하는 것입니다.

 sleep fork 같은 시스템 관리하는 함수를 직접 호출하면 저수준 프로그래밍이라고 하며, 이는 저수준 함수(API) 사용했다고 합니다.

 

 

2) Makefile

 

 소스코드를 좀 더 쉽게 컴파일하기 위해서 Makefile 만듭니다. 출력 파일명과 소스코드 파일 명을 gcc에게 알려줍니다.

 

--- Makefile 내용

 

4_4_user_process: 4_4_user_process.c
        gcc -o 4_4_user_process 4_4_user_process.c

 

 

3) 컴파일과 실행

 

 Makefile 작성했다면 컴파일하고, 에러가 없다면 프로그램을 실행합니다.

 입력한 문자가 3초마다 출력됩니다.

 

rpi_kernel_src/app_src# make
rpi_kernel_src/app_src# ./4_4_user_process

 

 

 

 

5. ftrace 설정

 

 이전 글에서 사용했던 ftrace_setting.sh파일을 수정해서 사용합니다.

 파일 이름을 clone_process_debug_ftrace_setting.sh 바꿔 놓습니다.

 

 핵심적으로 수정한 부분은  스택에 출력할 함수를 설정하는 것입니다.

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

 

 

 sched_process_fork, sched_process_exit 이벤트를 활성화하고 이를 통해 프로세스가 종료하고 생성하는 동작을 추적합니다.

 

echo 1 > /sys/kernel/debug/tracing/events/sched/sched_process_fork/enable
echo 1 > /sys/kernel/debug/tracing/events/sched/sched_process_exit/enable

 

 

6. 유저 프로세스와 ftrace 로그확보

 

 위에서 작업한 ftrace 스크립트와 4_4_user_process파일 이용해서 프로세스에 분석에 필요한 로그를 만들어 보겠습니다.

 

 두 개의 터미널 창이 필요합니다. 첫 번째 터미널에는 유저 프로세스 생성을 위해 만든 프로그램과 ftrace 실행합니다. 두 번째 터미널에 프로세스 확인하고 종료하는 하는 작업을 것입니다.

 

1) 첫 번째 터미널 작업

 

 첫 번째 터미널 창에서 ftrace 설정 스크립트를 실행하고 만든 프로그램을 실행합니다.

 두 번째 터미널 의해 프로그램이 종료되면 ftrace 종료합니다.

 

rpi_kernel_src# sh clone_process_debug_ftrace_setting.sh
rpi_kernel_src# ./app_src/4_4_user_process
rpi_kernel_src# sh get_ftrace.sh

 

 

 

2) 두 번째 터미널 작업

 

 첫 번째 터미널에서 실행한 프로그램의 프로세스를 확인합니다. 3~5 기다린 다음에 kill 명령으로 프로세스를 종료합니다.

 

~ $ ps -ely | grep 4_4_user_proc
~ $ sudo kill -9 15752

 

 

 

3) ftrace 메시지 확인

 

 get_ftrace.sh 파일을 수행한 디렉터리에 ftrace_log.c 파일이 생성된 것을 있습니다.

 

 rpi_kernel_src# vim ftrace_log.c

 

 

 

  이것으로 원하는 유저 프로세스의 ftrace 메시지를 가져오는 것까지 확인해 보았습니다.

  책의 파일 내용 분석은 다음 글에서 보도록 하겠습니다.

 

 

감사합니다.

 

  

 

<참고 자료>

1. [도서] 디버깅을 통해 배우는 리눅스 커널의 구조와 원리 p143~p155, wikibook

 

 

반응형