2008/07/22

ufs의 disk quota 동작

커널 모듈인 ufs는 화일 시스템에 쓰기 행위가 발생할 때 disk quota를 확인하는 과정을 밟습니다. 이러한 과정은 quotaon/off와는 관계없이 실행되는데, quota on이 되어 있으면 많은 것을 실행하는 것이고, quotaoff인 경우에는 빠르게 리턴하는 것이죠.

다음은 ufs_write시 일어나는 ufs 모듈내의 흐름입니다. 더 하부 모듈인 genunix, unix와 관련된 다른 모듈은 캡쳐하지 않았습니다. dtrace로 캡쳐했으며 캡쳐한 방법은 다음과 같습니다.

#dtrace -F -n 'io:::start/uid==31523 && args[1]->dev_statname =="cmdk0"/{trace(timestamp);self->trace=1;}' \
-n 'fbt:ufs:ufs_write:entry/self->trace/{trace(timestamp);self->ufs=1;}' \
-n 'fbt:ufs::/self->trace&&self->ufs/{trace(timestamp);}' \
-n 'fbt:ufs:ufs_write:return/self->trace&&self->ufs/ {trace(timestamp);self->trace=0;self->ufs=0;exit(1);}'



결과는 아래 그림과 같다.

그림에서 보듯이, ufs_write가 발생할때 chkdq라는 것을 지나가는데, chkdq()는 아래와 같은 내용을 가진다.
ufs quota는 inode quota와 data quota를 동시에 가지는데, 아래는 ufs_write()를 통해서 data block의 write가 일어난 경우를 잡은 경우이기 때문에 data quota만 체크한다.

그런데, 중요한것은 chkdq()에서 무엇을 하느냐 인데, 아래 코드에서 볼 수 있는 바와 같이,

