キャラクタースペシャルファイルを理解しようとしています。ウィキペディアから、これらのファイルは、一度に1文字ずつデータを送信するデバイスの「インターフェースを提供する」ことを理解しています。私の理解では、システムはデバイスドライバーを直接呼び出すのではなく、何らかの方法でキャラクターデバイスを呼び出します。しかし、ファイルはこのインターフェースをどのように提供しますか?システムコールを変換する実行可能ファイルですか?誰かが何が起こっているのか説明できますか。
キャラクタースペシャルファイルを理解しようとしています。ウィキペディアから、これらのファイルは、一度に1文字ずつデータを送信するデバイスの「インターフェースを提供する」ことを理解しています。私の理解では、システムはデバイスドライバーを直接呼び出すのではなく、何らかの方法でキャラクターデバイスを呼び出します。しかし、ファイルはこのインターフェースをどのように提供しますか?システムコールを変換する実行可能ファイルですか?誰かが何が起こっているのか説明できますか。
回答:
それらは実際にはまさに-インターフェースです。「メジャー」および「マイナー」番号でエンコードされ、カーネルへのフックを提供します。
キャラクターデバイスとブロックデバイスの2つのフレーバーがあります(まあ、3つですが、名前付きパイプはこの説明の範囲外です)。
ブロックデバイスは、出力をバッファリングし、後で取得するためにデータを保存できるストレージデバイスである傾向があります。
キャラクターデバイスは、オーディオやグラフィックカードのようなもの、またはキーボードやマウスのような入力デバイスです。
いずれの場合も、カーネルが(ブート時またはudevなどのプログラムを介して)正しいドライバーをロードすると、さまざまなバスをスキャンして、そのドライバーで処理されるデバイスが実際にシステムに存在するかどうかを確認します。その場合、適切なメジャー/マイナー番号で「リッスン」するデバイスをセットアップします。
(たとえば、システムによって検出された最初のオーディオカードのデジタル信号プロセッサは14/3のメジャー/マイナーペアを取得し、2番目は14,35などを取得します。)
major 14 minor 3とマーク/dev
さdsp
れたキャラクターデバイスとしてnamedにエントリを作成するのはudev です。
(Linuxの非常に古いバージョンまたは最小フットプリントバージョンでは、/dev/
動的にロードされず、すべての可能なデバイスファイルが静的に含まれる場合があります。)
次に、ユーザー空間プログラムが適切なメジャー/マイナー番号で「キャラクタースペシャルファイル」としてマークされたファイルにアクセスしようとすると(たとえば、オーディオプレーヤーがデジタルオーディオを送信しようとしている場合/dev/dsp
)、カーネルはこのデータが必要であることを認識しますメジャー/マイナー番号が添付されているドライバーを介して送信されます。おそらく、ドライバーはそれをどう処理するかを知っているという。
すべてのファイル、デバイスまたはその他は、VFS内で6つの基本操作をサポートします。
さらに、デバイスファイルはI / Oコントロールをサポートしているため、最初の6ではカバーされていないその他のさまざまな操作が可能です。
キャラクタースペシャルの場合、seekとtellはストリーミングインターフェースをサポートしているため実装されていません。つまり、シェルでリダイレクトを使用して行われるように、直接読み取りまたは書き込みを行います。
echo 'foo' > /dev/some/char
sed ... < /dev/some/char
最小限の実行可能なfile_operations
例
最小限の例を見ると、すべてが明らかになります。
主なアイデアは次のとおりです。
file_operations
syscallに関連する各ファイルのコールバックが含まれますmknod <path> c <major> <minor>
それらを使用するキャラクターデバイスを作成します file_operations
cat /proc/devices
character_device.ko
カーネルモジュール:
#include <asm/uaccess.h> /* copy_from_user, copy_to_user */
#include <linux/errno.h> /* EFAULT */
#include <linux/fs.h> /* register_chrdev, unregister_chrdev */
#include <linux/jiffies.h>
#include <linux/module.h>
#include <linux/printk.h> /* printk */
#include <uapi/linux/stat.h> /* S_IRUSR */
#define NAME "lkmc_character_device"
MODULE_LICENSE("GPL");
static int major;
static ssize_t read(struct file *filp, char __user *buf, size_t len, loff_t *off)
{
size_t ret;
char kbuf[] = {'a', 'b', 'c', 'd'};
ret = 0;
if (*off == 0) {
if (copy_to_user(buf, kbuf, sizeof(kbuf))) {
ret = -EFAULT;
} else {
ret = sizeof(kbuf);
*off = 1;
}
}
return ret;
}
static const struct file_operations fops = {
.owner = THIS_MODULE,
.read = read,
};
static int myinit(void)
{
major = register_chrdev(0, NAME, &fops);
return 0;
}
static void myexit(void)
{
unregister_chrdev(major, NAME);
}
module_init(myinit)
module_exit(myexit)
ユーザーランドテストプログラム:
insmod /character_device.ko
dev="lkmc_character_device"
major="$(grep "$dev" /proc/devices | cut -d ' ' -f 1)"
mknod "/dev/$dev" c "$major" 0
cat /dev/lkmc_character_device
# => abcd
rm /dev/lkmc_character_device
rmmod character_device
GitHub QEMU + Buildrootアップストリームを実際に実行するボイラープレート付き:
より複雑な例:
read
、write
、lseek
固定サイズの内部バッファと、代わりに文字デバイスのdebugfsの上に:https://github.com/cirosantilli/linux-kernel-module-cheat/blob/6788a577c394a2fc512d8f3df0806d84dc09f355/kernel_module/fops.cpoll
:https : //github.com/cirosantilli/linux-kernel-module-cheat/blob/6788a577c394a2fc512d8f3df0806d84dc09f355/kernel_module/poll.cioctl
:https : //github.com/cirosantilli/linux-kernel-module-cheat/blob/6788a577c394a2fc512d8f3df0806d84dc09f355/kernel_module/poll.canon_inode_getfd
file_operations
ファイルシステムファイルなしでファイル記述子にa を関連付けます:https : //stackoverflow.com/questions/4508998/what-is-anonymous-inode/44388030#44388030*off = 1;
、そしてなぜそれが設定されているの1
か?
read
、同じopen(
ファイル記述子への複数の呼び出しにわたって渡されます。ドライバーは、それで何でもできます。通常のセマンティックは、読み取られたバイト数を含めることです。この例ではしかし、私たちは単純なセマンティックを持っている:0
最初の読み取りのために、1
後の最初の読み取り。実行して、printkまたはGDBステップをデバッグしてみてください。
「一度に文字」は誤った呼び名です(文字デバイスは必ずしもシークアンドテルをサポートしないという考えと同じです)。実際、「一度にブロック」(つまり、テープドライブ*などの厳密にレコード指向)デバイスは、キャラクターデバイスでなければなりません。キャラクターデバイスは必然的にシーク不可能でなければならないという考えもあります-キャラクターデバイスドライバーは完全なfile_operations
は、デバイスが操作をサポートするかどうかに応じてllseekを自由に定義できる構造を定義します。ほとんどの人が例として考えているキャラクターデバイスは、null、urandom、TTYデバイス、サウンドカード、マウスなどです。これらは、それらのデバイスが何であるかの詳細のためにすべて探せませんが、/ dev / vcs、/ dev / fb0 、および/ dev / kmemもキャラクターデバイスであり、すべてシーク可能です。
前述したように、キャラクターデバイスドライバーは、誰かがファイルで呼び出したいすべての操作(シーク、読み取り、書き込み、ioctlなど)の関数ポインターを持つfile_operations構造体を定義します。このデバイスファイルを開いた状態で実行されます。したがって、読み取りと書き込みは、引数を使用して必要な処理を実行できます。大きすぎる書き込みの受け入れを拒否したり、適切な内容のみを書き込むことができます。要求されたバイト数全体ではなく、1つのレコードに対応するデータのみを読み取ることができます。
では、ブロックデバイスとは何ですか?基本的に、ブロックデバイスはディスクドライブです。他の種類のデバイス(ramdiskやループバックなどの仮想ディスクドライブを除く)は、ブロックデバイスではありません。ユーザープロセスから/ dev / sdaなどにアクセスしている場合でも、キャラクターデバイスとは異なる方法で、I / O要求システム、ファイルシステム層、バッファー/キャッシュシステム、および仮想メモリシステムに統合されます。。ページが例外として言及している「未加工デバイス」でさえ、キャラクターデバイスですです。
*一部のUNIXシステムは、現在「固定ブロックモード」と呼ばれるものを実装しています。これにより、カーネルグループと分割I / O要求は、ディスクドライブの場合とほぼ同じ方法で構成済みのブロック境界に適合します-ブロックとしてデバイス。キャラクタデバイスは、「可変ブロックモード」に必要です。このモードでは、1回のwrite(2)呼び出しが1つのブロックを書き込み、1回のread(2)呼び出しが1つのブロックを返すため、ユーザープログラムからブロック境界が保持されます。モード切り替えは、個別のデバイスファイルではなくioctlとして実装されるようになったため、キャラクターデバイスが使用されます。シークにはバイト数ではなくレコード数のカウントが含まれ、ネイティブシーク操作はioctlとして実装されるため、可変レコードテープドライブはほとんど「シーク不可」です。
キャラクターデバイスは、カーネルモジュール(またはカーネル自体)によって作成できます。デバイスが作成されると、作成者はopen、readなどの標準呼び出しを処理する実装する関数へのポインターを提供します。次に、Linuxカーネルはこれらの関数をキャラクターデバイスに関連付けます。たとえば、ユーザーモードアプリケーションがread()キャラクタデバイスファイルの関数である場合、syscallになり、カーネルはこの呼び出しをドライバーの作成時に指定された読み取り関数にルーティングします。キャラクターデバイスの作成に関するステップバイステップチュートリアルがここにあります。サンプルオブジェクトを作成し、デバッガーを使用してデバイスオブジェクトを作成する方法とハンドラーが呼び出されるタイミングを理解する手順を作成できます。