web-dev-qa-db-ja.com

Linuxは実際のファイルと存在しない(例:デバイス)ファイルをどのように区別しますか?

これはかなり低レベルの質問であり、質問するのに最適な場所ではない可能性があることを理解しています。しかし、それは他のどのSEサイトよりも適切であると思われたので、ここにいきます。

Linuxファイルシステムでは、一部のファイルが実際にexistのようになっていることを知っています。たとえば、/usr/bin/bashは存在するファイルです。ただし、(私が理解している限り)実際には存在しないものもあり、より多くのvirtualファイルです(例:/dev/sda/proc/cpuinfoなど)。質問は次のとおりです(2つありますが、関連性が高すぎて個別の質問にすることはできません)。

  • Linuxカーネルは、読み取りコマンド(またはそのような)が発行されたときに、これらのファイルが実在する(したがってディスクから読み取る)かどうかに関係なく、どのように機能しますか?
  • ファイルが実在しない場合:例として、/dev/randomからの読み取りはランダムなデータを返し、/dev/nullからの読み取りはEOFを返します。この仮想ファイルからどのデータを読み取るか(したがって、データが仮想ファイルに書き込まれた場合、またはどのような場合にどうするか)はどのように計算されますか?各ファイルに適切な個別の読み取り/書き込みコマンドへのポインターを持つある種のマップがあります。それとも仮想ディレクトリ自体ですか?したがって、/dev/nullのエントリは単にEOFを返す可能性があります。
28
Joe

つまり、ここには基本的に2つの異なるタイプがあります。

  1. 通常のファイルシステム。データとメタデータを含むディレクトリにファイルを保持します(ソフトリンク、ハードリンクなどを含む)。これらは、常にではありませんが、多くの場合、永続ストレージ用のブロックデバイスによってサポートされます(tmpfsはRAMにのみ存在しますが、それ以外は通常のファイルシステムと同じです)。これらのセマンティクスはよく知られています。読み取り、書き込み、名前の変更など、すべて期待どおりに機能します。
  2. さまざまな種類の仮想ファイルシステム。 /proc/sysは、sshfsifuseなどのFuseカスタムファイルシステムと同様に、ここでの例です。これらは実際には、ある意味「カスタム」であるセマンティクスを持つファイルシステムを参照しているだけなので、はるかに多様性があります。したがって、/procの下のファイルから読み取る場合、通常のファイルシステムのように、以前に他の何かが書き込んで格納した特定のデータに実際にはアクセスしていません。あなたは本質的にカーネルの呼び出しを行っており、オンザフライで生成されるいくつかの情報を要求しています。そして、このコードは、どこかにreadセマンティクスを実装している単なる関数なので、好きなように実行できます。したがって、/proc下のファイルの奇妙な動作は、たとえば、実際にはシンボリックリンクではないふりをするようなものです。

重要なのは、/devが実際には通常、最初の種類の1つであることです。最近のディストリビューションでは/devをtmpfsのようにするのが普通ですが、古いシステムでは、特別な属性を持たない、ディスク上のプレーンディレクトリにするのが普通でした。重要なのは、/devの下のファイルがデバイスノードであることです。これは、FIFOまたはUnixソケットに似たタイプの特殊ファイルです。デバイスノードにはメジャー番号とマイナー番号があり、それらの読み取りまたは書き込みは、FIFOがカーネルを呼び出して出力をこのドライバは、必要なことは何でも実行できますが、通常、ハードディスクにアクセスしたり、スピーカーでサウンドを再生したりするなど、ハードウェアに何らかの影響を与えます。

