UNIXというシステム

Ippei Kishida

Last-modified:2016/04/26 23:38:13.

『UNIX今日の技』のうち、UNIX の仕組とか文化とか慣習とか思想とかについて纏めたものです。

1 UNIXのシステムディレクトリ(の岸田による解釈)

勿論、UNIX は自由度の高いシステムですので、役割や名前は「自由に」変更することができます。 が、特に理由がない場合は伝統に従っておいた方が無難です。 あなたの後に管理を引き継いだ人が、あなたによる特殊ルールも覚えねばならないのは可哀想です。

以下は岸田の解釈なのであまり過信なさらぬよう。 お使いの UNIX におけるディレクトリ構造の厳密な定義は「man hier」で得ることができます。

1.1 ホームディレクトリ

歴史的経緯からすると(FreeBSDのように) /usr/home 以下にあるべきだと思うのですが、ホームディレクトリの重要性、特殊性、アクセシビリティ、マウント状態のシンプルさという観点から(Linuxのように) /home にするのにも説得力があります。 システムによって /home, /usr/home の両者がありうるということだけ覚えておけば良いでしょう。 一方しかない場合は他方にシムリンクを張っておいてやるのも良いかもしれません。

スーパーユーザのホームディレクトリは、システムによっては存在しないことがあります。 その場合、「スーパーユーザのホームディレクトリは / である」というポリシーであると考えられます (スーパーユーザも / もどちらも「ルート」と呼びますね)。 私はスーパーユーザの作業環境(ドットファイルや管理用スクリプト)で / が汚れるのが嫌いなので、こういう場合は /root を手動で作ることにしています (Linux や FreeBSD はデフォルトで /root が作られます)。

これが /home/root ではないのは何故でしょうか? それは、スーパーユーザはシステム障害時、例えば /home がマウントできない場合にも作業する必要があるからです。

1.2 バイナリの置かれる場所

バイナリ(実行形式)は基本的に、以下のディレクトリに入れられます。 /bin /sbin /usr/bin /usr/sbin /usr/local/bin /usr/local/sbin ディレクトリ名の末尾が bin になっているものは一般ユーザが使うコマンド、sbin になっているものは主にスーパーユーザによって使われる管理用のコマンドが入ります。

/bin, /usr/bin, /usr/local/bin の違いは何かというと、システムに対する重要度みたいなもんです(浅い階層に置かれているものほど重要)。 特に /usr/local 以下はアプリケーションのインストール先になることが多いので、/usr/local/bin に入ってるのはアプリケーションプログラムだと思っておけば大体間違いないと思います。

1.3 ソースの置かれるべき場所

初期の UNIX の理念に従えば、 /src :/bin, /sbin のソース /usr/src :/usr/bin, /usr/sbin のソース /usr/local/src:/usr/local/bin, /usr/local/sbin のソース のように置かれるべきだと思うのですが、最近の UN*X の開発は激しく、カーネル本体とユーザランド(カーネル以外の部分)のバージョンの整合性などもよく問題になるため、大抵の場合はカーネルとユーザランドをワンセットで配布、インストールしたりします。 そのため、/usr/src/ に全てのソースを入れ、その中でサブディレクトリを切って対処する場合が多いようです (Linux, FreeBSD はこうなってるようです)。

私が moon に vasp をインストールする時に /usr/local/src にソースを置いて、/usr/local/vasp 以下にインストールしたのはこれらのことを考慮した上でのことです。 (/usr/local/bin に入れると Red Hat の rpm で入れた物との区別がつき難くなるし、vasp のバージョン管理もややこしくなる)

1.4 その他のディレクトリ

/       :全ての大元
/etc    :計算機全体の管理に関わる設定
/mnt    :フロッピーや CD-ROM のマウントポイント
/dev    :UNIXは全てのデバイスをファイルとして扱う。それらは /dev に置かれる
/proc   :現在稼動中のプロセスの情報を管理。通常は ps コマンドなどで必要な情報を取り出す
         (仮想ディレクトリ。実体はメモリ上にあり、ディスク上にはない)
/tmp    :一時的な作業ディレクトリ。一般ユーザにも開放されている
/var    :システムログやメールスプールなど、随時更新されていく性質のファイル

2 ホームディレクトリのリードパーミッションはなぜ開いているか?

ドットファイルやスクリプトなどを他のユーザに参考にしてもらうためであり、これは UNIX の文化・伝統です。 他人のホームディレクトリをうろつくのは決してやましいことではありません。 見られたくないものはパーミッションで制限されてる筈です。 他人の技を大いに参考にしましょう。

