そもそも UNIX とはなんですか? [1992.11]

計算機は石頭だ,教えたことしかできない,とよくいわれる.

しかし,UNIX 計算機にさわってみると,利用者が教えたわけでもないのに,実にさまざまなコマンドやプログラムを初めから知っている.初級利用者にとって,このコマンド群とこれらを起動し,組み合わせるシェルが UNIX であろう.コマンドだけが UNIX ではもちろんないが,UNIX の必要条件として重要である.次に,C プログラムを書くようになると意識するのが,標準ライブラリとシステムコールの存在である.C から見る限りライブラリとシステムコールの差はマニュアルの章の差だけだが,ライブラリはシステムコールとのつなぎ,または,走らせたプログラムそれだけに閉じた仕事しか行なわない.プログラムそのものの実行,他のプログラムとの通信,入出力といった他のと関わりのある仕事はシステムコールによってなされる.システムコールの種類,働きは UNIX を特徴づける大きな要素である.

例えば,UNIX ではファイルを新たに作成するのに creat (create ではない) システムコールを使う [注: 最近は open を使う].これに対して,消去しようとするときに使うシステムコールの名前は,delete でも remove でも purge でも kill でもなく,unlink である.UNIX ではひとつのファイル実体に複数の名前を付けることが可能であり,それには link システムコールを使う.unlink でこのリンクを消していき,リンクが 0になり,ファイルを使っているプログラムもなくなったとき,そのファイルが専有していたディスク領域や管理用の表の項目はすべて解放される.これらがシステムコールの働きである.

システムコールは働きを決めたものであるから,働きかける対象を決めなければいけない.上の例のファイル,プロセス,ユーザ,が UNIX の重要な操作対象である.プロセスはプログラムの実行そのものといえる.ひとつのプロセスは必ずあるひとつのプログラムを実行しているし,プログラムはひとつのプロセスでのみ実行される.ユーザは人間である利用者の UNIX での表現である.ファイルやプロセスの持ち主,計算機資源の利用割当てなどはこのユーザごとに決められる. そして,これらの間の関係,システムコールによって起動されるこれらに対する作用すべてが UNIX を特徴づけている. 例えば,作成したファイルには持ち主であるユーザがある. このファイルにあるプロセスがデータを書き込むと,それを使っているすべてのプロセスは,書き込まれたデータを直ちに読むことができる.これらは,単独のシステムコールの動作というより,UNIX そのもの動作といえる.

これら利用者の方向からではなく,接続されたデバイスからの要求による動作も UNIX の一側面を形づくっている.コマンド,ライブラリ,システムコールとその操作対象,UNIX そのものの動作,これらすべてで UNIX が成り立つ.


rm したファイルを復活したいのですが? [1992.12]

そのファイルは,別の名前にリンクされていないか? エディタに読み込んではないか? ファイルをオープンしているプロセスは? 磁気テープにバックアップが残っているか? どれもノーなら,あきらめざるを得ない.プリントアウトからの再打ち込みが残された手段だ.

UNIX のファイルは,ディスクの (例えば 8K バイトの) ブロックに分けて収められる.収めたブロックは連続になることはあまりない.ファイルをどのブロックに順に収めたかの情報はディレクトリにのみあって,rm したとたんに消去される.ファイルが収まっていたブロックは未使用ブロックとして登録され,別のプロセスがブロックを要求したときにまっさきに使われ,新たなデータが書き込まれる. つまり,新たなデータが書き込まれるまでは,rm してもブロックのデータ自体は残っている.よって,rm した直後にマシンを止めるか,そのディスクパーティションを切り離すことができれば,ブロックをすべて調べることでデータを復活することはできないことはない.特に,1ブロックに収まっているテキストファイルなら,strings /dev/rsd0a のようにして連続した文字列を得ることで,ある程度は復活できるだろう.

誤って別のディスクパーティションを newfs (mkfs) してしまったときも同様である.newfs のパラメータがそれまでのファイルシステムと同じなら,newfs はデータブロックの内容を壊さない.ただし,どんなファイルがあったか,ファイルをどのブロックに収めていたかの情報は,完全に失われる.データの断片からファイルを復活するのは忍耐と根気のいる作業であるが,世の中にはこの作業に取り組まざるを得ないシステム管理者が後を断たない.だが,別のディスクを format されてしまった管理者に比べれば !

rm コマンドを mv に置き換えて,ファイルの消去を遅らせ,事故を防ごうとしている例があるが,別の意味でこれも危険である.この仕掛けに慣れたころ,別のマシン,別のユーザでとびきり重要なファイルを本物の rm で失うことになるだろう.結局,UNIX では残念ながら,rm には気をつける,以外にない.使っているシェルにファイル名のコンプリーション機能があるなら必ずこれを使って,正規表現を使わない,Emacs の Dired でだけ消去して,rm を使わない,といった自衛しかない.


