[부팅] User space boot, init, systemd, run level

markyang92·2021년 9월 24일
1

boot

목록 보기
6/7
post-thumbnail

부팅 프로세스

  1. BIOS/UEFI ROM 펌웨어가 부트로더를 찾아 로드한다.
  2. 부트 로더가 로드된다.
    2.1. 디스크상의 '커널 이미지'(/boot/vmlinuz-x.x.x-xx 등)를 발견 혹은 위치 로드.
    2.2. Device Tree Pointer를 넘기던 이게 커널에 포함되어 있던 한다.
    2.3. 커널 로드전에 initramfs 실행 유무 및 위치,
    2.4.커널 커맨드(rootfs) 등을 커널에 넘긴다.
  3. (option) initramfs
  4. kernel이 디바이스 및 디바이스 드라이버로 초기화 <------ 여기
    4.1.. 커널이 rootfs를 마운트한다.
  5. 커널은 PID 1init 프로세스를 시작한다. 여기서 사용자 공간이 시작된다. <------ 여기
  6. init 프로세스는 시스템 프로세스의 나머지들을 시작 시킨다.
  7. init로그인 프로세스를 시작한다.

4. init설정

  • rpi4-64 기준 /sbin/init


  • 커널 초기화에서 사용자 공간으로 이동하기 위해, 커널rootfs 를 마운트하고, 루트 파일시스템에 있는 프로그램을 실행한다.
    • 이는 ramfs or block device상의 실제 파일 시스템 mount를 통해 이루어진다.
  • 이 함수는 PID 1인 첫 번째 스레드를 만들고, kernel_init()의 코드를 실행한다.
  • 사용자 공간을 설정하는 작업을 수행할 것이다.
  • ramfs에서, /init을 찾는데 실패하면, init/do_mounts.c 안의 함수 prepare_namespace()를 불러, 파일 시스템을 마운트하려 할 것이다.
  • block device상의 파일 시스템 mount되었다면, /sbin/init, /etc/init, /bin/init, /bin/sh를 성공할 때까지 차례로 실행하려고 시도한다.

4-1. init이 ramfs에서 실행

  • bootargs, bootcmd에서 rdinit=<위치>로 지정할 수 있고, default로 rdinit=/init이다.
    • 이를 처리하는 모든 코드는 init/main.c에 있고, 함수 rest_init()에서 시작한다.

4-2. init이 block device상의 파일 시스템 mount 에서 실행

  • bootargs, bootcmd에서 init=<위치>로 지정할 수 있고, default로 /sbin/init, /etc/init, /bin/init, /bin/sh를 성공할 때 까지 차례로 실행
  • block device상의 파일 시스템 mount를 하기 위해서,
    • root=/dev/<디스크이름><파티션번호>, root=/dev/<디스크이름>p<파티션번호>가 필요하다.
      • grub를 쓰는 x86_64 computer: root=(hd0,gpt2) -> root=UUID=<UUID>
      • u-boot를 쓰는 rpi4의 SDcard: root=/dev/mmcblk0p1
      • u-boot를 쓰는 rpi4 NFS Rootfs: root=/dev/nfs rootfstype=nfs nfsroot=<serverip>:/nfs/client1/root,v3,tcp

4-3. rootfs


