/******************** * キャラクターデバイスドライバー************************/ (1)キャラクタデバイスドライバの紹介 キャラクター デバイスとは、バイト ストリームによってアクセスされるデバイスを指し、キャラクター デバイスのドライバーはキャラクター デバイス ドライバーと呼ばれます。 このタイプのドライバーは、ほとんどの単純なハードウェア デバイスに適しています。たとえば、パラレル プリンターの場合は、/dev の下にデバイス ファイル (/dev/printer など) を作成してアクセスします。 ユーザー アプリケーションは、標準の open 関数を使用して dev/printer を開き、write を使用してファイルにデータを書き込み、read を使用してデータを読み取ります。 呼び出しプロセス:
いわゆるドライバーは最終的な書き込み機能を提供し、プリンター ハードウェアのレジスタにアクセスしてプリンターと直接通信します。 (2)主装置番号と副装置番号 a. 機器番号の紹介 キャラクター デバイスへのアクセスは、ファイル システム内のデバイス ファイルを通じて行われます。これらのファイルは /dev にあります。表示するには「ls -l」を使用します。 デバイスはデバイス番号によって識別されます。デバイス番号は、メジャー デバイス番号とマイナー デバイス番号の 2 つの部分で構成されます。 通常、メジャー デバイス番号は、デバイスに対応するドライバーを識別します。Linux では、複数のドライバーが同じメジャー デバイス番号を共有できます。 マイナー デバイス番号は、デバイス ファイルが参照するデバイスを決定するために使用されます。 カーネルでは、デバイス番号を格納するために dev_t 型 <linux/types.h> が使用されます。 2.4 カーネルは 16 ビットのデバイス番号 (マスター用に 8 ビット、スレーブ用に 8 ビット) を使用しますが、2.6 カーネルは 32 ビット (マスター用に 12 ビット、スレーブ用に 20 ビット) を使用します。 ドライバー内のデバイス番号にアクセスするには、<linux/kdev_t.h> で定義されているマクロを使用する必要があります。 デバイス番号を取得します:
b. デバイス番号の割り当てと解放 キャラクター デバイスを作成する前に、ドライバーはデバイス番号を取得する必要があります。 配布する: #include <linux/fs.h> int register_chrdev_region(dev_t first、unsigned int count、char *name); //first: 割り当てるデバイス番号範囲の開始値(セカンダリデバイス番号は通常 0 に設定されます) //count: 要求された連続番号の範囲 //name: 番号に関連付けられたデバイス名 (/proc/devices を参照) カーネルに動的に割り当てるように要求することもできます。 int alloc_chrdev_region(dev_t *dev、unsigned int firstminor、unsigned int count、char *name); //firstminor: 通常は 0 //*dev: カーネルから返されたデバイス番号を格納する リリース: void unregister_chrdev_region(dev_t first、unsigned int count); //モジュールのクリア関数で呼び出されます カーネルが割り当てたデバイス番号は、Documentation/devices.txt にあります。 c. デバイスファイルを作成する デバイス ドライバー モジュールがシステムからメジャーおよびマイナー デバイス番号を適用し、insmod を通じてカーネルにロードされると、/dev の下にデバイス ファイルを作成してデバイスにアクセスできるようになります。 キャラクタデバイスの作成: $>mknod /dev/mychar c major minor システム内の既存のデバイス番号と競合しないように、ドライバーでメジャーデバイス番号とマイナーデバイス番号を動的に割り当てる方法がよく使用されます。 動的に割り当てられる場合、/dev の下のデバイス ファイルも /proc/devices を解析して動的に作成する必要があります。 char_load および char_unload スクリプトを参照してください。 (3)キャラクタデバイスの基本データ構造 キャラクタデバイスドライバに最も関連のある3つの基本データ構造は、ファイル、file_oepeations、inodeです。 a.file_operations データ構造 構造体にはいくつかの関数ポインターが含まれています。これらの関数は、実際にハードウェアと対話する関数です。 ユーザー空間から呼び出される open や write などの関数は、最終的にここでのポインターが指す関数を呼び出します。開いている各ファイルは、一連の関数に関連付けられています。 <linux/fs.h> およびドライバブックのp54を参照してください。 2.6 カーネル構造の初期化: 構造体ファイル操作my_fops = { .owner = このモジュール、 .llseek = my_llseek、 .read = my_read、 .write = my_write, .ioctl = my_ioctl、 .open = my_open, .release = my_release, } 2.4 カーネル構造の初期化: 構造体ファイル操作my_fops = { 所有者: THIS_MODULE、 llseek: my_llseek、 ... } b.ファイル構造 <linux/fs.h> ファイルはカーネル構造体であり、実際にはファイルを開いた後にユーザーによって返されるファイル記述子 fd に対応します。 ファイル構造は開いているファイルを表します。システム内の開いているファイルごとに、カーネル空間内に対応するファイル構造があります。 これは、オープン時にカーネルによって作成され、最後のクローズ関数までファイルを操作するすべての関数に渡されます。その時点で、ファイルのすべてのインスタンスが閉じられた後、カーネルは構造体を解放します。 ユーザー空間プロセスが新しいプロセスをフォークすると、新しいプロセスと古いプロセスは開いているファイル記述子 fd を共有します。この操作ではカーネル空間に新しいファイル構造は作成されず、作成されたファイル構造の数が増えるだけです。 <linux/fs.h> を参照 mode_t f_mode; FMODE_READ と FMODE_WRITE は、ファイルが読み取り可能か書き込み可能かを示すために使用されます。 loff_t f_pos; 現在の読み取りおよび書き込み位置、loff_t は 64 ビットです unsigned int f_flags; O_RDONLY、O_NONBLOCK、O_SYNC などのファイル フラグ。フラグは<linux/fcntl.h>で定義されています。 struct file_operations *f_op; ファイル関連の操作。カーネルは、open を実行するときにこのポインタに値を割り当てます。マイナーデバイス番号に応じて、ドライバーのオープンメソッドに異なる f_ops を割り当てることができます。 void *private; ハードウェアデバイスを表す構造体は通常、private に割り当てられます。 struct dentry *f_dentry; ファイルに対応するディレクトリエントリ (dentry) 構造体。 inode には filp->f_dentry->d_inode 経由でアクセスできます。 ファイルの残りの部分はドライバーとはほとんど関係ありません。 c.inode構造 カーネルは、inode 構造を使用して実際のファイル (通常のファイルまたはデバイス ファイル) を表します。 各ファイルには 1 つの inode 構造しかありませんが、ファイル記述子に対応するファイル構造は複数存在する場合があります (複数のオープン呼び出し)。これらのファイルはすべて同じ inode を指します。 inodeは<linux/fs.h>で定義されています。 dev_t i_rdev; デバイスファイルを表すinode構造体の場合、i_rdevには実際のデバイス番号が含まれます。 struct cdev *i_cdev cdev は、キャラクターデバイスを表すカーネル内部構造体です。 inode が文字デバイスを表す場合、i_cdev はカーネル内の struct cdev を指します。 その他の構造はデバイス ドライバーとはほとんど関係ありません。 次のマクロを使用して、inode からデバイス番号を取得します。
(4)キャラクターデバイスの登録 カーネルは、struct cdev 構造体を使用して文字デバイスを表します。 ドライバーは独自の cdev をカーネルに登録する必要があります。 <linux/cdev.h> を参照 a. 通常はデバイス構造にcdevを追加します 構造体scull_dev{ ... struct cdev cdev; /* キャラクタデバイス構造体*/ } b. 初期化 void cdev_init(構造体 cdev *cdev、構造体 file_operations *fops) c. cdevにコンテンツを設定する
d. cdevの設定をカーネルに追加する int cdev_add(struct cdev *dev, dev_t num, unsigned int count); //num: デバイスに対応する最初の番号 //count: デバイスに関連付けられたデバイス番号の数、通常は 1 // cdev_add が返されると、カーネルはデバイスが使用可能であると判断するため、それを呼び出す前にデバイスのハードウェア初期化を完了する必要があります。 (5)旧登録機能 2.4 の古い登録関数は多くのドライバー関数にまだ存在していますが、新しいコードではそれらを使用しないでください。 登録する: int register_chrdev(符号なしintメジャー、 定数char *名前、 構造体file_operations *fops); //指定されたメジャーデバイス番号のマイナーデバイス番号として0〜255を登録し、各デバイスに対応するデフォルトのcdev構造体を作成します。 ログアウト: int unregister_chrdev(unsigned int major, const char *名前); (6)開いて解放する a.開く デバイスの初期化は、ドライバーのオープン メソッドで完了します。オープンが完了すると、ハードウェアが使用可能になり、ユーザー プログラムは書き込みなどの方法でデバイスにアクセスできるようになります。オープン作業には次のものが含まれます。
オープンプロトタイプ。 int (*open) (構造体inode *inode、構造体ファイル *filp); // open 内の inode から dev ポインタを取得し、それを file->private_data に割り当てます。 //構造体scull_dev *dev; //dev = contain_of(inode->i_cdev、struct scull_dev、cdev); //filp->private_data = dev; //(dev が静的に割り当てられている場合は、open や write などのメソッドで dev に直接アクセスできますが、module_init 中に dev が動的に割り当てられている場合は、上記のメソッドを通じてのみそのポインタを取得できます) b.リリース すべての close 呼び出しで release メソッドが呼び出されるわけではありません。ファイル カウンターがゼロに達した場合にのみ release が呼び出され、dev 構造体が解放されます。 (7)読む、書く 読み取りと書き込みの仕事は、ユーザー空間からカーネルにデータをコピーすること、またはカーネル データをユーザー空間にコピーすることです。そのプロトタイプは次のとおりです。 ssize_t 読み取り(構造体ファイル *filp、char __user *buff、size_t カウント、loff_t *offp); ssize_t write(struct file *filp, const char __user *buff, size_t count, loff_t *offp); //buff: ユーザー空間バッファへのポインタ //offp: ユーザーがファイル内でアクセス操作を実行する場所 //読み取りおよび書き込みでは、データをコピーした後、offp を更新し、コピーされた実際のバイト数を返す必要があります。 (8)ユーザー空間とのデータ交換 __user *buff は、読み取りおよび書き込みにおいて、ユーザー空間へのポインタです。カーネルはその内容を直接参照することはできません (つまり、buff の値に直接アクセスすることはできません)。データのコピーは、カーネルが提供する関数を通じて行う必要があります。理由は次のとおりです。
カーネルとユーザー空間の間でデータを交換するための関数は、<asm/uaccess.h>に示されています。 のように:
ユーザー空間にアクセスする関数はすべてスリープ可能である必要があり、これらの関数は再入可能である必要があります。 copy_to_user などの関数の戻り値が 0 でない場合、読み取りまたは書き込みはユーザー空間に -EFAULT を返す必要があります。 メジャー デバイス番号はデバイス ドライバーを示すために使用され、マイナー デバイス番号はこのドライバーを使用するデバイスを示します。 カーネルでは、dev_t はデバイス番号を表し、メジャーデバイス番号とマイナーデバイス番号で構成されます。 #include <linux/kdev_t.h> #define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS)) //デバイス番号に基づいてメジャーデバイス番号を取得します#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK)) //マイナーデバイス番号を取得します#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi)) //指定されたメジャーデバイス番号とマイナーデバイス番号に基づいてデバイス番号を生成します#include <linux/fs.h> //静的: 指定されたデバイス番号に適用します。from はデバイス番号を参照し、count はこのドライバーを使用するデバイス数 (マイナー デバイス番号) を参照します。デバイス名 int register_chrdev_region(dev_t from, unsigned count, const char *name); //nameの長さは64バイトを超えることはできません //デバイス番号を動的に適用します。カーネルは未使用のメジャーデバイス番号を割り当てます。割り当てられたデバイスはdevに格納されます。baseminorはマイナーデバイス番号の開始番号を参照します。countはデバイスの数を参照します。nameデバイス名int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, 定数char *名前) //デバイス番号を解放します。from はデバイス番号を参照し、count はデバイスの数を参照します void unregister_chrdev_region(dev_t from, unsigned count) //cat /proc/devices でデバイスの使用状況を確認できます。カーネル ソース コードの documentings/devices.txt で、デバイス番号の静的割り当てを確認できます。///カーネルは struct cdev を使用して、キャラクタ デバイス ドライバを記述します。#include <linux/cdev.h> 構造体cdev{ struct kobject kobj; //カーネルはキャラクタデバイスドライバを管理します struct module *owner; //通常はTHIS_MODULEに設定して、ドライバモジュールが使用中にアンロードされないようにする const struct file_operations *ops; //操作方法(vfs) struct list_head list; //複数のデバイスが同じドライバーを使用する可能性があるため、リンクリストを使用して記録します dev_t dev; //デバイス番号 unsigned int count; //デバイス数}; ////////キャラクターデバイスドライバー////////// 1. デバイス番号を申請する 2. cdevデバイスドライバオブジェクトを定義する 構造体 cdev mycdev; //file_operationsのファイル操作オブジェクトを定義する struct file_operations fops = { .owner = このモジュール、 .read = 読み取り関数.... }; 3. fopsオブジェクトをmycdevに関連付ける cdev_init(&mycdev, &fops); //mycdev.ops = &fops; mycdev.owner = このモジュール; 4. カーネルにデバイスドライバを追加し、ドライバに対応するデバイス番号を指定します。 cdev_add(&mycdev, デバイス番号, セカンダリデバイス番号の数); 5. モジュールをアンインストールするときは、カーネルからデバイス ドライバーを削除し、デバイス番号を登録解除します。 cdev_del(&mycdev); ///////////デバイスファイルの作成 mknod /dev/device ファイル名 c メジャーデバイス番号 マイナーデバイス番号 /////////inode ノードオブジェクトは、権限、デバイス番号、その他の情報を含むファイル/デバイスファイルを記述します struct inode { ... dev_t i_rdev; //デバイス ファイルに対応するデバイス番号 struct cdev *i_cdev; //対応するデバイス ドライバー オブジェクトのアドレスを指します... }; ////ファイル オブジェクトは、ファイルが開かれたときに作成され、閉じられたときに破棄されるファイル記述子を記述します。struct file { ... const struct file_operations *f_op; //対応するファイル操作オブジェクトのアドレス unsigned int f_flags; //ファイルオープンフラグ fmode_t f_mode; //権限 loff_t f_pos; //ファイル記述子オフセット struct fown_struct f_owner; //どのプロセスに属しているか unsigned int f_uid, f_gid; void *private_data; //ドライバープログラマーが使用するために... }; デバイスファイルのデバイス番号は、ファイル内のメンバーf_path.dentry->d_inode->i_rdevを通じて取得できます。 ///<asm/errno.h> 内のエラーコード //// /////////構造体file_operations //// inode はアプリケーションによって開かれたファイルのノード オブジェクトを表し、file はファイルを開くことによって取得されたファイル記述子を表します。 成功した場合は0を返し、失敗した場合はエラーコードを返します。 buf はユーザープロセス内のバッファを指し、len は buf のサイズを示します (read を呼び出すときにユーザーによって渡されます) off は fl ファイル記述子の操作オフセットを表し、戻り値は実際にユーザーに提供されるデータのバイト数です。 ユーザープロセスはドライバーにデータを渡す
toはユーザープロセスのバッファを参照し、fromはデータを保持するドライバのバッファを参照し、nはバイト数であり、戻り値は0です。 to はドライバーを参照します... ユーザーから... n はバイト数です...。 静的インライン unsigned long __must_check copy_to_user(void __user *to, const void *from、符号なしlong n) { if (access_ok(VERIFY_WRITE, to, n)) n = __copy_to_user(宛先、送信元、n); return n; //戻り値はコピーする残りのバイト数です} extern inline long copy_from_user(void *to, const void __user *from, long n)
/////////// /// 動的にメモリを適用してクリアします。サイズはアプリケーションのサイズです (128K 以下)。 //flags はフラグです (通常は GFP_KERNEL)。成功した場合はアドレスを返し、失敗した場合は NULL を返します。 // GFP_ATOMIC、システムのメモリ緊急プールを使用 void *kmalloc(size_t size, gfp_t flags); // 適用後にメモリをクリアする必要があります void *kzalloc(size_t size, gfp_t flags); // 適用されたメモリはクリアされています void kfree(const void *objp); // kmalloc/kzalloc のメモリを再利用します void *vmalloc(unsigned long size); // 大きなメモリ空間を適用します void vfree(const void *addr); // vmalloc のメモリを再利用します // kmalloc によって適用されたメモリは連続した物理アドレスを持ちますが、vmalloc は必ずしも連続しているわけではありません ///// container_of(ptr, type, member) type はメンバーメンバーを含む構造体です。 //ptr は型構造体のメンバーのアドレスです。 //このマクロは、構造体メンバーのアドレスに従って構造体変数の最初のアドレスを取得します#define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) 15 型定義構造体led_dev_t { 16 dev_t mydevid; 17 符号なし整数 *rLEDCON; 18 符号なし整数 *rLEDDAT; 19 構造体 cdev mycdev; 20 }LED_DEV; LED_DEV myled; //ind->i_cdev は、myled.mycdev のメンバーを指すアドレスです。//構造体変数 myled の最初のアドレスは、container_of(ind->i_cdev, LED_DEV, mycdev); によって取得できます。 /////// デバイスファイルを自動的に作成する//// #include <linux/device.h> 1. 構造体クラス *cl; cl = class_create(owner, name); //owner は、それが属するモジュール、name クラス名を参照します //作成後、/sys/class/class name を表示できます void class_destroy(struct class *cls); //作成されたクラスを破棄するために使用されます 2. デバイスファイルを作成する 構造体デバイス *device_create(構造体クラス *cls、構造体デバイス *parent、 dev_t devt、void *drvdata、 const char *fmt、...) __attribute__((format(printf, 5, 6))); device_create(class, NULL, device number, NULL, "mydev%d", 88); ///dev/ ディレクトリに mydev88 という名前のデバイス ファイルを作成します void device_destroy(struct class *cls, dev_t devt); //作成されたデバイス ファイルを破棄するために使用します//////// int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops); //デバイス番号を登録し、ドライバーオブジェクトを作成する void unregister_chrdev(unsigned int major, const char *name); //デバイス番号を登録解除し、ドライバーオブジェクトを削除する static inline int register_chrdev(unsigned int major, const char *name, 定数構造体ファイル操作 *fops) { __register_chrdev(major, 0, 256, name, fops) を返します。 } int __register_chrdev(unsigned int major, unsigned int baseminor, 符号なし整数カウント、const char *名前、 定数構造体ファイル操作 *fops) { 構造体 char_device_struct *cd; 構造体 cdev *cdev; 整数エラー = -ENOMEM; cd = __register_chrdev_region(メジャー、ベースマイナー、カウント、名前); (IS_ERR(cd)の場合) PTR_ERR(cd) を返します。 cdev = cdev_alloc(); もし (!cdev) 2 番目の出力に移動します。 cdev->owner = fops->owner; cdev->ops = fops; kobject_set_name(&cdev->kobj, "%s", 名前); エラー = cdev_add(cdev, MKDEV(cd->major, baseminor), count); もし(エラー) 外出する; cd->cdev = cdev; メジャーを返す? 0 : cd->major; 外: kobject_put(&cdev->kobj); アウト2: kfree(__unregister_chrdev_region(cd->major, baseminor, count)); エラーを返します。 } 要約する 以上がこの記事の全内容です。この記事の内容が皆様の勉強や仕事に何らかの参考学習価値をもたらすことを願います。123WORDPRESS.COM をご愛顧いただき、誠にありがとうございます。これについてもっと知りたい場合は、次のリンクをご覧ください。 以下もご興味があるかもしれません:
|
<<: JSON.stringify を使用する際に発生する循環参照の問題を解決する方法の詳細な説明
>>: Linux サーバーで MySQL リモート接続を有効にする方法
この記事では、次のような効果を持つ JS ページング効果の例を紹介します。クールだと思いませんか? ...
GTID の利点により、従来のファイル POS ベースのレプリケーションを GTID ベースのレプリ...
いわゆる 3 列適応レイアウトとは、両側の幅が固定され、中央のブロックの幅が適応されることを意味しま...
Docker には多くのログ プラグインがあります。デフォルトでは json-file を使用します...
目次K8Sマスター基本アーキテクチャポッドオーケストレーションコンセプトPod オブジェクトのプロパ...
検索ページ: search.wxml ページ: <view class="form&...
コードをコピーコードは次のとおりです。 <!--リストタグ: <dl>: 階層リス...
JDKダウンロードアドレス: http://www.oracle.com/technetwork/j...
目次文字列の長さ: 長さcharAt() charCodeAt()文字列に値が含まれているかどうかを...
この記事の例では、ページング効果表示を実現するためのミニプログラムの具体的なコードを参考までに共有し...
CSS の適用範囲はグローバルです。プロジェクトがどんどん大きくなり、参加する人が増えるにつれて、命...
説明する: Windows 10 に VM をインストールし、VM で Docker を実行し、Do...
1. スタートアップメニューでは、カーソルを最初の行に移動します - eを押します 2. UTF-8...
序文Linux システムのすべてのハードウェア デバイスは、ファイルの形式で表現され、使用されます。...
重要なポイント: 1. CSS3 3Dアニメーションをマスターする2. ページめくり後のページ内容の...