초기 사용자 공간 rootfs, BusyBox, init

markyang92·2022년 3월 6일
0

boot

목록 보기
6/6
post-thumbnail

초기 사용자 공간 설정

  • 커널 초기화에서 사용자 공간으로 이동하기 위해, 커널rootfs 를 마운트하고, 루트 파일시스템에 있는 프로그램을 실행한다.
    • 이는 ramfs or block device상의 실제 파일 시스템 mount를 통해 이루어진다.

init 설정

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

init 특징

  • 이 함수는 PID 1인 첫 번째 스레드를 만들고, kernel_init()의 코드를 실행한다.
    • 사용자 공간을 설정하는 작업을 수행할 것이다.
  • ramfs에서, /init을 찾는데 실패하면, init/do_mounts.c 안의 함수 prepare_namespace()를 불러, 파일 시스템을 마운트하려 할 것이다.
  • block device상의 파일 시스템 mount되었다면, /sbin/init, /etc/init, /bin/init, /bin/sh를 성공할 때까지 차례로 실행하려고 시도한다.
  • rootfs: User Space 요소를 담고 있다.
    • 램디스크 일 수도 있고, 블록 장치를 통해 접근할 수 있는 파일 시스템일 수도 있다.
    • rootfs 없이 커널을 부팅하면 커널 패닉이 발생한다.

rootfs에서 필요한 것

  • 커널은 부트로더로부터 포인터로 전달된 initramfs 혹은 root= 매개 변수를 통해 커널 커맨드라인에 지정된 블록 장치를 마운트함으로써 rootfs를 구한다.
  • rootfs가 구해지면, 기본 설정으로 이름이 init인 첫 번째 프로그램을 실행한다.

