シンボリックリンクについて教えてください [1996.2]

ディレクトリエントリは名前と i ノード番号の組でできている.ファイルの実体は i ノード番号で識別されるので,ひとつの i ノード番号が複数のディレクトリエントリに含まれていれば,ファイルに複数の名前がつくことになる.すでに存在するファイルについてそのファイルの i ノード番号をもったディレクトリエントリを作成するシステムコールが link である.コマンドは ln だ.新たにリンクが作られたとき,どちらの名前が親であるといった関係はない.どちらもまったく同等の扱いとなる.ファイル実体側ではリンクカウントを保持していて,いくつの名前がついているかや,プロセスからオープンされているかを数えている.この数が 0 になると,ファイル実体を消去する.link システムコールにより,このカウントが1増える.逆にリンクを消去するのは unlink システムコールで,これを使う代表的コマンドは rm である.i ノード番号はファイルシステム (mount コマンドで表示されるディスクパーティションごとのファイルの管理単位) に固有の番号なので,ファイルシステムを越えてリンクを作ることはできない.この制限を解消したのがシンボリックリンクである.

シンボリックリンクは 1983年にリリースされた 4.2BSD で実装された機能である.シンボリックリンクは上で述べたリンク (シンボリックに対してハードリンクと呼ばれる) と違って i ノードの代わりにファイルのパス名を保持する.ハードリンクと違って,リンクの同等性はない.また,シンボリックリンクがつくられても実体のファイルのリンクカウントは増えない.よって,シンボリックリンクがあってもファイル実体が消去されていることがあり得る.また作成時にはリンクがループすることも検知できない.そこで,リンクをオープンして例えば 20回シンボリックリンクをたどるとエラーとしている.

このようにシンボリックリンクは同等性がなかったり,実体のないリンクが作れたりと,UNIX の作りからいって「美しくない」機能 / 実装であるとして嫌う人もいるようだ.とはいえ,コピーを伴わないでファイルシステムを越えてファイルを指すのはシンボリックリンクだけである.例えばプリンタスプールを行なう lpr コマンドは通常はプリントしようとするファイルをスプール領域にコピーするが,-s オプションを付ければコピーをせずシンボリックリンクを作成する.これにより大きなファイルのプリントができる,といった便利さがある.


man ページを印刷したいのですが. [1996.4]

UNIX は研究者の日常をサポートすることがその目的のひとつであった.プログラムを作ったり,デバッグを行なうのはもちろん,データ整理,マニュアルや論文を書く仕事,つまり文書処理のサポートも当初から充実していた.例えば,テキストエディタ,テキストフォーマッタ,スペルチェッカ,表作成,グラフ作成,参考文献欄作成支援などである.自分たちが便利に使うために整っていったシステムであるから,当時のデバイスで質のよい出力がほどほどの労力で完成するよう,バランスのとれたシステムとなっている.

UNIX だけで閉じないのは出力用の外部デバイスである.当時利用できたのは,タイプライタ,Tektronix や HP のグラフィックターミナル,プロッタ,電算写植機等でこれらを使いこなす多くのコマンドや設定ファイルが,現在の UNIX にも付属している.本来,多様な出力デバイスに対応するためには,フォーマッタ等の出力は機種には依存しない形式にすべきである.有名な TeX の出力ファイルの拡張子は dvi であるが,これが device independent の意味であるのはご存じの通り.UNIX は歴史的な理由でコマンドは比較的機種依存の出力をする.man ページの清書に使われているテキストフォマッタ troff の出力は New Hampshire にあった Graphic Systems 社の C/A/T という写植機のコマンドである.この写植機を使える人はもういないだろう.この出力を PostScript に変換するコマンドのひとつが,anonymous FTP で手にはいる thack である.

troff -t -man man.1 | thack | lpr -Ppostscript

とすればきれいな出力が得られる

graph は UNIX に備わっているグラフ生成コマンドである.これは,X Y の2点を組にした入力をグラフにしてプロッタのコマンドとして出力する.こちらは特定のプロッタには依存していない.このプロッタコマンドを PostScript に変換するコマンドがやはり anonymous FTP で手にはいる plot2ps である.この 2 はもちろん to の意味である.

spline < data | graph | plot2ps | lpr -Ppostscript

とすれば,点を滑らかな曲線で結んだグラフが得られる.

UNIX はプログラミングから文書作成までの研究者環境をはじめから大事にしていた.国内では,日本語の障壁のためか,このような発想はなかったようである.日本語 OS と名がついていても,マニュアルが日本語でファイル名に漢字が使える程度のものだ.日本語 OS というなら,日本語の綴り検査,形態素解析コマンドくらい,はじめから付属してほしいものだ.


FreeBSD 4.8R の man は groff を呼び出している. 次のようにして別のソフトウェアを使わなくても PostScript プリンタに出力できる.

man -t man | lpr -Ppostscript

