[Linux Kernel] 커널 스레드(프로세스) 생성 과정 소스 분석
systemd 프로세스는 유저 공간에서 생성된 프로세스의 부모 프로세스 역할을 수행한다.
보통은 init 프로세스라 부르기도 한다.
보통 유저 프로세스에서 부모 프로세스가 소멸 되면, init 프로세스가 부모 역할을 수행하게 된다.
ps -ejH
프로세스 생성 과정
크게 두가지로 분류 할 수 있다.
1. 유저 레벨에서 생성된 프로세스
라이브러리(GNU C : glibc) 의 도움을 받아 커널에게 프로세스 생성 요청
2. 커널 레벨에서 생성된 프로세스
커널 내부의 kthread_create() 함수를 호출하여 커널 프로세스 생성
대부분 이를 커널 스레드 라고 부른다.
둘의 공통점은 _do_fork() 함수를 호출한다는 점이다.
init 프로세스 : 유저 레벨 프로세스 생성
kthreadd 프로세스 : 커널 레벨 프로세스 생성
두 프로세스는 생성 역할을 맡게 되는데, 두 프로세스의 리소스를 copy 하여 만들기 때문이다.
이유는 속도 향상을 위해서 이다.
실제로 리눅스 커널에서는 위와같은 방식으로 속도 향상에 신경을 많이 쓴다.
바로 커널 메모리 할당자인 Slub Memory Allocator 도 이와 유사한 기법을 활용한다.
드라이버에서 메모리 할당을 요청할 때 자주쓰는 구조체를 정의해서
해당 구조체에 대한 메모리를 미리 확보해 놓는다.
아래 경로에 보면 유저영역에서 커널에 요청을 위한 라이브러리가 모여있다.
유저 코드는 이 라이브러리와 링킹되어 메모리에 적재돼 실행된다.
유저 레벨 프로세스 생성
위 그림은 이전 글에서 ftrace 로 분석한 유저 레벨에서의 프로세스 생성 과정 이다.
커널 레벨 프로세스 생성
위 그림을 간단히 설명하면, 크게 두 가지로
1. kthreadd 프로세스에게 커널 프로세스 생성 요청
kthread_create() kthread_create_on_node()
2. kthreadd 프로세스는 깨어나 자신에게 커널 프로세스 생성 요청 점검 후 생성
kthreadd() create_kthread()
리눅스 드라이버에서 워크를 워크큐에 큐잉하면 커널은 커널 스레드의 종류인 워커 스레드를 더 생성한다.
커널에서 메모리가 부족하면 페이지를 확보하는 kswapd 스레드를 깨워 실행한다.
즉, 리눅스 커널 시스템이 많은 일을 할때, 커널 스레드를 생성한다.
커널 스레드는 커널 공간에서 리소스 관리로 메모리와 전원 부분을 관리한다.
커널 스레드는 커널 공간에서만 생성되며, 유저 공간과 상호작용 하지 않는다.
커널에서 직접 실행, 휴먼 동작을 직접 제어하고, 대부분 시스템 부팅 시 생성 되고, 종료 시 까지
백그라운드로 실행 된다.
커널 스레드 항목 확인
- kthreadd 프로세스로 커널 스레드들의 부모 프로세스 이다.
스레드 핸들러 함수는 kthreadd() 이다.
- 워커 스레드는 워크큐에 큐잉된 워크를 실행하는 프로세스 이다.
스레드 핸들러 함수는 worker_thread() 이다. process_one_work() 를 호출해 워크를 실행하는 기능 수행
- ksoftirqd 프로세스는 soft irq 를 위해 실행하는 프로세스 이다.
smp_boot 형태의 스레드 이며, run_ksoftirqd() 함수가 스레드 핸들러 이며, Soft irq 서비스를 실행
_do_softirq()함수에서 ksoftirqd를 깨운다.
- irq/86-mmc1 스레드는 irq 스레드 라고 하며, 인터럽트 후반부 처리를 위해 사용된다.
이름으로 기능을 유추 할 수 있다. 86번 mmc1 인터럽드의 후반부 처리하는 irq 스레드 인 것 이다.
ps axjf
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
0 2 0 0 ? -1 S 0 0:00 [kthreadd]
2 3 0 0 ? -1 I< 0 0:00 \_ [rcu_gp]
2 4 0 0 ? -1 I< 0 0:00 \_ [rcu_par_gp]
2 8 0 0 ? -1 I< 0 0:00 \_ [mm_percpu_wq]
2 9 0 0 ? -1 S 0 0:00 \_ [rcu_tasks_rude_]
2 10 0 0 ? -1 S 0 0:00 \_ [rcu_tasks_trace]
2 11 0 0 ? -1 S 0 0:00 \_ [ksoftirqd/0]
2 12 0 0 ? -1 I 0 0:02 \_ [rcu_sched]
2 13 0 0 ? -1 S 0 0:00 \_ [migration/0]
2 14 0 0 ? -1 S 0 0:00 \_ [cpuhp/0]
2 15 0 0 ? -1 S 0 0:00 \_ [cpuhp/1]
2 16 0 0 ? -1 S 0 0:00 \_ [migration/1]
2 17 0 0 ? -1 S 0 0:00 \_ [ksoftirqd/1]
2 20 0 0 ? -1 S 0 0:00 \_ [cpuhp/2]
2 21 0 0 ? -1 S 0 0:00 \_ [migration/2]
2 22 0 0 ? -1 S 0 0:00 \_ [ksoftirqd/2]
2 25 0 0 ? -1 S 0 0:00 \_ [cpuhp/3]
2 26 0 0 ? -1 S 0 0:00 \_ [migration/3]
2 27 0 0 ? -1 S 0 0:00 \_ [ksoftirqd/3]
2 30 0 0 ? -1 S 0 0:00 \_ [kdevtmpfs]
2 31 0 0 ? -1 I< 0 0:00 \_ [netns]
2 34 0 0 ? -1 S 0 0:00 \_ [kauditd]
2 36 0 0 ? -1 S 0 0:00 \_ [khungtaskd]
2 37 0 0 ? -1 S 0 0:00 \_ [oom_reaper]
2 38 0 0 ? -1 I< 0 0:00 \_ [writeback]
2 39 0 0 ? -1 S 0 0:00 \_ [kcompactd0]
2 59 0 0 ? -1 I< 0 0:00 \_ [kblockd]
2 60 0 0 ? -1 I< 0 0:00 \_ [blkcg_punt_bio]
2 61 0 0 ? -1 S 0 0:00 \_ [watchdogd]
2 62 0 0 ? -1 I< 0 0:00 \_ [kworker/0:1H-kblockd]
2 63 0 0 ? -1 I< 0 0:00 \_ [rpciod]
2 64 0 0 ? -1 I< 0 0:00 \_ [kworker/u9:0-hci0]
2 65 0 0 ? -1 I< 0 0:00 \_ [xprtiod]
2 66 0 0 ? -1 S 0 0:00 \_ [kswapd0]
2 69 0 0 ? -1 I< 0 0:00 \_ [nfsiod]
2 70 0 0 ? -1 I< 0 0:00 \_ [kthrotld]
2 71 0 0 ? -1 I< 0 0:00 \_ [iscsi_eh]
2 72 0 0 ? -1 I< 0 0:00 \_ [iscsi_destroy]
2 73 0 0 ? -1 I< 0 0:00 \_ [nvme-wq]
2 74 0 0 ? -1 I< 0 0:00 \_ [nvme-reset-wq]
2 75 0 0 ? -1 I< 0 0:00 \_ [nvme-delete-wq]
2 78 0 0 ? -1 I< 0 0:00 \_ [DWC Notificatio]
2 79 0 0 ? -1 I< 0 0:00 \_ [uas]
2 80 0 0 ? -1 S< 0 0:00 \_ [vchiq-slot/0]
2 81 0 0 ? -1 S< 0 0:00 \_ [vchiq-recy/0]
2 82 0 0 ? -1 S< 0 0:00 \_ [vchiq-sync/0]
2 83 0 0 ? -1 I< 0 0:00 \_ [zswap-shrink]
2 87 0 0 ? -1 I< 0 0:00 \_ [sdhci]
2 88 0 0 ? -1 S 0 0:00 \_ [irq/66-mmc0]
2 90 0 0 ? -1 I< 0 0:00 \_ [mmc_complete]
2 91 0 0 ? -1 I< 0 0:00 \_ [kworker/3:1H-kblockd]
2 93 0 0 ? -1 S 0 0:00 \_ [jbd2/mmcblk0p2-]
2 94 0 0 ? -1 I< 0 0:00 \_ [ext4-rsv-conver]
2 96 0 0 ? -1 I< 0 0:00 \_ [ipv6_addrconf]
2 98 0 0 ? -1 I< 0 0:00 \_ [kworker/2:1H-kblockd]
2 102 0 0 ? -1 I< 0 0:00 \_ [kworker/1:1H-kblockd]
2 192 0 0 ? -1 S 0 0:00 \_ [vchiq-keep/0]
2 195 0 0 ? -1 S< 0 0:00 \_ [SMIO]
2 208 0 0 ? -1 I< 0 0:00 \_ [mmal-vchiq]
2 210 0 0 ? -1 S 0 0:04 \_ [v3d_bin]
2 212 0 0 ? -1 S 0 0:05 \_ [v3d_render]
2 215 0 0 ? -1 S 0 0:00 \_ [v3d_tfu]
2 216 0 0 ? -1 I< 0 0:00 \_ [mmal-vchiq]
2 217 0 0 ? -1 S 0 0:00 \_ [v3d_csd]
2 218 0 0 ? -1 S 0 0:00 \_ [v3d_cache_clean]
2 223 0 0 ? -1 I< 0 0:00 \_ [mmal-vchiq]
2 225 0 0 ? -1 I< 0 0:00 \_ [mmal-vchiq]
2 227 0 0 ? -1 I< 0 0:00 \_ [mmal-vchiq]
2 231 0 0 ? -1 I< 0 0:00 \_ [mmal-vchiq]
2 260 0 0 ? -1 I< 0 0:00 \_ [cfg80211]
2 271 0 0 ? -1 I< 0 0:00 \_ [brcmf_wq/mmc1:0]
2 273 0 0 ? -1 S 0 0:00 \_ [brcmf_wdog/mmc1]
2 470 0 0 ? -1 S 0 0:00 \_ [irq/57-vc4 hdmi]
2 471 0 0 ? -1 S 0 0:00 \_ [irq/58-vc4 hdmi]
2 473 0 0 ? -1 S 0 0:00 \_ [cec-vc4]
2 480 0 0 ? -1 S 0 0:00 \_ [irq/54-vc4 hdmi]
2 481 0 0 ? -1 S 0 0:00 \_ [irq/53-vc4 hdmi]
2 499 0 0 ? -1 S 0 0:00 \_ [irq/63-vc4 hdmi]
2 501 0 0 ? -1 S 0 0:00 \_ [irq/64-vc4 hdmi]
2 502 0 0 ? -1 S 0 0:00 \_ [cec-vc4]
2 503 0 0 ? -1 S 0 0:00 \_ [irq/60-vc4 hdmi]
2 508 0 0 ? -1 S 0 0:00 \_ [irq/59-vc4 hdmi]
2 511 0 0 ? -1 S 0 0:00 \_ [card1-crtc0]
2 513 0 0 ? -1 S 0 0:00 \_ [card1-crtc1]
2 514 0 0 ? -1 S 0 0:00 \_ [card1-crtc2]
2 515 0 0 ? -1 S 0 0:00 \_ [card1-crtc3]
2 516 0 0 ? -1 S 0 0:00 \_ [card1-crtc4]
2 517 0 0 ? -1 S 0 0:00 \_ [card1-crtc5]
2 712 0 0 ? -1 I< 0 0:00 \_ [kworker/u9:2-hci0]
2 774 0 0 ? -1 I< 0 0:00 \_ [cryptd]
2 817 0 0 ? -1 S< 0 0:00 \_ [krfcommd]
2 1029 0 0 ? -1 I 0 0:00 \_ [kworker/2:0-mm_percpu_wq]
2 1157 0 0 ? -1 I 0 0:00 \_ [kworker/1:1-events]
2 1385 0 0 ? -1 I< 0 0:00 \_ [kworker/1:2H]
2 1425 0 0 ? -1 I< 0 0:00 \_ [kworker/2:0H]
2 1593 0 0 ? -1 I 0 0:00 \_ [kworker/u8:1-events_unbound]
2 1596 0 0 ? -1 I 0 0:00 \_ [kworker/1:0]
2 1650 0 0 ? -1 I< 0 0:00 \_ [kworker/0:0H]
2 1651 0 0 ? -1 I 0 0:00 \_ [kworker/3:2-events]
2 1683 0 0 ? -1 I 0 0:00 \_ [kworker/u8:3-events_unbound]
2 1684 0 0 ? -1 I 0 0:00 \_ [kworker/0:1-events]
2 1685 0 0 ? -1 I 0 0:00 \_ [kworker/3:1-events]
2 1704 0 0 ? -1 I 0 0:00 \_ [kworker/2:1]
2 1705 0 0 ? -1 I< 0 0:00 \_ [kworker/3:2H]
2 1718 0 0 ? -1 I 0 0:00 \_ [kworker/3:0-events]
2 1720 0 0 ? -1 I 0 0:00 \_ [kworker/0:0-events]
2 1721 0 0 ? -1 I 0 0:00 \_ [kworker/3:3-events]
2 1734 0 0 ? -1 I 0 0:00 \_ [kworker/0:2-events]
2 1735 0 0 ? -1 I 0 0:00 \_ [kworker/u8:0-brcmf_wq/mmc1:0001:1]
2 1740 0 0 ? -1 I 0 0:00 \_ [kworker/u8:2-events_unbound]
0 1 1 1 ? -1 Ss 0 0:03 /sbin/init splash
1 130 130 130 ? -1 Ss 0 0:01 /lib/systemd/systemd-journald
1 161 161 161 ? -1 Ss 0 0:00 /lib/systemd/systemd-udevd
1 353 353 353 ? -1 Ss 108 0:00 avahi-daemon: running [raspberrypi.local]
353 359 353 353 ? -1 S 108 0:00 \_ avahi-daemon: chroot helper
1 354 354 354 ? -1 Ss 0 0:00 /usr/sbin/cron -f
1 355 355 355 ? -1 Ss 104 0:00 /usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only
1 374 374 374 ? -1 Ssl 0 0:00 /usr/libexec/polkitd --no-debug
1 401 401 401 ? -1 Ssl 0 0:00 /usr/sbin/rsyslogd -n -iNONE
1 418 418 418 ? -1 Ss 0 0:00 /usr/sbin/dhcpcd -b
1 430 430 430 ? -1 Ss 0 0:00 /lib/systemd/systemd-logind
1 431 431 431 ? -1 Ss 65534 0:00 /usr/sbin/thd --triggers /etc/triggerhappy/triggers.d/ --socket /run/thd.socket --user nobody --deviceglob /dev/input/event*
1 434 434 434 ? -1 Ssl 0 0:00 /usr/libexec/udisks2/udisksd
1 436 436 436 ? -1 Ss 0 0:00 /sbin/wpa_supplicant -u -s -O /run/wpa_supplicant
1 486 486 486 ? -1 Ssl 0 0:00 /usr/sbin/ModemManager
1 496 496 496 ? -1 Ss 115 0:00 /usr/bin/epmd -systemd
1 504 504 504 ? -1 SLsl 0 0:00 /usr/sbin/rngd -r /dev/hwrng
1 528 528 528 ? -1 Ssl 0 0:00 /usr/sbin/lightdm
528 586 586 586 tty7 586 Ssl+ 0 0:45 \_ /usr/lib/xorg/Xorg :0 -seat seat0 -auth /var/run/lightdm/root/:0 -nolisten tcp vt7 -novtswitch
528 646 528 528 ? -1 Sl 0 0:00 \_ lightdm --session-child 18 21
646 672 672 672 ? -1 Ssl 109 0:17 | \_ /usr/sbin/pi-greeter
528 697 528 528 ? -1 S 0 0:00 \_ lightdm --session-child 14 21
1 587 587 587 ? -1 Ss 0 0:00 sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups
587 918 918 918 ? -1 Ss 0 0:00 \_ sshd: devk [priv]
918 953 918 918 ? -1 S 1001 0:03 | \_ sshd: devk@pts/0
953 956 956 956 pts/0 1743 Ss 1001 0:00 | \_ -bash
956 977 977 956 pts/0 1743 S 0 0:00 | \_ sudo su
977 978 977 956 pts/0 1743 S 0 0:00 | \_ su
978 979 979 956 pts/0 1743 S 0 0:00 | \_ bash
979 1743 1743 956 pts/0 1743 R+ 0 0:00 | \_ ps axjf
587 920 920 920 ? -1 Ss 0 0:00 \_ sshd: devk [priv]
920 975 920 920 ? -1 S 1001 0:00 | \_ sshd: devk@notty
975 976 976 976 ? -1 Ss 1001 0:00 | \_ /usr/lib/openssh/sftp-server
587 1555 1555 1555 ? -1 Ss 0 0:00 \_ sshd: devk [priv]
1555 1562 1555 1555 ? -1 S 1001 0:00 | \_ sshd: devk@pts/1
1562 1564 1564 1564 pts/1 1564 Ss+ 1001 0:00 | \_ -bash
587 1557 1557 1557 ? -1 Ss 0 0:00 \_ sshd: devk [priv]
1557 1581 1557 1557 ? -1 S 1001 0:00 \_ sshd: devk@notty
1581 1582 1582 1582 ? -1 Ss 1001 0:00 \_ /usr/lib/openssh/sftp-server
1 603 603 603 ? -1 Ss 0 0:00 /sbin/wpa_supplicant -s -B -P /run/wpa_supplicant.wlan0.pid -i wlan0 -D nl80211,wext -c /etc/wpa_supplicant/wpa_supplicant.conf
1 650 650 650 ? -1 Ss 109 0:00 /lib/systemd/systemd --user
650 651 650 650 ? -1 S 109 0:00 \_ (sd-pam)
650 670 670 670 ? -1 S<sl 109 0:00 \_ /usr/bin/pipewire
670 679 670 670 ? -1 S<l 109 0:00 | \_ /usr/bin/pipewire-media-session
650 671 671 671 ? -1 S<sl 109 0:00 \_ /usr/bin/pulseaudio --daemonize=no --log-target=journal
650 678 678 678 ? -1 Ss 109 0:00 \_ /usr/bin/dbus-daemon --session --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only
650 683 683 683 ? -1 Ssl 109 0:00 \_ /usr/libexec/gvfsd
650 688 683 683 ? -1 Sl 109 0:00 \_ /usr/libexec/gvfsd-fuse /run/user/109/gvfs -f
1 674 674 674 ? -1 SNsl 110 0:00 /usr/libexec/rtkit-daemon
1 713 370 370 ? -1 S 0 0:00 /usr/bin/hciattach /dev/serial1 bcm43xx 3000000 flow -
1 728 728 728 ? -1 Ss 0 0:00 /usr/libexec/bluetooth/bluetoothd
1 835 835 835 ? -1 Ss 0 0:00 /usr/sbin/cupsd -l
835 836 836 835 ? -1 S 7 0:00 \_ /usr/lib/cups/notifier/dbus dbus://
1 837 837 837 ? -1 Ssl 0 0:00 /usr/sbin/cups-browsed
1 922 922 922 ? -1 Ss 1001 0:00 /lib/systemd/systemd --user
922 924 922 922 ? -1 S 1001 0:00 \_ (sd-pam)
922 945 945 945 ? -1 Ssl 1001 0:00 \_ /usr/bin/pipewire
945 954 945 945 ? -1 Sl 1001 0:00 | \_ /usr/bin/pipewire-media-session
922 946 946 946 ? -1 Ssl 1001 0:00 \_ /usr/bin/pulseaudio --daemonize=no --log-target=journal
922 951 951 951 ? -1 Ss 1001 0:00 \_ /usr/bin/dbus-daemon --session --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only
1 1702 1702 1702 ? -1 Ssl 100 0:00 /lib/systemd/systemd-timesyncd
위에서 대표적인 커널스레드를 소개했으니, 아래에서는 생성 과정을 소스로 알아가 보자.
커널 스레드 생성 과정 소스 분석
kthread 프로세스에게 커널 스레드 생성 요청
실제 kthread_create 함수에 커널 스레드 핸들러 함수를 지정해야 한다.
kthread_create() 함수 분석
매크로 형태로 작성 되어있다. 이는 스텍프레임을 안 사용하여 실행 속도 증가를 위함이다.
하지만 실행파일이 커지는 단점을 가지고 있다. 참고하면 좋다.
인자값들을 보면
threadfn 을 통해 스레드 핸들러 함수를 전달하고, data 는 스레드 핸들러 함수로 전달하는
데이터 이다. node 는 말 그대로 node 정보를 전달, name은 커널 스레드 이름 이다.
/include/linux/kthread.h
아래는 커널 레벨에서 커널 스레드 생성 코드 이다.
vhost_worker 스레드 핸들러 함수로 vhost_dev data 를 같이 전달한다.
name 은 vhost-pid 인 것을 확인 할 수 있다.
drivers/vhost/vhost.c
아래 매개변수로 void* 캐스팅을 되는 것을 확인 할 수 있다.
drivers/vhost/vhost.c
kthread_create_on_ndoe() 함수 분석
아래 함수는 특별한 역할을 하지는 않는다. 단지, task_struct 리턴 값을 전달하주며,
va_start va_end로 va_list를 전달한다.
kernel/kthread.c
__kthread_create_on_ndoe() 함수 분석
실제 아래 함수가 호출이 되고, kthread_create_info 에 스레드 핸들러 함수를 heap 메모리에 할당하여 처리한다.
kfree 로 해제는 당연하다.
주요 깊게 봐야 될 부분은 list_add_tail을 통해 커널 스레드 생성 요청을 관리하는 kthread_create_list 연결 리스트에
&create->list 를 추가한다는 점이다. kthread 프로세스는 kthread_create_list 연결 리스트를 확인해
커널 스레드 생성 요청이 있었는지 확인 한다.
그리고 kthread 프로세스의 테스크 디스크립터인 kthread_task를 인자로 kthread 프로세스를 깨운다.
kthread 프로세스가 커널 스레드를 생성
kthreadd() 함수 분석
이렇게 위의 과정으로 kthread 프로세스를 깨우게 되면, 스레드 핸들러 kthreadd()함수가 실행이 된다.
kthreadd() 함술의 핵심 기능은 다음과 같다.
- kthread_create_lnfo 연결 리스트를 확인해 프로세스 요청 확인
- create_kthread() 함수를 호출해 프로세스 생성
list에 요청 건이 없다면, schdule()함수로 진입해 휴면상태에 진입 하고
요청이 있다면, __set_current_state 를 실행한다.
연결리스트 알고리즘에서 _list 는 head, 로서 info 정보를 담고있는 구조체에 next 로 접근한다.
_list의 next 로 마지막 _info 의 list_head 를 가리키게 된다.
바로 info 구조체에서 list 필드의 offset 을 계산해 info 구조체의 시작 주소를 알 수 있다.
그리고 create_kthread(create) 함수를 호출해 커널 스레드를 생성한다.
create_kthread() 함수 분석
kernel_thread 호출 시 CLONE_FS CLONE_FILES SIGCHLD flag OR 연산으로 설정한다.
즉, 리소스에 3개 해당 항목을 추가하게 된다.
kernel_thread() 함수 분석
kernel/fork.c
kernel_clone 함수 호출이 되며, 이는 유저 레벨 생성 시 에도 동일하게 호출되는 함수이다.
여기까지를 정리하자면,
kthread_create 를 통해 스레드 핸들러함수와 핸들러에 전달할 매개변수 그리고 이름을 지정하고,
kthraedd 프로세스가 깨어나면, list 와 info 연결리스트를 통해 요청 된 프로세스를 생성하게 된다.
kernel_clone, do_fork() 함수 분석
Armv8 부턴 do_fork 에서 kernel_clone 으로 변경 되었다.
kernel_clone 함수의 동작은 크게 두 가지가 있다.
- copy_process 함수를 호출해서 프로세스를 생성, 부모 프로세스의 리소스를 자식 프로세스에게 복제
- copy_process 함수 호출해 프로세스를 만든 후 wake up new task 함수로 프로세스를 꺠우고, 깨운다는 것은 스케줄러에게 프로세스 실행을 요청하는 것이다.
copy_process에서 리턴된 p 값으로 테스크 디스크립터의 주소를 담고 있는 변수로 오류가 있는지 검사한다.
그리고 ftrace 이벤트 중 sched_process_fork 를 활성화 해주는 함수가 trace_shced_process_fork 함수 이다.
wake_up_new_task 함수를 통해 생성한 프로세스를 깨운다. 그리고 생성한 PID 반환
copy_process() 함수 분석
사실 프로세스를 생성하는 핵심 동작이 이 함수에 구현되어 있다.
양이 많아서 핵심 부분만 설명 하자면,
아래 함수로 생성할 프로세스의 테스크 디스크립터인 task_struct 구조체와 프로세스가 실행 될 스택공간을 할당한다.
아래 함수를 통해 task_struct 스케줄링 정보를 초기화 한다.
부모 프로세스의 리소스들을 복사 한다.
wake_up_new_task() 함수 분석
프로세스 생성의 마지막 단계로 kernel/sched/core.c
다행히 길지 않다.. ㅎ
p->state 에 TASK_RUNNING 으로 상태 전환 후
__set_task_cpu 로 thread_info 구조체의 cpu 필드에 현재 실행 중인 cpu 번호를 할당한다.
나중에 모든 프로세스의 cpu 번호 출력은 thread_info 구조체를 통해 리스트 업 되어진다.
그리고 __task_rq_lock 을 시작으로 런큐 주소를 읽은 다음, activate_task 함수를 호출해
런큐에 새롭게 추가된 프로세스를 삽입한다.
이로서 커널 레벨에서의 커널 스레드 생성 과정을 소스로 알아보았다.
유저 레벨에서도 Armv8 기준인 kernel_clone 은 공통적으로 호출이 되어지는 것으로 보아
두 과정 모두 부모 프로세스가 다를뿐 부모의 리소스를 그대로 복제하여 자식 프로세스가 생성 됨을
알 수 있다.
아래는 참고를 위해 ftrace 에서 본 생성 종료 콜 스텍이다.
ftrace 를 통한 프로세스 생성 및 종료 함수 흐름(콜 스텍) process 1424
1. 프로세스 생성 단계의 함수 흐름
2. 프로세스 종료 단계의 함수 흐름