2007/10/30

솔라리스 UFS 화일 시스템의 최대 화일 개수와 inode의 부족

단일 디렉토리 내에서는 32767개의 디렉토리만 지원하므로, 각 디렉토리는 서브
디렉토리를 가져야만 더 많은 디렉토리를 가질 수 있고, 각 디렉토리별로 더 많은
화일을 가질 수 있게 됩니다.

단일 디렉토리에 형성할 수 있는 화일의 개수는 이론적으로 제한이 없습니다만,
inode의 개수에 제한을 받습니다. 32비트 화일 시스템에서는 최대 40억개의 inode를 (unsigned long long for 64bit, unsigned long for 32bit) 구성 하실 수 있습니다만, inode에 할당하는 블럭의 크기(디폴트는 1 inode에 2kb를 사용)에 따라 변화가 있으므로 다음과 같은 방법으로 체크하실 필요가 있습니다.
inode만 보고 싶은 경우에는
#df -F ufs -o i /

혹은 기타 다른 상황까지 보고 싶은 경우에는 fstyp 이용
# fstyp -v /dev/dsk/c0t0d0s0 | less
ufs
magic 11954 format dynamic time Tue Oct 30 13:59:33 2007
sblkno 16 cblkno 24 iblkno 32 dblkno 760
sbsize 2048 cgsize 8192 cgoffset 64 cgmask 0xffffffc0
ncg 627 size 30803962 blocks 30337448
bsize 8192 shift 13 mask 0xffffe000
fsize 1024 shift 10 mask 0xfffffc00
frag 8 shift 3 fsbtodb 1
minfree 1% maxbpg 2048 optim time
maxcontig 128 rotdelay 0ms rps 167
csaddr 760 cssize 10240 shift 9 mask 0xfffffe00
ntrak 48 nsect 128 spc 6144 ncyl 10028
cpg 16 bpg 6144 fpg 49152 ipg 5824
nindir 2048 inopb 64 nspf 2
nbfree 1748247 ndir 31696 nifree 3392581 nffree 255808
cgrotor 101 fmod 0 ronly 0 logbno 1584
version 2

.....

참고 화일 /usr/include/sys/types.h

//
// 32bit의 경우
typedef ulong_t ino_t; /* expanded inode type */
...
typedef ulong_t fsfilcnt_t; /* count of files */

// 64bit의 경우
typedef u_longlong_t ino_t; /* expanded inode type */
...
typedef u_longlong_t fsfilcnt_t; /* count of files */
//
//
inode가 부족하다고 판단이 되는 경우에는 tunefs를 통해서 inode 블럭당 사용되는 용량을
줄여서 즉, space 기준으로 최적화를 함으로써 inode를 좀 더 획득할 수 있습니다.

그러나, inode가 풍부함에서 디렉토리를 나열하거나(ls) 하는데, 오랜 시간이 걸리면서
sys% 가 증가하는 경우에는 캐쉬 테이블의 크기가 부족해지는 경우가 대부분입니다.
이런경우 일단, 가장 쉬운 workaround로는
/etc/system에 튜닝을 하실 필요가 있습니다. 단 메모리가 충분이 있어야 합니다.
튜닝 대상이 되는 시스템 변수는 ncsize이며, ufs_ninode라는 변수의 상위 개념에 해당합니다.
일단 현재 사용되는 값을 확인하기 위해서는 다음과 같은 방법을 사용합니다.
# echo "ncsize/D" | mdb -k
ncsize:
ncsize: 129700
숫자를 확인하셨으면 다음과 같이 필요한 만큼의 숫자를 결정하신 후 /etc/system에 입력합니다.
set ncsize= 1042865

또한, 디렉토리 나열을 느리지 않은데, 화일 /O(데이타를 읽거나, 쓸때)가 느리다고 판단이 되는 경우에는
다음과 같은 시스템 변수의 튜닝을 할 필요가 있습니다.
segmap_percent

설정 이전에 먼저 보는 법은 다음과 같습니다.
# echo "segmap_percent/D" | mdb -k
segmap_percent:
segmap_percent: 12

확인이 되었으면, 다음과 같이 설정을 하되, 너무 큰 값을 설정하지 않도록 주의 합니다.
이 설정은 커널이 I/O를 위해서 많은 메모리를 사전 할당하도록 하므로, 메모리 사용에 커다란
영향을 주게 됩니다.
set segmap_percent=25

