tail(1)
Plan9にはtail(1)はあるけど、head(1)がない。頭隠して尻隠さず?
戯言はおいておいて、最近古本屋で見つけた「デーモン君のソースコード探検」にtailの実装にmmapを使っているという話が書かれていた。確かにお尻からfseek(fseeko)でちまちまさかのぼるより効率が良さそうだ。Plan9はmmapを持たないので、どんな実装になっているのか調べてみた。
オプションによって挙動は変わるが、お尻のcount行を表示はtail.c:keep()関数で処理している。何のことはない、count行分をバッファに残しながら、頭からreadするという、すごく単純な実装だった。
/* * read whole file, keeping the tail * complexity is length(file)*length(tail). * could be linear. */ void keep(void) { int len = 0; long bufsiz = 0; char *buf = 0; int j, k, n; for(n=1; n;) { if(len+Bsize > bufsiz) { bufsiz += 2*Bsize; if(!(buf = realloc(buf, bufsiz+1))) fatal("out of space"); } for(; n && len<bufsiz; len+=n) n = tread(buf+len, bufsiz-len); if(count >= len) continue; if(units == CHARS) j = len - count; else { /* units == LINES */ j = buf[len-1]=='\n'? len-1: len; for(k=0; j>0; j--) if(buf[j-1] == '\n') if(++k >= count) break; } memmove(buf, buf+j, len-=j); } if(dir == REV) { if(len>0 && buf[len-1]!='\n') buf[len++] = '\n'; for(j=len-1 ; j>0; j--) if(buf[j-1] == '\n') { twrite(buf+j, len-j); if(--count <= 0) return; len = j; } } if(count > 0) twrite(buf, len); }
話は戻って、「デーモン君のソースコード探検」はNetBSD 1.6を素材に使っているが、OpenBSDのtailはNetBSDがmmapを使う以前にブランチしたものらしく、fseekoでちまちまファイルポインタを動かしている。mmapを使わない理由はあるのだろうか?
CVSリポジトリへのリンクを示すが、この処理を行っている箇所は、forward.c:rlines()。
また、同書では-fオプションの実装を調べていて、selectでタイムアウトするテクニック*1を紹介しているのだが、今の実装はどちらのBSDでも、kqueue&keventを使っている。
単純なコマンドでも、読んでみると勉強になるなぁ。
デーモン君のソース探検―BSDのソースコードを探る冒険者たちのための手引き書 (BSD magazine Books)
- 作者: 氷山素子
- 出版社/メーカー: アスキー
- 発売日: 2004/02
- メディア: 単行本
- 購入: 5人 クリック: 184回
- この商品を含むブログ (29件) を見る