3 ハードリンク

UNIX のファイルシステムは、一つのファイルに対して複数の名前を付けることができます。 コピーとは違い、一方で行った編集は他方でも反映されます。 また、一つの名前を削除した後も全ての名前を消すまでファイルが消えることがありません。

普通にリンクと言った場合にはハードリンクを指すことが多いのですが、ハードリンクは完全に見分のつかない新しい「本当の」名前を生成します。 しかしハードリンクは新しい名前と古い名前の見分けが全くつかず、ディレクトリツリーを絡ませるのでディレクトリへのハードリンクは禁止されています。 また、(マウントされた)他のファイルシステムへのハードリンクができません。 このように、その強力すぎる機能の為、またその制限の為、ハードリンクが使われることはあまりありません。

4 シンボリックリンク(シムリンク)

新しい「本当」の名前を生成するハードリンクとは違い、ファイルシステム上のどこかをポイントするファイルを作ることができます。 これがシムリンクです。 「パスとして使えるショートカット」と言えばイメージし易いでしょうか。 例えば、幾つかの UNIX では ippei のホームディレクトリに「/home/ippei」でも「/usr/home/ippei」でもアクセスできます。 これはシムリンクで実現されている機能です。

シムリンクでもハードリンクと同じく一つのファイルに対して複数の名前でアクセスできます。 しかしシムリンクは本当の名前を生成するわけではないので古い名前を削除するとファイルそのものがなくなってしまいます。 この状態をデッドリンクと呼びます。

シムリンクを使うことで、ディレクトリへのリンクやファイルシステムを跨ぐリンクができるようになります。

4.1 シムリンクの作り方

ln -s ファイルの本体 リンクを置く場所

ここでファイルの本体は絶対パスで指定しておくのがコツです。 相対パスだとシムリンクの置き場所を変更した時にリンクの指す場所が変わってしまいます。

4.2 シムリンクの使い方

4.2.1 よく使うディレクトリへのシムリンクをホームディレクトリ直下に張る

ln -s /host/moon1/home/ippei/study /home/ippei/study

としておけば、ホームディレクトリで「cd study」とするだけで本体のディレクトリへ飛べます。

4.2.2 コマンドサーチパスの通ったディレクトリに置く

ln -s /home/ippei/src/ren /home/ippei/bin/ 

として、さらに /home/ippei/bin にパスを通しておけばファイルシステムの何処にいても「ren」だけでコマンド ren を呼出せます。

4.2.3 ファイル名に意味を持たせる

例えば、あるプログラムが特定のディレクトリに「license」というファイルを必要としていたとします。 この時、取得したバージョンによって別の名前を「license.20020101,license.20030101,license.20040101」のように付けておいて「現在使用しているバージョンに license というシムリンクを張る」ということができます。 これがスマートな管理の仕方じゃないかな、と私は勝手に思っています。

4.2.4 NFS共有ディレクトリ内に異なるファイルを置く

クラスタ計算に上手く対応できていないプログラム(※1)だとか、あるいはNFS共有しているディレクトリに計算機毎に異なるファイルを置く必要がある場合の回避方法として使えます。 これはシムリンクが異なるファイルシステムに対してもリンク先を指定できるという特性によっています。

分かり難いですね、具体例を挙げます。

medea からパラレル計算を moon に投げるには、moon間で ssh のパスフレーズなしのログインを出来るようにする必要があります。 そこで私は各 moon固有のハードディスクにある /.home_medea_ssh に ssh の鍵を作って /home/medea/.ssh は /.home_medea_ssh へのシムリンクとしました(※2)。 こうすることで全ての moon で共通の筈の/home/medea/.sshが計算機毎に違うように見える、というわけです。

やっぱり分かり難いですね。 まあ、「NFS共有を回避する手段がある」ということだけ覚えておくと便利かも、という話です。 でもやはりシステム全体の簡潔性という観点からは好ましくないので、「やむを得ない場合のみの回避法」としてのみ使うのがベターだと思います。

:※1:MaterialsStudioとか

:※2:今の私なら /etc/sshd/config をいじると思います。

5 端末という概念

英語にすると terminal、我々もよく使いますね「ターミナル」です。