User space boot

  • 부트로더에 의해 로딩된 커널은 먼저 시스템에 연결된 메모리, 디스크, 키보드, 마우스 등의 디바이스를 검사한다.
  • fork를 사용하지 않고 생성되는 프로세스, 스레드 생성
    • 이들은 메모리 관리와 같은 커널의 여러가지 동작 수행
    • [ process name ]으로 표기되어 있다.
      • 여기선 PID 1/sbin/init splash

      • 여기선 PID 1/sbin/launchd

  • 커널이 rootfs를 마운트하고 init프로세스를 시작한다.
  • 커널은 PID 1init 프로세스를 시작한다. 여기서 사용자 공간이 시작된다.
  • 사용자 공간은 다음의 순서로 시작된다.
  1. init
  2. udeved(https://velog.io/@markyang92/devicefile#udevd), syslogd 같은 필수적인 저수준 service
  3. 네트워크 환경 설정
  4. 중간 수준 service, 고 수준 service (e.g., cron)
  5. 로그인 프롬프트, GUI, 기타 고수준 App

init

  • init은 사용자 공간 프로그램이다.
  • 위치: /sbin/
  • 목적: 시스템상의 필수적인 서비스 프로세스 시작, 멈춤
  • 종류

    • System V init: 전통적인 init
    • systemd: 최근 배포판에서 사용하는 init
    • Upstart
    • others..
  • man init을 쳐보면 확실히 무슨 init을 사용하는지 확인할 수 있다.

    본인 컴퓨터(Ubuntu 20.04 LTS)에서는 systemd를 사용한다.


init 런레벨/오퍼레이밍모드

  • 리눅스 시스템에서 어느 시점에 이르면 (crond, udeved)같은 기본 프로세스 세트가 동작하게된다.
  • Sys V init에서 이런 상태를 런레벨이라고 한다.
    • run-level은 0~6까지 숫자로 표시하고, 단 하나의 런레벨에 대부분의 시간을 할애한다.
  • 런 레벨의 용도: 시스템이 시동, 정지, 단일-사용자모드, 콘솔 모드 상태 인지 등을 구분할 수 있게한다.

  • 시스템 구동 시 최초로 실행되는 사용자 레벨 프로세스

  • 시스템 구동에 필요한 각종 스크립트 실행

    • 컴퓨터 이름설정
    • 타임존 설정
    • fsck로 디스크 상태확인
    • 파일 시스템 마운트
    • /tmp 디렉토리의 오래된 파일 삭제
    • 네트워크 인터페이스 구성
    • 패킷 필터 설정
    • 네트워크 서비스 시작
    • 기타 데몬 시작

  • 운영체제가 부팅 이후의 머신 상태를 결정
    • 어떤 프로그램을 실행할 것인지 결정
  • single user: 파일시스템 마운트, 네트워크 비활성화, 시스템 관리용 쉘 접근
  • multi-user: 일반적인 사용자 접근
  • SysV는 run level 사용
  • systemd에서는 오퍼레이팅 모드 사용
SysV Runlevelsystemd TargetInfo
0runlevel0.target / poweroff.targetShutdown / Halt system
1, singlerunlevel1.target / rescue.targetSingle User Mode
  • 파일 시스템 마운트
  • 네트워크 비활성화
  • 시스템 관리용 쉘 접근
  • 2,4runlevel2.target / runlevel4.target / multi-user.targetUser defined runlevels /Defaults to mimic 3
    3runlevel3.target / multi-user.targetMulti-user, non-graphical
    5runlevel5.target / graphical.targetMulti-user, graphical
    6runlevel6.target / reboot.targetReboot system
    Emergencyemergency.targetEmergency Shell Access
    • 부트 타임에 systemd가 동작하는 과정은 아래와 같다.
    1. systemd가 설정을 로딩한다.
    2. systemd가 부트 목표를 알아낸다. (default: default.target)
    3. systemd가 기본 부트 목표의 모든 종속 요소들, 그 종속 요소들의 종속 요소들 등을 파악한다.
    4. systemd는 종속 요소들과 부트 목표를 활성화한다.
    5. 부팅 후에 systemd는 (uevents와 같은) 시스템 이벤트들에 대응하고 추가 요소들을 활성화 시킨다.
    • systemd
      • 커널 로그 엔트리 관리 (journald)
      • 네트워크 연결 관리 (networkd)
      • 로그인 관리(logind)

    unit

    • systemd에서 프로세스, 서비스만 실행하는 것이 아니라 파일 시스템 마운트, 네트워크 소켓 모니터링, 타이머 그 외 많은 것 실행 가능하다.
      • 이런 각 형태를 unit type이라고 한다.
      • 각각의 구체적인 기능을 unit이라고 한다.
    Unit TypeDescription
    Service unitsUNIX System의 전통적인 서비스 대몬 제어
    Mount units시스템으로의 파일 시스템 연결을 제어한다.
    Target units다른 유닛을 제어하는 '유닛', 즉 유닛을 그룹으로 나누어 컨트롤할 수 있다.

    default target unit

    • default boot의 목표는 보통 여러개의 서비스 유닛들과 마운트 유닛들을 종속성으로 함께 그룹화하는 Target Unit이다.

    현재 런레벨 확인

    $ who -r

    $ who -r
    • 현재 시스템의 런레벨을 확인한다.

      현재 시스템의 런 레벨 및 런 레벨이 설정된 시간 또한 표시된다.

    시작 서비스


    init의 시작 서비스 위치: /etc/init.d/

    여기 있는 스크립트들은 시작 서비스


    실행 레벨에 맞게 시작 서비스 구동: rc5 -> /etc/rc5.d/


    Sys V init

    • 구현: 각기 다른 런레벨로 질서 정연하게 부트업 되어 init 스크립트 실행
    • 전형적인 System V 설치는 두 가지 주요 요소가 있다.
    1. central 설정 파일
    2. sym link fram에 의해 늘어나게 된 여러 부트 스크립트
    • 아래는 /etc/init.d의 스크립트 파일들

      Ubuntu 에서는 systemd가 Sys V 호환성 모드로서 적용됨

    Sys V script

    기초

    • 시스템에서 Sys V init을 보유하고 있다면, /etc/inittab 파일에서 다음의 라인을 찾아본다.
    id:5:initdefault:
    • inittab에 있는 모든 라인은 다음과 같은 형식을 갖춘다. 4개의 필드가 :으로 구분된다.
    1. 고유한 식별자
    2. 적용되는 런레벨 숫자 (위 예제: 5; 적용되는 런레벨이 5)
    3. init이 취해야하는 동작 (위 예제: initdefault; 디폴트 런레벨을 5로 하라)
    4. 실행해야 할 명령(선택)

    wait

    • wait: 언제 어떻게 Sys V init이 명령을 실행할 것인지 결정한다.
    l5:5:wait:/etc/rc.d/rc 5
    • 이 라인은 '시스템 설정'과 '서비스'의 대부분을 작동시키는 트리거 역할을 한다.
    • 5:wait는 '런레벨 5'로 들어갈 때, /etc/rc.d/rc 5를 한번 실행한다. 그리고 다른 어떤 동작을 취하기 전에 이 명령이 완료될 때까지 '대기'한다.
      • 즉, rc 5 명령은 /etc/rc5.d에서 번호와 함께 시작하는 어떤 것이든 이를 실행한다. (번호 순)

    respawn

    • respawn: 뒤에 나오는 명령을 실행하도록 init에 지시하고, 명령이 실행을 마친 상태라면 '다시 실행하도록' 한다.
    1:2345:respawn:/sbin/mingetty tty1
    • getty는 '로그인 프롬프트'를 제공한ㄸ다.
    • tty1(첫 번째 가상 콘솔)을 위해 사용 된다.
    • respawn동작을 함으로써, 로그아웃한 후에 다시 로그인 프롬프트로 되돌아 가게 한다.

    ctrlaltdel

    • ctrlaltdel: 가상 콘솔에서 Ctrl + Alt + Del을 눌렀을 때 시스템이 하는 일을 통제한다.
    • 대부분의 시스템에서는 shutdown명령을 사용하는 리부팅 명령이다.

    sysinit

    • sysinit: 런레벨에 들어가기 전 시동이 될 때, init이 먼저 실행해야하는 동작

    Sys V Start-up 절차

    • Sys V init가 로그인하도록 허용하기 직전에 어떻게 시스템 서비스를 가동하는지 본다.
    l5:5:wait:/etc/rc.d/rc 5
    • 위 라인(/etc/inittab 내)이 여러 다른 프로그램의 트리거 역할을 한다.
    • rc 5명령은 다음 명령들을 순서대로 실행함으로써 rc5.d 디렉토리에서 프로그램들을 시작한다.
    S10sysklogd start
    S12kerneld start
    S15netstd_init start
    S18netbase start
    ...중략
    S99sshd start
    
    • 각 명령에 'S'는 명령이 start 모드에서 실행되어야한다는 것이다.
    • 그 뒤 숫자는 일련의 순서(00~99)에서 rc가 그 명령을 시작해야 하는 곳이 어딘지를 나타낸다.
      • rc*.d 명령은 보통 /sbin, /usr/sbin에서 프로그램을 시작하는 셸 스크립트다.
    • 'K'는 정지 모드를 위한 명령이다. 여기서 rc는 start대신 stop을 인수로 명령을 실행한다.

    • rc*.d 디렉토리의 내용은 사실상 또 다른 디렉토리의 파일들로 이어지는 심볼릭 링크들이다.

      이러한 여러 개의 서브디렉토리로부터 수많은 심볼릭 링크들이 형성되는 것을 link farm이라고한다.
      • 리눅스 배포판들은 이런 링크들을 포함해 모든 런레벨에서 동일한 스타트업 스크립트를 사용할 수 있도록 하고 있다.

    서비스 시동/정지

    • init에 대한 대몬 서비스를 수동으로 시동/정지 할 수 있다.

    httpd 제어

    • httpd: 웹 서버 프로그램
    1. 시작
    $ init.d/httpd start
    1. 정지
    $ httpd stop

    부트 절차 수정

    • Sys V init에서 부팅 절차를 변경하려면, 보통 link farm을 수정해야한다.
    • 가장 흔한 예는 init.d디렉토리의 명령들 중 하나가 특정한 런레벨에서 실행이 되지 않도록하는 것이다.
      (당연하게도) 매우 신중해야한다.
      • 따라서.. 링크의 이름 첫머리에 밑줄(_)을 넣는 방법을 많이 사용한다.
    $ sudo mv S99httpd _S99httpd
    • 이렇게 하면 파일명이 더 이상 S또는 K로 시작하는 것이 아니기 때문에, rc가 pass한다.

    서비스 추가

    1. 서비스를 추가할 때는 init.d디렉토리에 스크립트를 생성한다.
    2. rc*.d 디렉토리에 심볼릭 링크를 만든다.
      2.1 가장 쉬운 방법은 init.d에 이미 있는 스크립트 중 잘 돌고 있는 것을 복사해 수정하는 것이다.
    3. 서비스를 추가할 때 부팅 절차에서 그 서비스를 시작할 적절한 시점을 선택한다.
      3.1 만약 너무 빨리 시작하면, 다른 일부 서비스에 대한 종속성 때문에 동작하지 않을 수도 있다.
      3.2 필수적인 서비스가 아닐 경우, 대부분의 시스템 관리자들은 90번대의 숫자를 선호한다. 이는 시스템에 포함된 대다수의 서비스 이후에 그 서비스를 동작하게 한다.

    run-parts

    • Sys V init을 사용하든 말든 run-parts라는 유틸리티를 통해 Sys V init 스크립트를 실행할 수 있다.
      • 또한, 주어진 디렉토리에서 다수의 실행 가능한 프로그램들을 일종의 예측 가능한 순서에 따라 실행할 수 있다.
    • 디폴트 동작: 디렉토리의 모든 프로그램을 실행한다.
      • 옵션을 주어 일부 무시해라고 할 수 있다.
    • Ubuntu의 경우, /etc/init.d runlevel 디렉토리의 모든 "start" 를 실행 하며 regex S[0-9]{2} 식을 사용한다.

    Sys V init 제어

    • telinit 사용한다.
    • 예를들어 런레벨 3으로 변경하려면 아래와 같은 명령을 사용한다.
    $ sudo telinit 3
    • 런레벨을 변경할 때 init은 inittab 파일에 없는 프로세스라면 새로운 런레벨을 위해 이들을 제거하려고 할 것이다. (따라서, 런레벨을 변경할 때는 조심!!)

    • 작업을 추가/제거/변경하려면 init에 그 변경에 대해 전달해야 한다. 그리고 리로딩한다.
      아래는 그에 대한 명령이다.
    $ sudo telinit q

    • 단일 사용자 모드로 변경
    $ sudo telinit s


    시스템 정지

    • init은 시스템의 정지와 재부팅하는 방법을 제어한다.
    $ sudo shutdown -h now
    
    $ sudo shutdown -h +10 (10분 후)
    • reboot
    $ sudo shutdown -r now
    
    $ sudo shutdown -r +10 (10분 후)

    • now 인수 말고 다른 것을 명시하면 shutdown명령은 /etc/nologin이라는 파일을 생성해 시스템은 슈퍼 사용자를 제외하고 로그인을 금지시킨다.

    • shutdown은 정지 시간이 되면 init에게 shutdown process를 실행하게 한다.
      • systemd에서는 shutdown 유닛을 활성화한다는 의미이다.
      • Sys V init에서는 런레벨이 0 또는 6으로 변경된다는 의미이다.
    1. init은 모든 프로세스가 잘 정지되도록 요청한다.
    2. 프로세스가 반응하지 않는다면 initTERM 시그널을 사용한다.
    3. TERM 시그널이 동작하지 않는다면, init은 정지하지 않고 남아있는 것들에 KILL 시그널을 사용한다.
    4. 시스템은 시스템 파일들을 잠그고 정지를 위해 다른 준비를한다.
    5. 시스템이 루트 이외의 모든 파일 시스템의 마운트를 해제한다.
    6. 시스템이 루트 파일 시스템을 ro로 재마운트한다.
    7. 시스템이 sync 프로그램으로 모든 버퍼 데이터를 파일 시스템에 기록한다.
    8. 리부팅하거나 reboot(2) 시스템 콜로 정지 시키도록 커널에 지시한다.

    mongodb 를 init 스크립트에 추가

    1. MongoDB용 init.d 스크립트를 다운로드한다.
      1.1. /etc/init.d/mongodb
    $ curl https://raw.githubusercontent.com/mongodb/mongo/master/debian/init.d | sudo tee /etc/init.d/mongodb >/dev/null

    1. 실행 permission 지정
    $ sudo chmod +x /etc/init.d/mongodb

    1. init 서비스 명령 실행
    $ sudo service mongodb status
    $ sudo service mongodb start
    $ sudo service mongodb stop

    profile
    pllpokko@alumni.kaist.ac.kr

    0개의 댓글