4/02/2017

Linux Init Process 기본구조 및 비교

1 Linux Kernel 과 Filesystem의 Init 

Linux Kernel에서 File system 은 필수이며, 불가변의 존재로 없으면 동작이 되지 않는다. 
그리고 커널이 가장 먼저 찾아 실행하는 Process가 File system의 init process이다.
물론  RTOS인 경우 e.g Vxwork는 과거에는 File System이 없이도 Booting 가능하게 만들으며,  File system은 옵션이었지만, Linux는 아니다.

현재 Linux의 File System의 기본변화는 기존 System V Init 에서 Systemd 기반으로 구성으로 변경일 것 같다.

두개의 구조를 간단히 비교를 해보자.

1.1 PS로 각 Process 를 확인

일단 ps로 본인의 pid의 1 값이 무엇인지 확인을 해보고 가능하다면 /proc/cmdline를 본인의 Kernel Argument를 확인하자.

  • /proc/cmdline
Kernel 의 argument를 확인가능하며, init=/sbin/init 부분을 주목해서 보자 이외 Filesystem은 root=xxxx

만약 상위 옵션이 없다면, 생략되어 init를 아래 커널 소스처럼 다양한 곳에서 찾는다.

  • ps command ( process의 상태를 파악하는 tool)
  1. PID : Process ID -
  2. PGID: Process Group ID
  3. PPID: Parent Process ID - fork를 할 경우
  4. SID: Session ID

이를 이용하여 현재 동작 중인 daemon과 관련 app들을 파악을 해보자


$ ps axo pid,ppid,pgid,sid,comm   //필요한 정보만 보기 
  PID  PPID  PGID   SID COMMAND
    1     0     1     1 init              // PID 0 rest_init 에서 kernel_init-> init  
    2     0     0     0 kthreadd          // PID 0 rest_init 에서 kthreadd 
    3     2     0     0 ksoftirqd/0              // kthreadd에서 실행
    5     2     0     0 kworker/0:0H             // kthreadd에서 실행
    7     2     0     0 rcu_sched                // kthreadd에서 실행
    8     2     0     0 rcu_bh                   // kthreadd에서 실행
....

이제 ps command를 이용하여 다양하게 확인을 해보자.

$ ps -ef                       // PPID 0 , 1, 2 를 중심 분석  
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 20:25 ?        00:00:01 /sbin/init        
root         2     0  0 20:25 ?        00:00:00 [kthreadd]
root         3     2  0 20:25 ?        00:00:00 [ksoftirqd/0]   
root         5     2  0 20:25 ?        00:00:00 [kworker/0:0H]
root         7     2  0 20:25 ?        00:00:00 [rcu_sched]
root         8     2  0 20:25 ?        00:00:00 [rcu_bh]

$ ps -ejH    // Process Tree 만들기  (init 1 기준으로 PPID와 PID의 Tree 분석)
  PID  PGID   SID TTY          TIME CMD
    2     0     0 ?        00:00:00 kthreadd             // kthreadd 기준 (상위참조)
....
    1     1     1 ?        00:00:01 init                 // init 기준 
  284   282   282 ?        00:00:00   upstart-udev-br
  291   291   291 ?        00:00:00   systemd-udevd
  439   439   439 ?        00:00:00   dbus-daemon
  497   497   497 ?        00:00:00   systemd-logind
  498   498   498 ?        00:00:00   rpc.idmapd
  499   499   499 ?        00:00:00   bluetoothd
  537   537   537 ?        00:00:00   rsyslogd
  554   554   554 ?        00:00:00   cupsd
  591   590   590 ?        00:00:00   upstart-file-br
  677   677   677 ?        00:00:00   rpcbind
  731   731   731 ?        00:00:00   ModemManager
  755   755   755 ?        00:00:00   NetworkManager
  797   797   755 ?        00:00:00     dhclient
 1549  1549   755 ?        00:00:00     dnsmasq
  767   767   767 ?        00:00:00   rpc.statd
  772   439   439 ?        00:00:00   polkitd
  792   792   792 ?        00:00:00   cups-browsed
  831   830   830 ?        00:00:00   upstart-socket-
  883   883   883 tty4     00:00:00   getty
  887   887   887 tty5     00:00:00   getty
  895   895   895 tty2     00:00:00   getty
  896   896   896 tty3     00:00:00   getty
  899   899   899 tty6     00:00:00   getty
  953   953   953 ?        00:00:00   sshd
 1734  1734  1734 ?        00:00:00     sshd
 1777  1734  1734 ?        00:00:00       sshd
 1778  1778  1778 pts/1    00:00:00         bash
 1849  1849  1778 pts/1    00:00:00           ps