rootfs를 만들기위한 요소설명
init보통 일련의 스크립트를 실행함으로, 모든 것을 시작시키는 프로그램
shellinit과 기타 프로그램이 호출하는 shell script를 실행하기 위해 필요하다.
daemoninit 자체도 daemon이다.
  • syslogd
  • sshd

  • 등 수많은 대몬들이 init에의해 제어되어 실행된다.
    shared library-
    conf 파일들/etc 에 저장되어 있다.
    장치 노드다양한 디바이스 드라이버에 접근할 수 있게 해주는 특수 파일들
    /proc, /sys커널 자료 구조를 디렉토리와 파일의 계층 구조로 나타내는 2개의 가상 파일 시스템
    여러 프로그램과 라이브러리 함수들이 procsys에 의존한다.
    커널 모듈/lib/modules/<kernel_version>
    • 흥미롭게도 리눅스 커널은 init= rdinit=을 통해 지정된 프로그램의 존재 외에는 파일과 디렉토리의 레이아웃에 신경 쓰지 않는다.
    FHS설명
    /bin필수 바이너리
    /dev디바이스 노드, 기타 특수 파일들
    /etcconf 파일들
    /lib필수 shared library (libc..)
    /procproc 파일 시스템
    /syssysfs 파일 시스템
    /sbin스템(system) 에 필수 바이너리
    /tmp임피 파일, 휘발성 파일들
    /usr추가 프로그램. 시스템 부팅할 때 필요한 것은 담지말자
    /var실행 중 변경될 수 있는 파일과 디렉토리
  • log message

  • staging rootfs

    $ mkdir ~/rootfs
    $ cd ~/rootfs
    $ mkdir bin dev etc home lib proc sbin sys tmp usr var
    $ mkdir usr/bin usr/lib usr/sbin
    $ mkdir -p var/log
    $ tree ./rootfs -d
    ./rootfs
    ├── bin
    ├── dev
    ├── etc
    ├── home
    ├── lib
    ├── proc
    ├── sbin
    ├── sys
    ├── tmp
    ├── usr
    │   ├── bin
    │   ├── lib
    │   └── sbin
    └── var
        └── log

    POSIX 파일 접근 권한


    • rootfs에선 전부 root:root로 만들자.
    $ cd ~/rootfs
    $ sudo chown -R 0:0 *

    init 프로그램


    shell

    • bash: Unix Bourne Shell의 Superset
    • ash: Bourne Shell에 기반을 둔 shell. BusyBox는 bash와 더 호환이 되도록, 확장된 버전의 ash를 갖고 있다. bash보다 훨씬 작고 Embedded System에서 매우 인기 있는 shell이다.
    • hush: 매우 작은 shell. 메모리가 매우 작은 장치에서 유용하다. BusyBox용 버전이 있다.

    BusyBox

    • BusyBox의 기원은 1.44MB 플로피 디스크로 리눅스를 부팅할 수 있도록 1996년 Bruce Perens에 의해 Debian installer에 통합됐다. 그리고 리눅스의 심장부에 자리 잡았다.
    • BusyBox는 필수 리눅스 유틸리티의 필수 기능을 수행하도록 처음부터 작성되었다.
      개발자는 80:20 규칙(프로그램의 가장 유용한 기능 80%는 20%의 코드로 작성된다)를 활용했다. 따라서 BusyBox 도구는 데스크톱 도구가 제공하는 기능의 부분집합을 구현하지만, 대부분의 경우에 충분히 유용한 기능을 제공한다.
    • BusyBox가 택한 또 하나의 묘책은 모든 도구를 하나의 바이너리로 묶어서 도구들 사이에 코드를 공유하기 쉽도록 하는 것이다.
      • Applet(작은 응용프로그램)들의 모음으로, 각각은 주 기능을 [applet]_main의 형태로 export한다.
        • cat 명령은 coreutils/cat.c에 구현되어 있고, cat_main을 export한다.
      • BusyBox의 main함수 자체는 커맨드라인 Argument에 근거해 호출을 올바른 Applet으로 보낸다.
        • 그래서 파일을 읽으려면, 다음과 같이 실행하고 싶은 애플릿의 이름 뒤에 해당 애플릿이 요구하는 인자를 붙여서 busybox를 실행한다.
        $ busybox cat my_file.txt
    • 사실 이렇게 사용하는 것은 너무 불편하니까 Symbolic link를 만들자!!!
    $ ls -l bin/cat bin/busybox
    
    bin/cat -> busybox
    • 커맨드라인에 cat을 입력했을 때, 실제로 실행되는 프로그램은 busybox 이다.
      • busybox는 argv[0]에 전달된 명령 이름 (cat)을 추출한 뒤에 표에서 cat에 대응되는 cat_main을 찾으면 된다.
        • 이 모든 것은 libbb/appletlib.c 중 아래에 나와있는 부분에 있다.
    applet_name = argv[0];
    applet_name = bb_basename(applet_name);
    run_applet_and_exit(applet_name, argv);
    • busybox는 init 프로그램, 다양한 수준의 복잡도를 갖는 몇 가지 shell, 대부분의 관리 작업을 위한 유틸리티를 포함해 300개가 넘는 애플릿을 담고 있다.
      심지어 간단한 버전의 vi 편집기도 있어서 장치에 있는 텍스트파일을 수정할 수도 있다.
    • 요약하면, busybox의 전형적인 설치는 하나의 프로그램과 각 애플릿을 위한 심볼릭 링크로 이뤄져있지만, 개별 응용프로그램의 묶음인 것 처럼 동작한다.

    BusyBox Build

    • BusyBox는 커널 같은 Kconfig, Kbuild 시스템을 사용하므로, 컴파일은 간단하다.
    $ git clone git://busybox.net/busybox.git
    $ cd busybox
    $ git checkout <사용하고픈 버전>
    $ make distclean
    $ make defconfig
    • make menuconfig를 실행해 구성을 미세 조정하고 싶을 수도 있다.
      • Busybox Settings > Installation Options(CONFIG_PREFIX)에서 설치 경로가 스테이징 디렉토리(~/rootfs)를 가리키도록 설정 하자.
    • Cross Compile
    $ make ARCH=arm CROSS_COMPILE=arm-cortex_a8-linux-gnueabihf-
    • Install
      • BusyBox를 install하면 바이너리를 CONFIG_PREFIX로 설정된 디렉토리로 복사하고 이를 가리키는 심볼릭 링크들을 만들 것이다.

    • BusyBox의 대안 ToyBox
      • ToyBox는 표준(특히 POSIX-2008, LSB 4.1)을 충실히 따르는 것을 더 강조하고 GNU 확장과의 호환은 덜 강조한다.
      • ToyBox는 BusyBox보다 작은데, 부분적으로는 좀 더 적은 애플릿을 구현하기 때문이다.
      • 또한, 라이선스로 GPL v2가 아닌 BSD를 택하고 있어, 안드로이드 처럼 BSD 라이선스를 택한 운영체제와 라이선스 호환되고, 모든 새로운 안드로이드 장치에 포함되어 있다.

    rootfs용 라이브러리

    • 대표적으로 glibc가 있는데, 상당히 크다.
      • crossTool-NG로 빌드한 glibc 2.22의 경우 라이브러리, locale, 기타 지원 파일이 33MB에 달한다.
        • musl libc, uClibc-ng를 사용하면 크기를 더 줄일 수 있다.

    readelf로 필요 라이브러리 알아내자!

    $ cd ~/rootfs
    $ arm-cortex_a8-linux-gnueabihf-readelf -a bin/busybox | grep "program interpreter"
    [Requesting program interpreter: /lib/ld-linux-armhf.so.3]
    
    $ arm-cortex_a8-linux-gnueabihf-readelf -a bin/busybox | grep "Shared library"
    0x00000001 (NEEDED) Shared library: [libm.so.6]
    0x00000001 (NEEDED) Shared library: [libc.so.6]
    • 툴체인 sysroot 디렉토리에서 이들 파일을 찾아서 스테이징 디렉토리로 복사해야한다.
    $ arm-cortex_a8-linux-gnueabihf-gcc -print-sysroot
    /home/markyang/x-tools/arm-cortex_a8-linux-gnueabihf/arm-cortex_a8-linux-gnueabihf/sysroot
    • sysroot 안의 /lib/ld-linux-armhf.so.3을 보면 사실은 심볼릭 링크이다.
    $ cd (위의 sysroot)
    $ ls -l lib/ld-linux-armhf.so.3
    lrwxrwxrwx 1 markyang markyang 10 Mar 3 12:22 lib/ld-linux-armhf.so.3 -> ld-2.22.so
    • 심볼릭 링크를 보존하도록 cp -a를 사용해 각각을 복사한다.
    $ cd ~/rootfs
    $ cp -a (위 sysroot)/lib/ld-linux-armhf.so.3 lib
    $ cp -a (위 sysroot)/lib/ld-2.22.so lib
    • 최소한의 임베디드 파일시스템 크기가 필요할 때만 이렇게 할 가치가 있다.
      dlopen(3)호출을 통해 로드되는 라이브러리(대부분 플러그인)를 놓칠 위험이 있다. 나중에 NSS(name service switch) 라이브러리의 예를 살펴보자.

    strip 을 통한 크기 축소

    profile
    pllpokko@alumni.kaist.ac.kr

    0개의 댓글