元の質問に答えるには:

  1. 「ファイルが存在する」かどうかに関連する2つの質問があります。これらは、デバイスノードファイルが文字通り存在するかどうか、それをサポートするカーネルコードが意味があるかどうかです。前者は、通常のファイルシステムの場合と同様に解決されます。最新のシステムはudevまたはそのようなものを使用してハードウェアイベントを監視し、それに応じて/devの下のデバイスノードを自動的に作成および破棄します。しかし、古いシステム、または軽量のカスタムビルドでは、すべてのデバイスノードを文字通りディスク上に作成し、事前に作成できます。一方、これらのファイルを読み取るときは、メジャーデバイス番号とマイナーデバイス番号によって決定されるカーネルコードを呼び出します。これらが妥当でない場合(たとえば、存在しないブロックデバイスを読み取ろうとしている場合)は、何らかのI/Oエラーが発生します。

  2. どのカーネルファイルがどのデバイスファイルを呼び出すかを決定する方法はさまざまです。 /procなどの仮想ファイルシステムの場合、独自のread関数とwrite関数を実装します。カーネルは、それがどのマウントポイントにあるかに応じて、そのコードを呼び出すだけで、ファイルシステムの実装が残りを処理します。デバイスファイルの場合、メジャーデバイス番号とマイナーデバイス番号に基づいてディスパッチされます。

25
Tom Hunt

これが、ほぼ最新のArch Linuxサーバー上の/dev/sda1のファイルリストです。

% ls -li /dev/sda1
1294 brw-rw---- 1 root disk 8, 1 Nov  9 13:26 /dev/sda1

したがって、sda/dev/のディレクトリエントリには、iノード番号1294があります。これは、ディスク上の実際のファイルです。

ファイルサイズが通常表示される場所を確認します。代わりに「8、1」が表示されます。これはメジャーおよびマイナーデバイス番号です。また、ファイル権限の「b」にも注意してください。

ファイル/usr/include/ext2fs/ext2_fs.hには、この(フラグメント)C構造体が含まれています。

/*
 * Structure of an inode on the disk
 */
struct ext2_inode {
    __u16   i_mode;     /* File mode */

この構造体は、ファイルのiノードのディスク上の構造を示しています。その構造体には、興味深いものがたくさんあります。長い目で見てください。

i_modestruct ext2_inode要素は16ビットであり、ユーザー/グループ/その他、読み取り/書き込み/実行のアクセス許可に9つ、setuid、setgid、スティッキに3つだけを使用します。 「プレーンファイル」、「リンク」、「ディレクトリ」、「名前付きパイプ」、「Unixファミリソケット」、「ブロックデバイス」などのタイプを区別するための4ビットがあります。

Linuxカーネルは、通常のディレクトリルックアップアルゴリズムに従い、i_mode要素の権限とフラグに基づいて決定を行うことができます。 「b」の場合、ブロックデバイスファイルは、メジャーおよびマイナーデバイス番号を見つけることができ、従来は、メジャーデバイス番号を使用して、ディスクを処理するカーネル関数(デバイスドライバー)へのポインターを検索します。マイナーデバイス番号は、通常、SCSIバスデバイス番号、またはEIDEデバイス番号などのように使用されます。

/proc/cpuinfoのようなファイルの処理方法に関するその他の決定は、ファイルシステムのタイプに基づいて行われます。あなたが行う場合:

% mount | grep proc 
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)

/procのファイルシステムタイプが「proc」であることがわかります。 /procのファイルから読み取ると、ReiserFSまたはDOSファイルシステムでファイルを開くと、カーネルがさまざまな関数を使用してファイルを検索するのと同じように、カーネルはファイルシステムのタイプに基づいて別のことを行います。ファイルのデータを見つけます。

17
Bruce Ediger

結局のところ、これらはすべてUnix用のファイルであり、それが抽象化の美点です。

カーネルがファイルを処理する方法、これは別の話です。

/ procと最近の/ devと/ run(別名/ var/run)は、RAM内の仮想ファイルシステムです。/procは、カーネル変数および構造へのインターフェース/ウィンドウです。

Linuxカーネル http://tldp.org/LDP/tlk/tlk.html およびLinuxデバイスドライバー、第3版 https://lwn.net/Kernel/LDD3 /

FreeBSDオペレーティングシステムの設計と実装も楽しんだ http://www.Amazon.com/Design-Implementation-FreeBSD-Operating-System/dp/0321968972/ref=sr_1_1

あなたの質問に関連している関連ページを見てください。