$ ps axjf

    0     1     1     1 ?           -1 Ss       0   0:01 /sbin/init
    1   284   282   282 ?           -1 S        0   0:00 upstart-udev-bridge --daemon
    1   291   291   291 ?           -1 Ss       0   0:00 /lib/systemd/systemd-udevd --daemon
    1   439   439   439 ?           -1 Ss     102   0:00 dbus-daemon --system --fork
    1   497   497   497 ?           -1 Ss       0   0:00 /lib/systemd/systemd-logind
    1   498   498   498 ?           -1 Ss       0   0:00 rpc.idmapd
    1   499   499   499 ?           -1 Ss       0   0:00 /usr/sbin/bluetoothd
    1   537   537   537 ?           -1 Ssl    101   0:00 rsyslogd
    1   554   554   554 ?           -1 Ss       0   0:00 /usr/sbin/cupsd -f
    1   591   590   590 ?           -1 S        0   0:00 upstart-file-bridge --daemon
    1   677   677   677 ?           -1 Ss       0   0:00 rpcbind
    1   731   731   731 ?           -1 Ssl      0   0:00 /usr/sbin/ModemManager
    1   755   755   755 ?           -1 Ssl      0   0:00 NetworkManager
  755   797   797   755 ?           -1 S        0   0:00  \_ /sbin/dhclient -d -sf /usr/lib/NetworkManager/nm-dhcp-client.action -pf /run/sendsigs.omit.d/network-manager.dhcl
  755  1549  1549   755 ?           -1 S    65534   0:00  \_ /usr/sbin/dnsmasq --no-resolv --keep-in-foreground --no-hosts --bind-interfaces --pid-file=/run/sendsigs.omit.d/n
    1   767   767   767 ?           -1 Ss     117   0:00 rpc.statd -L
    1   772   439   439 ?           -1 Sl       0   0:00 /usr/lib/policykit-1/polkitd --no-debug
    1   792   792   792 ?           -1 Ss       0   0:00 /usr/sbin/cups-browsed
    1   831   830   830 ?           -1 S        0   0:00 upstart-socket-bridge --daemon
    1   883   883   883 tty4       883 Ss+      0   0:00 /sbin/getty -8 38400 tty4
    1   887   887   887 tty5       887 Ss+      0   0:00 /sbin/getty -8 38400 tty5
    1   895   895   895 tty2       895 Ss+      0   0:00 /sbin/getty -8 38400 tty2
    1   896   896   896 tty3       896 Ss+      0   0:00 /sbin/getty -8 38400 tty3
    1   899   899   899 tty6       899 Ss+      0   0:00 /sbin/getty -8 38400 tty6
    1   953   953   953 ?           -1 Ss       0   0:00 /usr/sbin/sshd -D
  953  1734  1734  1734 ?           -1 Ss       0   0:00  \_ sshd: jhlee [priv]  
 1734  1777  1734  1734 ?           -1 S     1000   0:00      \_ sshd: jhlee@pts/1   
 1777  1778  1778  1778 pts/1     1850 Ss    1000   0:00          \_ -bash
 1778  1850  1850  1778 pts/1     1850 R+    1000   0:00              \_ ps axjf


1.2 Kernel init 와 kthread/kthreadadd 실행 

kernel은 kernel argument 혹은 boot parameter (/proc/cmdline) 안에 init가 미정의되면 아래와 같이 기본위치의 init를 찾아 boot를 진행한다.

start_kernel () 함수 arch 에서 호출되는 실질적인 linux kernel의 시작이므로, 이것 기준으로 분석을 한다. 

  • ARM 의 start_kernel 시작 