:「端末」と「ターミナル」:岸田の主観ですが、日本語の「端末」と「ターミナル」とでは若干ニュアンスが違う気がします。「ターミナル」だと「具体的な何らかのアプリケーションとしての terminal」、「端末」は「抽象的な論理的なデバイスとしての terminal」のような気がします。とりあえず以下では端末で統一しますが、分かり難ければ「putty」や「TeraTermPro」などの具体的なターミナルアプリケーションに読み換えると読み易くなると思います。

端末はシェルを始めとするストリーム入出力の土台になります。 例えばキーボード入力は端末に解釈されて、そのコードをアプリケーションに渡します。 「[C-h]を打ったときに[BackSpace]として扱う」という処理をするのは、基本的にはシェルではなく端末の仕事です。 そして、「[BackSpace] が来たら直前の1文字を消す」というのがシェルの仕事になります。

「でも『C-h をバックスペースとして扱う設定(stty erase ‘^H’)』をシェルの初期化スクリプトに仕込んだで」。 はい、その通り。 stty は zsh の内部コマンドではなく外部コマンドで、使用している端末の設定を行うという機能を持ちます。 『stty erase ‘^H’』によって「[C-h] を[BackSpace]と同等に扱う」ことを設定しているのです。 Windows 端末(Putty など)では stty を使わなくても GUI で設定できます。

なお、UNIX では端末とシェルが分離してますが、DOS では一体化していてシェルがキー入力を処理します。 この辺は UNIX がリモートログインを前提に設計されていることに因ります。 性質の違う端末からログインしているのに、リモートホストのシェルでローカルの端末の挙動を決定されたら不便だからです。

キーコードに対する挙動を変更するにはシェルではなく端末の設定をいじってやる必要があるかもしれない、ということです。