http://www.tldp.org/LDP/tlk/dd/drivers.html

7
Rui F Ribeiro

@RuiFRibeiroと@BruceEdigerの回答に加えて、ユーザーが行う区別は、カーネルが行う区別とは厳密には異なります。実際には、通常のファイル、ディレクトリ、シンボリックリンク、デバイス、ソケットなど、さまざまな種類のファイルがあります(完全なリストを作成するつもりはないので、常にいくつかを忘れています)。 lsを使用すると、ファイルのタイプに関する情報を取得できます。これは、行の最初の文字です。例えば:

$ls -la /dev/sda
brw-rw---- 1 root disk 8, 0 17 nov.  08:29 /dev/sda

最初の「b」は、このファイルがブロックデバイスであることを示します。ダッシュは通常のファイル、「l」はシンボリックリンクなどを意味します。この情報はファイルのメタデータに格納されており、たとえばシステムコールstatを介してアクセスできるため、カーネルはファイルやシンボリックリンクなどを異なる方法で読み取ることができます。

次に、/bin/bashのような「実際のファイル」と/proc/cpuinfoのような「仮想ファイル」を区別しますが、lsは両方を通常のファイルとして報告するため、違いは別の種類です。

ls -la /proc/cpuinfo /bin/bash
-rwxr-xr-x 1 root root  829792 24 août  10:58 /bin/bash
-r--r--r-- 1 root wheel      0 20 nov.  16:50 /proc/cpuinfo

何が起こるかは、それらが異なるファイルシステムに属していることです。 /procは、疑似ファイルシステムprocfsのマウントポイントですが、/bin/bashは、通常のディスクファイルシステム上にあります。 Linuxがファイルを開くと(ファイルシステムによって異なります)、データ構造fileにデータが入力されます。これには、他の属性の中でも、このファイルの使用方法を説明するいくつかの関数ポインターの構造があります。したがって、ファイルの種類ごとに異なる動作を実装できます。

たとえば、これらは/proc/meminfoによってアドバタイズされる操作です。

static int meminfo_proc_open(struct inode *inode, struct file *file)
{
    return single_open(file, meminfo_proc_show, NULL);
}

static const struct file_operations meminfo_proc_fops = {
    .open       = meminfo_proc_open,
    .read       = seq_read,
    .llseek     = seq_lseek,
    .release    = single_release,
};

meminfo_proc_openの定義を見ると、この関数は、メモリ使用量に関するデータを収集するタスクである関数meminfo_proc_showから返された情報をメモリ内のバッファに入力していることがわかります。その後、この情報は通常どおり読み取ることができます。ファイルを開くたびに、関数meminfo_proc_openが呼び出され、メモリに関する情報が更新されます。

5
lgeorget

ファイルシステム内のすべてのファイルは、ファイルI/Oを許可するという意味で「本物」です。ファイルを開くと、カーネルはファイル記述子を作成します。ファイル記述子は、ファイルのように機能するオブジェクト(オブジェクト指向プログラミングの意味で)です。ファイルを読み取る場合、ファイル記述子はその読み取りメソッドを実行し、それがファイルシステム(sysfs、ext4、nfsなど)にファイルからのデータを要求します。ファイルシステムは、ユーザー空間への統一されたインターフェイスを提供し、読み取りと書き込みを処理するために何をすべきかを知っています。次に、ファイルシステムは他の層に要求を処理するように要求します。たとえばext4ファイルシステムの通常のファイルの場合、これにはファイルシステムのデータ構造の検索(ディスクの読み取りが含まれる場合があります)が含まれ、最終的にはディスク(またはキャッシュ)からの読み取りによってデータが読み取りバッファーにコピーされます。たとえばsysfsのファイルの場合、それは通常、バッファに何かをsprintf()するだけです。ブロック開発ノードの場合、ディスクドライバーにいくつかのブロックを読み取ってバッファーにコピーするように要求します(メジャー番号とマイナー番号は、ファイルシステムに要求するドライバーを指示します)。

3
jpkotta