最近はディスクも安価なので,同じ型番のディスクを2台装備し, 夜中に rsync で常用のディスクの内容をすべてもう1台のディスクにコピーしている. ミラーリング (RAID Level 1) とは違う運用だ.昨日のファイルではあるが,復活できるのは気分的に楽である.また,自分で作ったファイルは,ほぼすべて CVS で管理している. 履歴が残るのもありがたいし,複数の計算機でファイルの同期をとるのにも重宝している. [2003.5.24]


UNIX のプロセスとはなんですか? [1993.1]

複数のプログラムを並行に実行したいときには,おのおののプログラムに1台ずつ計算機を与えるのが簡単だ.1台の計算機でいくつものプログラムを並行に実行させるには,おのおののプログラムがそれぞれ専用の仮想計算機で実行していると考え,時間を区切っておのおのの仮想計算機が現実のハードウェアとなるようにする.この実行中プログラムとそれ専用の仮想計算機の組が UNIX のプロセスである.OS の観点では,CPU やメモリ領域などの計算機資源の割当て,シグナルや割込みの対象であり,利用者から見れば,走っているプログラムがプロセスである.

プロセスの生成には fork システムコールを使う.fork を呼び出すと,呼び出したプロセスのメモリのデータ領域とスタックの複製をもち,(通常は) プログラム領域 (テキスト領域) を共有した新たなプロセスが作られる.fork 自体は親プロセスと子プロセスの両方に異なった返却値をもって戻る.ファイル実行のシステムコール (execve など) を呼び出すと,プロセスの複製されていたメモリ領域をそのファイル内容で初期化し,指定された番地から実行を開始する.このシステムコールはエラー以外では元のプログラムには戻らない (正常に開始できたら,元のプログラムはなくなってしまう).これからわかるように,UNIX のプロセスは木構造をなす.プロセスを作るには必ずプロセスが必要である. プロセス生成の根となるプロセスは init プロセスと呼ばれ,システムのブート時にいわば手作りされる.これからコンソールやネットワークで利用者のログインを待つプロセスを生成されていく.プロセスは exit システムコールを呼んだり,特定のシグナルを受けることで生存を終える.終了時にはステータスを親プロセスに知らせる.親プロセスはこのステータスを wait システムコールで受取らなければいけない.受取るまでは,子プロセスはゾンビ (zombie) プロセスとしてカーネルのプロセステーブルを専有する.子より先に親が終了したときには,init プロセスが終了ステータスを受取ってくれる.

プロセスは,<sys/proc.h> と <sys/user.h> で定義されている proc 構造体と user 構造体の値でその状態が表わされる.構造体の内容はものによって異なっているが,user はプログラム走行中に必要なもの,proc は常に必要なものであることは変わらない.


ファイルの消しかたは? [1993.2]

名前に - や *,空白などの文字があるファイルを消すには,少しの知識と充分な注意がいる.

- ではじまる名前: rm ./-name (ファイルのあるディレクトリで実行する.- や -- をオプションの終わりの意味とすることもあるので,rm - -file や rm -- -file で -file を消せることもある.また,SUN OS の /etc/unlink はオプションをとらないので,/etc/unlink -file のようにのまま名前を - ではじめてよい../- や - -- は rm だけでなく,他のプログラムでも使えるテクニックである.)

空白やエスケープのある名前: rm -i match*the*file (rm をそのファイルにマッチする正規表現で起動し,-i で目的のファイルだけに y と答える.空白や * なら,単に rm -i 'f le' のようにクオートしてもよい.すべてが EUC 漢字の名前などで正規表現が困難なら,ls -i で i ノード番号を得て,例えば番号が 300 だったら,

find . -inum 300 -ok rm '{}' \;

とする.)

UNIX でファイルを消すのに使うシステムコールは unlink である.unlink の引数はファイルパス名の文字列であって,i ノード番号ではない.上の find で i ノード番号を指定するが,find はディレクトリをみて i ノード番号から名前を得,それを unlink に渡す.よって,ハードリンクされて名前を二つ以上もっているファイルは,find の探索範囲にあればすべて rm の候補となる.unlink はディレクトリからその名前を消去し,ファイルのリンクの数 (いくつの名前がついているか) を減らす.リンクの数が 0 になったらファイルが占めているディスクブロックを開放する.

だが,ディレクトリや i ノード情報もディスクにある.UNIX システムコールを経由せずにディスク上の情報を書き換えてしまうことも可能である.もちろんファイルシステムの首尾一貫性を失う可能性のあるたいへん危険な操作である.これを行なうコマンドとして clri (clear an inode) がある.これはディスクのファイルシステムのスーパーブロックに収められている i ノード情報をクリアするコマンドである.実行した直後は,首尾一貫性が失われている.このコマンドは,なにかの事故により,どこのディレクトリにも存在しなかったり,名前に / が含まれたファイルができてしまったときの最後の砦である./ が含まれた名前のファイルは,UNIX では決して作れないが,他の OS のファイルシステムをマウントしたり,NFS したとき,バグにより作られる可能性がある.clri の後,fsck を実行して首尾一貫性を回復すると共に,ディスクブロックを開放しなければならない.