‡ graph や spline は FreeBSD には付属していない.ライセンスの問題でもあったのだろうか.Solaris 8 (SUNOS 5.8) には付属している.[2003.5.24]


man ページを見るこつを教えてください [1996.5]

オプションを忘れたら,ともかく -? を付けてコマンドを実行するって -? つまり,間違ったオプションを指定して,使い方が表示されるのを期待しているわけだが,echo コマンドのオプションは出てこないだろう.man コマンドはもうご存じと思うが,オプションを調べるだけかな?

マニュアルは10程度のセクションに分かれている.同じ名前で別のセクションにはいっている項目がある.例えば write や wait. これを見るには,man 2 wait のようにセクションを指定する.このへんは man man すれば書いてある.各セクションには intro 項目がはいっている.時間を作ってこれらを見ておくとよい.man 2 intro とすれば,システムコールのエラー番号とその説明が見られる.

キーワードで man を検索するのが man -k だ.catman コマンドで man ページからデータベースを作っておけば,man -k で検索できる.ただし,項目ごとにごく簡単なメッセージが得られるだけである.また,man ページのソースがあるディレクトリ man1 man2 … と同じところに,cat1 cat2 … といったディレクトリを作って書き込み可能としておくと,man コマンドで読まれたファイルの整形出力が cat ディレクトリに保持され,次からは高速に表示されるようになる.表示をページごとにするコマンドは環境変数 PAGER で設定できる.逆戻りできる less コマンドを -m -s を付けて使うと便利だ.

C の関数のページもよく使うだろう.引数の型や順序の確認は重要だ.include するファイルもわかる.だが,見かたには注意がいる.fgets を調べたらまず左のように記述されている.これを見てプログラムを書いた.

マニュアル 作ったプログラム
#include <stdio.h>

char *
fgets(char *str, int size, FILE *stream)
#include <stdio.h>
char *str;

while (fgets(str, 256, stdin)) { … }

このプログラムは思ったようには動かない.str=(char *)malloc(256); か,char str[256]; のように宣言して記憶領域の確保が必要だ.これはマニュアルの概要説明の欄からはわからないことである.strcpy, strcat 等の文字列処理関数も同様の注意が必要だ.マニュアルは,あたかも fgets のプログラムを自分で書いたときのように記述されている.fgets を呼ぶときの形には書いていない.


マクロについて教えてください [1996.7]

C 言語の #define が身近なマクロだろう.ソースの文字列レベルでマクロ名をその定義で置き換える.引数をとることもできる.プログラムソースから意味不明の数を追い出したり,定型的な処理をまとめるのによく使われる.コンパイラによる構文や型,意味チェックの前に行なわれるので,いくつもの型に使える定義が作れるし,関数呼び出しのオーバヘッドもない.だが,実行時にはマクロ定義の情報はないので,デバッガを利用するときには注意が必要だ.

マクロはもともとアセンブラを効率的に書くのに考案された.macro instruction が元の名前のようだ.アセンブラの1命令は計算機の機械語の1命令に対応するのが原則だ.だが,よく出てくる一連の命令列に名前を付け,それをあたかも計算機の命令のようにアセンブラ命令として書けるようにしたのがマクロ命令 (= 大きな命令) である.オペランド (アセンブラ命令の引数にあたる) によって展開型を変えたり,局所的なラベルを自動生成するといった,C のものよりずっと機能の多いマクロ処理機能が使用できた. もちろん C では,マクロではなく言語がその機能を担っているわけで,C のマクロが機能不足というわけではない.

ちょっと感じの違うマクロは Emacs エディタのキーボードマクロだ.C-x ( を入力すると,それ以降のキーボード入力が記録される.テキストの入力や削除,カーソル移動,サーチやバッファ移動等,エディタ機能はすべて有効なまま記録が進む.C-x ) で記録を終了した後は,C-x e を打つことによって,記録した入力があたかもキーボードから打たれたかのようにエディタに送られ,エディタは自動走行する.記録に名前をつければ M-x で呼び出せるし,さらにキーに割り当てれば他の Emacs コマンドとまったく同様に使用できる.一連の命令に名前をつけるという意味では,結果を確かめた動作がそのまま定義になるマクロそのものである.

C のマクロはシンプルであるが,それでも ANSI C で新しい機能が増えている.マクロ引数の展開時の文字列化機能と,トークンを連結する ## オペレータの導入である. マクロの定義中にダブルクオートで囲まれたマクロ引数があるとき,ANSI C では展開されなくなった.注意が必要である.

マクロは文字列の置き換えであるから,やれば何でもできてしまう.Bourne shell の (BSD ではなく,Bell 研究所 の) ソースには

#define BEGIN {

といった定義があった. マクロ定義を知らなければ,プログラムを理解することも再利用もできない.このへんを嫌って Java ではマクロはなくなった.これも見識であろう.


この文章はマクロについての答えであってマグロについてではない


時計について教えてください [1996.8]