Machine ID 와 Device Tree 전달 
head ->start_kernel ()  
 
  • start_kernel () 함수 (init/main.c)
start_kernel->arch_call_rest_init->rest_init->kernelinit

arch_call_rest_init 함수
    https://elixir.bootlin.com/linux/v4.20.4/source/init/main.c#L532


  • rest_init (kthreadd)
kthread 로 init 와 kthreadd 를 실행하며, kthread 기준으로 다른 kthread 실행하며,주로 k*로 시작 상위 ps 부분 참조

rest_init->kernel_init
  https://elixir.bootlin.com/linux/v4.20.4/source/init/main.c#L398
  https://elixir.bootlin.com/linux/v4.20.4/source/kernel/kthread.c 


$ linux //kernel source 이동 
$ vi ./init/main.c

static int __ref kernel_init(void *unused)
{
        int ret;

        kernel_init_freeable();
        /* need to finish all async __init code before freeing the memory */
        async_synchronize_full();
        free_initmem();
        mark_readonly();
        system_state = SYSTEM_RUNNING;
        numa_default_policy();

        flush_delayed_fput();

        if (ramdisk_execute_command) {
                ret = run_init_process(ramdisk_execute_command);
                if (!ret)
                        return 0;
                pr_err("Failed to execute %s (error %d)\n",
                       ramdisk_execute_command, ret);
        }

        /*
         * We try each of these until one succeeds.
         *
         * The Bourne shell can be used instead of init if we are
         * trying to recover a really broken machine.
         */
        if (execute_command) {
                ret = run_init_process(execute_command);
                if (!ret)
                        return 0;
                panic("Requested init %s failed (error %d).",
                      execute_command, ret);
        }
        if (!try_to_run_init_process("/sbin/init") ||
            !try_to_run_init_process("/etc/init") ||
            !try_to_run_init_process("/bin/init") ||
            !try_to_run_init_process("/bin/sh"))
                return 0;

        panic("No working init found.  Try passing init= option to kernel. "
              "See Linux Documentation/init.txt for guidance.");
}


각각의 Kernel version을 확인하고 실행되는 방법을 확인가능 아래링크 참조
이외에도 kxxx 시작하는 thread실행

  • kernel_init
file sytem의 init 를 실행
  https://elixir.bootlin.com/linux/v4.20.4/source/init/main.c#L1067
  https://elixir.bootlin.com/linux/v4.13.16/source/init/main.c#L980
  https://elixir.bootlin.com/linux/v3.0.81/source/init/main.c#L777


1.3 PID 0 (swapper or sched)  

PID: Process ID를 말하며, Linux Kernel이 Boot 후 Linux Kernel이 어떻게 User Process를 실행이 되는지 알아보자.

  • PID 0 (swapper or sched) 
swapper or sched 의 두 역할을 하는 것이라고 Kernel의 영역이라고 하며, 이전 처럼 swap-in/out을 하지는 않는다고 한다.
아래말에 주로 core idle 상태일 때 돌아가는 idle process로 loop를 돌린다고 하는데, scheduler 역할을 하는 것으로 보인다.

기존의 swap 기능은 kswapd[0-9] 이곳에서 해준다고 한다.
  https://unix.stackexchange.com/questions/83322/which-process-has-pid-0
  http://egloos.zum.com/nzcv/v/5795841

kswapd0 
  https://elixir.bootlin.com/linux/v4.20.4/source/mm/vmscan.c#L3750


  • ARM 기준 Kernel 소스

$ linux //kernel source 이동 
$ grep -r swapper .    // 정확히 swapper라고 정의된 곳을 찾음 , swapper_pg_dir 은 MMU에서 사용하는 virtual_address 의 시작주소인 것 같다 
$ vi ./include/linux/init_task.h
INIT_TASK_COMM 
$ grep -r INIT_TASK_COMM . 

./include/linux/init_task.h:#define INIT_TASK_COMM "swapper"    
./include/linux/init_task.h: .comm  = INIT_TASK_COMM,    \   // INIT_TASK 에서 사용 
./kernel/sched/core.c: sprintf(idle->comm, "%s/%d", INIT_TASK_COMM, cpu);       // CONFIG_SMP 일 경우 