5.1 “C-[” で [ESC]

[ESC] は “C-[” になってる端末が多いです。 私の使ってる Happy Hacking Keyboard では[ESC]キーが近く打つのが苦ではないので“C-[” は使ってませんが、109キーボードを使ってる人なんかには便利かもしれません。

その他の一般的によく使われるキーコード * [C-h] erase * [C-c] interrupt * [C-z] suspend

5.2 エコーバックをさせない

今日はちょっとプログラマ寄りの話。

通常の作業でキー入力はそのまま画面に表示されます。 この機能はエコーバックと呼ばれます。 しかし、su や telnet でパスワードの入力する時にはエコーバックされません。 これがどのような仕組みで実現されているか分かるでしょうか?

プログラムが管理できるのは入力されたストリームであって、入力時の様子を制御することは原理的に不可能です。 ということで「エコーバックさせない」処理を行う場合はプログラム内部でどうにかするのではなく、プログラムから端末に「エコーバックしないようにしてくれ」とお願いすることになります。

例えば ruby なら以下のコードが参考になるでしょう。 このコードではシェルを介して端末を「エコーバックをしない」ように設定し、エコーバックさせない状態で文字列を入力、そしてエコーバックをさせるように戻しています。

#!/usr/bin/ruby
print "Password : "                      # プロンプト表示
system("stty -echo > /dev/null 2>&1");   # 端末を「エコーバックさせない」に設定
pwd = STDIN.readline                     # 標準入力から変数への入力
system("stty echo > /dev/null 2>&1");    # 端末を「エコーバックさせる」に戻す
print pwd                                # 入力された内容を表示

なお、tcsh や zsh はプロンプトを出す時に自動的にエコーバックするように復帰します。 ですから、シェルから「stty -echo」してやっても次のプロンプトではエコーバックするように戻ってしまいます (勿論 tcsh や zsh で書かれたシェルスクリプトの中ではエコーバックの設定は復帰しません)。

bsh や bash はそのような復帰は行わないので、「stty -echo」してやると通常のコマンド入力でもエコーバックを行わないようにすることができます。

5.3 [Enter] なしでの入力

どういう仕組みになっているのか、いまんところよく分かってません。 誰か教えてくれると嬉しいです。

適当なアプリのソース読めば分かるんだとは思うのですが。

6 cron

cron は UNIX における定期的な自動実行の仕組みのことです。

英語的には「クローン」が正しいけど、日本語ローカルでは「クーロンと呼ぶのもアリ」ということらしいです。 ちなみに私は「クーロン」と呼んでます。

6.1 一般ユーザの場合

例えば私は計算機の稼働状況をチェックする手製スクリプト machinestatus を15分毎に実行させています。

一般ユーザでは ~/.crontab に以下のように定義して、「crontab ~/.crontab」してやれば cron に反映されます。 HOME=/home/ippei HOST=‘odin.adachi.lab’ /15 * * * /home/ippei/bin/machinestatus > /tmp/machinestatus.txt 2> /dev/null; mv /tmp/machinestatus.txt /home/ippei/wwwAdachiLab/machinestatus.txt

以下の設定は、平日8:00 に目覚ましを鳴らします。 0 8 * * 1-5 /usr/local/bin/mpg123 asa.mp3 # mpg123 は mp3 を演奏するアプリケーション

:補足:cron の実行の仕組みはシェルを経由しないので、.crontab で指定するコマンドは絶対パスで記述しておいた方が無難です。また、cron によって実行されるスクリプトの中身についてもパスは効かないので同様に絶対パスで書いた方が良いでしょう。

6.2 管理者レベルの場合

RedHat Linux では /etc/crontab から /etc/cron.* を呼ぶようになってるようです。 スーパーユーザが「crontab ~/.crontab」でコマンドラインから反映させた設定は /var/spool/cron/root に記述されるようです。

FreeBSD では /etc/crontab から /etc/periodic 以下のファイルを呼び出して実行する仕組みになっているようです。

6.3 秒単位での起動

cron による自動実行では分単位の指定しかできません。 では、30秒毎に起動したいコマンドがある場合はどうするべきでしょうか?

答えは、~/.crontab に以下のように指定することです。

* * * * * command & sleep 30; command;

同様、12時34分56秒 に起動したい場合は、 34 12 * * * sleep 56; command

7 #!/bin/sh の意味

スクリプトの一行目に書かれる「#!/bin/sh」は、「/bin/sh を起動し、その標準入力として2行目以下を与えなさい」。 大体こんな意味です。

(厳密にはちょっと違うみたいですが。 というのは、スクリプトから呼ばれたコマンドが標準入力を必要とする場合にはコマンドにはキーボードから入力するようになりますし、シェルスクリプトでは「#」の扱いが異ったりします。)

8 パスワード

研究室内部ではそれ程気にしなくても良いと思いますが、基本的にパスワードには辞書に載っているような単語は使ってはいけません。 辞書の総当たりというのはパスワードクラックの常套手段です。 数字や記号、大小文字の入り混じったランダム文字列がベストですが、そうなると今度は覚えるのが大変になります。

「辞書にあるような単語でキーボード上で手の位置をずらして入力する」(例:「adachilab」→「qeqdy8oqg」)というのは辞書攻撃を避けるそこそこ有効な手段でしょう。

9 「死んだかな?」と思ったら……

9.1 X が固まった!?

Ctrl-Alt-Backspace が「X の強制終了」にバインドされています. まずはこれを試してみましょう.

9.2 仮想コンソール

「X が固まった, ネットワーク経由のログインもできない」という時のための Tips です. また, X も screen も入れてないインストール直後の状態で, 何かの作業中に別の作業をしたい時にも使えます.

PC-UNIX は仮想コンソールという機能を持っています. システムは 8枚の仮想コンソールを持っていて, X 環境ではないコンソールの状態で Alt-Fn で別のコンソールを扱えます. 例えば, [Alt-F2] では2枚目のコンソールに移動します.

ここに X を起動すると, システムは 9枚目のコンソールを作り, そこを X に割当てます. X から仮想コンソールに直接移動することもできますが, X では [Alt-Fn] は別の機能にバインドされているため, (例:[Alt-F4]アプリケーションの終了) [Ctrl-Alt-Fn] にバインドされます. 仮想コンソールから X に復帰するには [Alt-F9] で行けます.

以上の話は FreeBSD での設定なのですが, 調べた限りでは Linux でも同様のようです.

9.3 フリーズしたとき

C-c や C-z で終了やサスペンドを試みる リモートログインしていた場合はその計算機に直接繋っている端末(ディスプレイ、キーボード)で試してみる 別の計算機から ping を打ち、通れば telnet や ssh でログインを試みる 直接繋がってる端末で Ctrl-Alt-Backspace で X-window-system の終了を試みる 直接繋がってる端末で Ctrl-Alt-Delete でシステムの強制再起動を試みる 画面だけ死んでるのかもしれないということにして、直接繋がってる端末で root[enter]passwd[enter]shutdown -r now[enter] と打ってみる 変化が起きるのを待つ にっちもさっちも行かないようなら、更に5秒間祈ってから電源を切る *同じく5秒間祈ってから電源を入れる