時計が計るのは時刻と時間の二つだが,まず時刻.UNIX では 1970年1月1日 00:00:00 GMT からの経過秒数で時刻を保持している.マイクロセコンド単位の時刻も保持しているが,精度はハードウェアや OS によって異なる.経過秒数と地域の時差と夏時間の設定により,その地域での日付時刻が得られる.特に夏時間は気軽に変更されてしまうため,設定ファイル変更で簡単に設定できるようになっている.時刻合わせはスーパーユーザが date コマンドで設定するが,日付は西暦の下2桁から指定する.よって西暦2000年以降は日付がいれられない.GNU プロジェクトの shellutils にある date は年を 4桁で指定できるようになっている.UNIX に2000年問題はあまりないのだが,2038年に大問題が待っている. 1970年からの経過秒数が1月19日03:14:07 GMT で31bit を越えるのだ.ファイルの日付時刻などもすべてこの経過時間で保持されているのでいろいろ面倒がおきるだろう.逆に UNIX の寿命は68年と決められていたのかもしれない.

ネットワークで接続されたマシン間での時刻合わせは重要である.特にファイルシステムをネットワーク経由で共有しているときなど,時刻が合っていないとファイルの修 正時刻がファイルの新旧を反映せず,make でファイル更新がなされないようなことも起きる.時計は rdate コマンドがあれば手軽に合わせられる.これは tcp の37番 port で1900年1月1日からの経過秒の32bit の数値を得て日付をセットする (RFC 868).ただしこちらも2036年までだ.リモートホストの時刻を見やすく得るには telnet host daytime を打ってみればいい (RFC 867).

時計が進んでいる場合,いきなり正しい値に戻すと,その間に作られたファイルが未来のファイルになったりする. OS によっては,時計の進みを速くしたり遅くするシステムコールを用意して,時刻の飛び越しを防ぐことが可能になっているものもある.あれば date コマンドがこれを使っているだろうから man でチェックしてみよう.

ネットワーク内のマシン間の時刻合わせを本格的に実現するプロトコルが Network Time Protcol (RFC 1119) だ. これの実現である xntp はマシン間の時刻合せだけでなく長波による標準電波受信機や手軽な GPS 受信機から時刻を得て,標準時に時刻を合わせる機能も用意している.通信の世界では,国中の交換機がひとつのクロックに同期して動作するようなことになっている.計算機の時計はもともとそんなに正確ではないが,時報と合っているのは大切なことだ.


パスワードの仕組みを教えてください [1996.10]

伝統的 UNIX ではファイル /etc/passwd に利用者名,暗号化されたログインパスワード等の情報が書かれていて,誰でも読めた.コロンで区切られた第2欄がパスワード情報だ. 利用者が passwd コマンドに入力したパスワード文字列は DES や MD5といった手法で暗号化される.このとき,日付時刻等から2文字の種 (salt と呼ばれる) を作って暗号化に使用する.salt を使うのは,同じパスワード文字列が同じ暗号文字列に変換されるのを防ぐためだ.暗号文字列の複号ができなくとも,同じパスワードが同じ文字列になってしまっては,同じパスワードを使った人同士でお互いのパスワードがわかってしまう.パスワード情報の最初に2文字がこの選ばれた salt 文字列になっている.多数のアカウントを自動生成するときには salt をランダムに選ぶよう気をつけよう.

ログインするときにパスワードを入力するが,暗号化された文字列から元のパスワードを復元して,入力文字列と比較をするのではない.パスワード情報の salt を使って入力された文字列を暗号化し,同じ暗号化文字列になれば正しいパスワードであるとするのだ.もちろん,複号困難な暗号化手法を使用する.login コマンドは,存在しないユーザ名が入力されてもパスワードを要求する.また,パスワードの暗号化にはわざと時間をかけるようにする.これにより,適当なパスワードをいれて試す攻撃を少しでも防ごうとするのだ.しかし /etc/passwd が読める限り,高速の暗号化ルーティンを使って多数の文字列 (例えば辞書すべて) を暗号化して比較することも非現実の話しではなくなってきた.いまではパスワード情報を隠す UNIX システムも多い.

しかし,ネットワーク時代ではこれらの防御策はもはや有効ではない.telnet や ftp コマンドでは,パスワード文字列そのものがネットワークを通して送られているのだ. 葉書にパスワードを書いているようなもので,簡単にのぞかれると思って間違いない.最近は,外部から login するためのホストには 1回限りの使い捨てパスワードの機能をいれたり,時刻に同期してパスワードを表示するカードを使ったりして,入力したパスワードがのぞかれても比較的安全なシステムを構築することができる.ネット上のアタックはいまや他人ごとではない.真剣に考えたほうがよいだろう.


いまは ssh ばかりです [2003.5.29]
ssh とは rsh と同様の機能のソフトウェアだが,パスワードや通信内容の暗号化,通信相手の認証,トンネリング機能等を加えたより強力なものだ.フリーで実装されている OpenSSH と商用になっている SSH がある.[2003.7.3]


   タイトル一覧  ホーム
webmaster