1.4  PID 1 (init 와 systemd)

Linux Kernel이 Filesystem을 mount 한 후 처음으로 실행하는 Process로 init

  • boot paramenter (/proc/cmdline) (init or systemd을 직접설정할 경우) 
예전처럼 boot parmeter에서 init을 기본값사용하며 (/sbin/init 만 결정)

init=/sbin/init   // 이전 SysVinit (bin 역시 실제 init)
/sbin/init -> /lib/systemd/systemd   // systemd 구성일 경우 심볼링크로 연결되며, init 옵션 생략 (/sbin/init)

  http://man7.org/linux/man-pages/man7/bootparam.7.html


  • sysVinit 구성 과 systemd 구성의 기본 init 
기존 SysVinit (/sbin/init) 와 systemd 중 PID 0을 정해 boot를 현재 systemd로 변경중.
  1. sysvinit : /sbin/init 와 telinit                        
  2. systemd: /sbin/init -> /lib/systemd/systemd 
  3. systemd 와 sysvinit 같이 혼용/sbin/init -> /lib/systemd/systemd 

** init 6  (reboot) 실행하면 상위와 같이 실행 

2  Linux의 boot mode 

보통 bootmode라고 하면 hardware 적인 bootmode를 말하며, 이곳에서 말하는 boot mode는 기존의 SysVInit boot 과 Systemd boot 중
Linux Kernel이 Filesystem을 mount 한 후 PID 1 (Init) 선택에 의한 실행을 말한다.

  • SysVinit 과 Systemd bootmode
  1. SysVinit  만 사용 /sbin/init 와 telinit 사용하여 /etc/inittab, 확인후 /etc/rc.d 의 boot script 실행
  2. Systemd 와 SysVinit 혼용 /etc/init.d와 /etc/rc[0~6].d 사용하지만 기반은 systemd
  3. Systemd 만 사용할 경우  

init 관련내용
  https://ko.wikipedia.org/wiki/%EB%A6%AC%EB%88%85%EC%8A%A4_%EC%8B%9C%EC%9E%91_%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4
  https://en.wikipedia.org/wiki/Init#Replacements_for_init

telinit (SysVinit 에서 Run Level 변경목적)
  https://www.freedesktop.org/software/systemd/man/telinit.html#

  • Systemd 와 SysVinit 혼용방법
기본은 systemd 기반 init으로 (/lib/systemd/systemd) boot를 한 후 내부의 service들이  SysV Script들을 실행하는 것으로 파악된다. 


/sbin/init -> /usr/lib/systemd 

/dev/initctl (사용여부는 잘모름)
systemd-initctl.service 에 의해 control 된다고 하며, SysV client를 제한적으로 지원

Systemd 기반에서 SysV Script 생성
SysV Script 생성할때 필요하지 사용할때는 필요 없는 것으로 파악 

  • Systemd 의 SysV init 관련설정내용 

  • systemd의 /sbin/telinit 확인(systemctl 대체) 
systemctl 로 링크되어 사용하며, SysVinit 이 아닌이상 필요성을 모르겠지만, systemctl default로 설정가능  
/sbin/telinit -> /bin/systemctl 



2.1 Linux의 Run Level 종류  (SysvInit 과 Systemd)

Runlevel은 init 0~6 을 실행하여 각각의 실행이 가능하며, SysvInit을 사용하건 Systemd를 사용하든 동일하다.

  • Run level 설정 및 관리 
/sbin/init: init 5 이런식으로 linux의 runlevel을 설정하여 boot script을 실행을 한다.
runlevel는 개별의 linux마다 조금씩은 다르지만, 기본적으로 거의 유사하다.
  1. level 0는 halt
  2. level 1는 single user mode ( Super User 사용 service가 제한적)
  3. level 2 multiuser not support network 
  4. level 3은 multiuser
  5. level 5는 X11 interface 유저 (그래픽유저)
  6. level 6는 reboot

  • SysVinit RunLevel 관리 (Systemd와 SysVinit혼용 동일)
  1. boot : /sbin/init 
  2. 설정확인 : /etc/inittab  or runlevel 
  3. 설정변경 : /etc/inittab 변경  
  4. service 관리: chkconfig 관리

  • Systemd RunLevel 관리
  1. boot : /lib/systemd/systemd 
  2. 설정확인: systemctl get-default  or runlevel
  3. 설정방법: systemctl set-default
  4. service 관리: systemctl 이용 