<br /><br /><span style="font-family: courier new;font-size:85%;" >124 int<br />125 chkdq(struct inode *ip, long change, int force, struct cred *cr,<br />126         char **uerrp, size_t *lenp)<br />127 {<br />128         struct dquot *dqp;<br />129         uint64_t ncurblocks;<br />130         struct ufsvfs *ufsvfsp = ip->i_ufsvfs;<br />131         int error = 0;<br />132         long abs_change;<br />133         char *msg1 =<br />134 "!quota_ufs: over hard disk limit (pid %d, uid %d, inum %d, fs %s)\n";<br />135         char *msg2 =<br />136 "!quota_ufs: Warning: over disk limit (pid %d, uid %d, inum %d, fs %s)\n";<br />137         char *msg3 =<br />138 "!quota_ufs: over disk and time limit (pid %d, uid %d, inum %d, fs %s)\n";<br />139         char *msg4 =<br />140 "!quota_ufs: Warning: quota overflow (pid %d, uid %d, inum %d, fs %s)\n";<br />141         char *errmsg = NULL;<br />142         time_t now;<br />143<br />144         /*<br />145          * Shadow inodes do not need to hold the vfs_dqrwlock lock.<br />146          */<br />147         ASSERT((ip->i_mode & IFMT) == IFSHAD ||<br />148             RW_LOCK_HELD(&ufsvfsp->vfs_dqrwlock));<br />149         ASSERT(RW_WRITE_HELD(&ip->i_contents));<br />150<br />151         if (change == 0)<br />152                 return (0);<br />153         dqp = ip->i_dquot;<br />154<br />155         /*<br />156          * Make sure the quota info record matches the owner.<br />157          */<br />158         ASSERT(dqp == NULL || ip->i_uid == dqp->dq_uid);<br />159<br />160 #ifdef DEBUG<br />161         /*<br />162          * Shadow inodes and extended attribute directories<br />163          * should not have quota info records.<br />164          */<br />165         if ((ip->i_mode & IFMT) == IFSHAD || (ip->i_mode & IFMT) == IFATTRDIR) {<br />166                 ASSERT(dqp == NULL);<br />167         }<br />168         /*<br />169          * Paranoia for verifying that quotas are okay.<br />170          */<br />171         else {<br />172                 struct dquot *expect_dq;<br />173                 int mismatch_ok = 0;<br />174<br />175                 /* Get current quota information */<br />176                 expect_dq = getinoquota(ip);<br />177                 /*<br />178                  * We got NULL back from getinoquota(), but there is<br />179                  * no error code return from that interface and some<br />180                  * errors are "ok" because we may be testing via error<br />181                  * injection.  If this is not the quota inode then we<br />182                  * use getdiskquota() to see if there is an error and<br />183                  * if the error is ok.<br />184                  */<br />185                 if (expect_dq == NULL && ip != ufsvfsp->vfs_qinod) {<br />186                         int error;<br />187                         struct dquot *xdqp;<br />188<br />189                         error = getdiskquota((uid_t)ip->i_uid, ufsvfsp, 0,<br />190                             &xdqp);<br />191                         switch (error) {<br />192                         /*<br />193                          * Either the error was transient or the quota<br />194                          * info record has no limits which gets optimized<br />195                          * out by getinoquota().<br />196                          */<br />197                         case 0:<br />198                                 if (xdqp->dq_fhardlimit == 0 &&<br />199                                     xdqp->dq_fsoftlimit == 0 &&<br />200                                     xdqp->dq_bhardlimit == 0 &&<br />201                                     xdqp->dq_bsoftlimit == 0) {<br />202                                         mutex_enter(&xdqp->dq_lock);<br />203                                         dqput(xdqp);<br />204                                         mutex_exit(&xdqp->dq_lock);<br />205                                 } else {<br />206                                         expect_dq = xdqp;<br />207                                 }<br />208                                 break;<br />209<br />210                         case ESRCH:     /* quotas are not enabled */<br />211                         case EINVAL:    /* error flag set on cached record */<br />212                         case EUSERS:    /* quota table is full */<br />213                         case EIO:       /* I/O error */<br />214                                 mismatch_ok = 1;<br />215                                 break;<br />216                         }<br />217                 }<br />218<br />219                 /*<br />220                  * Make sure dqp and the current quota info agree.<br />221                  * The first part of the #ifndef is the quick way to<br />222                  * do the check and should be part of the standard<br />223                  * DEBUG code. The #else part is useful if you are<br />224                  * actually chasing an inconsistency and don't want<br />225                  * to have to look at stack frames to figure which<br />226                  * variable has what value.<br />227                  */<br />228 #ifndef CHASE_QUOTA<br />229                 ASSERT(mismatch_ok || dqp == expect_dq);<br />230 #else /* CHASE_QUOTA */<br />231                 if (expect_dq == NULL) {<br />232                         /*<br />233                          * If you hit this ASSERT() you know that quota<br />234                          * subsystem does not expect quota info for this<br />235                          * inode, but the inode has it.<br />236                          */<br />237                         ASSERT(mismatch_ok || dqp == NULL);<br />238                 } else {<br />239                         /*<br />240                          * If you hit this ASSERT() you know that quota<br />241                          * subsystem expects quota info for this inode,<br />242                          * but the inode does not have it.<br />243                          */<br />244                         ASSERT(dqp);<br />245                         /*<br />246                          * If you hit this ASSERT() you know that quota<br />247                          * subsystem expects quota info for this inode<br />248                          * and the inode has quota info, but the two<br />249                          * quota info pointers are not the same.<br />250                          */<br />251                         ASSERT(dqp == expect_dq);<br />252                 }<br />253 #endif /* !CHASE_QUOTA */<br />254                 /*<br />255                  * Release for getinoquota() above or getdiskquota()<br />256                  * call when error is transient.<br />257                  */<br />258                 if (expect_dq) {<br />259                         mutex_enter(&expect_dq->dq_lock);<br />260                         dqput(expect_dq);<br />261                         mutex_exit(&expect_dq->dq_lock);<br />262                 }<br />263         }<br />264 #endif /* DEBUG */<br />265<br />266         /*<br />267          * Shadow inodes and extended attribute directories<br />268          * do not have quota info records.<br />269          */<br />270         if (dqp == NULL)<br />271                 return (0);<br />272         /*<br />273          * Quotas are not enabled on this file system so there is nothing<br />274          * more to do.<br />275          */<br />276         if ((ufsvfsp->vfs_qflags & MQ_ENABLED) == 0) {<br />277                 return (0);<br />278         }<br />279         mutex_enter(&dqp->dq_lock);<br />280         if (change <>dq_flags |= DQ_MOD;<br />282                 abs_change = -change;   /* abs_change must be positive */<br />283                 if (dqp->dq_curblocks <>dq_curblocks = 0;<br />285                 else<br />286                         dqp->dq_curblocks += change;<br />287                 if (dqp->dq_curblocks <>dq_bsoftlimit)<br />288                         dqp->dq_btimelimit = 0;<br />289                 dqp->dq_flags &= ~DQ_BLKS;<br />290                 TRANS_QUOTA(dqp);<br />291                 mutex_exit(&dqp->dq_lock);<br />292                 return (0);<br />293         }<br />294<br />295         /*<br />296          * Adding 'change' to dq_curblocks could cause an overflow.<br />297          * So store the result in a 64-bit variable and check for<br />298          * overflow below.<br />299          */<br />300         ncurblocks = (uint64_t)dqp->dq_curblocks + change;<br />301<br />302         /*<br />303          * Allocation. Check hard and soft limits.<br />304          * Skip checks for uid 0 owned files.<br />305          * This check used to require both euid and ip->i_uid<br />306          * to be 0; but there are no quotas for uid 0 so<br />307          * it really doesn't matter who is writing to the<br />308          * root owned file.  And even root cannot write<br />309          * past a user's quota limit.<br />310          */<br />311         if (ip->i_uid == 0)<br />312                 goto out;<br />313<br />314         /*<br />315          * Disallow allocation if it would bring the current usage over<br />316          * the hard limit or if the user is over his soft limit and his time<br />317          * has run out.<br />318          */<br />319         if (dqp->dq_bhardlimit && ncurblocks >= (uint64_t)dqp->dq_bhardlimit &&<br />320             !force) {<br />321                 /* If the user was not informed yet and the caller      */<br />322                 /* is the owner of the file                             */<br />323                 if ((dqp->dq_flags & DQ_BLKS) == 0 &&<br />324                     ip->i_uid == crgetruid(cr)) {<br />325                         errmsg = msg1;<br />326                         dqp->dq_flags |= DQ_BLKS;<br />327                 }<br />328                 error = EDQUOT;<br />329                 goto out;<br />330         }<br />331         if (dqp->dq_bsoftlimit && ncurblocks >= (uint64_t)dqp->dq_bsoftlimit) {<br />332                 now = gethrestime_sec();<br />333                 if (dqp->dq_curblocks <>dq_bsoftlimit ||<br />334                     dqp->dq_btimelimit == 0) {<br />335                         dqp->dq_flags |= DQ_MOD;<br />336                         dqp->dq_btimelimit = now +<br />337                             ((struct ufsvfs *)ITOV(ip)->v_vfsp->vfs_data)<br />338                             ->vfs_btimelimit;<br />339                         if (ip->i_uid == crgetruid(cr)) {<br />340                                 errmsg = msg2;<br />341                         }<br />342                 } else if (now > dqp->dq_btimelimit && !force) {<br />343                         /* If the user was not informed yet and the     */<br />344                         /* caller is the owner of the file              */<br />345                         if ((dqp->dq_flags & DQ_BLKS) == 0 &&<br />346                             ip->i_uid == crgetruid(cr)) {<br />347                                 errmsg = msg3;<br />348                                 dqp->dq_flags |= DQ_BLKS;<br />349                         }<br />350                         error = EDQUOT;<br />351                 }<br />352         }<br />353 out:<br />354         if (error == 0) {<br />355                 dqp->dq_flags |= DQ_MOD;<br />356                 /*<br />357                  * ncurblocks can be bigger than the maximum<br />358                  * number that can be represented in 32-bits.<br />359                  * When copying ncurblocks to dq_curblocks<br />360                  * (an unsigned 32-bit quantity), make sure there<br />361                  * is no overflow.  The only way this can happen<br />362                  * is if "force" is set.  Otherwise, this allocation<br />363                  * would have exceeded the hard limit check above<br />364                  * (since the hard limit is a 32-bit quantity).<br />365                  */<br />366                 if (ncurblocks > 0xffffffffLL) {<br />367                         dqp->dq_curblocks = 0xffffffff;<br />368                         errmsg = msg4;<br />369                 } else {<br />370                         dqp->dq_curblocks = ncurblocks;<br />371                 }<br />372         }<br />373<br />374         if (dqp->dq_flags & DQ_MOD)<br />375                 TRANS_QUOTA(dqp);<br />376<br />377         mutex_exit(&dqp->dq_lock);<br />378         /*<br />379          * Check for any error messages to be sent<br />380          */<br />381         if (errmsg != NULL) {<br />382                 /*<br />383                  * Send message to the error log.<br />384                  */<br />385                 if (uerrp != NULL) {<br />386                         /*<br />387                          * Set up message caller should send to user;<br />388                          * gets copied to the message buffer as a side-<br />389                          * effect of the caller's uprintf().<br />390                          */<br />391                         *lenp = strlen(errmsg) + 20 + 20 +<br />392                             strlen(ip->i_fs->fs_fsmnt) + 1;<br />393                         *uerrp = (char *)kmem_alloc(*lenp, KM_NOSLEEP);<br />394                         if (*uerrp != NULL) {<br />395                                 /* errmsg+1 => skip leading ! */<br />396                                 (void) sprintf(*uerrp, errmsg+1,<br />397                                     (int)ttoproc(curthread)->p_pid,<br />398                                     (int)ip->i_uid, (int)ip->i_number,<br />399                                     ip->i_fs->fs_fsmnt);<br />400                         }<br />401                 } else {<br />402                         /*<br />403                          * Caller doesn't care, so just copy to the<br />404                          * message buffer.<br />405                          */<br />406                         cmn_err(CE_NOTE, errmsg,<br />407                             (int)ttoproc(curthread)->p_pid,<br />408                             (int)ip->i_uid, (int)ip->i_number,<br />409                             ip->i_fs->fs_fsmnt);<br />410                 }<br />411         }<br />412         return (error);<br />413 }<br /></span></pre><span style="font-family: courier new;font-size:100%;" >흥미로운 것은 솔라리스 10에 새로이 생긴 zfs에서는 어떻게 quota를 사용할까 ?<br />zfs는 metadata에 대한 quota를 사용하지 않는다, 기본적으로 화일의 수를 할당된 공간과 별도로 체크하는 것이 의미가 없기 때문이다. 따라서, 또한, zfs는 dataset layer에서 블럭을 할당할때마다 used byte가 할당이 되며, 모든 블럭은 객체지향적으로 상속되므로, uber block은 하위 블럭의 총합에 대한 것을 늘 유지하게 되므로 quota check하는 것이 매우 단순하다.<br /><br /></span><pre><span style="font-family: courier new;font-size:85%;" ><span style="font-size:100%;">아래가 dataset에서 quota를 check하는 모듈이다. 매우 단순함을 알 수 있다.<br />코드의 길이만 봐도 ufs의 경우에는 300라인에 해당하지만, zfs의 경우에는<br />40여라인밖에 되지 않는다. 아래의 모듈에서는 위 ufs용 모듈인 chkdq()와는 달리<br />사용자 id를 기준으로 비교하는 라인도 없다.<br /><br />코드의 길이가 중요한 이유는 data를 쓰기하는 경우에, 솔라리스는 mutex_lock()을 설정하는데<br />코드의 길이가 길게 되며, lock이 잡히는 시간이 증가하게 되므로, 병목의 발생 가능성이 증가하게<br />된다.<br /><br />따라서, zfs는 quota() 기능 사용에 따른 오버헤드가 매우 작을 수 있음을 예측할 수 있다.<br />반면, 이런 오해가 있을 수도 있다. ufs는 단일 화일 시스템을 여러 사용자를 기준으로 사용 공간을<br />나누어 줄 수 있는데, zfs는 불가능한 것이 아닌가?<br /><br />그러나, 이런 오해는 ufs와 zfs의 근본적인 차이를 이해하지 못해서 발생하는 것이다. zfs는 모든 것이<br />pool에 기반하기 때문에 사용자별로 별개의 zfs를 만들어서 할당할 수 있는 볼륨기능이 통합되어 있다.<br />따라서, 단일 화일시스템을 사용자별로 쪼개고 붙이고할 필요가 없다. 그냥 필요한 공간만큼 할당만<br />하면 된다.  혹여나 모 사용자에게 너무 많이 할당했다면, 반납하고 필요한 사용자에게 할당하면 된다.<br /><br />반면, ufs는 볼륨 매니저에서 사용자별로 쪼개놓지 않으면 하나의 단일 화일 시스템에서 여러 사용자용<br />쿼타를 설정해야하므로 매우 불편할 뿐 아니라, 다른 화일 시스템의 사용자에게 남는 공간을 떼어줄 수도<br />없다.<br /><br />어떤 화일 시스템을 사용해야 하는가? 관리의 포인트를 중히 여긴다면 선택의 여지가 없다. 두말할 필요없이<br />zfs라고 할 수 있겠다. zfs 기반으로 서비스를 제공하는 순간 많은 사람들이 편하게 된다는 점을 염두에<br />두어야 한다. 서비스 업체들은 자사 엔지니어가 스토리지 관리때문에 고객들에게 뺏기는 시간이 혁신적으로<br />짧아진다는 것을 알아야한다. 왜냐하면, 시간은 곧 돈이기 때문이다.</span><br /><br /><br /><br />2736 int<br />2737 dsl_dataset_check_quota(dsl_dataset_t *ds, boolean_t check_quota,<br />2738     uint64_t asize, uint64_t inflight, uint64_t *used, uint64_t *ref_rsrv)<br />2739 {<br />2740         int error = 0;<br />2741<br />2742         ASSERT3S(asize, >, 0);<br />2743<br />2744         /*<br />2745          * *ref_rsrv is the portion of asize that will come from any<br />2746          * unconsumed refreservation space.<br />2747          */<br />2748         *ref_rsrv = 0;<br />2749<br />2750         mutex_enter(&ds->ds_lock);<br />2751         /*<br />2752          * Make a space adjustment for reserved bytes.<br />2753          */<br />2754         if (ds->ds_reserved > ds->ds_phys->ds_unique_bytes) {<br />2755                 ASSERT3U(*used, >=,<br />2756                     ds->ds_reserved - ds->ds_phys->ds_unique_bytes);<br />2757                 *used -= (ds->ds_reserved - ds->ds_phys->ds_unique_bytes);<br />2758                 *ref_rsrv =<br />2759                     asize - MIN(asize, parent_delta(ds, asize + inflight));<br />2760         }<br />2761<br />2762         if (!check_quota || ds->ds_quota == 0) {<br />2763                 mutex_exit(&ds->ds_lock);<br />2764                 return (0);<br />2765         }<br />2766         /*<br />2767          * If they are requesting more space, and our current estimate<br />2768          * is over quota, they get to try again unless the actual<br />2769          * on-disk is over quota and there are no pending changes (which<br />2770          * may free up space for us).<br />2771          */<br />2772         if (ds->ds_phys->ds_used_bytes + inflight >= ds->ds_quota) {<br />2773                 if (inflight > 0 || ds->ds_phys->ds_used_bytes <>ds_quota)<br />2774                         error = ERESTART;<br />2775                 else<br />2776                         error = EDQUOT;<br />2777         }<br />2778         mutex_exit(&ds->ds_lock);<br />2779<br />2780         return (error);<br />2781 }</span><br />

2008/07/11

Unix history


Unix의 정신을 이어받은 제품들의 히스토리 그림입니다. 소스는 어디인지 모르겠네요.

2008/07/08

추격자 : %sys의 범인은 누구일까 ?

솔라리스 뿐만 아니라, 유닉스 시스템을 이용하다 보면 간혹 사용자의 cpu% 사용율 보다, sys%의 사용율이 높은 경우가 있습니다. 이런 경우에 도대체 왜 sys% 높은지 답답할 때도 있습니다. 심한 경우에는 sys%가 높음에도 내 애플리케이션의 응답 시간이나 총처리량이 나쁜 경우에는 더욱 답답하죠.

운영체제 입장에서 sys%는 커널에서 동작되는 시간의 백분율입니다. 커널은 사용자 애플리케이션에서 호출하는 시스템 콜을 서비스하기 위해서 각종 서비스 모듈들과 관련 장치 드라이버, 내부 캐쉬, 커널 코어등의 코드(함수)들의 모음인데 s사용자가 단순히 open()을 했다고 해도, 커널 입장에서는 specfs, 관련 화일 시스템(ufs, zfs ... ), 각종 디바이스 드라이버등이 실행되어 집니다. 따라서, 어떤 계층에서 문제가 발생하는 지 알기가 쉽지 않았다고 할 수 있습니다. 커널내의 함수들이 유기적으로 작동되니까요.

그렇지만, 솔라리스 10이 생기면서 이 부분을 거의 정확하게 집어낼 수 있게되었습니다.

일단, 평상시에 어떤 커널 모듈이 많이 호출되는 지만 알면, 관련 패치가 있는 지를 쉽게 찾아볼 수 있습니다. 다음은 가장 바쁜 커널 모듈을 찾는 dtrace 스트립트입니다. 바쁘다는 관점은 두가지가 있습니다. 호출 횟수가 많은 것과 호출 당 처리 시간이 긴 것 두가지가 있을 수 있습니다. 이 두가지의 데이타를 dtrace는 한번에 관찰할 수 있습니다.

다음은 이것을 위한 dtrace script입니다. 커널 모듈에 대한 모든 콜을 트랩해서 호출 횟수와 호출당 소요 시간을 5초간 저장했다가 보여주는 역할을 합니다.

#dtrace -q -n 'fbt:::entry{self->trace=timestamp;}' -n 'fbt:::return/self->trace/{@c[probemod]=count();@e[probemod]=sum(timestamp-self->trace);}' -n 'tick-5s{printa(@c,@e);clear(@c);clear(@e);trunc(@c);trunc(@e);}'

...
mm 448 1677093
net80211 473 1608838
sha1 480 902546
fifofs 1315 7142051
specfs 1344 8330793
ip 1359 10275445
ufs 1439 6542160
usba 1542 5783685
skge 2335 12904313
pciehpc 2748 6571366
uhci 3221 14217584
nvidia 3260 61999111
TS 3347 12659840
ehci 3650 11595263
pcie_pci 5753 46414892
npe 13172 89669520
pcplusmp 110784 269213239
genunix 331691 1106575148
unix 500814 82226651922614
acpica 1929748 42631


제 랩탑에서 돌려본 예입니다. acpi module이 가장 많이 호출되죠. 반면에 총 처리 시간이 가장 긴 모듈은 unix 모듈입니다. 솔라리스 코어니 당연한 것이겠죠. sys%는 일정 시간동안 cpu를 점유한 커널 모듈의 시간이므로 어떤 모듈을 볼 것인지 상위 3~4개 정도 보면 직감이 나타나게 됩니다. 문제가 있는 시스템의 경우에는 요. 우측 두개의 숫자를 스프레드 시트를 이용해서 계산을 해보면, 호출당 처리 시간도 나오게 됩니다.

5초가 데이타가 나오므로, 연속적으로 저장하게 되면, 챠트도 형성할 수 있게 됩니다. 오픈솔라리스.org에 있는 chime이라는 툴을 사용하면, 챠트도 볼 수 있습니다.

위의 경우는 커널 모듈별만 계산했는데, 특정 모듈이 의심이 된다고 여기게 되면, 모듈내 어떤 함수에 문제가 있었는 지도 알 수 있습니다. 위에서 acpi 모듈이 의심이 간다면, apci 의 함수를 볼 수 있습니다.

#dtrace -q -n 'fbt:acpica::entry{self->trace=timestamp;}' -n 'fbt:acpica::return/self->trace/{@c[probefunc]=count();@e[probefunc]=sum(timestamp-self->trace)}' -n 'tick-5s{printa(@c,@e);clear(@c);clear(@e);trunc(@c);trunc(@e);}'

...
AcpiUtStatusExit 41842 174456493
AcpiFormatException 42079 189609876
AcpiUtGetMutexName 54557 112240926
AcpiNsGetNextValidNode 93576 205688515
AcpiNsGetNextNode 196392 872796601
AcpiUtDebugPrint 259500 507489364
AcpiUtTrackStackPtr 364434 750592053


acpica 모듈에서는 AcpiUtTrackStackPtr()가 호출당 처리 시간이 긴 것 같군요.

이런 방법으로 의심이 되는 모듈을 추적해볼 수 있습니다.

한편으로는 애플리케이션 관점에서 어떤 시스템콜 - 즉, 커널에게 어떤 서비스를 요청하는 지를 조사해볼 필요가 있습니다.
사용자 pid가 1234라고 했을때,
예전에 이런 목적으로 truss 를 사용했었습니다.
#truss -c -p 1234

dtrace는 이것보다 훨씬 더 화려한 출력을 제공합니다.

#dtrace -n 'syscall:::entry/pid==1234/{@[probemod,probefunc]=count()}'
를 사용하게 되면 pid 1234가 실행할때 발생하는 시스템 콜을 모듈별, 함수별로 보관했다 호출 횟수를 보여줌으로 어떤 서비스를 많이 쓰게 될 지 추측할 수 있게됩니다.

혹은

#dtrace -n 'syscall:::entry/pid==123/{self->sys=1}' \
-n 'fbt:::entry/self->sys/{@[probemod]=count();self->trace=timestamp;}'

와 같이 하게되면, pid 1234가 시스템 콜을 요청할 때, 작동되는 커널의 모듈들의 호출 회수를 저장했다가 보이게 할 수도 있습니다. 즉, 해당 애플리케이션에 의한 sys% 사용을 정확하게 잡아낼 수도 있습니다. 이 앞에서 썼던 timestamp 방식을 도입하면, 호출당 소요 시간도 추정할 수 있겠죠.


2008/07/02

솔라리스 10 서버 성능 모니터링에 관한 방안 제안



솔라리스10에서 시스템에서 발생할 성능 관련 문제를 감지하기 위한 자동화된 모니터링 환경 구축을 위해서는 다음과 같은 구조를 권고합니다.



성능 관련해서 저장해야할 정보는

1) 성능에 영향을 주는 장애가 발생하나 를 일단 봐야 합니다.