와 같이 튜닝하시고 테스트후 조정하는 과정을 거쳐야 합니다.

상황에 따라서는 위 두개의 튜닝을 동시에 진행할 수 있습니다. 그럴 수록 커널 메모리의 상태를 잘 이해하고 있어야 합니다. 참고로 커널이 사용하는 메모리의 상태를 보기 위해서는 다음과 같은 명령어를 사용할 수 있습니다.

#echo "::memstat" | mdb -k ; 솔라리스 9이후 부터 가능. 메모리가 큰 장비에서는 오래 걸림.

참고로 솔라리스 10의 ZFS를 사용하면 UFS가 가지는 위와 같은 문제가 근원적으로 발생하지 않습니다.

2007/10/29

프로세스의 서비스 종료 방법

솔라리스10 ( 08/07 이후 )에서는 시스템이 부팅하면서 자동적으로 시작되는 모든 프로세스들은 SMF라는 서비스 관리하에서 구동이 됩니다. 따라서, 해당 서비스를 시작하거나 종료하기 위해서는 SMF의 관리 명령어인 svcadm을 사용해야 합니다.

telnet 서비스를 종료하기 위해서는
#svcadm disable telnet
과 같이 실행하며, 어떤 서비스가 있고 어떤 상태에 있는 지를 알기 위해서는
#svcs -a 를 사용합니다.(이전 블로그에 언급한 바 있습니다.)

이때 사용되는 telnet 혹은 telnet:default 와 같은 아규먼트를 서비스를 지칭하는 FMRI라고 합니다.

그런데, 어느날 prstat를 실행했는데 잘 모르는 프로세스가 실행되고 있다면 어떻게 해야 할까요?
즉, 특정 프로세스가 어떤 서비스에 의해서 시작되게 되었는지를 알아야 해당 FMRI를 찾아서 종료시킬 수 있을텐데, 프로세스만 실행되고 있다면 바로 알기가 어렵습니다. 이때, 사용할 수 있는 것이 pargs 라는 유틸리티입니다.

pargs는 아큐먼트로 오는 프로세스 아이디를 받아서, 그 프로세스가 실행할때 넘겨받은 아규먼트를 보여주는 유틸리티입니다만, -e 옵션을 사용하면, 그 프로세스가 실행할 당시에 상속받은 모든 환경 변수를 보여주는 기능도 합니다.

일반적으로 SMF에 의해서 실행된 모든 서비스들은 해당하는 FMRI에 대한 변수가 상속되어져 있기 때문에 의문의 프로세스 ID와 pargs -e 를 이용하여 해당 프로세스를 구동시킨 FMRI를 찾을 수 있습니다.

1298 noaccess 167M 89M sleep 59 0 0:01:50 0.0% java/23


와 같은 프로세스가 돌고 있는 것을 발견했을때, 이 프로세스가 어떤 FMRI로 시작했는 지를 알기위해서는 다음과 같이 실행합니다.
# pargs -e 426
#pargs -e 1298
1298: /usr/java/bin/java -server -Xmx128m -XX:+BackgroundCompilation -XX:PermSize=32m
envp[0]: LANG=ko
envp[1]: LD_LIBRARY_PATH=/usr/jdk/instances/jdk1.5.0/jre/lib/sparc/server:/usr/jdk/instances/jdk1.5.0/jre/lib/sparc:/usr/jdk/instances/jdk1.5.0/jre/../lib/sparc:/usr/lib/webconsole
envp[2]: NLSPATH=/usr/dt/lib/nls/msg/%L/%N.cat
envp[3]: PATH=/usr/sbin:/usr/bin
envp[4]: SMF_FMRI=svc:/system/webconsole:console
envp[5]: SMF_METHOD=/lib/svc/method/svc-webconsole start
envp[6]: SMF_RESTARTER=svc:/system/svc/restarter:default
envp[7]: TZ=ROK
envp[8]: XFILESEARCHPATH=/usr/dt/app-defaults/%L/Dt

위에서 envp[4]에서 해당 프로세스는 webconsole:console에서 서비스가 시작되었음을 알 수 있습니다. 따라서, 이 프로세스가 필요없다면, 이 서비스의 FMRI를 이용하여 죽일 수 있습니다.

다음과 같은 방법으로 서비스를 종료시킴으로써 프로세스를 종료시킬 수 있습니다.

#svcadm disable webconsole