/usr/lib or /lib or /etc 기준으로 확인 

상위에서 init 1~6으로 실행하며 아래의 것이 연결되어 실행
$ ls -alh /lib/systemd/system/runlevel*.target // 상위 telinit(systemctl) 과 연결 
lrwxrwxrwx 1 root root 15 May 11  2016 /lib/systemd/system/runlevel0.target -> poweroff.target
lrwxrwxrwx 1 root root 13 May 11  2016 /lib/systemd/system/runlevel1.target -> rescue.target
lrwxrwxrwx 1 root root 17 May 11  2016 /lib/systemd/system/runlevel2.target -> multi-user.target
lrwxrwxrwx 1 root root 17 May 11  2016 /lib/systemd/system/runlevel3.target -> multi-user.target
lrwxrwxrwx 1 root root 17 May 11  2016 /lib/systemd/system/runlevel4.target -> multi-user.target
lrwxrwxrwx 1 root root 16 May 11  2016 /lib/systemd/system/runlevel5.target -> graphical.target
lrwxrwxrwx 1 root root 13 May 11  2016 /lib/systemd/system/runlevel6.target -> reboot.target

$ ls -alh /lib/systemd/system/default.target
lrwxrwxrwx 1 root root 16 May 11  2016 /lib/systemd/system/default.target -> graphical.target

$ ls /lib/systemd/system/runlevel*.target.wants // 각 연결된 부분확인 
$ ls /etc/systemd/system/reboot.target.wants/
$ ls /lib/systemd/system/multi-user.target.wants/


$ grep /etc/init -r /lib/systemd/system/  // systemd service에서 SysV script 실행부분 찾기
$ grep /etc/init -r /etc/systemd/system/
$ grep /etc/rc -r /lib/systemd/system/
/lib/systemd/system/rc-local.service:ExecStart=/etc/rc.local start

$ which service
/usr/sbin/service

$ cat /usr/sbin/service // systemctl로 shell script 

$ service --status-all  // 전체 service 관리확인 (systemd 와SysVInit 혼용) 

/lib/systemd/systemd/default.target
/etc/systemd/system/default.target 의 link에 따라 변경됨


2.2 SysVinit (/sbin/init) 관리될 경우  

상위에서 설명했듯이 booting은 /sbin/init  각 service 설정은 chkconfig를 관리하며, 확인가능한 곳이 /etc/inittab 과,
각 runlevel에 맞는 /etc/rc[0~6].d script 관리되어짐 (Symbol link로 구성)


  • 기본적인 Service 관리 할 곳 
/etc/rc.d/init.d or /etc/init.d  service에 관련된 모든 script이 존재.

  • service 관리 (chkconfig 사용법)
chkconfig : 서비스의 등록 및 제거  확인가능
  https://linux.die.net/man/8/chkconfig

  • Init 명령어
  https://en.wikipedia.org/wiki/Init
  http://www.tldp.org/LDP/intro-linux/html/sect_04_02.html

  •  /etc/inittab 사용 
  https://wiki.kldp.org/KoreanDoc/html/Boot_Process-KLDP/iterateinittab.html
  https://www.linux.com/news/introduction-services-runlevels-and-rcd-scripts

  • /etc/rc script 
  https://www.netbsd.org/docs/guide/en/chap-rc.html
  https://access.redhat.com/documentation/ko-KR/Red_Hat_Enterprise_Linux/6/html/Installation_Guide/s2-boot-init-shutdown-init.html


2.3 Systemd 로 관리될 경우  

기본적으로 Systemd는 Unit 단위로 관리가 진행되며 systemctl 이라는 명령어로 쉽게 모든것이 관리가 되어진다.
그리고, Unit은 용도에 따라 이름이 변경되어 관리되어지지만, 문법은 거의 비슷하다.

  • unit의 기본 문법 
