デバイス ファイルは、Linux システムの特別なファイルであり、ユーザー空間プログラムがファイル操作を通じてデバイスにアクセスできるように、デバイスのインターフェイスを表すために使用されます。デバイス ファイルの実装には、inode、file、file_operations という 3 つの重要な構造が関係します。 i ノード構造は、デバイス番号、権限、サイズなどのデバイス ファイルのメタデータを保存するために使用されます。ファイル構造は、現在の場所、オープンモード、プライベートデータなどのデバイスファイルのステータス情報を保存するために使用されます。 file_operations 構造体は、開く、読み取り、書き込み、閉じるなどのデバイス ファイル操作関数を格納するために使用されます。この記事では、これら3つの構造の定義と機能、使用例と注意点を紹介します。
ドライバーはハードウェアを下方向に制御し、インターフェイスを上方向に提供します。ここで上方向に提供されるインターフェイスは、最終的に 3 つの方法でアプリケーション層に対応します: デバイス ファイル、/proc、/sys、最も一般的に使用されるものは次のとおりです。デバイス ファイルを使用します。最も一般的に使用される Linux デバイスはキャラクタ デバイスです。この記事では、キャラクタ デバイスを例として使用して、キャラクタ デバイス ファイルを作成して開く内部メカニズムを分析します。
Linux ではすべてがファイルです。Linux でファイルを作成すると、それに対応するファイル システムに i ノードが作成されます。ファイルの実体とファイルの i ノードは 1 対になっています。 -one 対応 , 作成された i ノードはメモリに保存されます。最初に開いたときは、メモリに i ノードのバックアップが作成されます。同じファイルを複数回開いても、複数の i ノードは生成されません。すべてのファイルが開かれたとき、が閉じられている場合、メモリ内の i ノード インスタンスは解放されます。この場合、mknod (または他のメソッド) を使用してデバイス ファイルを作成すると、ファイル システムに i ノードも作成されます。この i ノードは、他の i ノードと同様に、ファイルに関する静的な情報 (不変の情報) を保存するために使用されます。 . )、このデバイス ファイルに対応するデバイス番号、ファイルのパス、対応するドライバー オブジェクトなどが含まれます。 VFS の 4 つの主要なオブジェクトの 1 つである inode は、ドライバー開発時に自分で埋める必要はほとんどありませんが、必要に応じて open() メソッドで i ノードを表示し、ファイル構造を埋めることがより必要です。
ファイル タイプが異なると、inode のフィルされたメンバーの内容も異なります。キャラクタ デバイスの作成を例にとると、add_chrdev_region が実際には ドライバー オブジェクト と (グループ) ## を組み合わせていることがわかります。 # デバイス番号 がリンクされています。デバイス ファイルを作成すると、実際には デバイス ファイル と デバイス番号 が接続されます。この時点で、この 3 つは結合されます。このようにして、カーネルは struct inode のインスタンスを作成する機能を備えています。以下は 4.8.5 カーネルの i ノードです。この i ノードは、最も特定のファイル システムの i ノードをさらにカプセル化した VFS の i ノードです。また、ドライバー開発に関係する i ノードでもあります。特定のファイル システムについては、struct ext2_inode_info などの構造体もあります。
リーリー
」 構造体 inode
–602–>i_mode はアクセス許可制御を示します –604–>UID –605–>GID
–606–>i_flags ファイル システム フラグ
–630–>ハードリンク数
–635–>i_size ファイル サイズ (バイト単位)
–636–>最終アクセス時刻
–637–>最終変更時刻
–638–>最終変更時刻
–669–>i_dentry; //カタログネックレスリスト
–673–>i_count 参照カウント、参照カウントが 0 になると、inode インスタンスが解放されます
–675–>i_writecountライター数
–679–>デバイス ファイルを作成するとき、i_fops には def_chr_fops、blk_blk_fops、def_fifo_fops、および bad_sock_fops のいずれかが書き込まれます。作成プロセス中に呼び出される init_special_inode()
を参照してください。 –683–>union、pipe、cdev、blk.link などの特殊なファイル タイプ i_cdev は、この i ノードがキャラクタ デバイス ファイルに属していることを示します。この記事でデバイス ファイルを作成する場合、関連するデバイス番号のドライバ オブジェクト cdevが使用されます。 –702–>inode
のプライベートデータ
」#上記のメンバー struct def_chr_fops のみを追求する価値があり、後で非常に役立ちます:
//fs/char_dev.c 429 const struct file_operations def_chr_fops = { 430 .open = chrdev_open, 431 .llseek = noop_llseek, 432 };ログイン後にコピーstruct file
Linux内核会为每一个进程维护一个文件描述符表,这个表其实就是struct file[]的索引。open()的过程其实就是根据传入的路径填充好一个file结构并将其赋值到数组中并返回其索引。下面是file的主要内容
//include/linux/fs.h 877 struct file { 878 union { 879 struct llist_node fu_llist; 880 struct rcu_head fu_rcuhead; 881 } f_u; 882 struct path f_path; 883 struct inode *f_inode; /* cached value */ 884 const struct file_operations *f_op; 885 886 /* 887 * Protects f_ep_links, f_flags. 888 * Must not be taken from IRQ context. 889 */ 890 spinlock_t f_lock; 891 atomic_long_t f_count; 892 unsigned int f_flags; 893 fmode_t f_mode; 894 struct mutex f_pos_lock; 895 loff_t f_pos; 896 struct fown_struct f_owner; 897 const struct cred *f_cred; 898 struct file_ra_state f_ra;f 904 /* needed for tty driver, and maybe others */ 905 void *private_data; 912 struct address_space *f_mapping; 913 } __attribute__((aligned(4))); /* lest something weird decides that 2 is OK */ログイン後にコピー“
struct file
–882–>f_path里存储的是open传入的路径,VFS就是根据这个路径逐层找到相应的inode
–883–>f_inode里存储的是找到的inode
–884–>f_op里存储的就是驱动提供的file_operations对象,这个对象在open的时候被填充,具体地,应用层的open通过层层搜索会调用inode.i_fops->open,即chrdev_open()
–891–>f_count的作用是记录对文件对象的引用计数,也即当前有多少个使用CLONE_FILES标志克隆的进程在使用该文件。典型的应用是在POSIX线程中。就像在内核中普通的引用计数模块一样,最后一个进程调用put_files_struct()来释放文件描述符。
–892–>f_flags当打开文件时指定的标志,对应系统调用open的int flags,比如驱动程序为了支持非阻塞型操作需要检查这个标志是否有O_NONBLOCK。
–893–>f_mode;对文件的读写模式,对应系统调用open的mod_t mode参数,比如O_RDWR。如果驱动程序需要这个值,可以直接读取这个字段。
–905–>private_data表示file结构的私有数据”
我在Linux设备管理(二)_从cdev_add说起一文中已经分析过chrdev_open(),这里仅作概述。
//fs/chr_dev.c 348 /* 349 * Called every time a character special file is opened 350 */ 351 static int chrdev_open(struct inode *inode, struct file *filp) 352 { /* 搜索cdev */ ... 390 replace_fops(filp, fops); 391 if (filp->f_op->open) { 392 ret = filp->f_op->open(inode, filp); 393 if (ret) 394 goto out_cdev_put; 395 } ... 402 }ログイン後にコピー可以看出,这个函数有三个任务(划重点!!!):
“
chrdev_open()
–352-389–>利用container_of等根据inode中的成员找到相应的cdev
–390–>用cdev.fops替换filp->f_op,即填充了一个空的struct file的f_op成员。
–392–>回调替换之后的filp->f_op->open,由于替换,这个其实就是cdev.fops”
至此,我们知道了我们写的驱动中的open()在何时会被回调,这样我们就可以实现很多有意思的功能,比如,
我们可以在open中通过inode->cdev来识别具体的设备,并将其私有数据隐藏到file结构的private_data中,进而识别同一个驱动操作一类设备;
我们也可以在回调cdev.fops->open()阶段重新填充file结构的fop,进而实现同一个驱动操作不同的设备,这种思想就是内核驱动中常用的分层!
最后总结一下这些结构之间的关系:“
”
この記事を通じて、デバイス ファイルの 3 つの重要な構造、inode、file、file_operations について学びました。デバイスファイルの管理や操作に使用できます。実際のニーズに応じて適切な構造を選択し、正しいデバイス番号の使用、合理的な権限の使用、効果的な操作機能の使用など、いくつかの基本原則に従う必要があります。デバイス ファイルの 3 つの構造は、Linux システムの最も基本的な概念の 1 つであり、デバイスを抽象化してカプセル化し、システムの統一性と柔軟性を向上させることができます。この記事があなたにとって有益であり、インスピレーションとなることを願っています。
以上がLinux システムのデバイス ファイル: inode、file、および file_operationsの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。