キャラクターデバイスやキャラクター特殊ファイルはどのように機能しますか?

キャラクターデバイスやキャラクター特殊ファイルはどのように機能しますか?

文字特殊ファイルを理解しようとしています。 ~からウィキペディア、私が知っている限り、このファイルは一度に1文字ずつデータを転送するデバイスに「インターフェースを提供」します。私の理解は、システムがデバイスドライバを直接呼び出すのではなく、文字デバイスを呼び出すことです。しかし、ファイルはどのようにこのインターフェイスを提供しますか?システムコールを翻訳する実行ファイルですか?何が起こっているのかを説明できる人はいますか?

答え1

実際にはインターフェースです。これは「主」と「負」の数字でエンコードされ、カーネルへの接続を提供します。

これには文字デバイスとブロックデバイスという2つのタイプがあります(まあ、3つの名前付きパイプは現在この説明の範囲外です)。

ブロックデバイスは、出力をバッファリングして後で取得できるようにデータを格納するストレージデバイスである傾向があります。

文字デバイスは、オーディオやグラフィックカードなどのデバイスやキーボード、マウスなどの入力デバイスです。

それぞれの場合、カーネルが正しいドライバをロードするとき(起動時または次の方法で)ウデブ)さまざまなバスを検索して、このドライバが処理するデバイスが実際にシステムにあることを確認します。その場合は、適切なプライマリ/セカンダリ番号を「受信」するようにデバイスを設定します。

(たとえば、システムが見つけた最初のサウンドカードのDSPは、メジャー/マイナー番号のペア14/3を取得し、2番目のサウンドカードは14,35を取得する式です。)

udev の/dev名前にdspmajor 14 min 3 というラベルの付いた文字デバイスでエントリが作成されます。

(以前または最小サイズのLinuxバージョンでは、/dev/すべての可能なデバイスファイルを動的にロードせず、単に静的に含めることができます。)

次に、ユーザースペースプログラムが「文字特殊ファイル」とマークされたファイルにアクセスしようとし、適切な州/マイナー番号がある場合(たとえば、オーディオプレーヤーがデジタルオーディオを送信しようとする場合)、/dev/dspカーネルはデータを移動する必要がありますことを知っています。メジャー/マイナー番号を介してマイナー番号がドライバー転送に添付されます。おそらく、ドライバーはこれをどうするかを知っているでしょう。

答え2

各ファイル、デバイス、またはその他のエントリは、VFS内で6つの基本操作をサポートします。

  1. 開いている
  2. 閉鎖
  3. 読む
  4. 書く
  5. 探す
  6. 言う

さらに、デバイスファイルはI / O制御をサポートしているため、最初の6でカバーされていないその他の操作も可能です。

特殊文字の場合、検索と話し方がサポートされているため実装されません。ストリーミングメディアインターフェース。つまり、リダイレクトによってシェルから直接読み書きが行われます。

echo 'foo' > /dev/some/char
sed ... < /dev/some/char

答え3

最小限の実行可能なfile_operations

最小限の例を見ると、すべてが明らかになります。

重要なアイデアは次のとおりです。

  • file_operations各ファイル固有のシステムコールのコールバックが含まれます。
  • 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アップストリーム(実際に実行するための定型句を含む):

より複雑な例:

答え4

文字デバイスは、カーネルモジュール(またはカーネル自体)によって生成できます。デバイスが作成されると、コンストラクタはハンドル(オープン、読み取りなど)への標準呼び出しを実装する関数へのポインタを提供します。その後、Linuxカーネルはこれらの機能を文字デバイスに関連付けます。たとえば、ユーザーモードアプリケーションがキャラクタデバイスファイルでread()関数を呼び出すと、カーネルはこの呼び出しを次にルーティングします。ドライバの作成時に指定されたパス。キャラクターデバイスを作成する方法に関するステップバイステップのチュートリアルがあります。ここを使用すると、サンプルプロジェクトを作成し、デバッガを使用して段階的に進み、デバイスオブジェクトの作成方法とハンドラの呼び出し時期を確認できます。

関連情報