2) 컴포넌트 장애가 없다는 가정하에서 어떤 형태의 부하가 발생하고 어느 컴포넌트에 부하가 많이 몰리는 지를 축적하는 것이 중요합니다.



1)은 fmstat과 prtdiag, dmesg 를 주기적으로 보관하는 것이 중요합니다. 특히 fmstat는
cpu/memory/io component의 장애를 실시간 보고해주는 것이므로, 관리하는 것이 바람직합니다. 모니터링시에는
syslogd가 만들어주는 메세지 /var/adm/messages와 함께 일치된 시간상으로 관리하는 것이 바람직합니다.




$pfexec fmstat 1
module ev_recv ev_acpt wait svc_t %w %b open solve memsz bufsz

cpumem-retire 0 0
0.0 0.2 0 0 0 0 0 0
disk-transport 0 0 0.0 348.9 0 0 0 0 32b 0

eft 0 0 0.0 2.0 0 0 0 0 1.4M 0

fmd-self-diagnosis 1 0 0.0 0.1 0 0 0 0 0 0

io-retire 0 0 0.0 0.1 0 0 0 0 0 0

snmp-trapgen 0 0 0.0 0.1 0 0 0 0 32b 0

sysevent-transport 0 0 0.0 434.5 0 0 0 0 0 0

syslog-msgs 0 0 0.0 0.1 0 0 0 0 0 0

zfs-diagnosis 2 2 0.0 7.4 0 0 0 0 0 0

zfs-retire 0 0 0.0 0.1 0 0 0 0 0 0



2) 장애난 컴포넌트가 없는 경우에 서비스에 의해서 발생하는 부하에 의하여 서비스 응답이 장애성으로 판단되는 경우가 있는데
이러한 경우를 향후 추척하기 위해서는 3가지의 데이타를 축적하는 것이 바람직합니다. 시스템에서는 서비스를 위해서 사용되는 것은

CPU, memory, io(block io), net(streaming io)로 분류될 수 있겠습니다.

CPU와 메모리 추적을 위해서는 vmstat를 축적하시는 것이 바람직하며, 네트웍의 대역폭 조사를 위해서는 첨부되어 있는 netsum과 같은 유틸리의 값을 저장해놓는 것이 좋습니다.

동시에 io(블럭IO)를 위해서 예전에는 sar를 제안했습니다만, 솔라리스10에서는 보다 더 세련된 dtrace를 이용하여 실감나는 추적을 할 수 있습니다.


링크된 netsum은 다음과 같은 출력을 지원합니다. (플랫폼별로 실행화일이 다릅니다.)


$netsum -A -I wpi0 -i 5

Name inc/s out/s ipkts/s opkts/s ierr oerr Bi/pkt Bo/pkt Time

wpi0 20.4 B 16.0 B 0.2 0.2 0 0 102 80 14:24:18

wpi0 953.5 B 36.3 B 2.6 0.4 0 0 367 91 14:24:23

wpi0 0.0 B 0.0 B 0.0 0.0 0 0 0 0 14:24:28

이런 기록으로 네트웍 인터페이스에 대한 기록을 유지할 수가 있습니다.

그외 디스크 사용의 패턴에 관한 데이타를 저장해두고, 가장 빈번한 사용이 되는 disk가 어딘지를평소에 기록해놓는 것이 좋습니다. 솔라리스10 이전에는 이러한 기록이 매우 힘들었으나, 솔10에서는 dtrace 스크립트를 통해서 아주 훌륭한 데이타를 볼 수 가 있습니다.


다음은 hotspot.d라는 DTraceToolkit 0.99에 포함된 스크립트를 약간 수정한 스크립트입니다.
실행은 #dtrace -s hotspot.d 처럼 실행하며, 스크립트화해서 cron에 등록해서 백그라운드로
실행하시면 됩니다.



실행결과는 대략 다름과 같습니다. 이러한 데이타를 통하여 어떤 디스크가 어떻게 사용되고 있는 지를 파악할 수 있습니다.


혹은 io 패턴을 축적해놓는 것도 도움이 될 수 있는데, 역시 DTraceToolkit에 들어있는 iopattern이라는 스크립트를 이용하여 관련 정보를 기록해놓으면 크게 도움이 될 수 있습니다. (아래 링크 참조)



iopattern 이라는 스크립트는 다음과 같은 내용이 나오게 됩니다. 디스크의 패턴에 따라서, 향후 서비스 둔화 지점 파악과 스토리지 아키텍쳐를 어떤 방향으로 가져가야 할 지에 대한 기초 데이타를
제공하게 됩니다.









그외, DtraceToolkit에 내장된 여러 스크립트를 이용하게 되면, 매우 정확하고 정밀한 서버 관련 데이타를 축적할 수 있습니다.

DtraceToolkit : http://opensolaris.org/os/community/dtrace/dtracetoolkit/


솔라리스에서 사용자의 명령어 활동을 기록할 수 있을까 ? (CLI logging)

사용자 활동에 관한 로그는 기본적으로는 강제로 규정하게 하려면, 쉘 레이어에서 강제화 해야 하는데 현재 솔라리스에 들어있는 쉘은 강제적으로 활성화하는 방안이 없습니다.



현재 사용자의 터미널 활동을 로깅할 수 있는 방안으로는

1) 사용자의 쉘의 .profile이나 .login에서 로그인 시점에 script들이 자동으로 실행되어 자동으로 스크립트가 저장되도록 하는 방법. 사용자의 쉘 프로화일 파일 안에 다음과 같이 추가합니다.

...

exec script -a `date '+%m%d%y:%H%M'`.log




=> 사용자가 로그인하면 자동으로 저장되고, ctrl-d 혹은 logout하면 스크립트 내용이 저장됩니다.

매우 소극적 방법이고, 실제 사용자가 로그아웃할때까지 내용이 화일에 저장되지 않습니다. 또한, 사용자가
.profile을 변경해버리면 소용이 무용지물이 될 수 있습니다. 또한 쉘에 따라서 이런 구성이 지원 될 수도 있고, 안될 수도
있습니다.



