return - みる会図書館


検索対象: UNIX MAGAZINE 2006年3月号
15件見つかりました。

1. UNIX MAGAZINE 2006年3月号

int step=O, i , tend = TEND ; t = 0 . 0 ; k ; dt = KOEF_DT/ ( (double) ngridreal* (double)ngridreal) ; 1 . O/ngridrea1; dx dtdx2inv = dt/(dx*dx) ; while (t く tend){ / / 境界条件から両端のテータを設定 aC0] a[ngridreal + 1] / / 積分 a Cngridrea1] ; for(i=l ; i く =ngridreal; i + + ) { a[i] + = dtdx2inv*( a[i + 1] / / 時間を進める t 十 = dt ; 十十 step ; if( ! ( step % 10000 ) ) { printf ( " step=%d , t=%g\n ー return (step) ; int main(int argc , char **argv) { step static double a [NGRID] ; init—sin(NGRIDREAL, a) ; integrate (NGRIDREAL , a) ; output (NGRIDREAL , a) ; return(O) ; / / 初期条件を作る / / 積分をおこなう / / 結果の出力 リスト 2 MPI-Sendrecv 関数を用いて MPI 化したプログラム (onedim-mpi ・ c) * onedim_mpi . C * numericaly SOIve ID—diffusion equation with MPI #include く stdio . h> #include く math . h> #include "mpi ・ h" #define NGRIDREALTOTAL 4096 #define MAXARRAY 2050 0 . 02 #define KOEF_DT 0 .00390625 #define TEND 78 / / valiables for mpi / * fo て NGRIDREAL=1024 * / UNIX MAGAZINE 2006.3

2. UNIX MAGAZINE 2006年3月号

int int i nt MPI_Status stat ; numprocs ; myrank, left , right ; init—sin—mpi (int nlocalgrid, fo て ( i = 1 ; i く =nlocalgrid; i + + ) { igeta = myrank*nlocalgrid; dx = 1 . O/NGR 工 DREALTOTAL ; double x , dx ; int i, igeta; 2 .0*M_PI*dx* (i + igeta double a ロ ) a[i] sin(x) ; return 0 ; int output—mpi (int nlocalgrid , static int cnts=0 ; char buf [ 64 ] ; int i , lgeta; double dx ; FILE *fp; double a ロ ) cntS , myrank) ; 連載 / 天文学と UNIX / / ファイル名は rank により分ける sprintf (buf , "out%d—rank%d. dat " fp=fopen(buf , "w") ; dx = 1 . O/NGR 工 DREALTOTAL ; igeta = myrank*nlocalgrid; fprintf (fp, "#%d\n" , nlocalgrid) ; fo て ( i = 1 ; i く =nlocalgrid; i + + ) fprintf (fp, tend = TEND ; int step=0, i, k; double tend, t , dt , dx, dtdx2inv; int integrate—mpi(int nlocalgrid, double a ロ ) return 0 ; 十十 cntS ; fclose(fp); i, i + igeta, dx*(i¯l + igeta) , dt dx KOEF_DT/ ( (double) NGRIDREALTOTAL* (double) NGRIDREALTOTAL) ; 1 . O/NGRIDREALTOTAL ; dtdx2inv = dt/(dl*dx) ; UNIX MAGAZINE 2006 . 3 79

3. UNIX MAGAZINE 2006年3月号

ICPU の場合と比較して計算に時間がかかるのは当然で すね。 ☆ プログラムの並列化は、 MPI を使うと比較的簡単にでき ます。並列化する際のポイントは、 リスト 1 サンプル・プログラム (onedim. c) * onedim . C * numericaly S01ve one dim. Of diffusion equation #include く math . h> #include く stdio . h> 連載 / 天文学と UNIX ・いかに通信をさせないか 列化プログラムに挑戦してください。 に尽きます。これカ攤しいところですが、皆さんもぜひ並 ( だいさか・ひろし国立天文台 ) #define #define #define #define NGRIDREAL 1024 NGRID (NGRIDREAL + 2) TEND 0 .001 KOEF_DT 0 .00390625 / * for NGRIDREAL=1024 * / double a ロ ) int init—sin(int ngridreal , int i ; double x, dx; 1 . O/ngridrea1; dx for(i=l ; i く =ngridreal; 土 + + ) { 2 .0*M_PI*dx*(i aCi] sin(x); return 0 ; int output (int ngridreal , double a ロ ) int i ; double dx ; FILE *fp; fp=fopen("out . dat" 1 . O/ngridrea1 ; dx fprintf (fp, "#%d\n" ngridreal) ; for(i=l ; i く =ngridreal; i + + ) fprintf (fp, "%d %f %f\n" fclose(fp) ; return 0 ; UN 工 X MAGAZINE 2006.3 double tend, t , dt , dx, dtdx2inv; int integrate (int ngridreal , double a ロ ) 77

4. UNIX MAGAZINE 2006年3月号

/ / 時間を進める t + = dt ; + + step , if( ! (step%100) ) { if (myrank== の { printf ("step=%d, return(step) ; int main (int argc , char **argv) { MPI_Comm—rank (MPI_COMM—WORLD , MPI—Comm-size (MPI-COMM-WORLD , MPI_Init (&argc , &argv) ; int nlocalgrid, tmpb; static double a CMAXARRAY] ; / / 以下で隣の CPU を決める while(t く tend){ / / 境界のテータを MPI を使ってやりとりする 十 = MPI_Sendrecv(&(a[nIoca1grid] ) , 1 , 1 , MPI_COMM_WORLD , &stat) ; MPI-Sendrecv(&(a[1] ) , 1 , MPI_DOUBLE , MPI_DOUBLE , MPI_DOUBLE , MPI_DOUBLE , right , left , left , right , 0 , 1 , &(aCn10ca1grid + 1] ) , 1 , MPI_COMM_WORLD , &stat) ; a[i] + = dtdx2inv*( a[i + 1] for(i=l ; 土く =nlocalgrid; i + + ) { / / 積分 step, &myr ank ) ; &numprocs) ; numprocs ; numprocs ; / / おまじない / / おまじない / / おまじない left =myrank ー 1 ; right=myrank + 1 ; if (left ー 1 ) left if (right== numprocs ) right / / 各 CPU でのグリッド数を計算 nlocalgrid = NGRIDREALTOTAL tmpb NGRIDREALTOTAL / numprocs ; % numprocs ; fprintf (stderr , "myrank=%d , myrank, nlocalgrid, init—sin—mpi(nlocalgrid, a) ; integrate—mpi(nlocalgrid, a) ; output—mpi (nlocalgrid, a) ; / / 積分をおこなう / / 初期条件を作る tmpb, left, right) ; N/nproc=%d, N%%nproc=%d , left=%d, / / 結果を出力 ( 各 CPU が独立に出力 ) / / おまじない MPI—FinaIize ( ) ; return(O) ; 80 right=%d\n" UNIX MAGAZINE 2006.3

5. UNIX MAGAZINE 2006年3月号

図 6 プロセス間の親子関係 親 children children 〇 sibling 〇 1114 : 1115 : 1116 : 1117 : 1120 : 1121 : 1122 : nr_threads 十十 ; total_forks + 十 ; write—unlock—irq(&tasklist—lock) ; retval if (retval) return ERR PTR(retva1) ; return p ; 最後に、カウンタをインクリメントして呼出し元に戻り ます。 copy-thread ( ) されている copy-thread() のコードの主要部分です。 以下は、ファイル arch/i386/kernel/process. c て疋義 の初期設疋をおこないます。 copy-thread() では、アーキテクチャに依存するデータ childregs—>esp = esp; childregs—>eax = 0 ; *childregs = *regs ; ( (unsigned long) childregs childregs (struct pt—regs * ) (THREAD_SIZE + p—>thread—info) ) childregs ( (struct pt-regs * ) int err ; struct task_struct *tsk ; struct pt-regs * childregs ; struct pt—regs * regs) struct task—struct * p , unsigned long unused , unsigned 10 Ⅱ g esp , unsigned 10 Ⅱ g clone—flags , int copy—thread(int nr , 0470 : 0469 : 0468 : 0467 : 0456 : 0455 : 0454 : 0453 : 0452 : 0451 : { 0450 : 0449 : 0448 : UNIX MAGAZINE 2006.3 ド・スタックに待避されます。 456 ~ 468 行目では、親プ 発行すると、その時点でのレジスタの内容がカーネルモー 前述したように、ユーザープロセスがシステムコールを 特集 V Linux のプロセス [ 3 ] 図 7 thread-info 構本とカーネルモード・スタック 構造体 thread info ピーします。 スタの内容を子プロセスのカーネルモード・スタックへコ ロセスのカーネルモード・スタックに保存されているレジ 8KB スタック領域 カーネ丿レ 構造体 60 バイト pt—regs ・亠 0 00000 。← espO (childregs + 1 ) 空白 3 コンパイル・オプション CONFIG-4KSTACKS カ甘旨定された上昜は ださい。このため、 456 行目の、、一 1 " は 1 バイトではな pt-regs 型のポインタにキャストしている点に注意してく 話を copy-thread() に戻しましよう。 456 行目では、 時に、、 8192 " に置き換えられます ) 。 のではないでしようか (THREAD-SIZE は、コンパイル というコードがありましたが、この計算の意味が分かった andl %esp, %ebp movl $-THREAD_SIZE , %ebp た system-call() の先頭に セスの thread-info 構造体を得ることができます。前述し レジスタの下位 13 ビットをマスクすると、カレントプロ ため、カーネルは、カーネルモード・スタックを指す ESP ネルスタックと同じ 8KB3 の領域内に作成されます。この そして、 thread-info 構造体は、図 7 に示すようにカー 依存する情報を管理します。 ャに依存しない情報を、 thread-info はアーキテクチャに 1 の関係でリンクが張られ、 taskstruct はアーキテクチ の構造体から構成されています。この 2 つの構造体は 1 対 のは、実際には task-struct と thread-info という 2 つ こまで、、プロセス・デスクリプタ " と説明してきたも とややこしいのですこし詳しくみておきましよう。 のコピー先アドレスを指すポインタですが、計算がちょっ 456 ~ 467 行目のポインタ変数 childregs は、レジスタ 4KB です。 97

6. UNIX MAGAZINE 2006年3月号

特集▽ Linux のプロセス [ 3 ] return pid ; 1213 : 1223 : 10 Ⅱ g pid = alloc-pidmap() ; p = copy—process(clone-flags, stack—start, regs , stack—size, parent—tidptr , child—tidptr , pid) ; 1263 : 1264 : 1 , 213 行目の alloc-pidmap() では、子プロセスに割り 当てるプロセス ID を決める処理をおこないます。 1 , 223 行目の copy-process() は、新しいプロセスを生 成します。変数 p ロされる戻り値は、新たに生成され た子プロセスのプロセス・デスクリプタへのポインタを指 します ( 詳しくは杢します ) 。 こで重要なポイントは、 copy-process() から制御が 戻ってくるのは親プロセス側だけで、子プロセスは別の道 を通ることです。たとえば、雹甬の C プログラムでは、 親プロセスの処理 } if (pid > 0 ) { 子プロセスの処理 if (pid int pid = fork() という疑問はあとのお楽しみにとっておきましよう。 「子プロセスはどこへ行ってしまうんや ? 」 です。 すが、 copy-process() で戻ってくるのは親プロセスだけ のように、親と子プロセスの両方が fork ( ) から戻ってきま CLONE-STOPPED フラグが指定されていない場合 は、 wake-up-new-task() を呼び出して子プロセスをラン キューに登録します。つまり、この時点で子プロセスはス ケジューラに選択される候補となります。このサプルーチ ンでは、子プロセスの優先度やスライス時間の計算もおこ なわれます。 また、 CLONE-VFORK フラグが指定されていたら、 1 229 行目で亘言している completion 構造体へのポイン タを子プロセスに設疋し、 wait_for-completion() を呼び 出します。 wait-for-completion() は、以下のように completion 義されています。 copy-process() も、 kernel/fork. c で以下のように定 COPY-Pr0Cess try-to-wake-up() で起床させます。 pletion 構造体の done フラグをセットし、親プロセスを テムコールを実行したとき、 1 232 行目で設疋された c 。 m ー 一方、子プロセスは execve システムコールや exit シス } while ( !vfork—>done) ; schedule() ; —set—current—state (TASK—UNINTERRUPTIBLE) ; do { るサプルーチンです。 構造体の done フラグがセットされるまでスリープし続け 1228 : 1229 : 1230 : 1231 : 1232 : 1233 : 1234 : 1244 : 1245 : 1246 : 1247 : 1254 : 1255 : 1258 : 1262 : if ( ! IS-ERR(p) ) { struct completion vfork ; if (clone-flags & CLONE—VFORK) p—>vfork—done = &vfork ; init—completion(&vfork) ; wait—for—completion(&vfork) ; if (clone—flags & CLONE—VFORK) { p—>state = TASK—STOPPED ; else wake—up—new—task(p , clone—flags) ; if ( ! (clone—flags & CLONE-STOPPED) ) 0842 : 0843 : 0844 : 0845 : 0846 : 0847 : 0848 : 0849 0850 : 0851 : 0852 : 0853 : 0854 : 0860 : UNIX MAGAZINE 2006.3 static task—t *copy—process( int pid) int ——user *child—tidptr , int ——user *parent—tidptr , unsigned 10 Ⅱ g stack—size , struct pt—regs *regs , unsigned long stack—start , unsigned 10 Ⅱ g clone—flags , 93 if ( (clone—flags & CLONE—THREAD) & & return ERR—PTR(—EINVAL) ; (CLONE_NEWNSI CLONE_FS) ) (CLONE—NEWNSI CLONE_FS) ) if ((clone—flags & struct task—struct *P = NULL ; int retval ;

7. UNIX MAGAZINE 2006年3月号

0861 : 0868 : 0869 : ! (clone—flags & CLONE—SIGHAND) ) return ERR—PTR(—EINVAL) ; if ( (clone-flags & CLONE—SIGHAND) & & ! (clone—flags & CLONE—VM) ) return ERR—PTR(—EINVAL) ; ューザーごとに設定されている資源リミット値を調べ、 プロセス数が制限を超えていれば工ラーとします。ただ し、 CAP-SYS-ADMIN か CAP-SYS-RESOURCE のケーパビリティ 1 の有無、 root ユーザーにおけるユーザ ーごとのリミット値は無視されます。 資源リミットの値は、 bash では ulimit コマンドで確認 できます。 853 ~ 869 行目では、フラグの組合せをチェックし、以 下の場合は EINVAL 工ラーを返します (clone ( 2 ) のマ ニュアルも参照してください ) 。 ・ CLONE-FS と CLONE-NEWNS の両方のフラグが VM は指定されていない。 ・ CLONE-SIGHAND が指定されているが、 CLONE- SIGHAND は指定されていない。 ・ CLONE-THREAD が指定されているが、 CLONE- 同時に指定されている。 0897 : 0898 : if (nr—threads > = max_threads) goto bad—fork—cleanup—count ; 0875 : 0876 : 0877 : 0878 : retval —ENOMEM ; p = dup—task—struct (current) ; if (!p) goto fork-out ; システム全体の総プロセス数 (nr-threads) が、リミッ ト値 (max-threads) を超えていれば工ラーになります。 このリミット値は、 r 。。 t ユーザーでも超えることはできま せん。 大域変数 max-threads は、カーネルのプート時に物 理メモリ量から引算された値が設定されています。その値 は、 /proc/sys/kernel/threads-max で確認できます。 876 行目で呼び出している dup-task-struct ( ) は、新 たにプロセス・デスクリプタとカーネルモード・スタック 用のメモリ領域を確保し、カレントプロセスからプロセス・ デスクリプタの内容をコピーします。カーネルモード・ス タックはコピーしません。 dup-task-struct() は、たんにデスクリプタの内容をメ モリコピーするだけです。したがって、この時点ではまっ たく同じプロセスが 2 つできてしまいます。しかし、この 段階では新しいプロセスは誰からも存在を知られていない ので問題はありません。以降で、新しいプロセスに必要な 情報を設疋していきます。 0900 : 0901 : 0902 : 0903 : 0904 : if ( ! try—module—get (p—>thread—info—> exec—domain—>module) ) goto bad—fork—cleanup—count ; if (p—>binfmt & & ! try-module—get (p—>binfmt—>module) ) goto bad—fork—cleanup—put—domain ; 0880 : 0881 : 0883 : 0884 : 0885 : 0886 : 0887 : 0888 : 0889 : 0890 : 94 retval = —EAGAIN ; if (atomic—read(&p—>user—>processes) > = p—>signal—>rlim [RLIMIT—NPROC] —count) ; . rlim-cur) { atomic—inc (&p—>user—> goto bad—fork—free ; p—>user ! = &root—user) ! capable (CAP-SYS-RESOURCE) & & if ( ! capab1e(CAP—SYS_ADMIN) & & get—group—info (p->group-info) ; atomic—inc(&p—>user—>processes) ; Linux カーネルは、複数の実行ファイル形式をサポー トしています。たとえば、現在もっともよく使われている ELF (ExecutabIe and Linking Format) や、旧い形式 の a. out (Assembler OUTput format) のようなバイ ナリファイル、そして、シェル・スクリプトも実行ファイ ル形式の 1 っとしてサポートされています。 さらに、 Linux カーネルは、ほかの OS (Solaris など ) 用のバイナリを実行できる仕組みも実装しています。基本 的に UNIX のシステムコールやシグナルの仕様は POSIX UNIX MAGAZ 工 NE 2006.3 1 capabilities ( 7 ) のマニュアルを参照してください。 ています。 用にコンパイルされたバイナリを実行する仕組みを実装し ティ (personality) と呼ばれる機構で吸収し、ほかの OS 違いを実行ドメイン (execution domain) とパーソナリ るためロ少な違いがあったりします。そこで、それらの で標準化されていますが、各 OS カ融自に拡張を加えてい

8. UNIX MAGAZINE 2006年3月号

0256 : 0257 : 0258 : syscall-exit-work() の処理については前回の記事を参 popl %ebx popl %edx popl %edi popl %ebp 照してください。 popl popl popl popl popl addl iret %ecx %eax %ds %es $ 4 , %esp # システムコールの戻り値 システムコール番号を捨てる # 割込みハンドラからの復帰 上記のフラグがセットされていなければ、スタックに待 避させたレジスタの内容をロードして、割込み発生時の状 態に戻します。ただし、 EAX にロードする位置の内容は 236 行目で書き換えられているため、もとの値 ( システム コール番号 ) には戻らす、、システムコールの戻り値 " が格 納されます。 、、決まりごと " として、ハンドラルーチンの最後にはかなら ず iret 命令を実行します。この命令は、スタックから EIP と CS 、 EFLAGS 、 ESP 、 SS の内容をロードし、割込み 発生時の実行位置に復帰する処理をおこないます。 fork システムコール fork システムコールは、カレントプロセスがもつ反想ア ドレス空間やファイル・デスクリプタなどの資源をコピー し、新しいプロセスを生成します。このとき、 fork システ ムコールを実行したものが、、親プロセス " 、新しく作られた ものが、、子プロセス " です。 ューザープロセスが fork システムコールを発行する場 合には、以下のようにソフトウェア割込みを発生させます。 movl $ 0X2 , %eax # fork のシステムコール番号 int $ 0X80 「こんなコード、書いたことないで」 と言いたくなるかもしれません。それもそのはず、通常、 C 言語のプログラマーがこのようなアセンプリコードを書 く必要はありません。標準ライプラリの fork() を利用す ると、 fork() がプログラマーに代わってシステムコールを 発行してくれるのです。 ところが、 ( いつからそうなったのかは分かりませんが ) Linux の libc ライプラリの fork() は、実際は fork で UN 工 X MAGAZINE 2006.3 特集マ Linux のプロセス [ 3 ] はなく clone システムコールを使うように実装されていま す。もちろん、 Linux カーネルには fork システムコール も用意されているので、以降では、 fork と clone システム コールの両方のコードを紹介します。 sys-fork() と sys-clone sys-fork() と sys-clone() は、前述の system-call() の 235 行目から呼び出されるサプルーチンです。 どちらのコードも arch/i386/kernel/process. c で定 義されていますが、両者には、 do-fork() に指定するフラ グをユーザーが制御できるかどうかといった程度の違いし かありません。その未では、 fork() が clone システムコ ールで実装されているのも納得できます。 プロセスを生成する処理の実体は、 do-fork() で実装さ れています。 0738 : 0737 : 0736 : } 0735 : 0734 : 0733 : asmlinkage &regs , 0 , NULL , NULL) ; return d0-fork(SIGCHLD, regs ・ esp, int sys—fork(struct pt-regs regs) asmlinkage 0739 : { 0744 : 0745 : 0746 : 0747 : 0748 : 0749 : 0750 : 0751 : } int sys—clone(struct pt—regs regs) clone—flags = regs ・ ebx; newsp = regs . eCX ; parent—tidptr = regs . edx ; child—tidptr = regs . edi ; if ( ! newsp) newsp = regs . esp , return do—fork(clone—flags , newsp , &regs , 0 , parent—tidptr , child—tidptr) ; こで注目すべきポイントは構造体 pt-regs です。 の構造体は、ファイル include/asm-i386/ptrace. h で 図 5 のように定義されています。 これは、 system-call() がレジスタの内容をスタックに 格納した順番と同じです ( さきほどの図 4 も参照してくだ さい ) 。そして、 system-call() の 235 行目の ca Ⅱ命令に より、戻りアドレス ( 236 行目のアドレス ) がスタックに 積まれます。このため、 sys-clone() はスタック上のレジ スタの内容を、関数に渡された引数のように参照すること ができます。 91

9. UNIX MAGAZINE 2006年3月号

} else if (diffsecs > = 3600 . 0 ) { if (verbose & & dbg-at—times) printf ("\n\t-—> prev trim at %s\t" ptimeget—ctime ( 疇 - ent—>trim—at) ) ; しなおし、 verbose 変数の値にもとづいてメッセージを出 そして、次にローテーションをおこなうべき時刻を言 fr 算 ptimeget—ctime(ent—>trim_at) ) ; printf ( ”ーー > i11 trim at 0/os" ptimeset—nxtime (ent—>trim—at) ; if (verbose) { 力して、関数も終了します。 バックアップする時間間隔カ甘旨定されていなければ、時間 ります。 verbose 変数カ羸疋されていて、ログファイルを りますがローテーションをおこなう可能性があることにな こまでに一致しなかった場合には、ほかの条件にもよ return (free—or—keep) ; ptimeget—ctime (ent¯>trim—at) ) ; printf ("\n\t——> timematch at dbg—at—times) { } else if (verbose & & noaction & & 疇 - る場合は、メッセージを出力して関数を終了します。 理を実行しないことを示す noaction 変数が設疋されてい verbose 変数と dbg-at-times 変数に加え、実際には処 nxtime 関数により設疋します。 次にローテーションをおこなうべき時刻は、 ptimeset- return (free—or—keep) ; また、ほかの条件カ羸疋されている場合には、 printf ( " ーー > time is up\n ent—>hours く = 0 ) { } else if (verbose & & 疇 - します。 に関する条件は整っているという意味のメッセージを出力 verb ose UNIX MAGAZINE 2006.3 modtime , ent—>hours) ; printf ( " age (hr) : %d [%d] " , if (verbose & & (ent->hours > 0 ) ) ent—>fsize, ent—>trsize) ; printf ("size (Kb) : %d [%d] " , if (verbose & & (ent->trsize > 0 ) ) グファイルの様子に関する情報を出力します。 変数の値によりますが、まず設定されている値と実際のロ プログラミング・テクニック・ ローテートするかどうかの判断 指定がない場合にはローテーションをおこなわないのです。 ローテートしないと判断されたわけです。そのため、時刻 ドのこの位置に到達するということは、サイズに関しては なければサイズが指定されていたはずですが、ソースコー せん。というのも、時間間隔と時刻の両方が指定されてい ればローテーションをおこないますが、なければ実行しま トする時間間隔が指定されていない場合は、時刻指定があ 工ントリの hours フィールドが負の値、つまり口一テー ent—>trsize) ; " due tO size>%dK" snprintf (temp—reason , REASON—MAX , 疇 , ent—>rotate (ent—>fsize > = ent—>trsize) ) { } else if ((ent—>trsize > 0 ) & & を実行します。 ァイルサイズがその値以上になっていればローテーション ローテートするサイズカ甘旨定されている場合、実際のフ ” due tO —F request") ; snprintf (temp—reason , REASON—MAX , , ent—>rotate } else if (force) { 定し、メッセージをオ内しておきます。 同じです。この場合もエントリの rotate フィールドを設 条件に関係なく口一テートするのは、 -F オプションも ます。 ションにより口一テーションを実行する旨を格納しておき ドに 1 を設疋します。また、 temp-reason には一 R オプ テーションをおこなうため、エントリの rotate フィール このオプションカ甘旨定されていると、条件に関係なく口一 まずは一 R オプションが指定されているかどうかです。 requestor) ; due tO —R from O/OS" snprintf (temp—reason, REASON—MAX , 、 ent—>rotate if (rotatereq) { temp—reason [ 0 ] そのため、空文字列に初期化しなおしています。 今度はローテーションする理由をイ尉寺するのに利用します。 い理由を保持するために temp-reason を使いましたが、 うかどうかの判断です。さきほどは、ローテーションしな 次は、時刻以外の条件に関してローテーションをおこな 85

10. UNIX MAGAZINE 2006年3月号

特集 か Atomic に更新できない。これは、書込み能力の限界を 試すようなべンチマークでは苦しい。 2 つ目は、上記の問題を回避するための方法で発生する。 これは、一殳に、、部分 Logging" と呼ばれる方法で、たと えばメタデータだけを Logg ⅲ g の対象にしようというも のだ。これを利用すると、書込み川印がさらにややこしく なる。データを CoM 方式で処理する必要があるが、それ では fragmentation 問題の発生は防げない。既存のデー タを一部上書き、一部追加するような場合には、次のよう なことを考えなければならない。 追加部分、上書き部分、メタデータをどの順序で書けば 矛盾がなくなるのか。 ・どのような情報をログ上に残すのか。 ・どこまで終了していれば、完全に再実行してよいのか。 ・どこまで終了していれば、、、なかったこと " にしなくては いけないのか。 これらの点を相当考えたつもりでも、結果的には考慮不 足に陥っている場合が多い。 3 つ目の弱点は、もっとリである。すなわち、 、、ログを書き忘れても簡単にはバグが分からない " ことだ。事実、 Logging 方式における 2 大バグは、 ・ログに記録すべき更新情報をまとめるバッフアに更新記 録を書き忘れる そのバッフアを Write Barrier 付きでストレージに書 き込むことを忘れる である。 Logging 機能がないからといって、絶対確実にフ ァイルシステムが壊れるわけではない。かなりクリティカ ルなタイミングで電源断が発生しないかぎり、ファイルシ ステムは適当 ( いい加減 ) に更新されてしまう。また、壊れ た場合にも、木構造で問題となるポイントを参照しなけれ ば、障害カ験出されることはない。 さらに、 Logg ⅲ g 機能のために、ファイルシステム整合 性確認プログラム (fsck) がろくなチェックをしていない場 合もある 6 。これでは、不整合カ材衾出できない。 6 Linux の XFS など、、 return 0 ; " としか書かれていない。なんという ・ずいぶんと思い切りのよいシステムである。 UNIX MAGAZINE 2006 . 3 仮想ストレージとしてのファイルシステム [ 1 ] その結果、不整合カ緻命傷になるまで障害カ験出できな いという問題が発生する。もちろん、その場合に原因カ畤 定できる可能性はほとんどない。 今回は、ファイルシステムに求められる要件を概観した。 そもそも、我々はなぜストレージを使うのか、ストレー ジはどういう性質のものなのかも述べた。そして、ファイ ルシステムの大きな構造である VFS をとりあげ、その下 半分にあたる Storage Layout 層について駆け足で説明 0 次回は、 Attribute Handler 層について説明する。な ぜ、 VFS をこのポイントで分割したのかについても述べ る。それまで待てない人は、「特開 2004-334419 」という 資料を参照してほしい。 Layout lnterface を独立させた 理由が分かるはずだ。 そして、理想論と比較した現実のファイルシステムの状 況、最新の動向について説明する予定だ。具体的な標的は Linux である。今回は理侖を述べたので抑え気未だった 、、巧の教祖様 " の怨嗟の声にも期待していただきたい。 ( おくやま・けんいち NTT データ先端技術 ) ☆ 41