unit를 사용하고자 한다면 아래의 기본문법으로 사용 (*.service)
  https://www.freedesktop.org/software/systemd/man/systemd.unit.html


  • systemd의 init (PID 0) 의 기본이해 (중요)

systemd 설정 및 kernel argument 설정을 비롯하여 init에게 줄 수 옵션들을 확인
    https://www.freedesktop.org/software/systemd/man/init.html#

  • 이미 사용이 정해진 systemd의 unit 구성 (중요)
Unit의 기본동작원리를 알고자 하면 반드시 읽어야하며, 각각의 일반적인 사용되어지는 unit들을 설명하고 있다.
systemd로 booting 부터 이미 사용이름이 정해진 target/mount/slice 등 각각의 설명이 나온다.
      https://www.freedesktop.org/software/systemd/man/systemd.special.html#

    • Unit의 종류 (11개의 종류이지만 필요한것만 사용) 
    1. *.service units : 주로 daemon 과 일반 process로 구성 
    2. *.socket units:  IPC or Socket 기반의 활동 (encapsulate동작)
    3. *.target units:  Unit들을 group 화 하고 boot 동기화를 할때 유용
    4. *.device units:  Kernel device를 사용할 경우 udev sys
    5. *.mount units:  filesystem 안에 mount point 
    6. *.automount units:  filesystem의 automount 
    7. *.timer units: 다른 unit의 timer기반의 trigger로 사용 
    8. *.swap units:  mount unit 과 유사하며 memory sway 과 OS의 file (encapsulate동작)
    9. *.path units: path 기반으로 변경될 때 동작되는 구조 
    10. *.slice units: group unit으로 scope와 service를 계층으로 관리가 가능 
    11. *.scope units:  service unit과 거의 유사하지만 외부 process 관리목적 

    • systemctl 의 기본사용법
    각 Unit들을 등록부터 제거를 비롯하여 전반적인 관리하는 프로그램 
      https://www.freedesktop.org/software/systemd/man/systemctl.html#
      https://ahyuo79.blogspot.com/2017/03/systemctl.html

    • systemd에 관련된 모든것 검색 
      https://www.freedesktop.org/software/systemd/man/index.html

    • systemd의 각 unit의 Manual
      https://www.freedesktop.org/software/systemd/man/systemd.service.html#
      https://www.freedesktop.org/software/systemd/man/systemd.socket.html#
      https://www.freedesktop.org/software/systemd/man/systemd.device.html#
      https://www.freedesktop.org/software/systemd/man/systemd.mount.html#
      https://www.freedesktop.org/software/systemd/man/systemd.automount.html#
      https://www.freedesktop.org/software/systemd/man/systemd.swap.html#
      https://www.freedesktop.org/software/systemd/man/systemd.target.html#
      https://www.freedesktop.org/software/systemd/man/systemd.path.html#
      https://www.freedesktop.org/software/systemd/man/systemd.timer.html#
      https://www.freedesktop.org/software/systemd/man/systemd.slice.html#
      https://www.freedesktop.org/software/systemd/man/systemd.scope.html#

    • systemd 적용하는 linux 
      https://en.wikipedia.org/wiki/Systemd
      https://wiki.archlinux.org/index.php/Systemd
      https://wiki.debian.org/systemd
      http://chiccoder.tistory.com/42

    • systemd의 각 Unit들을 확인 및 설정 
    1. /lib/systemd/system: 기본으로 제공
    2. /etc/systemd/system:  /lib/systemd/system link or 본인 unit 정의 
    3. /run/systemd/system : 동작 중인 Unit의 확인 

    • systemd의  system설정 과 user 설정
    1. /etc/systemd/system.conf  : /lib/systemd/systemd --system 설정 
    2. /etc/systemd/user.conf : /lib/systemd/systemd --user 설정 
    system은 consol mode(root) 일 경우이며, user는 일반 유저로 login 했을 경우를 구분해서 동작
      https://www.freedesktop.org/software/systemd/man/systemd-system.conf.html#

    • systemd 의 기본 Manaul 
      https://www.freedesktop.org/software/systemd/man/systemd.html#
      https://manpages.debian.org/jessie/systemd/systemd.1.en.html