差分

移動先: 案内検索

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

3,795 バイト追加, 2022年8月3日 (水) 15:22
/* I/Oスケジューラ */
== デバイススペシャルファイル ==
=== I/Oの抽象化 O の抽象化 ===
Unixで使われた秀逸なアイデアを3つ上げろといわれたら、多分、多くの人が、階Unix で使われた秀逸なアイデアを3つ上げろといわれたら、多分、多くの人が、階層化ファイルシステム、パイプ、そしてデバイススペシャルファイル(Device Special File : 単にスペシャルファイルとも呼ぶ)層化ファイルシステム、パイプ、そしてデバイススペシャルファイル(単にス<ref> wikipediaではデバイス・ファイルの項目に「デバイスファイルまたはスペシャルファイルとは」と書いていますが、UNIXの世界では次のように「デバイススペシャルファイル」という表現をしています。ペシャルファイルとも呼ぶ)をあげるでしょう。今回はその中の2つに関係しています。デバイスを抽象化するこのアイデアによって、I[https:/Oのデバイスもすべてファイ/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.]ルと同じ統一したインタフェースで扱えるようになりました。例えばハードディ本稿もそれに従います。スクや端末といったものに対して、プロセスから直接ハードウェアにアクセス</ref>することはありません。必ずIをあげるでしょう。今回はその中の2つに関係しています。デバイスを抽象化するこのアイデアによって、 I/Oのスペシャルファイルを経由してアクセスしO のデバイスもすべてファイルと同じ統一したインタフェースで扱えるようになりました。ます。ディレクトリ例えばハードディスクや端末といったものに対して、プロセスから直接ハードウェアにアクセスする必要はありません。I/dev以下に用意されているスペシャルファイルがデバイスO のスペシャルファイルを経由してアクセスします。へのインタフェースです。ディレクトリ /dev 以下に用意されているスペシャルファイルがデバイスへのインタフェースです。
端末もスペシャルファイルとして抽象化されていて、たとえば現在使っている端末は端末もスペシャルファイルとして抽象化されていて、たとえば現在使っている端末は /dev/ttyのように見えます。ですから、ここに文字をリダイレクトするとtty のように見えます。スクリーンにその文字が出力されます。ですから、ここに文字をリダイレクトするとスクリーンにその文字が出力されます。
<pre class="bash">
$ echo 'hello' > /dev/tty
hello
</pre>
たとえばLinuxではIDEハードディスクが使えるコンピュータだとIDE 0 Masterに接続されているハードディスクはたとえばLinuxでは /dev/hdaになります。Slave側はhdb、IDE 1sda は SCSI ハードディスク、あるいは SCSI ハードディスクに見えるものです<ref>たとえば USB メモリは USB マスストレージクラスですが、デバイス用の SCSI エミュレーションによってSCSIデバイスのように見えます。</ref>。MasterLinuxのカーネルが認識した順番に /Slaveはhdcとhddという具合になります。dev/sda から /dev/sdb 、 /dev/hdaはハードディスク全sdc となります。体で、hda上にパーティションが設定されていればhda1、hda2/dev/sda はハードディスク全体で、 sda 上にパーティションが設定されていれば /dev/sda1 、 /dev/sda2 ...が各々のパーとなります。ティションを指しています。必ずしも /dev/sdaはSCSIハードディスク、あるいはSCSIハーsd? はSCSIハードディスクではなくSCSIのインタフェースが使えて、かつブロックデバイスであればかまいません。ドディスクに見えるものです。必ずしも実際に接続されているのは、コンパクトフラッシュメモリだったり、USB接続の外部HDDだったり、あるいはIDE接続のMOがide-scsiデバイスドライバ経由のためSCSI として取り扱われたりと状況でさまざまです。IDEハードディスクだと IDE 0 Master に接続されているハードディスクは /dev/sd?はSCSIハードディスクではなhda になります。くSCSIのインタフェースが使えて、かつブロックデバイスであればかまいません。実際に接続されているのは、コンパクトフラッシュメモリだったり、USBSlave側は hdb、 IDE 1 Master/Slave は hdcと hdd という具合になります<ref>接続の外部HDDだったり、あるいはIDE接続のMOがideUbuntuのハードウェアとデバイスの命名規則についての解説ページ [https://www.dell.com/support/kbdoc/ja-scsiデバイスドライバ経由のためSCSI として取り扱われたりと状況でさまざまです。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]</ref>。
;補足: 接続した順番にデバイスを決めていくので、USB機器などの接続では挿したタイミングでデバイス名が異なることが発生してしまう場合があります。そこで現在ではUUID(Universally Unique Idenifier )という方式を使ってデバイスをユニークに認識する方法を用意しています。fstab(5)、mount(8)、tune2fs(8)を参照してみてください。
/dev/cua0やcua0 や /dev/ttyS0はシリアルポートのスペシャルファイルです。cua?はttyS0 はシリアルポートのスペシャルファイルです。たとえばモデムやFAXモデムを接続しておいて、自分から呼び出して接続を行cua?はたとえばモデムやFAXモデムを接続しておいて、自分から呼び出して接続を行う(Call-Out)する時に使います。ttySする時に使います。ttyS?は相手から呼び出される時に使います。/dev/ttyUSB0 はUSB接続をしたシリアル通信のためのスペシャルファイルです。
=== レイヤ図 ===
プログラムは、スペシャルファイルやシステムコールからカーネルを経由し、最後はカーネル内にあるデバイスドライバを経由してハードウェアにアクセスします。下に簡単なレイヤー図を載せます。プログラムは、スペシャルファイルやシステムコールからカーネルを経由し、最後はカーネル内にあるデバイスドライバを経由してハードウェアにアクセスします。下に簡単なレイヤー図を載せます。
  +[[File:Device-Driver--------------------------------------------+ Layer.png| プログラム thumb| +------------------+--------------------------+ right|デバイススペシャル400px| システムコール | その | インタフェース |ファイル | インタフェース | ほか | +---------------------------------------------+ | カーネルでのその他の処理 | サービス +---------------------------------------------+ | キャラクタ | ブロック | ネットワーク| その | デバイス | デバイス | デバイス | デバイス | ほか | ドライバ +---------------------------------------------+ | ハードウェア | +---------------------------------------------+Device Driver Layer]]
=== ioctl ===
ioctl(2)はスペシャルファイルをコントロールしているデバイスドライバに対して命令を送るためのシステムコールです。これでデバイスをコントロールすることになります。(作成中)はスペシャルファイルをコントロールしているデバイスドライバに対して命令を送るためのシステムコールです。これでデバイスをコントロールすることになります。(執筆中)
== キャラクタデバイス ブロックデバイス デバイス ==
=== キャラクタデバイス ===
キャラクタデバイスは、時系列でデータが発生する端末、オーディオ、モデムキャラクタデバイスは、時系列でデータが発生する端末、オーディオ、モデムあるいはテープ装置といったシーケンシャルにバイト単位でアクセスするような入出力を行うデバイスドライバです。あるいはテープ装置といったシーケンシャルにバイト単位でアクセスするような入出力を行うデバイスドライバです。基本的にデータはキャッシュしません。データはキャッシュしません。
 
<pre class="bash">
$ ls -l /dev/{random,tty,st0,midi0}
crw-rw---- 1 root audio 35, 0 Mar 15 2002 /dev/midi0
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がキャラクタデバイスを意味する</pre>
先頭のcがキャラクタデバイスを意味する
=== ブロックデバイス ===
ブロックデバイスは、ハードディスクのようなランダムアクセスができ、かつブロックデバイスは、ハードディスクのようなランダムアクセスができ、かつ入出力がブロック単位でアクセスを行うことができるハードウェアへのデバイスドライバです。入出力がブロック単位でアクセスを行うことができるハードウェアへのデバイスドライバです。データをキャッシュするので効率良く入出力ができます。データをキャッシュするので効率良く入出力ができます。
 
<pre class="bash">
$ ls -l /dev/hd?
brw-rw---- 1 root disk 3, 0 Mar 15 2002 /dev/hda
brw-rw---- 1 root disk 34, 0 Mar 15 2002 /dev/hdg
brw-rw---- 1 root disk 34, 64 Mar 15 2002 /dev/hdh
</pre>
 
先頭のbがブロックデバイスを意味する
効率が良いと書きましたが、正確には「効率良く入出力するための工夫をして
いる (だから効率が良い)」といった方がいいでしょう。
効率が良いと書きましたが、正確には「効率良く入出力するための工夫をしている (だから効率が良い)」といった方がいいでしょう。  1つのブロックのことをセクタと呼び、普通はサイズは512バイトです。CD-ROM のようなデバイスでは最近は2KBですが、いずれにしてもセクターは2のN乗倍(512は2 の9乗、2Kは2の11乗)の値を取ります。最大値は記憶管理のページのの値を取ります。最大値は記憶管理のページのサイズ以下です。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 に吸い込まれるだけになります。サイズ以下です。32ビットアーキテクチャーなら4KB、64ビットアーキテクチャー/dev/null を入力にした場合、何も送られないことになります。以下にいくつかのデバイスファイル例をあげます。であれば8KBです。
ハードディスクなどのデバイスからブロックの内容が読み込まれたとき(ある* /dev/null 入力・出力とも何もしないいは書き込まれる途中)、データはメモリ中のバッファの中に保管されていま* /dev/random , /dev/urandom 乱数を返すす。1つのバッファは1つのブロックに対応しています。普通はページサイズが4KBでブロックサイズが512 バイトですから、一つのページは複数のバッファから出来ています。ブロックデバイスから幾つかのブロックでデータを読み込んで来た時(あるいは書き込もうとした時)、メモリ中にバッファのチェーンが作られます。このように、ブロックデバイスを読み書きする時には、ハードディスクのようなデバイスを直接読み書きするのではなく、まずこのバッファに対して読み書きが行われます。* /dev/zero ゼロを返す
== I/Oスケジューラ ==
さてまず、バッファに読み書きされることはわかりました。しかしハードディさてまず、バッファに読み書きされることはわかりました。しかしハードディスク、あるいはそれに相当する具体的なブロックデバイスに書き込む、あるいは読み込む必要があります。スク、あるいはそれに相当する具体的なブロックデバイスに書き込む、あるいは読み込む必要があります。ハードディスクを例に取ると、円盤の磁性体が回っていて、そこに読み書きするヘッドが移動して、そして始めて読み書きが始まります。その移動のことをシークといいますが、ハードディスクのシーク時間は数ミリ秒程度かかります。数ミリ秒というと、とても短い時間のように思えますが、CPUの処理時間から比べれば長い長い時間です。ハードディスクを例に取ると、円盤の磁性体が回っていて、そこに読み書きするヘッドが移動して、そして始めて読み書きが始まります。そのヘッドの移動のことをシークといいますが、ハードディスクのシーク時間は数ミリ秒程度かかります。数ミリ秒というと、とても短い時間のように思えますが、CPUの処理時間から比べれば長い長い時間です。
そこで有効な入出力をするためにスケジューラを用意します。スケジューラの役目は、全体のスループットの改善です。ですから、ある1つのプロセスだけを着目してみると、もしかすると、処理が遅くなっているという可能性もあります。そこで有効な入出力をするためにスケジューラを用意します。スケジューラの役目は、全体のスループットの改善です。ですから、ある1つのプロセスだけを着目してみると、もしかすると、処理が遅くなっているという可能性もあります。この当たりは単純に一つのI/Oスケジュリングのアルゴリズムが万能とはなかなかいかないので、Linux Oスケジューリングのアルゴリズムが万能とはなかなかいかないので、Linux 2.6.0以降ではDeadline 0以降ではDeadline I/O Scheduler、AnticipatoryScheduler、<del>Anticipatory I/O Scheduler、Complete Scheduler</del><ref>最新ディストリビューションの使っているLinuxカーネルの多くでは既に用意していません。</ref>、Complete Fairness Queueing Disk Scheduler、Noop I/O 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>
が用意されています。
;補足: Kernel 2.6.18以降 CFQ(Complete Fairness Queueing)スケジューラがデフォルトです。
;補足: Noop I/O Schedulerはスケジュールをしないスケジューラです。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の始まりです。IPの始まりです<ref>私の個人的経験でいわせてもらうと4.2BSDリリース時に入っていたTCP/IPはどう贔屓目に見ても、安定して利用するというには程遠く、プログラム中で引数をちょっと間違えるとシステム全体がいとも簡単にダウンしました。安定して使えたという実感は4IPはどう贔屓目に見ても、安定して利用するというには程遠く、プログラム中で引数をちょっと間違えるとシステム全体がいとも簡単にダウンしました。安定して使えたという実感は4.3BSDになってからです。</ref>当時のLANも多くの場合イーサーネット(Ethernet)で構築されていました。NIC (Network Interface Card) とか、あるいは LANポート と呼ぶネットワークインタフェースのためのデバイスがあり、そのデバイスファイルとして /dev/eth0 が作成されました。
当時、我々がLANと呼ぶものはイーサーネット(Ethernet)で構築されていました。NIC (Network Interface Card)とか、あるいはLANポートとか呼ぶものも、使っているのはイーサーネットでした。そのため/dev/eth0といった名前でネットワークのデバイスファイルが作成されました。むかしのLinuxもイーサーネットのデバイスは、もちろんUnix流に/dev/の下にあるデバイススペシャルファイルで、デバイスは/dev/eth0と見えていました。;補足: eth0は最初に認識しているイーサネットのネットワークインタフェースのデバイスで、複数のネットワークポートやNICが存在していた場合、 eth1、 eth2... となります。
;補足: eth0は最初に認識しているイーサネットのネットワークデバイスで、複数のネットワークポートやNICが存在していた場合、eth1、eth2むかしのLinuxもイーサーネットのデバイスは、もちろんUnix流に /dev/ の下にあるデバイススペシャルファイルで、デバイスは /dev/eth0 と見えていました。ところが今日のLinuxはネットワークデバイスに対してはスペシャルファイルとして用意していません。イーサーネットデバイスのはずである/dev/eth?というのはLinux 2...となります。2以降なくなりました。理由は単純に1つのイーサーネットのデバイスが、1つのIPアドレスを持つというわけではなくなったからです。現在はハードウェアに1つのイーサーネットのポートしかなくても、オペレーティングシステムとして、その1つのポートに複数のIPアドレスを割り当てることができます。
ところが今のLinuxはネットワークデバイスに対してはスペシャルファイルとして用意していません。イーサーネットデバイスのはずである/dev/eth?というのはLinux 2.2以降なくなりました。理由は単純に1つのイーサーネットデバイスが、1つのIPアドレスを持つというわけではなくなったからです。ハードウェアに1つのイーサーネットのポートしかなくても、オペレーティングシステム的にはその1つのポートに複数のIPアドレスを割り当てることができます。ネットワークインタフェースの設定にはifconfigを使いますが、通常はシステムの設定ファイルに指定のフォーマットで登録しておけば、システムのブート時に設定スクリプトが動き、自動的に割り当ててくれます。Debian系のディストリビューションだとネットワークインタフェースの設定にはifconfigを使いますが、通常はシステムの設定ファイルに指定のフォーマットで登録しておけば、システムのブート時に設定スクリプトが動き、自動的に割り当ててくれます。Debian系のディストリビューションだと /etc/network/interfacesに、RedHat interfaces に、RedHat 系だと/etc/sysconfig/network-scripts/ifcfg-eth0に記述します。eth0 に記述します。
;補足: 多くのディストリビューションがifconfigコマンドから [https://wiki.linuxfoundation.org/networking/iproute2 iproute2ユーティリティ] に含まれる ipコマンドに移行しています。頃合いをみて本説明もipコマンドでの説明に切り替える予定です。
ネットワークインタフェースの設定状況を見るネットワークインタフェースの設定状況を見るのには次のようにします。  <pre class="bash">
$ /sbin/ifconfig
eth0 Link encap:Ethernet HWaddr 00:C0:26:28:12:C5
inet addr:127.0.0.1 Mask:255.0.0.0
....
</pre> ;補足: loはループバック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