2) dtrace를 이용하여 실시간 모든 쉘의 행위를 감시할 수 있습니다.

=> 매우 적극적인 방법입니다. 모든 쉘을 죄다 감시하게 되므로, 불필요한 데이타도 컬렉션 될 수 있습니다.
DtraceToolkit에 포함된 shellsnoop 이라는 dtrace 스크립트를 이용하면, 현재 실행되고 있는 대부분의
쉘(sh,ksh,bash,csh,tcsh,zsh)의 입출력을 실시간으로 감시가 가능합니다. 물론, 사용자는 알 수 없습니다.
커널단에서 syscall을 hooking하여 쉘의 read,write를 캡쳐해서 출력해주는 유틸리티입니다. 매우 강력합니다.
첨부 화일 참고



3) 쉘 자체가 log 기능을 가지고 있는 쉘을 사용자 쉘로 지정하는 방법이 있습니다.

솔라리스가 제공하는 기본 쉘들은 이런 기능을 가지고 있지 않으며, 오픈 소스 중에서 사용하여야 합니다.

대표적으로 수도쉘등이 있습니다.

다음 링크를 참고하십시요. http://www.egbok.com/sudoscript/sudoshell.1.html

2008/06/30

버추얼박스에서 게스트와 호스트간의 공유 방식

버추얼 박스 상에서는 다양한 호스트/게스트가 올라올 수 있기 때문에, 각 조합에 따른 호스트/게스트 간의 폴더 공유 방식이 달라질 수 있습니다. 다음은 몇가지 경우에 대해서 구성가능한 매트릭스를 만들어봤습니다.

다음의 테이블에서 첫번째 컬럼에는 호스트 운영체제의 목록이며, 각 컬럼은 호스트에 따라 게스트별 사용이 권고되는 공유 화일 시스템입니다.

아래 테이블의 경우, 호스트와 게스트가 동시에 솔라리스와 리눅스 중 어느 한쪽인 경우에는 호스트에서 NFS를 구성하고 게스트에서 NFS를 공유하는 방식이 가장 추천되는 방법입니다. 물론, 솔라리스의 경우 내장형 CIFS나 혹은 Samba를 이용해서 CIFS를 서비스할 수 있으며, 리눅스 역시 Samba를 이용하여 CIFS를 서비스할 수 있으므로, 이러한 방법으로도 공유를 할 수 있습니다.

H \ G
Solaris Linux Windows
Solaris NFS NFS(CIFS) CIFS
Linux NFS(CIFS) NFS CIFS
Windows CIFS CIFS CIFS

기술적으로 버츄얼 박스는 게스트가 윈도우즈 계열인 경우 내부적으로 VboxSharedFolderFS이라는 가상의 화일시스템 서비스를 제공하여 호스트가 공유한 폴더를 마치 네트웍 공유 폴더인 것처럼 노출하여 윈도우즈 게스트가 호스트의 공유폴더를 cifs로 마운트할 수 있도록 제공해줍니다. 하지만, 게스트가 비 윈도우즈 계열인 경우에는 이러한 기능을 쓰지 않고 호스트나 게스트가 이미 내장하고 있는 공유 화일 시스템인 NFS/CIFS(samba)를 이용하여 네트웍 공유를 사용하면 됩니다. vbox에서 NAT 네트웍 구성을 가지도록 게스트OS를 설치하면 게스트운영체제는 default gateway로 10.0.2.2를 가지게 되는데, 이 IP가 곧 호스트 서버이기도 합니다.

따라서, 호스트서버에서 export된 nfs나 cifs(samba)는 이 IP를 이용하여 vbox 내부 게스트들에게 마운트(Map Network drive...)되어 질 수 있게 됩니다.

각 운영체제별 공유 디렉토리 생성 및 제공 방안은 구글링해보시기 바랍니다.

참고로 솔라리스는 /etc/dfs/dfstab에 예제 라인을 복사한후 원하는 디렉토리로 변경하고, 커멘트를 없애고 저장한 후에 nfs 서비스를 시작 혹은 재시작 하도록 하면 됩니다.

#svcadm enable -r nfs/server
-----> nfs/server와 연관된 다른 서비스도 모두 시작됩니다. 이미 서비스가 시작되어 있는 경우 restart를 하시면 됩니다.

공유하고자 하는 디렉토리가 공유 서비스 중인지 확인하기 위해서는 다음과 같이 실행해봅니다.
#share

명령어 결과로 원하는 내용이 나오면, 버추얼 박스 게스트에서 마운트할 수 있는 지 확인할 수 있습니다. 게스트가 솔라리스인 경우에는

#dfshares 10.0.2.2
와 같이 공유가능한 지 확인할 수 있으며, nfs automount가 활성화되어 있는 경우에는

#cd /net/10.0.2.2
처럼 그냥 일반 디렉토리로 접근하면 공유된 화일 시스템을 보실 수 있게 됩니다. 노틸러스에 북마크해놓으면 늘 자동으로 접근할 수 있게 됩니다.

2008/06/25

웹2.0 시대로 진입하는 문 : zembly.com

zembly.com이라는 사이트를 가보게 되었습니다.

간단하게 얘기하면 lightweight code들이 잔뜩 모여진 wiki로 보여집니다.

백과사전이 아니라 백과 웹 애플리케이션 사이트를 만들겠다는 것으로 보입니다.



iGoogle에서 필요한 컨텐츠 모듈을 작성, 선택해서 제 화면을 만들듯이,



내가 사용하는 웹서비스(현재는 주로 소셜 서비스와 iPhone정도가 있네요)용 플러그인용
코드(widget)를 여러 사람이 참여해서 만들게 하고, 내가 사용하는 웹서비스에 장착해서 사용할 수
있도록 하는 것입니다. iGoogle과 다른 점은 iGoogle code는 iGoogle에서만 사용가능한 코드를
개발하는 것인데, zembly.com은 facebook, meebo, iphone등 나름 미국에서 상위의 서비스를
타겟하고 있다는 점입니다.


예전 코드 개발 사이트의 관점에서 보면 sourceforge.net + wikipedia + web2.0 services 라고

볼 수도 있겠습니다.


비즈니스 관점에서 바라보면, 웹서비스 업자들은 모든 기능을 자신이 개발하지

않아도 되고, zembly안에서 선점을 하게 되면 서비스 우위를 점할 수 있는 점이 있습니다.

따라서, 웹서비스 업자들에게는 매우 높은 관심을 받을 것으로 예상됩니다. 상황에 따라서,

웹서비스 업자들은 선점효과를 위해서 간이개발자들의 코드를 돈주고 살수도 있을 것으로

보입니다. 혹은 서비스 최종 이용자들에게 아이템을 팔듯이 이 간이 모듈을 대행 판매해주고

중개 수수료를 받을 수도 있습니다.



간이 모듈 개발자들 입장에서 보면 웹서비스를 자기의 마음대로 개인화(customization)를 할 수 있을 뿐 아니라,

웹서비스 사용자들에게 판매할 수도 있습니다. 물론, 서비스 업자들이 중개를 할 수도 있고, 직접

팔수도 있으리라 봅니다.



최종 사용자의 입장에서 보면 엄청나게 풍부한 기능들이 속출하게 되므로, 개인화 기능을 넘어서게

되면, 그룹 혹은 엔터프라이즈 기능으로 사용할 수 있는 수준으로 성장할 수 있는 잠재성을

볼 수 있게 됩니다. 그렇다면, 필요한 기능만을 위해서 비용을 지불할 수 있는 웹서비스를

기대해볼 수 있겠습니다.



이 프로젝트가 잘 진행되면 엄청난 후폭풍이 있을 수 있습니다. 진정한 mash-up이 가능한 웹서비스

세계가 도래할 수 있구요. 썬에서 예측했던 redshift 세상이 좀 더 빨리 구현될 수도 있겠습니다.

(SOA가 구현된 세상일 수도 있구요) 결론적으로 아이디어는 아주 훌륭한 것 같습니다.



기술적으로는 이런 서비스(?)가 가능하려면, 현존하는 모든 웹서비스들이 open API에 기반하여야

합니다. 그래야만, 외부에서 작성된 웹서비스를 pluggable하게 장착해서 사용할 수 있게 됩니다.

2008/06/13

Solaris의 버추얼 머신 : 버추얼박스(virtualbox)의 성능

썬에서는 얼마전에 독일계의 이노텍(innotek)이란 회사를 인수한 적이 있다. 이노텍이란 회사는 가상머쉰의 일종인 '버추얼박스(virtualbox)'라는 오픈 소스 소프트웨어를 제작 공급하던 업체이다.

썬은 버추얼 박스를 인수한 이후 솔라리스에서도 아주 잘 동작하도록 꽤 수고를 드린 것 같다. 최신 버젼으로 제공된 1.62 버젼은 솔라리스상에서 설치되어서 오픈솔라리스, 솔라리스10(u5+)은 물론 윈도우즈 계열과 리눅스 계열의 게스트 운영체제를 지원할 뿐 아니라 해당 게스트 운영체제들의 성능을 향상시켜주는 게스크 에디션을 제공하면서 게스트로 솔라리스, 윈도우즈,리눅스, FreeBSD, OS/2를 가장 퍼펙트하게 제공하는 최초의 솔라리스용 버추얼 버신이라고 할 수 있겠다.

버추얼 벅스는 Type2 (Hosted VM)기반의 버추얼 머신으로 다른 운영체제위에서 동작하며 호스트 운영체제도 가장 많은 운영체제를 지원한다.(솔라리스, 리눅스, MacOSX, 윈도우즈)

위키에 있는 버추얼 머신 비교를 보면 쉽게 이해가 간다.
이노텍의 버추얼박스는 흥미로운 탄생 역사를 가지고 있는데, 버추얼박스 소스의 초기 코드(오픈소스가 되기 이전)가 마이크로소프사에 라이센스되었는데, 이로 인해서 마이크로소프트의 Virtual PC 2007과 거의 흡사하다고 할 수 있겠다. 유일하게 다르다면 VirtualPC 2007은 호스트운영체제로 윈도우즈만 지원하는 반면 VirtualBox는 주요 데스크탑운영체제 대부분을 지원한다는 것이 다르다면 다르겠다.


버추얼 박스 1.62에서 WindowsXP를 돌리는 모습니다. vbox guest edition을 설치한 이후에 화면 크기의 조절이라던가 마우스 모드의 자동전환, 성능등이 획기적으로 개선되어 있다.



vbox 1.62/Solaris 상에서 PC Wizard 2008을 다운받아서 시스템 정보를 확인하는 스샷이다. 흥미로운 것은 CPU를 부정확하게 잡아내고 있는데 이유는 확실하지 않다. 감지하는 코어의 개수(운영체제 입장에서 CPU)는 한개만 나온다. 이것은 버추얼 박스가 한 CPU만 지원하기 때문에 그런 것으로 보인다. (맨 끝 PC Wizard 결과 화면 참고)