正規表現とはなんですか? [1993.3]

コマンドにファイル名をいくつも渡すのに,すべてのファイル名を書き並べるのは煩雑だ.短いパターン文字列に一定のルールを適用して複数の文字列を作り出すようにすれば,ファイルの名前がそのパターンとルールで作り出せるかどうかで名前を分類することができる.作り出せるとき,名前がパターンにマッチする,と言おう.UNIX のシェルは,打たれた1行の文字列をすべてこのパターンの並びとみなしてパターンにマッチするファイル名をすべて選んで並べる. こうしてできた文字列の最初のファイル名をサーチパスから探し出しプロセスにして実行し,残りを引数として渡す. 例えば,あるディレクトリに a.out x.c x.h の三つのファイルがあったとき,このディレクトリをカレントディレクトリにしてから * と打てば,a.out が実行され,名前 x.c x.h が a.out に引数として渡される.このパターンのことを普通は正規表現 (regular expression) と呼んでいる.

言語理論の定義では,終端記号の集合 A={a1,a2,…,an} 上の正規表現とは

  1. 空列ε
  2. A の任意の要素 ai
  3. R, S を正規表現としたとき,その和 R|S, 連接 RS, 閉包 R* の規則で生成される A 上の長さ 0 以上の列の集合

と定義されている.つまり,生成される (無限個あるかもしれない) 列の集合をいう.正規表現で生成される言語のクラスは,有限状態オートマトンで受理される言語のクラスと同じである.例えば,「a がひとつ以上連続し,その次に b がひとつ以上連続する」ような列の集合は正規表現で表わせるが,「a と b を同数含むような」列や「前半の文字列と後半の文字列が逆順であるような」列の集合は表現できない.

UNIX での正規表現は,主に文字列にマッチするパターンとして使われるので,いろいろ拡張されている.任意の 1 文字を表わす ? や,行頭 / 行末を表わす ^ $ といったメタ文字を導入したり,\( \) によるグルーピングとその参照が可能になっている.ただし,コマンドによってパターンの書き方が異なっているので注意が必要である.? の代わりにピリオドを使ったり,* の意味が異なったりする.また grep に代表されるパターン探索コマンドは,「パターンとマッチする文字列を含む行全体」を表示する.よって,abc を含む行を探すには,?*abc?* ではなく,単に abc と書けばよい.


rc が名前につくファイルについて教えてください [1993.4]

ホームディレクトリで何気なく ls -a など叩いてみると,普段は見えないピリオドファイルが以外に多いことに驚く.せいぜい .login や .cshrc くらいかと思いがちだ.ログインしたときの各種の設定値を収めたファイルがこれら rc ファイルだ.中身はシェルのプログラムの形で記述されている."rc" の名前は,MIT CTSS システムでファイルにいれた一連のコマンドを実行させること,"run command", "runcom" からきている.(CTSS: Compatible Time-Sharing System. 世界最初の本格的 TSS. 1963年完成.CPU は IBM 7094 であった.CTSS の成功を受けてかの MULTICS のプロジェクトが開始された.)

シェルによって読む (そして実行する) ファイルの名前が異なっている.また,ログインで起動されるシェル (ログインシェル) だけが読むファイルもある.ファイルの owner がまちがっている (ユーザ登録時の root のままなど) と読まないことがあるので注意がいる.ログインシェルはたいてい複数のファイルが読むが,その順番にも注意がいる.例えば,csh (newcsh) はホームディレクトリ (以下 ~/) の .cshrc, ~/.login の順に読む.csh を起動してスクリプトを実行させるような時は ~/.cshrc を読みこむ.(どちらも読まないことを指示する -f オプションがないとき.) よって,端末の初期設定の stty や tset, 到着メールチェックの from などは ~/.login にいれる.よく rsh (remote shell) が動かないと聞かれるが,多くの場合,~/.cshrc に stty や biff y が書いてあるのが原因である.at が動かないのも同じ原因であることが多い.どうしても ~/.cshrc に書きたいなら,

   if ( $?prompt ) then
      ログインシェルに必要な設定,stty dec ...
   endif

のように書けばよい.なお,rsh は (BSD 系) UNIX でよく使われているが,TCP/IP プロトコル群の標準ではない.この系列の rlogin は RFC 1282で紹介はされている.

ユーザがログインするときの初期設定とともに,UNIX のブート時のシステム設定をするのも rc ファイルである.伝統的な UNIX では,/etc/rc や /etc/rc.local といったファイルで,各種デーモンを起動したり,ファイルシステムのチェックを実行する.


   タイトル一覧  ホーム
webmaster