「デバイススペシャルファイル」の版間の差分

提供: UnixClassWiki
移動先: 案内検索
(I/Oの抽象化)
(I/Oスケジューラ)
 
(同じ利用者による、間の44版が非表示)
2行目: 2行目:
 
== デバイススペシャルファイル ==
 
== デバイススペシャルファイル ==
  
=== I/Oの抽象化 ===
+
=== I/O の抽象化 ===
  
Unixで使われた秀逸なアイデアを3つ上げろといわれたら、多分、多くの人が、階
+
Unix で使われた秀逸なアイデアを3つ上げろといわれたら、多分、多くの人が、階層化ファイルシステム、パイプ、そしてデバイススペシャルファイル(Device Special File : 単にスペシャルファイルとも呼ぶ)
層化ファイルシステム、パイプ、そしてデバイススペシャルファイル(単にス
+
<ref> wikipediaではデバイス・ファイルの項目に「デバイスファイルまたはスペシャルファイルとは」と書いていますが、UNIXの世界では次のように「デバイススペシャルファイル」という表現をしています。
ペシャルファイルとも呼ぶ)をあげるでしょう。今回はその中の2つに関係しています。
+
[https://www.usenix.org/legacy/event/bsdcon02/full_papers/kamp/kamp_html/ Before we continue, we need to fully understand the "device special file" in UNIX.]
デバイスを抽象化するこのアイデアによって、I/Oのデバイスもすべてファイ
+
本稿もそれに従います。
ルと同じ統一したインタフェースで扱えるようになりました。例えばハードディ
+
</ref>
スクや端末といったものに対して、プロセスから直接ハードウェアにアクセス
+
をあげるでしょう。今回はその中の2つに関係しています。
することはありません。必ずI/Oのスペシャルファイルを経由してアクセスし
+
デバイスを抽象化するこのアイデアによって、 I/O のデバイスもすべてファイルと同じ統一したインタフェースで扱えるようになりました。
ます。ディレクトリ/dev以下に用意されているスペシャルファイルがデバイス
+
例えばハードディスクや端末といったものに対して、プロセスから直接ハードウェアにアクセスする必要はありません。
へのインタフェースです。
+
I/O のスペシャルファイルを経由してアクセスします。
 +
ディレクトリ /dev 以下に用意されているスペシャルファイルがデバイスへのインタフェースです。
  
  
端末もスペシャルファイルとして抽象化されていて、たとえば現在使っている端
+
端末もスペシャルファイルとして抽象化されていて、たとえば現在使っている端末は /dev/tty のように見えます。
末は/dev/ttyのように見えます。ですから、ここに文字をリダイレクトすると
+
ですから、ここに文字をリダイレクトするとスクリーンにその文字が出力されます。
スクリーンにその文字が出力されます。
+
  
  
 +
<pre class="bash">
 
  $ echo 'hello' > /dev/tty
 
  $ echo 'hello' > /dev/tty
 
  hello
 
  hello
 +
</pre>
  
  
たとえばLinuxではIDEハードディスクが使えるコンピュータだとIDE 0 Masterに接続さ
+
たとえばLinuxでは /dev/sda は SCSI ハードディスク、あるいは SCSI ハードディスクに見えるものです<ref>たとえば USB メモリは USB マスストレージクラスですが、デバイス用の SCSI エミュレーションによってSCSIデバイスのように見えます。</ref>。
れているハードディスクは/dev/hdaになります。Slave側はhdb、IDE 1
+
Linuxのカーネルが認識した順番に /dev/sda から /dev/sdb 、 /dev/sdc となります。
Master/Slaveはhdcとhddという具合になります。/dev/hdaはハードディスク全
+
/dev/sda はハードディスク全体で、 sda 上にパーティションが設定されていれば /dev/sda1 、 /dev/sda2 ...となります。
体で、hda上にパーティションが設定されていればhda1、hda2...が各々のパー
+
必ずしも /dev/sd? はSCSIハードディスクではなくSCSIのインタフェースが使えて、かつブロックデバイスであればかまいません。
ティションを指しています。/dev/sdaはSCSIハードディスク、あるいはSCSIハー
+
実際に接続されているのは、コンパクトフラッシュメモリだったり、USB接続の外部HDDだったり、
ドディスクに見えるものです。必ずしも/dev/sd?はSCSIハードディスクではな
+
あるいはIDE接続のMOがide-scsiデバイスドライバ経由のためSCSI として取り扱われたりと状況でさまざまです。
くSCSIのインタフェースが使えて、かつブロックデバイスであればかまいませ
+
IDEハードディスクだと IDE 0 Master に接続されているハードディスクは /dev/hda になります。
ん。実際に接続されているのは、コンパクトフラッシュメモリだったり、USB
+
Slave側は hdb、 IDE 1 Master/Slave は hdcと  hdd という具合になります<ref>
接続の外部HDDだったり、あるいはIDE接続のMOがide-scsiデバイスドライバ経
+
Ubuntuのハードウェアとデバイスの命名規則についての解説ページ [https://www.dell.com/support/kbdoc/ja-jp/000132092/ubuntu-%E3%81%8A%E4%BD%BF%E3%81%84%E3%81%AE%E3%83%8F%E3%83%BC%E3%83%89%E3%83%89%E3%83%A9%E3%82%A4%E3%83%96%E3%81%8A%E3%82%88%E3%81%B3%E3%83%87%E3%83%90%E3%82%A4%E3%82%B9%E5%90%91%E3%81%91%E3%81%AE%E6%9D%A1%E4%BB%B6-%E8%AA%AC%E6%98%8E]
由のためSCSI として取り扱われたりと状況でさまざまです。
+
</ref>。
 
+
  
 
;補足: 接続した順番にデバイスを決めていくので、USB機器などの接続では挿したタイミングでデバイス名が異なることが発生してしまう場合があります。そこで現在ではUUID(Universally Unique Idenifier )という方式を使ってデバイスをユニークに認識する方法を用意しています。fstab(5)、mount(8)、tune2fs(8)を参照してみてください。
 
;補足: 接続した順番にデバイスを決めていくので、USB機器などの接続では挿したタイミングでデバイス名が異なることが発生してしまう場合があります。そこで現在ではUUID(Universally Unique Idenifier )という方式を使ってデバイスをユニークに認識する方法を用意しています。fstab(5)、mount(8)、tune2fs(8)を参照してみてください。
  
  
/dev/cua0や/dev/ttyS0はシリアルポートのスペシャルファイルです。cua?は
+
/dev/cua0 や /dev/ttyS0 はシリアルポートのスペシャルファイルです。
たとえばモデムやFAXモデムを接続しておいて、自分から呼び出して接続を行
+
cua?はたとえばモデムやFAXモデムを接続しておいて、自分から呼び出して接続を行う(Call-Out)する時に使います。
(Call-Out)する時に使います。ttyS?は相手から呼び出される時に使います。
+
ttyS?は相手から呼び出される時に使います。
 
+
/dev/ttyUSB0 はUSB接続をしたシリアル通信のためのスペシャルファイルです。
  
 
=== レイヤ図 ===
 
=== レイヤ図 ===
  
プログラムは、スペシャルファイルやシステムコールからカーネルを経由し、最
+
プログラムは、スペシャルファイルやシステムコールからカーネルを経由し、最後はカーネル内にあるデバイスドライバを経由してハードウェアにアクセスします。下に簡単なレイヤー図を載せます。
後はカーネル内にあるデバイスドライバを経由してハードウェアにアクセスし
+
ます。下に簡単なレイヤー図を載せます。
+
  
 
+
[[File:Device-Driver-Layer.png|thumb|right|400px|Device Driver Layer]]
+---------------------------------------------+
+
|               プログラム                  |
+
+------------------+--------------------------+
+
|デバイススペシャル| システムコール  |  その  | インタフェース
+
|ファイル          | インタフェース  |  ほか  |
+
+---------------------------------------------+
+
|            カーネルでのその他の処理        | サービス
+
+---------------------------------------------+
+
| キャラクタ | ブロック  | ネットワーク| その | デバイス
+
| デバイス  | デバイス  | デバイス    | ほか | ドライバ
+
+---------------------------------------------+
+
|                ハードウェア                |
+
+---------------------------------------------+
+
  
 
=== ioctl ===  
 
=== ioctl ===  
  
ioctl(2)はスペシャルファイルをコントロールしているデバイスドライバに対
+
ioctl(2)はスペシャルファイルをコントロールしているデバイスドライバに対して命令を送るためのシステムコールです。これでデバイスをコントロールすることになります。(執筆中)
して命令を送るためのシステムコールです。これでデバイスをコントロール
+
することになります。
+
  
== キャラクタデバイス ブロックデバイス ==
+
== デバイス  ==
 
=== キャラクタデバイス ===
 
=== キャラクタデバイス ===
  
キャラクタデバイスは、時系列でデータが発生する端末、オーディオ、モデム
+
キャラクタデバイスは、時系列でデータが発生する端末、オーディオ、モデムあるいはテープ装置といったシーケンシャルにバイト単位でアクセスするような入出力を行うデバイスドライバです。
あるいはテープ装置といったシーケンシャルにバイト単位でアクセスするよう
+
データはキャッシュしません。
な入出力を行うデバイスドライバです。基本的にデータはキャッシュしません。
+
  
 +
 +
<pre class="bash">
 
  $ ls -l  /dev/{random,tty,st0,midi0}
 
  $ ls -l  /dev/{random,tty,st0,midi0}
 
  crw-rw----    1 root    audio    35,  0 Mar 15  2002 /dev/midi0
 
  crw-rw----    1 root    audio    35,  0 Mar 15  2002 /dev/midi0
83行目: 68行目:
 
  crw-rw----    1 root    tape      9,  0 Mar 15  2002 /dev/st0
 
  crw-rw----    1 root    tape      9,  0 Mar 15  2002 /dev/st0
 
  crw-rw-rw-    1 root    tty        5,  0 Nov 18 11:35 /dev/tty
 
  crw-rw-rw-    1 root    tty        5,  0 Nov 18 11:35 /dev/tty
 +
</pre>
 +
 
  先頭のcがキャラクタデバイスを意味する
 
  先頭のcがキャラクタデバイスを意味する
 
  
 
=== ブロックデバイス ===
 
=== ブロックデバイス ===
  
ブロックデバイスは、ハードディスクのようなランダムアクセスができ、かつ
+
ブロックデバイスは、ハードディスクのようなランダムアクセスができ、かつ入出力がブロック単位でアクセスを行うことができるハードウェアへのデバイスドライバです。
入出力がブロック単位でアクセスを行うことができるハードウェアへのデバイ
+
データをキャッシュするので効率良く入出力ができます。
スドライバです。データをキャッシュするので効率良く入出力ができます。
+
 
  
 +
<pre class="bash">
 
  $ ls -l /dev/hd?
 
  $ ls -l /dev/hd?
 
  brw-rw----    1 root    disk      3,  0 Mar 15  2002 /dev/hda
 
  brw-rw----    1 root    disk      3,  0 Mar 15  2002 /dev/hda
98行目: 85行目:
 
  brw-rw----    1 root    disk      34,  0 Mar 15  2002 /dev/hdg
 
  brw-rw----    1 root    disk      34,  0 Mar 15  2002 /dev/hdg
 
  brw-rw----    1 root    disk      34,  64 Mar 15  2002 /dev/hdh
 
  brw-rw----    1 root    disk      34,  64 Mar 15  2002 /dev/hdh
 +
</pre>
 +
 
  先頭のbがブロックデバイスを意味する
 
  先頭のbがブロックデバイスを意味する
  
効率が良いと書きましたが、正確には「効率良く入出力するための工夫をして
 
いる (だから効率が良い)」といった方がいいでしょう。
 
  
1つのブロックのことをセクタと呼び、普通はサイズは512バイトです。CD-ROM  
+
効率が良いと書きましたが、正確には「効率良く入出力するための工夫をしている (だから効率が良い)」といった方がいいでしょう。
のようなデバイスでは最近は2KBですが、いずれにしてもセクターは2のN乗倍
+
 
(512は2 の9乗、2Kは2の11乗)の値を取ります。最大値は記憶管理のページの
+
 
サイズ以下です。32ビットアーキテクチャーなら4KB、64ビットアーキテクチャー
+
1つのブロックのことをセクタと呼び、普通はサイズは512バイトです。CD-ROM のようなデバイスでは最近は2KBですが、いずれにしてもセクターは2のN乗倍(512は2 の9乗、2Kは2の11乗)の値を取ります。
であれば8KBです。
+
最大値は記憶管理のページのサイズ以下です。
 +
32ビットアーキテクチャーなら4KB、64ビットアーキテクチャーであれば8KBです<ref>これは典型的な例で、ページサイズはハードウェアのアーキテクチャーに依存します。</ref>。
 +
 
 +
 
 +
ハードディスクなどのデバイスからブロックの内容が読み込まれたとき(あるいは書き込まれる途中)、
 +
データはメモリ中のバッファの中に保管されています。1つのバッファは1つのブロックに対応しています。
 +
普通はページサイズが4KBでブロックサイズが512 バイトですから、一つのページは複数のバッファから出来ています。
 +
ブロックデバイスから幾つかのブロックでデータを読み込んで来た時(あるいは書き込もうとした時)、
 +
メモリ中にバッファのチェーンが作られます。
 +
このように、ブロックデバイスを読み書きする時には、ハードディスクのようなデバイスを直接読み書きするのではなく、
 +
まずこのバッファに対して読み書きが行われます。
 +
 
 +
=== 擬似デバイス ===
 +
擬似デバイス  (Pseudo-devices) とはデバイスファイルのように見せかけているが、その先には具体的なハードウェアが結びつけられていないデバイスファイルです。
 +
たとえば /dev/null は、その先が何もないデバイスファイルです。
 +
 
 +
 
 +
<pre class="bash">
 +
% cat foo > /dev/null
 +
</pre>
 +
 
 +
 
 +
ファイル foo を読み込んで、デバイスファイル /dev/null に送り込みますが、送り先は何もないので /dev/null に吸い込まれるだけになります。
 +
/dev/null を入力にした場合、何も送られないことになります。以下にいくつかのデバイスファイル例をあげます。
 +
  
ハードディスクなどのデバイスからブロックの内容が読み込まれたとき(ある
+
* /dev/null 入力・出力とも何もしない
いは書き込まれる途中)、データはメモリ中のバッファの中に保管されていま
+
* /dev/random , /dev/urandom 乱数を返す
す。1つのバッファは1つのブロックに対応しています。普通はページサイズが
+
* /dev/zero ゼロを返す
4KBでブロックサイズが512 バイトですから、一つのページは複数のバッファ
+
から出来ています。ブロックデバイスから幾つかのブロックでデータを読み込
+
んで来た時(あるいは書き込もうとした時)、メモリ中にバッファのチェーン
+
が作られます。このように、ブロックデバイスを読み書きする時には、ハード
+
ディスクのようなデバイスを直接読み書きするのではなく、まずこのバッファ
+
に対して読み書きが行われます。
+
  
 
== I/Oスケジューラ ==
 
== I/Oスケジューラ ==
  
さてまず、バッファに読み書きされることはわかりました。しかしハードディ
+
さてまず、バッファに読み書きされることはわかりました。しかしハードディスク、あるいはそれに相当する具体的なブロックデバイスに書き込む、あるいは読み込む必要があります。
スク、あるいはそれに相当する具体的なブロックデバイスに書き込む、あるい
+
ハードディスクを例に取ると、円盤の磁性体が回っていて、そこに読み書きするヘッドが移動して、そして始めて読み書きが始まります。そのヘッドの移動のことをシークといいますが、ハードディスクのシーク時間は数ミリ秒程度かかります。数ミリ秒というと、とても短い時間のように思えますが、CPUの処理時間から比べれば長い長い時間です。
は読み込む必要があります。ハードディスクを例に取ると、円盤の磁性体が回っ
+
ていて、そこに読み書きするヘッドが移動して、そして始めて読み書きが始ま
+
ります。その移動のことをシークといいますが、ハードディスクのシーク時間
+
は数ミリ秒程度かかります。数ミリ秒というと、とても短い時間のように思え
+
ますが、CPUの処理時間から比べれば長い長い時間です。
+
  
そこで有効な入出力をするためにスケジューラを用意します。スケジューラの役目
+
そこで有効な入出力をするためにスケジューラを用意します。スケジューラの役目は、全体のスループットの改善です。ですから、ある1つのプロセスだけを着目してみると、もしかすると、処理が遅くなっているという可能性もあります。
は、全体のスループットの改善です。ですから、ある1つのプロセスだけを着
+
この当たりは単純に一つのI/Oスケジューリングのアルゴリズムが万能とはなかなかいかないので、Linux 2.6.0以降では
目してみると、もしかすると、処理が遅くなっているという可能性もあります。
+
Deadline I/O Scheduler、
この当たりは単純に一つのI/Oスケジュリングのアルゴリズムが万能とはなか
+
<del>Anticipatory I/O Scheduler</del><ref>最新ディストリビューションの使っているLinuxカーネルの多くでは既に用意していません。</ref>、
なかいかないので、Linus Elevator、Deadline I/O Scheduler、Anticipatory
+
Complete Fairness Queueing I/O Scheduler
I/O Scheduler、Complete Fairness Queueing Disk Scheduler といった複数
+
<ref> Inside the Linux 2.6 Completely Fair Scheduler https://www.ibm.com/developerworks/library/l-completely-fair-scheduler/index.html</ref>、
 +
Noop I/O Scheduler といった複数
 
のスケジュールが用意されています。
 
のスケジュールが用意されています。
 +
またLinux 5.0以降ではI/Oスケジューラがマルチキュー I/O スケジューラに変更され、mq-deadline、bfq (Budget Fair Queueing)、kyber、none <ref>
 +
https://www.kernel.org/doc/html/latest/block/index.html
 +
</ref>
 +
が用意されています。
 +
 +
 +
;補足: Noop I/O Scheduler (none) はスケジュールをしないスケジューラです。
 +
 +
 +
 +
; 調べてみよう :  ブロックデバイスが/dev/sdaの場合、I/Oスケジューラの設定は /sys/block/sda/queue/scheduler でされているので、実際に自分の使っている環境ではどうなっているか見てみよう。
 +
 +
 +
 +
; 調べてみよう : IBMサイトにあるLinuxのディスクI/Oに関する考察 ([http://public.dhe.ibm.com/software/dw/linux390/perf/Linux_disk_IO_considerations.pdf Linux disk I/O considerations ]) や openSUSE サイトにあるI/Oパフォーマンスのチューニング ( [https://doc.opensuse.org/documentation/leap/tuning/html/book-tuning/cha-tuning-io.html#cha-tuning-io-switch 12 Tuning I/O performance] )が参考になります。
  
 
== ネットワークデバイス ==  
 
== ネットワークデバイス ==  
  
1981年当時4.1BSDを改造しTCP/IPのスタックを搭載したのがUNIXのTCP/IPの始まりです。
+
1981年当時4.1BSDを改造しTCP/IPのスタックを搭載したのがUNIXのTCP/IPの始まりです
4.2BSDリリース時に入っていたTCP/IPはどう贔屓目に見ても、安定して利用するというには程遠く、プログラ
+
<ref>私の個人的経験でいわせてもらうと4.2BSDリリース時に入っていたTCP/IPはどう贔屓目に見ても、安定して利用するというには程遠く、プログラム中で引数をちょっと間違えるとシステム全体がいとも簡単にダウンしました。安定して使えたという実感は4.3BSDになってからです。</ref>
ム中で引数をちょっと間違えるとシステム全体がいとも簡単にダウンしました。
+
安定して使えたという実感は4.3BSDになってからです。
+
当時のLANも多くの場合イーサーネット(Ethernet)で構築されていました。
 +
NIC (Network Interface Card) とか、あるいは LANポート と呼ぶネットワークインタフェースのためのデバイスがあり、
 +
そのデバイスファイルとして /dev/eth0 が作成されました。
  
  
当時、我々がLANと呼ぶものはイーサーネット(Ethernet)で構築されていました。NIC (Network Interface Card)とか、あるいはLANポートとか呼ぶものも、
+
;補足: eth0は最初に認識しているイーサネットのネットワークインタフェースのデバイスで、複数のネットワークポートやNICが存在していた場合、 eth1、 eth2... となります。
使っているのはイーサーネットでした。そのため/dev/eth0といった名前でネットワークの
+
デバイスファイルが作成されました。
+
むかしのLinuxもイーサーネットのデバ
+
イスは、もちろんUnix流に/dev/の下にあるデバイススペシャルファイルで、
+
デバイスは/dev/eth0と見えていました。
+
  
  
;補足: eth0は最初に認識しているイーサネットのネットワークデバイスで、複数のネットワークポートやNICが存在していた場合、eth1、eth2...となります。
+
むかしのLinuxもイーサーネットのデバイスは、もちろんUnix流に /dev/ の下にあるデバイススペシャルファイルで、
 +
デバイスは /dev/eth0 と見えていました。
 +
ところが今日のLinuxはネットワークデバイスに対してはスペシャルファイルとして用意していません。
 +
イーサーネットデバイスのはずである/dev/eth?というのはLinux 2.2以降なくなりました。
 +
理由は単純に1つのイーサーネットのデバイスが、1つのIPアドレスを持つというわけではなくなったからです。
 +
現在はハードウェアに1つのイーサーネットのポートしかなくても、
 +
オペレーティングシステムとして、その1つのポートに複数のIPアドレスを割り当てることができます。
  
  
ところが今のLinuxはネットワークデバイスに対してはスペシャルファイルと
+
ネットワークインタフェースの設定にはifconfigを使いますが、通常はシステムの設定ファイルに指定のフォーマットで登録しておけば、システムのブート時に設定スクリプトが動き、自動的に割り当ててくれます。Debian系のディストリビューションだと /etc/network/interfaces に、RedHat 系だと /etc/sysconfig/network-scripts/ifcfg-eth0 に記述します。
して用意していません。イーサーネットデバイスのはずである/dev/eth?とい
+
うのはLinux 2.2以降なくなりました。理由は単純に1つのイーサーネットデバ
+
イスが、1つのIPアドレスを持つというわけではなくなったからです。ハード
+
ウェアに1つのイーサーネットのポートしかなくても、オペレーティングシス
+
テム的にはその1つのポートに複数のIPアドレスを割り当てることができます。
+
ネットワークインタフェースの設定にはifconfigを使いますが、通常はシステ
+
ムの設定ファイルに指定のフォーマットで登録しておけば、システムのブート
+
時に設定スクリプトが動き、自動的に割り当ててくれます。Debian系のディス
+
トリビューションだと/etc/network/interfacesに、RedHat 系だと
+
/etc/sysconfig/network-scripts/ifcfg-eth0に記述します。
+
  
  
 +
;補足: 多くのディストリビューションがifconfigコマンドから [https://wiki.linuxfoundation.org/networking/iproute2 iproute2ユーティリティ] に含まれる ipコマンドに移行しています。頃合いをみて本説明もipコマンドでの説明に切り替える予定です。
  
ネットワークインタフェースの設定状況を見る
+
 
 +
ネットワークインタフェースの設定状況を見るのには次のようにします。
 +
 
 +
 
 +
<pre class="bash">
 
  $ /sbin/ifconfig
 
  $ /sbin/ifconfig
 
  eth0    Link encap:Ethernet  HWaddr 00:C0:26:28:12:C5   
 
  eth0    Link encap:Ethernet  HWaddr 00:C0:26:28:12:C5   
179行目: 193行目:
 
           inet addr:127.0.0.1  Mask:255.0.0.0
 
           inet addr:127.0.0.1  Mask:255.0.0.0
 
  ....
 
  ....
  loはループバック
+
</pre>
 +
 
 +
;補足: loはループバックのための仮想デバイスです。127.0.0.1は特別なIPアドレスで、これは自分自身を指します。
 +
 
 +
 
 +
ifconfigを使う以外にも/proc/net/dev/を見ることで現在のネットワークインタフェースのデバイスの状況を確認することができます。
 +
 
 +
 
 +
<pre class="bash">
 +
$ cat /proc/net/dev
 +
Inter-|  Receive  ....
 +
face  |bytes    packets errs drop fifo frame ...
 +
    lo:  321508    2757    0    0    0    0  ...
 +
wlan0:      0      0    0    0    0    0    ...
 +
  eth0: 1087299  17929    0    0    0    0  ...
 +
</pre>
 +
 
 +
== udev hald ==
 +
 
 +
ホットプラグを実現するために動的に作られるデバイス(TBD)
 +
 
 +
== 脚注 ==
 +
 
 +
<references/>
 +
 
 +
----
 +
[[目次]]へ
  
== udev ==
 
  
ホットプラグを実現するために動的に作られるデバイス(作成中)
+
このページへのショートURL:
 +
http://uc2.h2np.net/i/2d.html

2022年8月3日 (水) 15:22時点における最新版

第七章

デバイススペシャルファイル

I/O の抽象化

Unix で使われた秀逸なアイデアを3つ上げろといわれたら、多分、多くの人が、階層化ファイルシステム、パイプ、そしてデバイススペシャルファイル(Device Special File : 単にスペシャルファイルとも呼ぶ) [1] をあげるでしょう。今回はその中の2つに関係しています。 デバイスを抽象化するこのアイデアによって、 I/O のデバイスもすべてファイルと同じ統一したインタフェースで扱えるようになりました。 例えばハードディスクや端末といったものに対して、プロセスから直接ハードウェアにアクセスする必要はありません。 I/O のスペシャルファイルを経由してアクセスします。 ディレクトリ /dev 以下に用意されているスペシャルファイルがデバイスへのインタフェースです。


端末もスペシャルファイルとして抽象化されていて、たとえば現在使っている端末は /dev/tty のように見えます。 ですから、ここに文字をリダイレクトするとスクリーンにその文字が出力されます。


 $ echo 'hello' > /dev/tty
 hello


たとえばLinuxでは /dev/sda は SCSI ハードディスク、あるいは SCSI ハードディスクに見えるものです[2]。 Linuxのカーネルが認識した順番に /dev/sda から /dev/sdb 、 /dev/sdc となります。 /dev/sda はハードディスク全体で、 sda 上にパーティションが設定されていれば /dev/sda1 、 /dev/sda2 ...となります。 必ずしも /dev/sd? はSCSIハードディスクではなくSCSIのインタフェースが使えて、かつブロックデバイスであればかまいません。 実際に接続されているのは、コンパクトフラッシュメモリだったり、USB接続の外部HDDだったり、 あるいはIDE接続のMOがide-scsiデバイスドライバ経由のためSCSI として取り扱われたりと状況でさまざまです。 IDEハードディスクだと IDE 0 Master に接続されているハードディスクは /dev/hda になります。 Slave側は hdb、 IDE 1 Master/Slave は hdcと hdd という具合になります[3]

補足
接続した順番にデバイスを決めていくので、USB機器などの接続では挿したタイミングでデバイス名が異なることが発生してしまう場合があります。そこで現在ではUUID(Universally Unique Idenifier )という方式を使ってデバイスをユニークに認識する方法を用意しています。fstab(5)、mount(8)、tune2fs(8)を参照してみてください。


/dev/cua0 や /dev/ttyS0 はシリアルポートのスペシャルファイルです。 cua?はたとえばモデムやFAXモデムを接続しておいて、自分から呼び出して接続を行う(Call-Out)する時に使います。 ttyS?は相手から呼び出される時に使います。 /dev/ttyUSB0 はUSB接続をしたシリアル通信のためのスペシャルファイルです。

レイヤ図

プログラムは、スペシャルファイルやシステムコールからカーネルを経由し、最後はカーネル内にあるデバイスドライバを経由してハードウェアにアクセスします。下に簡単なレイヤー図を載せます。

Device Driver Layer

ioctl

ioctl(2)はスペシャルファイルをコントロールしているデバイスドライバに対して命令を送るためのシステムコールです。これでデバイスをコントロールすることになります。(執筆中)

デバイス

キャラクタデバイス

キャラクタデバイスは、時系列でデータが発生する端末、オーディオ、モデムあるいはテープ装置といったシーケンシャルにバイト単位でアクセスするような入出力を行うデバイスドライバです。 データはキャッシュしません。


 $ ls -l  /dev/{random,tty,st0,midi0}
 crw-rw----    1 root     audio     35,   0 Mar 15  2002 /dev/midi0
 crw-rw-rw-    1 root     root       1,   8 Dec  3  2002 /dev/random
 crw-rw----    1 root     tape       9,   0 Mar 15  2002 /dev/st0
 crw-rw-rw-    1 root     tty        5,   0 Nov 18 11:35 /dev/tty
先頭のcがキャラクタデバイスを意味する

ブロックデバイス

ブロックデバイスは、ハードディスクのようなランダムアクセスができ、かつ入出力がブロック単位でアクセスを行うことができるハードウェアへのデバイスドライバです。 データをキャッシュするので効率良く入出力ができます。


 $ ls -l /dev/hd?
 brw-rw----    1 root     disk       3,   0 Mar 15  2002 /dev/hda
 brw-rw----    1 root     disk       3,  64 Mar 15  2002 /dev/hdb
 ....
 brw-rw----    1 root     disk      34,   0 Mar 15  2002 /dev/hdg
 brw-rw----    1 root     disk      34,  64 Mar 15  2002 /dev/hdh
先頭のbがブロックデバイスを意味する


効率が良いと書きましたが、正確には「効率良く入出力するための工夫をしている (だから効率が良い)」といった方がいいでしょう。


1つのブロックのことをセクタと呼び、普通はサイズは512バイトです。CD-ROM のようなデバイスでは最近は2KBですが、いずれにしてもセクターは2のN乗倍(512は2 の9乗、2Kは2の11乗)の値を取ります。 最大値は記憶管理のページのサイズ以下です。 32ビットアーキテクチャーなら4KB、64ビットアーキテクチャーであれば8KBです[4]


ハードディスクなどのデバイスからブロックの内容が読み込まれたとき(あるいは書き込まれる途中)、 データはメモリ中のバッファの中に保管されています。1つのバッファは1つのブロックに対応しています。 普通はページサイズが4KBでブロックサイズが512 バイトですから、一つのページは複数のバッファから出来ています。 ブロックデバイスから幾つかのブロックでデータを読み込んで来た時(あるいは書き込もうとした時)、 メモリ中にバッファのチェーンが作られます。 このように、ブロックデバイスを読み書きする時には、ハードディスクのようなデバイスを直接読み書きするのではなく、 まずこのバッファに対して読み書きが行われます。

擬似デバイス

擬似デバイス (Pseudo-devices) とはデバイスファイルのように見せかけているが、その先には具体的なハードウェアが結びつけられていないデバイスファイルです。 たとえば /dev/null は、その先が何もないデバイスファイルです。


 % cat foo > /dev/null


ファイル foo を読み込んで、デバイスファイル /dev/null に送り込みますが、送り先は何もないので /dev/null に吸い込まれるだけになります。 /dev/null を入力にした場合、何も送られないことになります。以下にいくつかのデバイスファイル例をあげます。


  • /dev/null 入力・出力とも何もしない
  • /dev/random , /dev/urandom 乱数を返す
  • /dev/zero ゼロを返す

I/Oスケジューラ

さてまず、バッファに読み書きされることはわかりました。しかしハードディスク、あるいはそれに相当する具体的なブロックデバイスに書き込む、あるいは読み込む必要があります。 ハードディスクを例に取ると、円盤の磁性体が回っていて、そこに読み書きするヘッドが移動して、そして始めて読み書きが始まります。そのヘッドの移動のことをシークといいますが、ハードディスクのシーク時間は数ミリ秒程度かかります。数ミリ秒というと、とても短い時間のように思えますが、CPUの処理時間から比べれば長い長い時間です。

そこで有効な入出力をするためにスケジューラを用意します。スケジューラの役目は、全体のスループットの改善です。ですから、ある1つのプロセスだけを着目してみると、もしかすると、処理が遅くなっているという可能性もあります。 この当たりは単純に一つのI/Oスケジューリングのアルゴリズムが万能とはなかなかいかないので、Linux 2.6.0以降では Deadline I/O Scheduler、 Anticipatory I/O Scheduler[5]、 Complete Fairness Queueing I/O Scheduler [6]、 Noop I/O Scheduler といった複数 のスケジュールが用意されています。 またLinux 5.0以降ではI/Oスケジューラがマルチキュー I/O スケジューラに変更され、mq-deadline、bfq (Budget Fair Queueing)、kyber、none [7] が用意されています。


補足
Noop I/O Scheduler (none) はスケジュールをしないスケジューラです。


調べてみよう 
ブロックデバイスが/dev/sdaの場合、I/Oスケジューラの設定は /sys/block/sda/queue/scheduler でされているので、実際に自分の使っている環境ではどうなっているか見てみよう。


調べてみよう 
IBMサイトにあるLinuxのディスクI/Oに関する考察 (Linux disk I/O considerations ) や openSUSE サイトにあるI/Oパフォーマンスのチューニング ( 12 Tuning I/O performance )が参考になります。

ネットワークデバイス

1981年当時4.1BSDを改造しTCP/IPのスタックを搭載したのがUNIXのTCP/IPの始まりです [8] 。 当時のLANも多くの場合イーサーネット(Ethernet)で構築されていました。 NIC (Network Interface Card) とか、あるいは LANポート と呼ぶネットワークインタフェースのためのデバイスがあり、 そのデバイスファイルとして /dev/eth0 が作成されました。


補足
eth0は最初に認識しているイーサネットのネットワークインタフェースのデバイスで、複数のネットワークポートやNICが存在していた場合、 eth1、 eth2... となります。


むかしのLinuxもイーサーネットのデバイスは、もちろんUnix流に /dev/ の下にあるデバイススペシャルファイルで、 デバイスは /dev/eth0 と見えていました。 ところが今日のLinuxはネットワークデバイスに対してはスペシャルファイルとして用意していません。 イーサーネットデバイスのはずである/dev/eth?というのはLinux 2.2以降なくなりました。 理由は単純に1つのイーサーネットのデバイスが、1つのIPアドレスを持つというわけではなくなったからです。 現在はハードウェアに1つのイーサーネットのポートしかなくても、 オペレーティングシステムとして、その1つのポートに複数のIPアドレスを割り当てることができます。


ネットワークインタフェースの設定にはifconfigを使いますが、通常はシステムの設定ファイルに指定のフォーマットで登録しておけば、システムのブート時に設定スクリプトが動き、自動的に割り当ててくれます。Debian系のディストリビューションだと /etc/network/interfaces に、RedHat 系だと /etc/sysconfig/network-scripts/ifcfg-eth0 に記述します。


補足
多くのディストリビューションがifconfigコマンドから iproute2ユーティリティ に含まれる ipコマンドに移行しています。頃合いをみて本説明もipコマンドでの説明に切り替える予定です。


ネットワークインタフェースの設定状況を見るのには次のようにします。


 $ /sbin/ifconfig
 eth0     Link encap:Ethernet  HWaddr 00:C0:26:28:12:C5  
          inet addr:192.168.100.100  Bcast:192.168.100.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
 ....
 lo       Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
 ....
補足
loはループバックのための仮想デバイスです。127.0.0.1は特別なIPアドレスで、これは自分自身を指します。


ifconfigを使う以外にも/proc/net/dev/を見ることで現在のネットワークインタフェースのデバイスの状況を確認することができます。


 $ cat /proc/net/dev
 Inter-|   Receive  ....
 face  |bytes    packets errs drop fifo frame ...
    lo:  321508    2757    0    0    0     0   ...
 wlan0:       0       0    0    0    0     0    ...
  eth0: 1087299   17929    0    0    0     0   ...

udev hald

ホットプラグを実現するために動的に作られるデバイス(TBD)

脚注

  1. wikipediaではデバイス・ファイルの項目に「デバイスファイルまたはスペシャルファイルとは」と書いていますが、UNIXの世界では次のように「デバイススペシャルファイル」という表現をしています。 Before we continue, we need to fully understand the "device special file" in UNIX. 本稿もそれに従います。
  2. たとえば USB メモリは USB マスストレージクラスですが、デバイス用の SCSI エミュレーションによってSCSIデバイスのように見えます。
  3. Ubuntuのハードウェアとデバイスの命名規則についての解説ページ [1]
  4. これは典型的な例で、ページサイズはハードウェアのアーキテクチャーに依存します。
  5. 最新ディストリビューションの使っているLinuxカーネルの多くでは既に用意していません。
  6. Inside the Linux 2.6 Completely Fair Scheduler https://www.ibm.com/developerworks/library/l-completely-fair-scheduler/index.html
  7. https://www.kernel.org/doc/html/latest/block/index.html
  8. 私の個人的経験でいわせてもらうと4.2BSDリリース時に入っていたTCP/IPはどう贔屓目に見ても、安定して利用するというには程遠く、プログラム中で引数をちょっと間違えるとシステム全体がいとも簡単にダウンしました。安定して使えたという実感は4.3BSDになってからです。

目次


このページへのショートURL: http://uc2.h2np.net/i/2d.html