솔라리스에서는

vaio-solaris:~ 499

$kstat cpu_info:1:cpu_info1:brand
module: cpu_info instance: 1
name: cpu_info1 class: misc
brand Intel(r) Core(tm)2 Duo CPU T7500 @ 2.20GHz


로 나오는 반면

버추얼 박스내의 PC Wizard에서는 Core2 Duo E4500@2200Mhz CPU로 감지했다. Core2 Duo면 두개의 코어인데 하나만 나오는 것도 다소 흥미롭다.

그렇다면 성능은 어떨까 ?


정말 흥미로운 성능 결과가 아닐 수 없다. 위 결과화면에서 보이는 두개의 선중 노란선이 버추얼박스의 성능 결과인 반면 파란색 선은 참고용 시스템의 결과이다. 참고용 시스템이라는 것이 보이는 데로, Core2 Duo E6600@2.4Ghz 시스템인데, 이 시스템과 비교해서 프로세서 스피드가 훨씬 뛰어나다.
메모리는 훨씬 성능이 좋다. 메모리 캐쉬는 같은 것으로 나오고. 믿을 만한 데이타인가 ? 살짝 호기심이 발동한다. Core2 Duo T7500의 native 성능 결과를 추후 올리겠다.

비디오는 당연히 느릴 것으로 예상했었고 상당히 느린 것으로 나왔다.
우측 위 태스크 관리자의 부분 이미지가 보인다. CPU가 하나만 보이는 것이 인상적이다. Core2 Duo인데, 반코어만 보인다는 얘기다. 웃긴다.

PC wizard를 수행하게 되면, 버추얼박스는 최대의 부하를 사용하게 되는데, 이와중의 솔라리스 상황을 살펴보았다.


VirtualBox가 거의 하나의 코어를 다 쓰는 것을 볼 수 있다. 49% 정도로 나오는 것은 CPU 두개짜리 박스에서 하나를 다 쓰게 되어서 나타나는 평균값이다. 그 다음에 나오는 프로세스는 Xorg인데, 3D 데스크탑을 쓰면서 버추얼 박스에서 Video Test를 하는 와중에 캡쳐한 것이라 다소 높게 나왔다. 비데오 테스트가 아닌 경우에는 이렇게 높게 나오지는 않았다. Direct3D 테스트는 실패한 것으로 보인다.

버추얼 박스는 흥미롭게 하나의 코어만을 게스트 운영체제에 보여주면서, 자신은 여러개의 스레드를 통하여 시스템의 모든 프로세스를 나름 공평하게 사용하는 것을 볼 수 있다.그렇지만, 전용 스레드 하나가 주로 사용되는 것은 어쩔 수 없다.

솔라리스에서 버추얼박스를 사용하면서 게스트 운영체제에 과부하를 걸게 되면 호스트 운영체제인 솔라리스가 뛰엄 뛰엄 실행되는 모습이 일부 보이는데, 이런 현상을 없애기 위해서 윈도우즈에서는 할 수 없는
프로세서 전용 할당과 같은 자원 분배기능을 할 수가 있다.

Core 2 Duo T7500 은 두개으 코어를 가지고 있는데 하나의 코어에서만 VirtualBox관련 프로세스들이 실행되게 하면, 나머지 프로세스에서 솔라리스가 실행되므로 매우 안정적으로 운영할 수 있다.


이렇게 하기 위해서는 프로세서 셋을 다음과 같이 구성한다.
#psrset -c 1 1
; 1번 프로세서 셋을 구성하면서 해당 셋에 1번 코어를 할당한다. 기본적으로 0번, 1번 두개의 코어가 있다.

vaio-solaris:~ 503
$psrinfo -v
Status of virtual processor 0 as of: 06/12/2008 18:03:09
온라인(06/11/2008 09:05:27 이후).
The i386 processor operates at 2200 MHz,
and has an i387 compatible floating point processor.
Status of virtual processor 1 as of: 06/12/2008 18:03:09
온라인(06/11/2008 09:05:33 이후).
The i386 processor operates at 2200 MHz,
and has an i387 compatible floating point processor.
vaio-solaris:~ 504
$psrinfo
0 온라인 06/11/2008 09:05:27 이후
1 온라인 06/11/2008 09:05:33 이후

다음은 버추얼박스가 실행하고 있는 중의 mpstat 결과이다.
$mpstat 1
CPU minf mjf xcal intr ithr csw icsw migr smtx srw syscl usr sys wt idl
0 16 0 2 466 190 709 79 36 24 0 13563 19 2 0 79
1 13 0 5 249 194 567 73 36 27 0 12633 18 4 0 78
CPU minf mjf xcal intr ithr csw icsw migr smtx srw syscl usr sys wt idl
0 0 0 1 563 210 1388 317 118 163 0 58609 30 18 0 52
1 7 1 0 573 154 1326 354 107 159 0 71542 27 26 0 48
CPU minf mjf xcal intr ithr csw icsw migr smtx srw syscl usr sys wt idl
0 2 0 0 557 200 1530 387 141 71 0 62210 27 17 0 56
1 0 0 14 583 181 1579 365 153 95 0 85893 32 22 0 45
CPU minf mjf xcal intr ithr csw icsw migr smtx srw syscl usr sys wt idl
0 0 0 0 608 206 1969 487 159 28 0 55857 25 15 0 60
1 2 0 0 625 214 1394 305 151 35 0 83667 28 21 0 51
CPU minf mjf xcal intr ithr csw icsw migr smtx srw syscl usr sys wt idl
0 0 4 0 555 214 1378 307 176 74 0 73979 25 18 0 57
1 1 1 0 572 198 1381 313 177 66 0 70269 24 18 0 58
CPU minf mjf xcal intr ithr csw icsw migr smtx srw syscl usr sys wt idl
0 0 13 0 663 303 1376 241 160 59 0 36496 20 14 0 66
1 0 6 8 550 168 1366 357 150 53 0 90470 27 30 0 44

두개의 CPU(core)가 나름 골고루 사용되고 있음을 볼 수 있다.

앞서 만든 processor set을 이용해서 1번 코어에 버추얼 박스를 바인딩 시킨 결과이다.

#psrset -b 1 `pgrep -d" " VirtualBox`
process id 4565: was not bound, now 1
process id 4560: was not bound, now 1


$mpstat 1
CPU minf mjf xcal intr ithr csw icsw migr smtx srw syscl usr sys wt idl
0 16 0 2 466 190 710 80 36 24 0 13761 19 2 0 79
1 13 0 5 250 194 568 73 36 27 0 13141 18 4 0 78
CPU minf mjf xcal intr ithr csw icsw migr smtx srw syscl usr sys wt idl
0 7 0 0 427 204 874 22 0 5 0 2141 4 1 0 95
1 0 0 14 371 108 324 65 0 6 0 165138 35 24 0 40
CPU minf mjf xcal intr ithr csw icsw migr smtx srw syscl usr sys wt idl
0 0 0 0 402 198 800 21 0 0 0 2220 6 2 0 92
1 0 0 0 367 102 321 71 0 0 0 162599 36 25 0 40
CPU minf mjf xcal intr ithr csw icsw migr smtx srw syscl usr sys wt idl
0 0 0 0 425 212 987 131 0 0 0 19995 8 2 0 90
1 0 0 0 370 99 313 74 0 1 0 160760 35 27 0 38
CPU minf mjf xcal intr ithr csw icsw migr smtx srw syscl usr sys wt idl
0 0 0 1 416 200 843 18 0 4 0 1895 4 2 0 94
1 0 0 14 382 112 313 71 0 6 0 167351 36 25 0 38
CPU minf mjf xcal intr ithr csw icsw migr smtx srw syscl usr sys wt idl
0 0 0 0 404 204 770 20 0 0 0 2015 6 3 0 91
1 0 0 0 362 101 318 67 0 0 0 161251 36 24 0 41
CPU minf mjf xcal intr ithr csw icsw migr smtx srw syscl usr sys wt idl
0 0 0 0 409 204 910 20 0 0 0 2333 5 1 0 94
1 0 0 0 379 106 320 76 0 0 0 165813 36 25 0 39



psrset으로 프로세서 관리를 한 이후에는 CPU 하나만 주로 쓰이고 있음을 알 수 있다. 이때에 버추얼 박스에서는 부하가 거의 걸려있지 않은 상태이고 아래 상태는 인터넷 익스플로러 하나에서 네이버 초기 화면을 열었을 때의 현상이다. 1번 코어가 거의 모두 사용되고 있음을 알 수 있다. 네이버는 '플래쉬'로 상당한 부하를 끊임없이 만들어 내고 있기 때문에 위와 같은 결과가 보임을 알 수 있다. 이때 0번 코어도 상당한 사용율 보이는 데 이 이유는 인터넷을 접근할때 발생하는 네트웍 트래픽의 처리를 0번 코아에서만 하기 때문에 접속 초기시 0번 코어를 상당히 사용하는 모습을 보여준다. psrset으로 cpu를 운영체제 기본 풀에서 분리해내면 그 프로세서서는 운영체제가 사용하지 않기 때문이다.


$mpstat 1
CPU minf mjf xcal intr ithr csw icsw migr smtx srw syscl usr sys wt idl
0 16 0 2 466 190 711 80 36 24 0 13753 19 2 0 79
1 13 0 5 251 193 567 73 36 27 0 13322 18 4 0 78
CPU minf mjf xcal intr ithr csw icsw migr smtx srw syscl usr sys wt idl
0 7 0 4 480 238 2132 540 0 153 0 7480 31 9 0 60
1 0 7 5 746 241 1065 497 0 219 0 55495 26 63 0 11
CPU minf mjf xcal intr ithr csw icsw migr smtx srw syscl usr sys wt idl
0 0 0 4 637 310 2125 544 0 196 0 7388 31 10 0 59
1 1 26 8 827 339 1149 529 0 245 0 31525 18 74 0 8
CPU minf mjf xcal intr ithr csw icsw migr smtx srw syscl usr sys wt idl
0 0 0 1 526 248 2628 736 0 382 0 8992 40 13 0 46
1 0 9 14 866 300 1186 576 0 491 0 18527 18 82 0 0
CPU minf mjf xcal intr ithr csw icsw migr smtx srw syscl usr sys wt idl
0 27 24 4 616 329 2632 699 0 261 0 8153 36 13 0 51
1 0 6 0 786 182 1078 522 0 376 0 42941 25 75 0 0
CPU minf mjf xcal intr ithr csw icsw migr smtx srw syscl usr sys wt idl
0 0 0 0 501 234 2258 695 0 390 0 8409 35 12 0 53
1 0 4 0 691 142 1052 497 0 508 0 44347 23 74 0 3
CPU minf mjf xcal intr ithr csw icsw migr smtx srw syscl usr sys wt idl
0 1 1 0 477 226 2151 554 0 287 0 7807 28 9 0 63
1 0 2 14 632 152 955 419 0 424 0 62805 27 63 0 10

