난이도 : 초급 Daniel Robbins, President/CEO, Gentoo Technologies, Inc
2001 년 10 월 01 일 Linux 2.4에는 Reiserfs, XFS, GFS와 같은 새로운 파일시스템 기능이 추가되었다. 파일시스템은 좋은 것이긴 하지만 실제 생산환경에 적용하는 방법에 대해서는 정확히 모르고 있는 것도 사실이다. 이 시리즈를 통해 필자는 Linux 2.4에 새롭게 향상된 파일시스템을 설치하는 과정을 설명한다. 이번에는 init wrapper를 사용하여 "devfs mode"로 시스템을 바꾸는 방법을 설명한다.
Are you ready?
Linux system을 devfs 또는 Device Filesystem으로 변환하는 작업을 마무리 할 것이다. 본 시리즈의
Part 4 에서는 devfs가
커널 레벨에서 디바이스 등록 문제를 해결하는 방법을 설명했다. Part
5 에서는 Linux 시스템을 devfs 호환 시스템으로 만들어서 devfs로의 변환을 위한 준비 단계를 제시했다.
init wrapper
시작
Part 5에서, init wrapper의 개념을 설명했다. 그리고 다양한 devfs 초기화 문제를 해결하는 데 있어서 적격인
이유도 설명했다. init wrapper를 단계별로 살펴보고 각각 무엇을 수행하는지 살펴보자:
init wrapper-윗 부분
#!/bin/bash
# Copyright 2001 Daniel Robbins <drobbins@gentoo.org>, Gentoo Technologies, Inc.
# Distributed under the GNU General Public License, version 2.0 or later.
trap ":" INT QUIT TSTP
export PATH=/sbin:/bin:/usr/sbin:/usr/bin
umask 022
if [ $$ -ne 1 ]
then
exec /sbin/init.system $*
fi
|
init wrapper는 bash 스크립트이다. 스크립트의 상단에 #!/bin/bash 가 있기 때문이다.
init wrapper가 실행하기 위해서는 bash 2.0 또는 이후 버전이 필요하다; /bin/bash
--version 을 타이핑하여 bash 쉘이 최신 버전인지 확인한다. 최신 버전이 아니라면 /bin/bash2
실행파일을 설치해야한다. 최신 버전이라면 #!/bin/bash2를 읽을 수 있도록 스크립트의 첫 번째
라인을 변경한다.
이제, 스크립트를 보자. trap 명령어는 스크립트가 실행되는 동안 사용자에 의해 스크립트가 인터럽트
되지 않도록 한다 (예를 들어, 부팅 시 control-C를 누른다). 그런 다음, 적절한 디폴트 경로를 export
하고 디폴트 umask를 022로 설정한다. 부팅 프로세스 동안 가능한 한 빨리 디폴트 umask를 설정하는 것이 좋다. 왜냐하면
2.4 kernel 이전 버전들은 디폴트 umask가 0이 되는 버그를 가지고 있었다. 심각한 보안 위협을 초래할 수 있다.
다음에, 첫 번째 조건문인 if [ $$ -ne 1 ]. bash가 $$를
현재 실행되는 프로세스의 process ID까지 확장한 것을 볼 수 있다. 이는 "process ID가 1인가?" 를 묻고 있는
것이다. 이것이 중요한 이유는 부팅 프로세스 동안 커널에 의해 시작한다면 PID 1을 갖게 된다. PID 1은 init
프로세스 동안 보존된다. PID 1이 아니라면, 시스템이 이미 부팅된 후에 명령행에서 실행하고 있다는 것을 알게된다.
일반적인 일은 아니다. 왜냐하면 /sbin/init 명령어는 수퍼유저가 이미 부팅된 시스템의 runlevel을
변경할 수 있도록 하는 이중의 목적을 가지고 있기 때문이다. 이와 같은 경우, 원래의 /sbin/init을
exec 하여 /sbin/init.system으로 이름을 변경한다. $*
변수를 사용하여 명령행 인자를 init.system 에 전달하면 init wrapper는 종료되고 init.system
은 실행된다.
커널 부팅 옵션
하지만, wrapper가 부팅 시간 동안 커널에 의해 시작된다면 bash의 PID는 1이
되고 bash가 계속하여 wrapper를 실행할 때 이러한 조건은 건너뛰게 된다. 다음 예제를 보자:
init wrapper-중간 부분
mount -n /proc
devfs="yes"
for copt in `cat /proc/cmdline`
do
if [ "${copt%=*}" = "wrapper" ]
then
parms=${copt##*=}
#parse wrapper option
if [ "${parms/nodevfs//}" != "${parms}" ]
then
devfs="no"
fi
fi
done
|
코드가 여기까지 진행되었다는 것은 부팅 프로세스 동안 커널에 의해 실행하고 있다는 의미가 된다; /proc을 root 파일시스템에
마운트한다. 이것은 현재 읽기 전용이다. 그런 다음 bash 코드를 실행한다. 커널을 통해서 우리는 어떤 옵션이 LILO 또는
GRUB을 통해 이것으로 전달하는지를 볼 수 있다. /proc/cmdline의 내용을 참고하면 된다. /proc/cmdline
내용은 다음과 같다:
/proc/cmdline의 내용
# cat /proc/cmdline
root=/dev/hda6 hda=89355,16,63 mem=524224K
|
/proc/cmdline를 이용한다. wrapper라고 하는 커널 부팅 변수를 위해 이것을 조사하면
된다. wrapper=nodevfs 커널 부팅 옵션 사이에 나타난다면 스크립트는 devfs를 실행할 수
없다는 것을 알게된다. 하지만, 변수가 /proc/cmdline에 나타나지 않는다면, wrapper는 devfs 초기화를 진행할
것이다. 이것으로 알 수 있는 것은 wrapper=nodevfs 커널 부팅 옵션으로 부팅하면 devfs를
쉽게 실행불가로 만든다는 것이다. 만일 그렇게 되면, devfs 변수는 no가
되고 반대의 경우 yes가 된다.
끝내기
다음은 wrapper의 나머지 부분이다:
init wrapper의 나머지 부분
if [ "$devfs" = "yes" ]
then
if [ -e /dev/.devfsd ]
then
clear
echo
echo "The init wrapper has detected that /dev has been automatically mounted by"
echo "the kernel. This will prevent devfs from automatically saving and"
echo "restoring device permissions. While not optimal, your system will still"
echo "be able to boot, but any perm/ownership changes or creation of new compat."
echo "device nodes will not be persistent across reboots until you fix this"
echo "problem."
echo
echo "Fortunately, the fix for this problem is quite simple; all you need to"
echo "do is pass the \"devfs=nomount\" boot option to the kernel (via GRUB"
echo "or LILO) the next time you boot. Then /dev will not be auto-mounted."
echo "The next time you compile your kernel, be sure that you do not"
echo "enable the \"Automatically mount filesystem at boot\" devfs kernel"
echo "configuration option. Then the \"devfs=nomount\" hack will no longer be"
echo "needed."
echo
read -t 15 -p "(hit Enter to continue or wait 15 seconds...)"
else
mount -n /dev /dev-state -o bind
mount -n -t devfs none /dev
if [ -d /dev-state/compat ]
then
echo Copying devices from /dev-state/compat to /dev
cp -ax /dev-state/compat/* /dev
fi
fi
/sbin/devfsd /dev >/dev/null 2>&1;
fi
exec /sbin/init.system $*
|
devfs가 yes가 될 경우에만 실행되는 조건문을 보고있다. 이것이 그러한
경우가 아니라면, devfs 초기화는 완전히 건너 뛰게 되고 devfs는 마운트되지 않는다. 이것이 전통적인 non-devfs
부팅이다.
하지만 devfs를 설정하고 있다면 조건문을 수행해야 한다. devfs가 커널에 의해 이미 설치되어 있는지를 확인한다; /dev/.devfsd
캐릭터 디바이스가 존재하는 지를 확인하는 것이다. devfs가 마운트되면 이 디바이스는 자동으로 커널에 의해 만들어진다. 그리고
앞으로의 devfsd 프로세스는 커널과 통신할 때 사용하게 될 것이다. devfs가 이미 마운트되었다면
(사용자가 "Automatically mount devfs at boot" 커널 옵션을 선택했기 때문에), 정보 메시지를 출력하여
사용자로 하여금 우리가 devfs의 영속 기능을 설정할 수 없게 된다는 것을 알 수 있도록 한다. devfs는 커널에 의해 마운트
되지 않았기 때문에 우리는 단지 그것만이 .
디바이스 영속성
모든 것이 정상이라면 devfs 설치를 시작한다: /dev가 /dev-state으로 바인드 마운트(bind-mount)되고 devfs
파일시스템은 /dev에 마운트 된다. 그런 다음, /dev-state/compat 디렉토리의 존재를 확인하고 반복적으로 그 내용을
/dev에 복사한다. 이러한 절차가 처음에는 불필요한 반복작업으로 보일 수도 있지만 필수적이고도 유용하다. 이유는 우리가 필요로
하는 compat 디렉토리는 devfsd의 영속 기능이 devfs-enabled 드라이버와 함께
작동할 수 있기 때문이다.
non-devfs 커널 모듈을 사용하게 된다면, /dev에 수동으로 디바이스 노드를 만들어야 한다. 이러한 접근방식의 문제점은
이 새로운 디바이스 노드가 devfsd에 의해 무시되어서 다음에 재부팅 할 때 사라지게 된다. 솔루션으로서는
/dev-state/compat 디렉토리를 갖는 것이다; non-devfs 모듈을 가지고 있다면, /dev-state/compat에
구식의 디바이스 노드를 만들면 그들은 부팅 시 수동으로 devfs 파일시스템에 추가 될 것이다. init wrapper 덕택이다.
마지막으로 devfsd를 시작하고 조건문을 종료하고 실제 init인, /sbin/init.system을
exec 하여 표준 시스템 부팅 프로세스를 시작한다.
Init wrapper
설치
init wrapper를 설치하는 방법이다. 우선, wrapper.sh용
소스를 그랩(grab)하고, 시스템에 저장한다. 그리고 나서, 다음과 같이 한다:
init wrapper 설치
# cd /sbin
# cp init init.system
# cp /path/to/wrapper.sh init
# chmod +x init
|
init wrapper가 설치되었다.
umount tweaking
init wrapper를 사용함으로서, 매우 복잡한 "initscript tweaking"을 피했다. 그럼에도
불구하고, 한 가지의 조정해야 할 것이 있다. rc 스크립트는 devfs가 마운트 된 /dev를 가지고 있는 root 파일시스템을
언마운트 하는데에 어려움을 겪을 것이다. 다행히, 간단한 픽스를 적용할 수 있다. rc 스크립트 디렉토리를 grep
한다. umount cd /etc/rc.d; grep -r umount * 또는
cd /etc/init.d; grep -r umount * 를 타이핑한다 (rc 스크립트가 설치된 배포판에
따라 다름). 그런 다음, umount를 참조하는 모든 스크립트에서 이것이 -r
옵션과 호출되는지를 확인한다. 가장 중요한 것은 root 파일시스템을 언마운트 하는 특정 umount
명령어이다.
-r 옵션은 umount에게 언마운트가 되지 않을 경우 읽기 전용으로 파일시스템을
리마운트 하도록 명령한다. 이것은 root 파일시스템을 영속 상태에 놓기에 충분하며 재부팅 준비가 되도록 한다.
이제, 재부팅 할 준비가 되었다; 그 전에, devfsd를 검토하여 /etc/devfsd.conf 를
진행시켜 호환 디바이스와 디바이스 영속을 가능토록 한다.
devfsd.conf
/etc/devfsd.conf를 편집기에 로딩한다. devfsd.conf의 첫 4줄이다:
devfsd.conf-윗 부분
REGISTER .* MKOLDCOMPAT
UNREGISTER .* RMOLDCOMPAT
REGISTER .* MKNEWCOMPAT
UNREGISTER .* RMNEWCOMPAT
|
위 4줄은 event (REGISTER 또는 UNREGISTER),
정규식 (.*), action (*COMPAT strings)으로 구성되어 있다.
첫 번째 줄은 디바이스가(.*는 모든 디바이스와 매치하는 정규식이다) 커널에 등록될 때 devfsd
에게 MKOLDCOMPAT action을 수행하도록 명령한다. MKOLDCOMPAT
action은 devfsd 에 빌트인 되고 "devfs를 통해 등록된 디바이스와 상응하는 기존의 모든
호환 디바이스를 만든다"라는 의미로 해석된다. 설정을 진행시켜 나가면서, 디바이스에서 실행되는 RM*COMPAT
action은 이러한 특별한 호환 디바이스를 마법처럼 사라지게 한다. 전체적으로 볼 때 위의 4 줄은 devfsd
에게 디바이스가 등록될 때 호환 디바이스를 만들도록 명령하고, 디바이스가 등록이 해지되었을 때 호환 디바이스를 제거하라고 명령하는
것이다. 이러한 명령어들 덕택에, IDE 디바이스 드라이버가 /dev/ide/host0/bus0/target0/lun0/disc
devfs 스타일의 디바이스를 시스템에 등록할 때, devfs는 자동적으로 /dev/hda
호환-스타일의 디바이스와 매칭(matching)시킨다. 이것은 구식의 디바이스 이름을 포함하고 있는 /etc/fstab을 읽는
mount와 fsck 같은 명령어에 매우 유용하다. 일반적으로 호환 디바이스를
만듦으로서 devfs로의 전환이 깔끔해진다. devfsd.conf의 다음 라인이다:
Module auto-loading
devfsd.conf
이 엔트리는 devfsd 에게 모든 디바이스(.*)가 "검색"될 때마다 MODLOAD
action을 실행하도록 명령한다. MODLOAD action은 modprobe /dev/mydev
가 실행되도록 한다. /dev/mydev는 특정 프로세스가 찾으려고 하는 디바이스의 이름이다. 이러한 기능으로 인해서 사운드
카드 드라이버가 music player를 시작할 때 자동으로 로딩될 수 있다.
디바이스 영속성
위 라인은 devfsd 에게 모든 디바이스 권한 또는 소유권 변경을 비롯하여 사용자가 만드는 새로운
호환 디바이스를 위한 리포지토리(repository)로서 /dev-state를 사용하도록 명령한다. 첫 번째 두 라인은 devfsd
에게 가상 터미널(pseudo-terminal) 디바이스가 커널에 등록되거나 속성이 변경될 때 어떤 특별한 action을 수행하지
않도록 명령하는 것이다. 이 라인이 없이 가상 터미널의 권한 및 소유권은 재부팅 시 보존된다.
다음 세 라인은 모든 다른 디바이스를 위해 /dev-state을 작동시킨다. 특별히, 디바이스가 등록되거나 devfsd
자체가 시작할 때 /dev-state로 부터 모든 속성을 복원할 것이다. 속성 변화와 새로 만들어진 호환 디바이스를 /dev-state에
백업한다.
CFUNCTION과 symlink
devfsd.conf를 끝마치기 위해서는 다음의 라인이 필요하다:
devfsd.conf, end
REGISTER ^cdrom/cdrom0$ CFUNCTION GLOBAL symlink cdroms/cdrom0 cdrom
UNREGISTER ^cdrom/cdrom0$ CFUNCTION GLOBAL unlink cdrom
REGISTER ^misc/psaux$ CFUNCTION GLOBAL symlink misc/psaux mouse
UNREGISTER ^misc/psaux$ CFUNCTION GLOBAL unlink mouse
|
마지막 4 라인은 선택적이다. 하지만 검토해 볼 가치는 있다. /dev-state 영속성이 디바이스 노드에 훌륭하게 작동되는
반면, symbolic link에는 전혀 영향을 미치지 않는다. 다시 말해서 무시된다. /dev/mouse 또는 /dev/cdrom
symlink가 존재 할 뿐만 아니라 재부팅 시에도 영속성을 유지하는 방법이 궁금할 것이다. 다행히도 devfsd는
설정이 가능하며 이 4 라인은 트릭을 수행 할 것이다. 첫 번째 두 라인은 /dev/cdrom/cdrom0 디바이스가 등록될
때 devfsd 에게 명령하여 /dev/cdrom symlink가 나타나도록 한다. 이를 위해서, devfsd
는 실제로 여러분이 지정한 libc 함수로 동적 호출을 수행한다. 이 경우 symlink() 와 unlink()이다.
마지막 두 라인은 /dev/misc/psaux (PS/2 mouse) 디바이스가 devfs로 등록될 때 /dev/mouse symlink를
만들기 위해서 독자적인 접근 방식을 사용한다. 시스템에 이 라인을 커스터마이징하고 파일을 저장하라. devfsd.conf를
다운로드 할 수 있다.
재부팅 시 주의사항
재부팅 하기 전에, Richard Gooch의 devfs FAQ를 검토해보는 것도 좋을 것 같다; devfs naming scheme에
대한 정보는 새로운 방식의 디바이스 이름에 익숙해 지는 데에 유용할 것이다. (참고자료).
고급 파일시스템 개발자 가이드,Part
5 를 읽어보기 바란다. 부팅 관련 문제를 해결하는데에 도움이 된다. 새로운 init wrapper가 실패할 경우 emergency
rescue instruction에 따라서 root 파일시스템을 읽기-쓰기로 리마운트 하고 다음 과정을 수행하여 제거할 수 있다:
pre-wrapper 상태로 리턴
# cd /sbin
# mv init wrapper.sh
# mv init.system init
|
파일시스템을 읽기 전용으로 리마운팅하고 재부팅하면 시스템은 pre-wrapper(wrapper 이전) 상태로 돌아온다.
참고자료
필자소개  | 
| Daniel Robbins는 Gentoo Technologies, inc.의 사장/CEO이다. 또 PC용 고급 Linux인 Gentoo
Linux의 창설자이자, 차세대 Linux 포트 시스템인 Portage 시스템의 창시자이다. 또한 Macmillan사에서
출판하는 Caldera OpenLinux Unleashed, SuSE Linux Unleashed, Samba Unleashed에
집필활동을 하고 있다. |
기사에 대한 평가
|