Last-Modified: 2013/6/2
いますぐ実践! Linux システム管理 / Vol.242 / 読者数:2403名
こんばんは、うすだです。
先日、会社のインターネット接続が、ADSL から光に変わりました。
ADSL でも遅いということはなかったのですが、速ければ速いほ…いえ、 将来のことを考えてということで、変えることになりました。
当日の工事は、慣れた兄ちゃんがテキパキと作業して、なんの問題もなく 滞りなく、なめらかに移行できました。
…ですが、なぜか、速いという実感を感じることができません。
逆に、画像などが読み込めない頻度が上がっている気がします。
手前が光でも、その先を多くのユーザで共有しているとか、ADSL の方が 実は少ないユーザで共有していて速いとか、疑惑が頭の中を駆けめぐって います。
そんなことないよ光の方が速いんだよと、もっともらしい技術的な説明を 誰かがしてくれるのを待っている、今日この頃です。
いつか原因究明しようと思いますが、と言うときは絶対しないことが明白 だとわかっていつつ、今回もはりきってまいります。
今回のお題 - ハードリンクとシンボリックリンクを理解する
月に2回、ゆるゆるとはいえ、発行し続けるというのは、自分で言うのも なんですが、思った以上の時間と労力を必要としています。
その中でも一番悩ましいのが、ネタです。カラカラに乾いた雑巾を絞って 探していますので、ときにはすごくニッチなお題になってしまうことも、 少なからずございます。
たとえば、前回の FS-Cache などは、NFS を使われていない方にとって、 ものすごくどうでもいいネタだった…かもしれません。
かたや、システム管理屋さんは、一見するとシステム管理に関係ないよう に思われる知識も、いろいろ知っておく必要があります。
カーネルやネットワーク、プログラミング、データベース、UI、などなど の幅広い知識を持っていますと、思わぬところで役に立ったりするのでは ないかと思います。
そして、その知識が、基礎的なものであればあるほど、役に立つ可能性が 高いのではないでしょうか。
…という言い訳めいた前フリをしたところで、今回は、だれもがお世話に なっている「リンク」について、おさらいしたいと思います。
ハードリンクやシンボリックリンクがどういうものか、具体的にイメージ
できていれば、なにか問題が起こったとき、役に立つ…かもしれません。
(ハードリンクにしておいてよかった! とか。あ、ありますかね…。)
まずはざっくりと基本をおさらい
普段、何気なくファイルやディレクトリなどと戯れていますが、これらが どういうモノなのか、意識することはないんじゃないかと思います。
ですので、リンク云々の話に入る前に、ファイルやディレクトリについて さらっとご説明したいと思います。
ご存じの方、文字ばっかりだとアナフィラキシーの症状が出る恐れのある 方は、次まですっ飛ばしていただいて構いません。
ファイルは、ドキュメントだったり画像だったり動画だったりしますが、 ようは、なにかのデータのまとまりを表す単位です。
ディレクトリは、ファイルを格納するための保管場所です。もし世の中に ファイルしか存在しなかったら、同じファイル名をつけられなかったり、 つけたファイル名を忘れて途方に暮れてしまったりしてしまいます。です が、ディレクトリを使ってファイルを分類すると、整理できて超便利! に なるわけですね。
そして、ディレクトリにはディレクトリを含められるので、階層的な構造 に仕立て上げることができます。これがファイルシステムです。
では、ファイルやディレクトリが、具体的にどうなっているかといいます と、実際のファイルシステムを斜め読めば、それとなく感じられます。
ここでは例として、EXT2(あるいは EXT3、EXT4)ファイルシステムの中を
覗き見してみたいと思います。
Linuxカーネルのソースコードをお持ちの方は、fs/ext[234]/ext[234].h
か include/linux/fs.h あたりをご参照いただくと、よいかもです。
まず、ファイルは、「iノード」と呼ばれるモノで管理されています。
(inode か ext2_inode 構造体を眺めると、具体的な構造が分かります。)
iノードには、ファイルの属性(種類やモード(i_mode)、所有者(i_uid)、 サイズ(i_size)、各種日時(i_atime, i_ctime, ...)など)と、ファイルの 中身の情報(i_block)が格納されています。
ここでポイントなのは、iノードにはファイル名の情報が含まれない、と いうことでしょうか。
さて次に、ディレクトリです。ディレクトリもファイルの一種です。
ディレクトリの中身は、「ディレクトリエントリ」です。
(ext2_dir_entry 構造体を眺めると、具体的な構造が分かります。)
ディレクトリエントリには、ファイルの実体であるiノードと、ファイル 名が格納されています。ここに格納されているiノードとは、32bit長の値 で、ファイルシステム内で一意なモノです。
ユーザがパス名でアクセスすると、ディレクトリエントリの中から、一致 するファイル名を探し出し、それに対応するiノードをもとに、いろいろ な情報をユーザに提供する、という芸当が可能になるわけです。
ちなみに、この32bitの値を「iノード番号」と呼びます。
iノード番号を知るには、「-i」オプションを指定してlsコマンドを実行
します。
$ ls -i1 /
7340033 bin/
45875201 boot/
19398657 cdrom/
3 dev/
35127297 etc/
50069505 home/
17 initrd.img@
13 initrd.img.old@
...中略...
1 proc/
...中略...
1 sys/
...後略...
/proc や /sys が 1 なのは、それぞれ procfs や sysfs をマウントして いるためだと思われます。
また、「stat」コマンドでも、iノードの情報を確認できます。
$ stat /etc/hosts
File: `/etc/hosts'
Size: 2562 Blocks: 8 IO Block: 4096 通常ファイル
Device: 805h/2053d Inode: 35132117 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2013-06-01 12:55:02.009358371 +0900
Modify: 2013-05-27 12:50:35.151572836 +0900
Change: 2013-05-27 12:50:35.207573114 +0900
Birth: -
というわけで、ハードリンクとは?
文章ばっかりでクラクラしてきました…が、気を取り直して進めます。
「ハードリンク」とは、すでにあるファイルを、別のファイル名(パス名) で参照できるようにするモノです。
ハードリンクを作成するには、「ln」コマンドを使います。元のファイル
名と、新たに作成するファイル名を引数に指定して実行します。
たとえば、foo.txt の別名で bar.txt というハードリンクを作成するに
は、以下のように実行します。
$ ln -s foo.txt bar.txt
別ディレクトリにあるファイルと同じ名前ハードリンクを作成するには、 元だけを指定します。
$ ls
$ ln -s foo/bar.txt
$ ls
bar.txt
もう少し具体的に説明すると、ハードリンクとは、既存のiノードを参照 するディレクトリエントリを1つ追加すること、とも言えます。
たとえば、さきほど作成した bar.txt と、その元の foo.txt のiノード を確認してみると、同じiノードであることが分かります。
$ ls -i1 foo.txt bar.txt
32256332 bar.txt
32256332 foo.txt
iノードにファイル名の情報が含まれない理由は、ここにあります。
そして、別のファイルシステムにあるファイルに対して、ハードリンクを 作成することができません。iノード番号が、ファイルシステム内でのみ 一意だからです。
また、iノードには、リンク数(i_nlink か i_links_count)という情報が 含まれています。通常は、あるディレクトリのディレクトリエントリから 参照されているため、リンク数は 1 です。
$ echo test > foo.txt
$ ls -li foo.txt
32256333 -rw-r--r-- 1 usu usu 5 6月 1 18:00 foo.txt
上記の場合、32256333 が iノード番号、その後の 1 がリンク数です。
ここでハードリンクを作成すると、2つのディレクトリエントリから参照
されることになるため、リンク数は 2 になります。
$ ln foo.txt bar.txt
$ ls -li foo.txt bar.txt
32256333 -rw-r--r-- 2 usu usu 5 6月 1 18:00 bar.txt
32256333 -rw-r--r-- 2 usu usu 5 6月 1 18:00 foo.txt
そして、ファイルを削除するコマンドといえば「rm」ですが、rmコマンド の役割は、厳密には、ディレクトリエントリを削除し、iノードのリンク 数を1つ減らすこと、だと言えます。
たとえば、ここで foo.txt を rm コマンドで削除すると、foo.txt だけ がなくなり、bar.txt は残ります。もちろん、中身は foo.txt のときの ままです。(リンク数は 1つ減って 1 になります。)
$ rm foo.txt
$ ls -li foo.txt bar.txt
ls: foo.txt にアクセスできません: そのようなファイルやディレクトリ\
はありません
32256333 -rw-r--r-- 1 usu usu 5 6月 1 18:00 bar.txt
つまり、foo.txt のディレクトリエントリはなくなりますが、32256333番 のiノードと、そのiノードを参照する bar.txt のディレクトリエントリ は存在するので、削除されずにすみます。
もちろん、ここでさらに bar.txt を削除すると、32256333番のiノードを 参照するディレクトリエントリがなくなる(リンク数が 0 になる)ため、 今度こそ(?)削除されることになります。
ちなみに、元のファイルを移動させても、iノードで参照しているため、 問題なく参照できます。
$ cat foo.txt
I am a foo.txt
$ ln foo.txt bar.txt
$ mv foo.txt foo2.txt
$ ls -li foo2.txt bar.txt
$ cat bar.txt
I am a foo.txt
32256335 -rw-r--r-- 2 usu usu 15 6月 1 18:00 bar.txt
32256335 -rw-r--r-- 2 usu usu 15 6月 1 18:00 foo2.txt
それから、ディレクトリに対するハードリンクは、root の権限があり、
ファイルシステムが対応している場合のみ、可能です。
(残念ながら、EXT2(EXT3,EXT4)では、対応していないようです。)
それじゃあ、シンボリックリンクって?
リンクには、前述のハードリンクの他に、「シンボリックリンク」という モノがあります。
シンボリックリンクも、すでにあるファイルに別名をつけて、その名前で 参照できるようにするモノです。
ハードリンクとは違い、元のファイルをiノードではなくパスで指定する ため、別のファイルシステムのファイルを参照できます。
シンボリックリンクの作成にも、ln コマンドを使います。
「-s」オプションと、元のファイルのパス、それを参照するシンボリック
リンクとなるファイル名を指定して ln コマンドを実行します。
たとえば、foo.txt のシンボリックリンク bar.txt を作成したい場合、 以下のように実行します。
$ ln -s foo.txt bar.txt
$ ls -li foo.txt bar.txt
32256334 lrwxrwxrwx 1 usu usu 7 6月 1 22:01 bar.txt -> foo.txt
32256333 -rw-r--r-- 1 usu usu 5 6月 1 18:00 foo.txt
ハードリンクとは違い、元のファイルのリンク数は増えません。
ですが、もちろん、元のファイルと同じようにアクセスできます。
$ cat foo.txt
test
$ cat bar.txt
test
$ echo test from bar.txt > bar.txt
$ cat foo.txt
test from bar.txt
また、引数に元のファイルのパスだけを指定すれば、同名のシンボリック
リンクが作成されます。
たとえば、/etc/passwd のシンボリックリンクをカレントディレクトリに
作成するには、以下のように実行します。
$ ln -s /etc/passwd
$ ls -l passwd
lrwxrwxrwx 1 usu usu 11 6月 1 21:58 passwd -> /etc/passwd
では、シンボリックリンクを削除するとどうなるか、といいますと、元の ファイルはどうもならず、シンボリックリンクが削除されるだけです。
$ rm bar.txt
$ ls -li foo.txt bar.txt
ls: bar.txt にアクセスできません: そのようなファイルやディレクトリ\
はありません
32256333 -rw-r--r-- 1 usu usu 5 6月 1 18:00 foo.txt
では逆に、元のファイルを消すとどうなるかといいますと、シンボリック リンクは残りますが、ファイル自体は失われてしまいます。
$ rm foo.txt
$ ls -li foo.txt bar.txt
ls: foo.txt にアクセスできません: そのようなファイルやディレクトリ\
はありません
32256334 lrwxrwxrwx 1 usu usu 7 6月 1 22:01 bar.txt -> foo.txt
$ cat bar.txt
cat: bar.txt: そのようなファイルやディレクトリはありません
…ひゃー、悲しいですね。
それから、ハードリンクでは、元のファイルを移動させても問題なく参照 できましたが、シンボリックリンクでは参照できなくなります。
$ echo test > foo.txt
$ ln -s foo.txt bar.txt
$ mv foo.txt foo2.txt
$ cat bar.txt
cat: bar.txt: そのようなファイルやディレクトリはありません
さらにそれから、ディレクトリに対するシンボリックリンクは、問題なく 作成できます。
$ ln -s /etc etc_link
$ ls etc_link/
...後略...
その他注意するところ
それぞれ特性や制約はありますが、どちらも、別名でのアクセスができる という点で、とっても便利な仕組みです。
以上で…と終わりにしたいところですが、あとひとつだけ、注意すべき点 がありますので、記します。
それは、アーカイブにしたときの扱いです。
ここでは、tar と zip でどうなのか、確認しておこうと思います。
まずは tar ですが、ハードリンクには気づいていただけるようです。
$ tar cfz test.tgz foo.txt bar.txt
$ tar tvfz test.tgz
-rw-r--r-- usu/usu 5 2013-06-02 00:20 foo.txt
hrw-r--r-- usu/usu 0 2013-06-02 00:20 bar.txt foo.txt へのリンク
ただ、どちらが元かというのは、作ったひとにしかわからないので、先に 指定した方がオリジナルだと判断されるようです。
$ tar cfz test2.tgz bar.txt foo.txt
$ tar tvfz test2.tgz
-rw-r--r-- usu/usu 5 2013-06-02 00:20 bar.txt
hrw-r--r-- usu/usu 0 2013-06-02 00:20 foo.txt bar.txt へのリンク
といっても、どちらも展開したら同じ結果になるんですけどね。
シンボリックリンクの場合も、問題なく扱えます。
$ tar cfz test.tgz foo.txt bar.txt
$ tar tvfz test.tgz
-rw-r--r-- usu/usu 5 2013-06-02 00:27 foo.txt
lrwxrwxrwx usu/usu 0 2013-06-02 00:27 bar.txt -> foo.txt
元がアーカイブに含まれないときなど、シンボリックリンクではなく実体
として取り込みたいときは、「h」オプションを指定します。
以下の場合は、オリジナルが含まれるため、ハードリンクとして含められ
ました。なんだかカシコイですね。
$ tar cfzh test2.tgz foo.txt bar.txt
$ tar tvfz test2.tgz
-rw-r--r-- usu/usu 5 2013-06-02 00:27 foo.txt
hrw-r--r-- usu/usu 0 2013-06-02 00:27 bar.txt foo.txt へのリンク
お次は zip です。
残念ながら、ハードリンクは考慮していただけないようです。
$ zip -r test.zip foo.txt bar.txt
$ unzip -l test.zip
Archive: test.zip
Length Date Time Name
5 06-02-13 00:20 foo.txt
5 06-02-13 00:20 bar.txt
10 2 files
$ unzip test.zip
$ ls -li foo.txt bar.txt
32256336 -rw-r--r-- 1 usu usu 5 6月 2 00:20 bar.txt
32256335 -rw-r--r-- 1 usu usu 5 6月 2 00:20 foo.txt
シンボリックリンクも、そのままだと、上記と同様です。
ですが、「-y」オプションを指定すると、シンボリックリンクを考慮して
くださいます。
$ zip -yr test.zip foo.txt bar.txt
$ unzip test.zip
Archive: test.zip
extracting: foo.txt
linking: bar.txt -> foo.txt
finishing deferred symbolic links:
bar.txt -> foo.txt
$ ls -li foo.txt bar.txt
32256339 lrwxrwxrwx 1 usu usu 7 6月 2 00:34 bar.txt -> foo.txt
32256338 -rw-r--r-- 1 usu usu 5 6月 2 00:20 foo.txt
おわりに
以上、ハードリンクとシンボリックリンクについて、おさらい的にさらり とご紹介しました。
それぞれの特徴をざっくりまとめますと、以下のようになります。
[ハードリンク]
- iノードで参照
- 同一ファイルシステム内に限られる
- ファイルのみ(と思っておいた方がいい)
- 元のファイルを移動しても問題ない
- 元のファイルを削除しても消えない
[シンボリックリンク]
- パスで参照
- 別のファイルシステムでもいける
- ディレクトリに対してもいける
- 元のファイルを移動すると参照できなくなる
- 元のファイルを削除すると消える
個人的には、tar コマンドはいろいろ考えているなあ、と思いました。
宿題の答え
前回の宿題は、
NFSマウント中にcachefilesdを停止・起動するとどうなるでしょうか。
でした。
というわけで、試してみましょう。
cachefilesd が動いていることを確認して、NFSマウントします。
# service cachefilesd status
cachefilesd (pid 2200) を実行中...
# mount -o fsc 192.168.1.1:/export/home/nfs /home/nfs
# du -sk /var/cache/fscache
692 /var/cache/fscache
アクセス前の、キャッシュディレクトリのサイズも、確認しておきます。
で、NFSマウントした中のファイルに、どーんとアクセスしてみます。
$ tar tvfz /home/nfs/foo/bar-0.0.1.tgz
…これは、単なる例なので、自由に、適当なアクセスをしてください。
そうすると、当然ですが、キャッシュサイズは増えていきます。
# du -sk /var/cache/fscache
80184 /var/cache/fscache
# du -sk /var/cache/fscache
353596 /var/cache/fscache
...後略...
では、アクセスさせたままの状態で、cachefilesd を止めてみましょう。
# service cachefilesd stop
cachefilesd を終了中: [ OK ]
すると、アクセスできなくなるか…というと、そんなことはありません。
その後も問題なくアクセスできます。
ちなみに、cachefilesd が動いていない状態でマウントすると、どうなる でしょうか。
# umount /home/nfs
# mount -o fsc 192.168.1.1:/export/home/nfs /home/nfs
# du -sk /var/cache/fscache
676156 /var/cache/fscache
(何かアクセス)
# du -sk /var/cache/fscache
676156 /var/cache/fscache
ご想像通り、問題なくアクセスできます。
キャッシュのサイズも、使われていないから当然ですが、変わりません。
もし、マウント後にサービスを開始しても、ちゃんとキャッシュされて、 キャッシュが使われるようになります。
# service cachefilesd start
cachefilesd を起動中: [ OK ]
# du -sk /var/cache/fscache
48 /var/cache/fscache
(何かアクセス)
# du -sk /var/cache/fscache
1824 /var/cache/fscache
ユーザ空間で動作するプロセスのせいでアクセスできなくなったりする、 というのは困りますので、当たり前なのかもしれませんね。
今回の宿題
今回の宿題は、
シンボリックリンクの場合ディレクトリエントリにどう記録されるか、
確認してみましょう。
です。
フツーに readdir() で読んだら…ということではなく、EXT2 などでベタ にどう記録されているのか、ということを調べてください。
検索すればわかると思いますので、宿題の答えでは、実際に生で読んで、 その通りだった! というところまでたどり着きたいです。
あとがき
某新聞で、能楽師の務めは、過去から未来へと芸を運ぶこと、という文章 を読みました。
その中の一文、「自分という存在は、長い線上の一点にすぎない。」に、 なんだか、ぐぐっと来るものがありました。
みなさんはまだお若いと思いますが、それとなく年を食ってくると、夢や 目標があいまいになり、それに向かってがんばることより、目の前のこと だけで限界だと思い込んだり、酒を飲んでくだを巻いたりしがちではない かと、勝手ながら思っています。
ですが、夢に向かって突き進むことだけでなく、後からくる人のために、 きちんと何かを残していくことも大事なのではないかということに、その 文章を読んで気づくことができました。
このメルマガをはじめたきっかけも、小銭稼ぎになるといいなあ…という 邪心だけでなく、わからなくて途方に暮れている若い管理者さんの助けに なりたい、という考えが、少なからずありました。
…あ、いえ、あります、です。現在進行形ですよ…。
というわけで、今後は、長い線上の一点として、未来のひとに何か残せる ようなことを考えてやっていこうと、強く思いました。
…いやいや、まだまだ、栗で世界征服の野望は、諦めていませんけどね。
小遣い稼ぎという邪心が、そこそこ達成できたとはいえ…。(^ε^;
今回も、ここまで読んでいただき、誠にありがとうございました。
次回は、6月16日(日) の未明にお会いしましょう!
「いますぐ実践! Linux システム管理」はこちらです。
メルマガの解除、バックナンバーなども、以下からどうぞ。
https://www.usupi.org/sysad/ (まぐまぐ ID:149633)
その他、作者に関するページは、概ね以下にございます。
https://www.usupi.org/kuri/ (まぐまぐ ID:126454)
http://usupi.seesaa.net/ (栗日記ブログ)
https://twitter.com/kuriking/ (twitter)
https://facebook.com/kuriking3 (facebook)
https://jp.pinterest.com/kuriking/pinterest)
https://www.instagram.com/kuri_king_/ (instagram)