2008/06/11

솔라리스10 (Solaris Community Edition)에서 MySQL 사용하기

솔라리스10에는 MySQL이 이미 들어있습니다. 굳이 다운받아서 컴파일하고 할 필요가 없다는 뜻이죠.
솔라리스 10에서는 MySQL을 활성화하기 위해서는 다음과 같이 하면 됩니다.

일단 mysql의 그룹과 사용자를 만듭니다. 이렇게 하기 위해서는 수퍼유저나 혹은 사용자 생성 권한을 가지고 있어야 합니다.

1. 그룹의 생성
#/usr/sbin/groupadd mysql
2. 사용자의 생성
#/usr/sbin/useradd -g mysql -d /var/mysql mysql
;여기서 -d /var/mysql 옵션에 유념해야 합니다. -d 옵션은 사용자 디렉토리를 설정하기 위해서 필요한 옵션인데 mysql man 페이지에는 이 부분이 빠져 있는데, 이 옵션이 빠져 있으면 후에 서비스를 활성화할때(svcadm 이용) 오류가 발생합니다.
3.MySQL 데이타베이스 디렉토리의 소유권한을 설정합니다.
#chown -R mysql:mysql /var/mysql
4.서비스를 구동합니다.
#svcadm enable mysql:version_50
혹은
#svcadm enable mysql
5.구동된 서비스가 정상적으로 작동하는 지 확인합니다.
#svcs -xv mysql
svc:/application/database/mysql:version_50 (MySQL RDBMS)
상태: online(2008년 6월 11일 수요일 오후 05시 48분 06초 이후)
참조: man -M /usr/share/man -s 1 MySQL 5.0.45
참조: http://dev.mysql.com/docs
참조: /var/svc/log/application-database-mysql:version_50.log
영향: 없음
6. mysql 클라이언트로 접속해봅니다.
#mysql -u root
mysql> show databases;

이렇게 해서 성공적으로 솔라리스에서 MySQL을 활성화했습니다. 쉽죠? ^__^

이후에는 MySQL 설정을 해야 합니다.
MySQL root 패스워드도 설정해야 하고, 필요한 데이타 베이스를 생성해야 합니다.
이 부분은 MySQL 관리자 영역이므로 여기서는 언급하지 않습니다.

개발자를 위한 선택 : 솔라리스

예전에 마이크로소프트웨어에 기고했던 글입니다.

:+: 하늘을 닮은 호수 :+: » Open Solaris 1

2008/06/10

솔라리스에서 사용자 패스워드 변경 자동화(expect 이용)

예전에는 CGI를 이용하여 웹 프로그래밍을 할 때에는 사용자 암호를 웹 인터페이스로 받아서 변경해야 하는 경우가 있었습니다.
지금의 웹 인터페이스에서는 JavaScript나 python등 고수준 스크립팅으로 백엔드 인증 서비스와 연결하는 방법을 대부분 사용합니다만 예전에는 /usr/bin/passwd라는 유틸리티를 이용하여 /etc/passwd 화일을 기반으로 패스워드를 변경해야 했습니다.

오늘날에는 필요하지 않을 듯 하고, 실제로 보안상의 이유로 권고되지 않습니다만, 간혹 시스템 마이그레이션이나 자동 업데이트를 배치로 처리하기 위해서 대량의 사용자의 암호를 임의로 변경하기 위해서는 여전히 이러한 스크립트를 통한 passwd 변경이 필요하게 됩니다.

이러한 목적으로 패스워드를 변경하기 위해서 예전에는 쉘스크립트의 표준 입출력을 조정하여 스크립트했습니다만, 솔라리스10에서는 이렇게는 안되는 것 같습니다(방법이 있는 지는 좀더 확인해봐야겠습니다)

어쨌든 오픈솔라리스에는 (솔라리스 Community Edition 03/2008. Opensolaris 05/08과는 다름)에는 expect라는 스탠다드 입출력을 전환하는 유명한 오픈 소스가 들어있습니다. 이 툴을 이용하여 패스워드 변경을 스크립팅할 수 있습니다.
다음은 mkpasswd라는 간단한 expect 스크립트 입니다. 사용방법은 다음과 같습니다.
#mkpasswd user1 oldpasswd newpasswd

#!/usr/bin/expect
#
# mkpasswd by Bonghwan Kim
#

spawn passwd [lindex $argv 0]
set new [lindex $argv 1]

expect "New Password:"
send "$new\r"
expect "Re-enter new Password:"
send "$new\r"
expect eof


mkpasswd를 실행할때 실행 아큐먼트로 구 패스워드와 새 패스워드를 주고 실행하면, 한번에 변경이 가능합니다.
여기서 두가지 중요한 것은 이 스크립트는 root 권한을 가지고 실행이 되어야 하며, 이 스크립트가 실행될때의 locale이 반드시 POSIX 'C' 이어여 합니다. 즉, 터미널에서 실행할때에는 LC_ALL=C;export LC_ALL 혹은 그에 준하는 명령어로 쉘의 로케일을 C로 전환합니다. 그 이유는 로케일이 달라지면 expect가 기대하는 문자열의 값이 로컬라이즈화 되어 스트링이 달라집니다. 예를 들어, ko 혹은 kr_KR.UTF-8으로 되어 있으면 "New Password:"라고 나오지 않고 "새 암호:" 와 같이 물어보기 때문입니다.


일반 사용자가 위와 같은 패스워드를 사용해야 하는 경우에는 위의 스크립트를 사용할 수 없습니다. 일반 사용자 특히 이미 패스워드가 설정되어 있는 경우에는 기존의 패스워드를 물어보는 과정이 추가되기 때문입니다. 일반 사용자가 자기의 패스워드를 자동으로 변경하고자 하는 경우에는 위의 스크립트를 약간 변경하여 다음과 같이 작성하면 작동합니다.

사용법은 LC_ALL=C mkpasswd jac0001 abc123 qwer123 와 같이 실행하면 됩니다.
#!/usr/bin/expect
spawn passwd [lindex $argv 0]
set old [lindex $argv 1]
set new [lindex $argv 2]

expect "Enter existing login password:"
send "$old\r"
expect "New Password:"
send "$new\r"
expect "Re-enter new Password:"
send "$new\r"
expect eof

참고로 실행하고 테스트된 플랫폼은 다음과 같습니다.
solaris:~ 522
$cat /etc/release
Solaris Express Community Edition snv_86 X86
Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
Use is subject to license terms.
Assembled 27 March 2008
solaris:~ 523
$expect -v
expect version 5.43.0

2008/06/04

오픈 솔라리스와 3차원(3D) 인터페이스

썬마이크로시스템즈는 지난 5월 오픈 솔라리스 배포판을 새롭게 내놓았습니다. 많은 분들이 이와 관련된 블로깅을 하고 있으므로 참고하시면 됩니다.
sangpill님의 http://blogs.sun.com/snoopy40/entry/new_solaris_for_devepolers_opensolaris
kyunghwa님의 http://suns.tistory.com/

흥미로운 부분은 오픈 솔라리스에는 오픈 소스에서 3D 데스트크탑 인터페이스인 컴피즈 퓨전(compiz fusion)이 내장되어 있습니다. 쓸만한 그래픽카드를 가지신 분이면 바로 활성화함으로써, 그 화려한 부분을 엿볼 수 있습니다.

주 메뉴(메인)에서 환경설정 -&gt; 모양새 를 선택하시면 테마를 변경할 수 있는 다이얼로그가 나오는데, 여기에서 Visualization Effects 란 탭을 선택해서 두번째 이후의 옵션을 선택하면, 3D 인터페이스가 나옵니다. 처음에 핫키를 모르는 분들을 위해서 몇가지 유용한 키를 언급해드리면,
작업창의 애플리케이션간의 스위칭을 위해서 Meta(Mod key or Windows Key) + Tab 을 누르시면 아주 멋진 윈도우 스윗칭 환경이 연출됨을 보실 수 있습니다.

맥에서 보이는 분위기와 비슷한 환경을 느껴보시고 싶으시다면 우측 위 코너로 마우스 포인터를 가져가시면 작업 데스크탑에서 실행되는 여러개의 애플리케이션들이 선택을 기다리면서 예쁘게 나열됩니다.
물론, 기존의 Alt-Tab도 작동을 합니다.

작업 데스크탑 간의 선택을 하시고 싶으시다면 마우스를 화면 좌측 위 코너로 가져 보시길 바랍니다.네개의 작업창이 모두 보이는 장면이 연출됩니다. 작업하시고 싶은 창을 선택하시면 됩니다. (혹은 Left Ctrl + Left Alt + Up Arrow 후에 Ctrl과 Alt유지하신 채 좌,우 화살키를 사용하셔도 됩니다.)



환상적입니다. 솔라리스에서 새로운 경험을 해보시기 바랍니다.

참고 이미지





* 솔라리스 컴피즈 설정 문제 해결(compiz setting configuration troubleshooting)

현재(2008년 6월)는 솔라리스가 Nvidia 그래픽카드와 가장 완벽하게 동작하고 있으며, ATI도 그런데로 동작하는 것으로
보고되고 있습니다. 인텔 내장보드의 경우에는 다소 xorg.conf에 수작업을 통한 구성변경이 필요한 것으로 되어 있습니다.



인텔 칩셋의 경우 Visual Effects Tab에서 작동 활성을 선택했는데 조금있다가 시스템이 멍해지는 현상이 발생하는
경우가 있습니다. 이런 경우에는 재부팅한 후 터미널 안전 모드로 로그인한후 터미널에서 다음과 같이 실행해서 기본 윈도우
매니저(default window manager)를 3차원 3D 매니저인 /usr/bin/compiz에서 이전 평면 윈도우
매니저인 /usr/bin/metacity 로 변경하도록 한다.



$gconftool-2 -s /desktop/gnome/applications/window_manager/default -t string "/usr/bin/metacity"



참고로 정확하게 설정되었는 지를 확인하는 방법은 다음과 같다.

$gconftool-2 -g /desktop/gnome/applications/window_manager/default



정상적으로 변경이 되었으면 로그아웃했다가 다시 로그인해본다. 물론, "그놈(Gnome)" 세션으로 로그인한다.

2008/05/14

다음과 솔라리스

제목이 다소 거창합니다만, 다음 잘 아시죠 ?

국내 Top2에 드는 최대 포털이면서, 독특하게 포털 기반의 미디어 회사를 염두에 둔 회사입니다.

(최근 광우병으로 아고라 담당 서버들이 거의 매일 자빠지는 것으로 보입니다. 공식적인 사항으로는 ...)



다음 블로그가 제공하는 미디어 스트리밍 서비스가 어떤가 보다가 문득 미디어 서버가 너무 느린 것 같아서 아주 기초적인 호기심으로 서버의 이름을 추적해봤습니다.

...

cfs.flvs.daum.net

...



cfs ? 갑자기 호기심이 들더군요. lustre란 의미보다는 그냥 "clustered" 화일 시스템이라 생각하면서, 그래도
혹시나 하는 의구심도 들더군요. 그래서 또 그냥 아무 생각없이 서버가 무슨 운영 체제를 사용할까 궁금해서 "포트 스캐닝"
해봤습니다. ( 저 이짓 자주하지 않습니다. 혹시 오해하실까봐 ㅡ.ㅡ; )



그랬더니 이런 데이타가 나오더군요..



SF-Port80-TCP:V=4.20%I=7%D=5/14%Time=482A9706%P=i386-pc-solaris2.11%r(GetR

SF:equest,BB1,"HTTP/1\.0\x20400\x20Host\x20Header\x20Required\r\nContent-T

...



혹시 빨간색 글자 보셨습니까? 오픈 솔라리스 플랫폼을 의미하는 아키텍쳐 스트링입니다. 햐.. 리눅스만
쓴다고 하던 이 친구들이 조용히 오픈 솔라리스를 사용하고 있었네요. 가슴이 뭉클해집니다. 한때, 최대의 썬/솔라리스 고객이었던
다음이 죄다 리눅스로 바꾸고 나선 거의 만날 일이 없었는데... 뒤에서 솔라리스를 조용히 사용하고 있었네요.. 그것도 미디어
스트리밍이라는 아주 중요한 서비스를 위해서 말이죠...


오픈 솔라리스의 지평이 조금씩 넓어지고 있다는 것이 현실적으로 증명된 것 같아서 여간 기쁘지 않네요.






2008/05/07

가상화 비교 (xVM & vmware ESX)

성능 측면에서 보면, 현재는 Xen3 기반이 Vmware ESX3보다 평균적으로 약간 떨어지는 것으로 되어 있는데 (XenSource Report : Xen vs. VMware 성능 비교 ) 성능은 CPU 사용과 IO 사용 측면에서 다소 차이가 있습니다.



CPU workload에서는 vmware가 약간 나은 것으로 되어 있는데, 이 부분은 다소 개선의 여지가 있음을 보여주는 것
같구요. 솔라리스 기반의 xVM server가 정식으로 나오면 빠르게 개선되지 않을까 생각합니다. 아직 썬의 xVM 서버는
베타라서 정식 비교는 힘들다고 생각합니다. 특히, 데스크탑은 컨트롤 도메인을 주 웍플레이스로 사용하면서 (X-window)
게스트를 접근하는 것이라 현재 오픈 솔라리스 기반의 버젼은 아직 불필요한 부하가 많다는 생각입니다.



반면 IO Workload를 기반으로 보면 첨부에서 보는데로 Host 방식의 Vmware ESX와 Para 방식의 Xen은 Io 인터페이스시에 극명한 차이가 나도록 되어져 있습니다.



Host 방식은 이론적으로 모든 Guest가 HW 디바이스를 직접 호스팅하는 것으로 보이지만, 실질적으로는 guest
instance에는 VM-aware device를 보여주며, VMM에서 native를 인터페이스 하도록 합니다.

VMM에서는 guest instance가 사용하는 VM-aware device driver들을 switching하면서, native 디바이스와 데이타 연결을 합니다. (첨부 1)



Para 방식은 Para 게스트 도메인의 디바이스 드라이버가 컨트롤 도메인의 네이티브 디바이스 드라이버와 마치 클라이언트-서버
형태의 접속을 사용하기 때문에 스위칭 오버헤드가 없습니다. 물론, 게스트 도메인과 컨트롤 도메인과의 커뮤니케이션 오버헤드가
있겠습니다만 디바이스 드라이버 스위칭 오버헤드보다는 낮은 것으로 평가됩니다.



즉, 진정한 비교가 되려면, Para에서는 Para용 Guest를 올려야 진정한 비교가 된다는 얘기겠죠. 그리고 이런 경우 xVM(Xen)이 vmware보다 우수한 성능이 나오리라 예상이 됩니다.



그리고 IO 작업이 많을 수록 CPU의 Idle time은 증가하기 때문에 현재 있는 vmware의 CPU 잇점은 그렇게 크지 않으리라 생각합니다.



2008/04/02

오픈 솔라리스(opensolaris)와 xmanager

썬솔라리스는 업데이트3(?) 부터인가 "Secure by default"라는 기능이 적용되어져 있습니다. 간단하게 얘기하자면, 쓸데없이 포트 스캐닝과 같은 것에 탐지되지 않도록 불필요한 서비스를 죽여놓는 것을 말합니다.
그런데, XDMCP 기반의 dtlogin 환경을 원격으로 접근하는 xmanager가 연결해야 하는 서비스도 이 범주에 해당하는 관계로 서비스가 막 설치한 환경에서 활성화되어 있지 않습니다. 따라서, xmanager를 이용하여 서버에 연결해야 하는 경우에는 다음과 같이 "secure by default"를 풀어야 합니다.

#/usr/sbin/netservices open

netservices는 다음의 서비스를 외부에서 접속되는 것을 차단하도록 구성합니다. 물론, 호스트 내부에서는 접속이 가능합니다.

system_log
cmsd
rpcbind
xserver
sendmail
ttdbserver
dtlogin
webconsole
smcwbem

참고로 서버에 연결하는 바람직한 방법은 ssh [-X] 를 사용하는 것이 보안상 바람직합니다. Windows 클라이언트에서 연결하고 싶은 경우에는 Sun의 Secure Global Desktop이나 SunRay 혹은 VNC server, Seamless RDP 서버등과 같은 보안이 강화된 서비스를 이용하여 클라이언트 접속을 하는 것이 바람직합니다.

2008/02/04

솔라리스에서 md5 첵섬 사용

솔라리스(Solaris) 10(최초 공개버젼에서는 못본것으로 기억합니다)에서는 기본적으로 md5 및 기타 첵섬을 위한 툴로 digest라는 툴을 제공합니다.

다음은 digest라는 툴을 이용해서 md5 digest를 구하는 예입니다.
#digest -a md5 datafile

참고로 digest가 제공하는 md5 이외의 알고리듬의 목록을 보시려면 다음처럼 실행하면 됩니다.
#digest -l

digesting 기법을 이용하면, 특정 디렉토리나 화일에 대해서 다이제스팅을 함으로써 시스템을 보안 강화를 요할 수 있습니다.(System hardening) 이러한 개념을 도입하여 편리하게 hardening을 지원해주는 유틸리티로 bart라는 유틸리티가 솔라리스에서는 기본적으로 제공됩니다.

2008/01/02

솔라리스에서 일반 사용자가 ifconfig만 실행하게 할 수 있을까요 ? (Solaris RBAC)

가장 쉬운 방법은 그 사용자의 아디디가 user1이라고 할때
user1을 모두 로그아웃 시키고,
#usermod -P"Network Management" user1
이라고 실행한 후 user1이 로그인해보면

$profiles -l 실행했을 경우
자신이 실행할 수 있는 root 전용 명령어들이 나타납니다.

이 때 나타나는 명령어는 Network Management Profile(Role)에서 주어진 내용
으로 다음과 같습니다.

Network Management:solaris:cmd:::/sbin/dladm:privs=sys_net_config
Network Management:solaris:cmd:::/sbin/ifconfig:uid=0
Network Management:solaris:cmd:::/sbin/route:privs=sys_net_config
Network Management:solaris:cmd:::/sbin/routeadm:euid=0;
privs=proc_chroot,proc_owner,sys_net_config
Network Management:solaris:cmd:::/usr/sfw/sbin/zebraadm:privs=basic
Network Management:suser:cmd:::/usr/bin/netstat:uid=0
Network Management:suser:cmd:::/usr/bin/rup:euid=0
Network Management:suser:cmd:::/usr/bin/ruptime:euid=0
Network Management:suser:cmd:::/usr/bin/setuname:euid=0
Network Management:suser:cmd:::/usr/sbin/asppp2pppd:euid=0
Network Management:suser:cmd:::/usr/sbin/ifconfig:uid=0
Network Management:suser:cmd:::/usr/sbin/ipaddrsel:euid=0
Network Management:suser:cmd:::/usr/sbin/ipqosconf:euid=0
Network Management:suser:cmd:::/usr/sbin/rndc:privs=file_dac_read
Network Management:suser:cmd:::/usr/sbin/route:uid=0
Network Management:suser:cmd:::/usr/sbin/snoop:uid=0
Network Management:suser:cmd:::/usr/sbin/spray:euid=0

그런데, 이때 중요한 것은 user1이 새로 로그인해서 ifconfig를 실행하려면 특수한 쉘을 사용해야만 실행할 수 있습니다. 그렇게 안할 수도 있습니다만, 여러가지 이유로 이렇게 하는 것이 보안상이나 운영면에서 좋습니다.

$pfexec ifconfig -a plumb

혹은

$pfksh
#ifconfig -a plumb <-이때 effective user id로 root를 받게 되는데, 앞서 나열된 명령어만 사용가능합니다. #exit $ 위처럼 프로파일을 사용하게 되면, 해당하는 명령어 이외에 다른 명령어 군들 도 실행할 수 있게 되는데, 이는 장단점이 있습니다. 실제로 ifconfig를 효과 적으로 컨트롤하려면 route 명령어도 사용할 수 있어야 하는 경우도 있으며, netstat와 같은 모니터링 커맨드도 돌릴 수 있어야 하기 때문에, 한번에 이런 것들을 가능하게 하는데는 이것이 가장 좋습니다. 하지만, 특수 쉘을 사용하지않고, root처럼 기본 쉘에서 바로 실행할 수 있도록 할 수 있는데 user1을 로그아웃 시킨후 다음과 같이 실행합니다. #usermod -K"defaultpriv=proc_fork,proc_exec,sysnet_config" user1 을 실행한 후 user1이 새로이 로그인하면 위의 ifconfig 명령어를 실행할 수 있습니다. 이때는 pfexec/pfksh과 같은 특수 쉘을 사용하지 않고 바로 실행할 수 있습니다. 이 방법의 장점은 사용자의 애플리케이션이 바로 위의 해당하는 기능을 위한 시스템 콜을 일반 사용자로서 바로 사용할 수 있다는 점입니다. 반면, 이 방법의 문제점은 사용자 관점의 명령어 기준으로 구분하지 않고, 접 근권한 관점에서 컨트롤하기 때문에 위의 옵션이 다른 어떤 커맨드가 작동가 능한지 일단 알기 어렵습니다. 해당 애플리케이션이 어떤 디바이스를 접근하 고 어떤 시스템콜을 쓰는지를 조사해보아야만 알수 있는 것이기 때문입니다. 즉, 저위의 정의에 따라서, 사용자는 애플리케이션 내부에서 ifconfig를 실행 할 수 있습니다. 또한, 기존에 미리 구성되어 있는 프로파일을 사용하지 않고, 완전히 개인화된 프로파일을 만들어서 제공할 수도 있습니다. 이런 경우에는 어떤 사용자가 실행할 수 있는 명령어군들을 완전히 정의해서 사용할 수 있으므로, 완벽한 컨트롤을 할 수가 있는 반면에, 사용자가 원하는 기능에 의존성이 있는 명령어가 어떤 것이 있는 지를 사전에 조사해놓고 사용하는 것이 좋습니다. 예를 들면, ifconfig만을 돌릴 수 있는 Network Management2를 만들었는데, 실제로 사용자는 ifconfig와 함께 snoop을 늘 같이 돌리기 원할 수 가 있으므로, 이러한 조사를 사전에 해서 사용상 제한이 없도록 하는 것이 바람직합니다. 완전한 개인화를 하기위해서는 다음과 같은 순서로 합니다. 1) 일단 Profile을 작성합니다. /etc/security/prof_attr 2) 만든 프로파일(Profile) 이름으로 실행될 실행 화일을 선언합니다. /etc/security/exec_attr 3) 일반 사용자에게 새로 만든 프로파일을 적용합니다. TIP ! user1을 로그아웃 시키지 않고 구성을 변경하려면 /etc/user_attr을 편집해야 합니다. 편집하게 되면 해당 사용자가 변경 이후에 실행하는 쉘부터 효과가 적용되어 나타납니다.

2007/12/27

솔라리스용 wxPython 컴파일 및 최적화하기.

솔라리스에서 사용할 wxPython 모듈을 포팅하고 최적화하는 방법을 입니다.
wxPython은 ./configure가 매우 뛰어난 호환성을 바탕으로 제작되었기 때문에 그다지 큰 수고로움이 필요없습니다.
일단, 훌륭한 컴파일러와 약간의 시간만 투자하면 됩니다.

컴파일러는 썬스튜디오 12를 권고합니다. 솔라리스에 들어있는 gcc가 오래됐을 뿐 아니라, gcc 새버젼을 받아서 재컴파일을 하더라도 솔라리스용으로 최적화하려면 스튜디오가 역시 필요하기 때문에 그냥 스튜디오 다운 받아서 사용하는 것이 편합니다. 스튜디오는 사용료가 무료이므로 그냥 다운 받아서 쓰면 됩니다.

소스의 형태는 주로 wxPython-src-2.8.7.1.tar.bz2와 같이 공급이 되는 데 이런 경우(bzip2 압축인 경우) 에는 다음과 같이 압축을 풀 수 있습니다. 물론, 그래픽 툴을 이용해도 좋습니다.

#bzip2 -dc wxPython-src-2.8.7.1.tar.bz2 | tar xvf -
#cd wxPython-src-2.8.7.1

경험적으로 썬 스튜디오가 확실히 gcc 보다 나은 것 같습니다.
스튜디오 12을 받아서 기본 환경으로 설치했다면 이제 cc /CC/dmake 등을 사용할 수 있게 되었을 겁니다.
그렇다면, wxPyhton 소스를 다운 받아서 압축을 풀고 디렉토리로 들어갑니다.


configure를 합니다. 최적화를 위해서는 다음과 같이 합니다. 설치시 바로 솔라리스 /usr에 설치할 목적으로 하는 경우에는 다음과 같이 구성하면 됩니다. 별도의 패키지를 구성하기 위해서는 --prefix 를 변경하면 됩니다.

#./configure \
--prefix=/usr \ <- /usr 에 설치합니다.
--with-gtk \ <- gtk+2 UI용 라이브러리를 빌드합니다.
--with-opengl \ <- 3D 인터페이스를 opengl을 이용하여 구성합니다.
--enable-unicode \ <- Unicode를 지원하도록 합니다. (필히 하는 것이 바람직합니다.)
CC=cc \ <- c 컴파일러로 cc 를 씁니다. GCC를 쓰고 싶으면 gcc 선언
CXX=CC \ <- c++ 컴파일러로 CC를 씁니다. GNU C++를 쓰고 싶으면 g++ 선언
CFLAGS="-fast -xO3 -L/opt/SUNWspro/lib -lsunmath" \ C 컴파일러의 최적화 옵션을 선언합니다. 가장 빠르게 하지만 최적화 수준은 level 03로 유지 (레벨이 너무 높으면 플랫폼간 호환성이 떨어집니다. )
CXXFLAGS="-fast -xO3" \ <- C++ 컴파일러의 최적화 옵션을 선언합니다.
LDFLAGS="-L/opt/SUNWspro/lib -R/opt/SUNWspro/lib -lsunmath" <- 링키지 에디터의 라이브러리 옵션을 선언합니다. 썬 스튜디오 12는 썬에서 개발된 고성능 수학함수 라이브러리를 제공합니다. wxPython의 경우 일부 모듈에서 수학 함수를 사용함으로써 더 좋은 성능을 확보할 수 있습니다.


이미 커멘트에서 언급했듯이 , wxPython이 제공하는 몇몇 수학함수 호출용 라이브러리의 성능을 보다 최적화 하기 위해서는 썬 스튜디오가 제공하는 수학 함수 라이브러리를 이용하면 libm 혹은 gcc libm의 라이브러리 대비 최고 200% 이상의 성능을 내는 경우도 있습니다. 따라서 , 안정성에 문제가 없는 지를 확인해보시고 사용하시길 권고 합니다.

2007/12/12

솔라리스 프로세스의 오픈 화일 보는 법

리눅스 사용자들을 보면, 왕왕 lsof 라는 툴을 사용하는 것을 보게된다. lsof라는 툴은 기본적으로 실행중인 프로세스가 어떤 화일을 열고 있는 지를 보여주는 툴인데, 오픈한 화일 뿐 만 아니라, 네트웍(소켓)을 오픈한 것도 보여주기 때문에, 어떤 포트를 사용하는 지 확인할 때 아주 유용한 툴로 널리 사용되어 왔다.

솔라리스에서는 이와 유사한 툴로 pfiles 라는 유틸리티가 제공이 되고 있으며, 솔라리스 8 에서는 /usr/proc/ bin 디렉토리에 있던 유틸리티였으나, 솔라리스 9때부터 /usr/bin으로 옮겨졌다.

lsof가 오랜동안 사용되어 오면서 보다 verbose한 내용을 제공하는 것은 사실이나, pfiles도 거의 동등한 수준에 내용을 제공하므로 상당히 유용할뿐 아니라, 솔라리스에서는 pfiles 면 충분하다는 생각이 든다.
pfiles의 사용법은 다음과 같다.

$pfiles

그런데, lsof나 pfiles는 프로세스의 특정 순간 (캡쳐를 시도하는 타임의 스냅샷)에 오픈된 화일만 보여준다. 만약, 해당 프로세스가 주기적으로 짧게 화일을 열고 닫는 것을 반복적으로 한다면, 이 유틸리티로는 매우 그 과정을 매우 찾기 힘들다. 리눅스에서는 그것을 해결하기가 현재는 매우 힘든 반면, 솔라리스 10에서는 dtrace라는 환상적인 툴을 제공함에 따라, 상상 이상의 데이타를 제공해준다.

다음의 dtrace 툴은 해당 프로세스가 언제 어떤 화일을 여는 지를 보여준다. dtrace에서 화일을 여는 것을 트레이스 하는 방법은 syscall의 open을 추적하는 방법이 있을 수도 있고, dtrace가 제공하는 io Provider를 이용하는 방법도 있다. 단순 화일명이외에 화일이 위치하는 디스크의 이름등과 같이 구체적인 정보를 보고자하는 경우에는 io Provider를 화일 이름정도만을 트레이스 하고 싶으면 open을 트레이스 하는 것이 편리하다.

/* open.d */
#!/usr/sbin/dtrace

syscall::open:entry
/execname == firefox-bin/
{
trace(copyinstr(arg0));
}



#dtrace -s open.d
와 같이 실행하고 있으면, dtrace는 firefox-bin 이라는 실행프로그램이 나타날때까지 가만히 있는다. 나타나게 되면, 그때 부터 트레이스를 시작한다. 실행 화일 이름은 실제로 실행된 프로그램의 이름과 간혹 다른 경우가 있다. 대게 환경 설정과 같은 것을 수반하기 위해서 쉘스크립트를 실행하는 경우가 있는데, 이런 경우에는 시작하는 쉘이 invoke하는 실제의 애플리케이션의 이진화일의 이름을 주는 것이 좋다.

위의 예처럼 firefox는 대게 firefox라는 쉘스크립트로 실행되지만, 실제 실행해보면, 실행 화일은 firefox-bin이라는 화일이 수행되고 있다.

위의 예에서 lsof와 같이 프로세스의 pid로 추적을 하고 싶으면서 dtrace의 아규먼트로 pid를 주고 싶다면 다음과 같이 변경해서 실행시키면 된다.

/* open2.d */
#!/usr/sbin/dtrace

syscall::open:entry
/pid == $1/
{
trace(copyinstr(arg0));
}

실행 예
#dtrace -s open2.d `pgrep firefox-bin`

2007/12/11

솔라리스 화일 시스템 성능 테스트 스크립트

솔라리스와 같은 유닉스 혹은 유닉스 유사 계열은 주로 서버사이드에서 많이 사용되는 관계로 화일 시스템의 혹독한 테스트를 필요로 하는 경우가 왕왕 있다. 이런 경우를 위해서 다양한 화일 시스템 테스트 툴이 있는데, iGen이라던가, bonnie라던가 하는 것들이 웹에서 쉽게 찾아서 사용할 수 있는데,단순한 화일 쓰기 테스트만을 테스트하기 위해서 복잡한 툴을 사용하는 것은 다소 번거로운 것 같아서 하나 작성해보았다.

/dev/urandom에서 생성되는 랜덤 데이타 화일을 기반으로 테스트 화일을 1M 기준으로 동시에 작성하도록 되어 있다. 총 종료 시간을 측정하기 위해서는 실행화일 끝에 wait를 두도록 한다.

#!/usr/bin/bash
ARG1=$1; NUM_OF_FILES=${ARG1:=10}

set i=0
i=$((i+1))

maketmpfile() {
# 1MB file generation
tmpfilename=test$RANDOM

#echo $tmpfilename#

dd if=/dev/urandom of=$tmpfilename bs=1024 count=1024 > /dev/null 2>&1 &

# for debugging
#echo " dd if=/dev/urandom of=$tmpfilename bs=1024 count=1024"
#

}

while true
do
# for debugging
#echo -n "$i:"
#

maketmpfile
i=$((i+1))
if [ $i -gt $NUM_OF_FILES ] ; then
exit 1
fi
done
wait

테스트 방법은

$timex filebench_v2.bash 1000
과 같이 실행함으로써 테스트할 수 있다.