ホームページ >システムチュートリアル >Linux >Linux 中級 - 「ドライバー」 ハードウェアを制御するために習得する必要がある低レベルの知識
駆動された認知
ドライバーは、基礎となるハードウェア デバイスの操作をカプセル化し、上位層に関数インターフェイスを提供します。
デバイス分類: Linux システムは、デバイスを キャラクター デバイス、ブロック デバイス、およびネットワーク デバイスの 3 つのカテゴリに分類します。
呼び出しプロセス全体について説明する例を示しましょう
/dev の下の pin4 を呼び出して、読み取りおよび書き込み可能な方法で開きます。 **==上位層のオープン呼び出し カーネルでソフト割り込みが発生します。割り込み番号は 0X80 で、ユーザー空間からカーネル空間に入ります ==**
(カーネル関数) を呼び出し、system_call は /dev/pin4 デバイス名に基づいて必要なデバイス番号を見つけます。
を呼び出します。sys_open がドライバーを見つけます。
リンク リスト で、 メジャー デバイス番号とマイナー デバイス番号 に基づいてピン 4 のオープン関数を見つけます。 ピン 4 のオープンはレジスタ操作です
#「」 ドライバーは単に
ドライバーを追加するだけであると書きます:ドライバーを追加すると何が行われるのでしょうか?
機器名######デバイス番号
- ## デバイスドライバ機能(IOポートを駆動するための操作レジスタ)
- 」
==要約==dev
の下の pin4
ピンをオープンする場合、プロセスは次のとおりです。 ユーザー モードは open # を呼び出します。 ## ("/de/pin4", O_RDWR)、カーネルの場合、上位層から open 関数を呼び出すとソフト割り込みがトリガーされます (システム コールの場合は特別で、割り込み番号は 0x80 で、0x80 は 0x80 を表します)システム イベント) コール)、
システムはカーネル状態 に入り、system_call に進みます。これは、このソフト割り込みの割り込みサービス プログラムへの入り口と考えることができ、その後、渡されたシステムコール番号に基づいて対応するコールを決定し、システムコールサービスプログラム(この場合、
VFS の
sys_open が呼び出されます)を呼び出します。
sys_open は、デバイス名とデバイス番号に基づいて、カーネル ドライバー リスト
から関連するドライバー関数 を検索します (各ドライバー関数はノード )。 **= =ドライバー関数にはレジスタを通じて IO ポートを制御するコードが含まれており、これにより IO ポートを制御して関連関数を実装できます==**。
#「ユーザー モードでは、開発中に C と C ライブラリの基礎が必要です。C ライブラリは、ファイル、プロセス、プロセス間通信、スレッド、ネットワーク、およびインターフェイス (GTk)」
ユーザーモード:」
# これは、ユーザーがプログラムを作成し、プログラムを実行するレベルを指します。
#「カーネル状態:」
ユーザーが特定のハードウェア デバイスを使用したい場合は、前の記事で説明したように、カーネル モード デバイス ドライバー,ハードウェアを動作させるためにが必要です WireringPi ライブラリ
, は、ユーザーがハードウェア デバイスを制御するためのインターフェイス を提供します。wiringPi ライブラリがない場合は、wiringPi ライブラリの機能を自分で実装する、つまりデバイス ドライバを自分で記述する必要があります。このようにして、別のタイプの基板が入手できたら、開発も完了することができます。
Linux ではすべてがファイルです. あらゆる種類のファイルとデバイス (下図に示すマウス、キーボード、画面、フラッシュ、メモリ、ネットワーク カードなど) はすべてファイルです。 ファイルなのでファイル操作機能を利用してこれらの機器を操作することができます。
1 つの質問は、open や read などのファイル操作関数は、開かれているファイルがどのハードウェア デバイスであるかをどのようにして知るのかということです。 ①open関数に対応するファイル名を入力して、対応するデバイスを制御します。 ②==デバイス番号(メジャーデバイス番号とマイナーデバイス番号)==を渡します。さらに、これらのドライバーの場所と、これらのドライバーの実装方法も理解する必要があります。各ハードウェア デバイスは、異なるドライバーに対応します (これらのドライバーは独自に実装されます)。
Linux デバイス管理はファイル システムと密接に統合されています、さまざまなデバイスは、==devices File= というファイル形式で /dev ディレクトリに保存されます =*。アプリケーションは、通常のデータ ファイルを操作するのと同じように、これらのデバイス ファイルを開いたり、閉じたり、読み書きしたり、デバイス上で操作を完了したりできます。 **これらのデバイスを管理するために、システムはデバイスに番号を付けます**。*各デバイス番号は、==メジャー デバイス番号== と ==マイナー デバイス番号==* に分割されます (図に示すように)。下の図) を表示します:)。 **メジャーデバイス番号**は、さまざまな種類のデバイスを区別するために使用されます。 devices であり、最初のデバイス番号 は、同じタイプの複数のデバイスを区別するために使用されます。 一般的に使用されるデバイスについては、Linux には従来の番号があります。たとえば、ハードディスクのメジャー デバイス番号は 3 です。 ****キャラクタ デバイスまたはブロック デバイスには、メジャー デバイス番号とマイナー デバイス番号
があります。==メジャーデバイス番号とマイナーデバイス番号を総称してデバイス番号==**と呼びます。#「
」 メジャーデバイス番号
は、このドライバーを使用する各デバイスを表すために使用されます。
は、特定のドライバーを表すために使用されます。 マイナーデバイス番号」
#たとえば、組み込みシステムには 2 つの LED インジケーターがあり、LED ライトを個別にオンまたはオフにする必要があります。次に、LED ライト用の キャラクター デバイス ドライバ を作成し、その プライマリ デバイス番号をデバイス番号 5 として登録します。 セカンダリ デバイス番号は 1 と 2 です。 ## それぞれ。 #。ここで、セカンダリデバイス番号はそれぞれ 2 つの LED ライトを表します。
==ドライバーのリンクされたリスト==
#「#system_call 関数はどのようにして詳細なシステム コール サービス ルーチンを見つけますか?」
すべてのデバイスのドライバーの管理、追加または検索
デバイス番号追加は、ドライバーの作成が完了してカーネルにロードされた後に行われます
。 Searchはドライバーを呼び出しており、アプリケーション層のユーザー空間はオープン関数
を使用して検索します。 ドライバーがリンク リストに挿入される順序は、によって取得されます。つまり、メジャー デバイス番号とマイナー デバイス番号は、異なるデバイスを区別するだけでなく、デバイスの種類と異なる種類のデバイスを区別するだけでなく、ドライバを区別する役割も果たします プログラムはリンク リストの特定の位置にロードされます. 以下に紹介するドライバ コードの開発は、ドライバを追加することに他なりません。 (デバイス番号、デバイス名、デバイス ドライバー関数の追加) および ドライバーの呼び出し 。
」
各システムコールはシステムコール番号に対応しており、システムコール番号はカーネル内の対応する処理関数に対応している。
struct inode
構造によって記述され、この構造には ファイル タイプ、アクセス許可 などのファイルのすべての情報が記録されます。
ディレクトリまたは
/sys などの他のディレクトリに対応するファイルを持ちます。
構造を割り当て、開いているファイル
を記述します。
(2) struct inode 構造体に記録されているデバイス番号に基づいて、対応するドライバーが見つかります。ここではキャラクターデバイスを例に挙げます。 Linux オペレーティング システムでは、各キャラクター デバイスに struct cdev 構造があります。この構造は、キャラクタ デバイスのすべての情報を記述します。その中で最も重要なのは、キャラクタ デバイスの操作機能インターフェイスです。
(3) struct cdev 構造体を見つけた後、Linux カーネルは、struct cdev 構造体が配置されているメモリ空間の最初のアドレスを struct inode 構造体の i_cdev メンバーに記録し、に記録されている関数操作インターフェイスを使用します。 struct cdev 構造体 アドレスは、struct ファイル構造体の f_ops メンバーに記録されます。
(4) タスクが完了すると、VFS 層はファイル記述子 (fd) をアプリケーションに返します。この fd は struct ファイル構造に対応します。次に、上位層アプリケーションは fd を通じて struct ファイルを見つけ、その struct ファイル内でキャラクターデバイスを操作するための関数インターフェース file_operation を見つけます。
このうち、cdev_init と cdev_add は、それぞれキャラクタ デバイスと file_operation 関数の操作インターフェイスのバインドを完了し、キャラクタ ドライバをカーネルに登録するために、ドライバのエントリ関数内で呼び出されています。
デバイス名を手動で作成する
sudo mknod デバイス名 デバイス タイプ (c はキャラクター デバイス ドライバーを表します) メジャー デバイス番号 マイナー デバイス番号
b: ブロック (バッファーされた) 特殊ファイルを作成します。 c、u: キャラクター (バッファなし) スペシャル ファイルを作成します。 p: FIFO を作成します。 手動で作成したデバイス名を削除して、rm だけを実行します。以下に示すように:上位層プログラム を通じてデバイスを開きます。ドライバーがない場合は、実行時にエラーが報告されます。カーネル ドライバーでは、上位層システムが open および wirte を呼び出します。関数は
sys_call をトリガーし、sys_call は
sys_open、 および
sys_write、sys_open、および sys_write を呼び出し、カーネルの ## で
メジャー デバイス番号 を渡します。 # ドライバーのリンクされたリスト デバイス ドライバーを見つけて、開いて内部に書き込むを実行します。プロセス全体をスムーズに進めるために、最初にドライバー (デバイス ドライバー ファイル) を準備する必要があります。
module_init(pin4_drv_init);
//pin4_drv_init
functionint __init pin4_drv_init(void)
//実際のドライバーエントリdevno = MKDEV(major,minor);
// デバイス番号を作成register_chrdev(major, module_name,&pin4_fops);
//登録ドライバーは、上で準備した構造をカーネル ドライバーのリンク リストに追加するようにカーネルに指示しますpin4_class=class_create(THIS_MODULE,"myfirstdemo");
//デバイスは dev のコードによって自動的に生成され、クラスが作成されますpin4_class_dev =device_create(pin4_class,NULL,devno,NULL,module_name);
//デバイスファイルを作成します。 /dev
に上位層が開くための追加ファイルを持たせることです。sudo mknod デバイス名デバイス タイプ (c はキャラクター デバイス ドライバーを表します) メジャー デバイス番号マイナー デバイス番号
ドライバー モジュール コードのコンパイル (モジュールのコンパイルには、構成されたカーネル ソース コードが必要です。コンパイルと接続後に生成されるカーネル モジュールのサフィックスは、**.ko
です。コンパイル プロセスは、最初にカーネル ソース コード ディレクトリで、最上位の Makefile ファイルを読み取り、モジュール ソース コードが配置されているディレクトリに戻ります。): **
#include //file_operations声明 #include //module_init module_exit声明 #include //__init __exit 宏定义声明 #include //class devise声明 #include //copy_from_user 的头文件 #include //设备号 dev_t 类型声明 #include //ioremap iounmap的头文件 static struct class *pin4_class; static struct device *pin4_class_dev; static dev_t devno; //设备号 static int major =231; //主设备号 static int minor =0; //次设备号 static char *module_name="pin4"; //模块名 //led_open函数 static int pin4_open(struct inode *inode,struct file *file) { printk("pin4_open\n"); //内核的打印函数和printf类似 return 0; } //read函数 static int pin4_read(struct file *file,char __user *buf,size_t count,loff_t *ppos) { printk("pin4_read\n"); //内核的打印函数和printf类似 return 0; } //led_write函数 static ssize_t pin4_write(struct file *file,const char __user *buf,size_t count, loff_t *ppos) { printk("pin4_write\n"); //内核的打印函数和printf类似 return 0; } static struct file_operations pin4_fops = { .owner = THIS_MODULE, .open = pin4_open, .write = pin4_write, .read = pin4_read, }; //static限定这个结构体的作用,仅仅只在这个文件。 int __init pin4_drv_init(void) //真实的驱动入口 { int ret; devno = MKDEV(major,minor); //创建设备号 ret = register_chrdev(major, module_name,&pin4_fops); //注册驱动 告诉内核,把这个驱动加入到内核驱动的链表中 pin4_class=class_create(THIS_MODULE,"myfirstdemo");//让代码在dev下自动>生成设备 pin4_class_dev =device_create(pin4_class,NULL,devno,NULL,module_name); //创建设备文件 return 0; } void __exit pin4_drv_exit(void) { device_destroy(pin4_class,devno); class_destroy(pin4_class); unregister_chrdev(major, module_name); //卸载驱动 } module_init(pin4_drv_init); //入口,内核加载驱动的时候,这个宏会被调用,去调用pin4_drv_init这个函数 module_exit(pin4_drv_exit); MODULE_LICENSE("GPL v2");
/SYSTEM/linux-rpi-4.19.y/drivers/char
将以上代码复制到一个文件中,然后下一步要做的是就是:将上面的驱动代码编译生成模块,再修改Makefile。(你放那个文件下,就改哪个文件下的Makefile)obj-m += pin4drive.o
添加到Makefile中即可。下图:Makefile文件图
.ko
文件发送给树莓派**然后回/SYSTEM/linux-rpi-4.19.y
下使用指令:ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 make modules
进行编译生成驱动模块。然后将生成的.ko
文件发送给树莓派:scp drivers/char/pin4driver.ko pi@192.168.0.104:/home/pi
编译生成驱动模块会生成以下几个文件:
.o
的文件是object文件,.ko
是kernel object,与.o的区别在于其多了一些sections,比如.modinfo
。.modinfo section
是由kernel source里的modpost工具生成的, 包括MODULE_AUTHOR, MODULE_DESCRIPTION, MODULE_LICENSE, device ID table以及模块依赖关系等等。depmod 工具根据.modinfo section生成modules.dep, modules.*map等文件,以便modprobe更方便的加载模块。“
- コンパイルプロセスでは、次の手順を実行しました:
- まず、Linux カーネルが配置されているディレクトリに移動し、pin4drive.o ファイルをコンパイルします。
- MODPOST を実行すると、一時的な pin4drive.mod.c ファイルが生成され、このファイルに基づいて pin4drive.mod.o がコンパイルされます (
)- 次に、pin4drive.o ファイルと pin4drive.mod.o ファイルを接続して、モジュール ターゲット ファイル pin4drive.ko、
を取得します。- 最後に、Linux カーネルが配置されているディレクトリをそのまま残します。
”
pin4test.c (上位層の呼び出しコード) をクロスコンパイルして Raspberry Pi に送信すると、送信された .ko がpi ディレクトリ ファイル と pin4test
の 2 つのファイルは、以下に示すとおりです。
カーネルドライバーをロードします
以下に、pin4
という名前のデバイス ドライバーが表示されます (これはコード行に関連しています static char *module_name=”pin4″; //ドライバー コード内のモジュール名)、デバイス番号もコードに関連します。
lsmod
ドライバがインストールされていることが確認できます。
もう一度 ./pin4test を実行して上位コードを実行しましょう
その後、再度実行してくださいpin4testdmesg |grep pin4 を使用できます。以下の図に示すように: ドライバー呼び出しが成功したことを示します
ドライバーをインストールした後、コマンド sudo rmmod ドライバー名
(ko を記述する必要はありません) を使用してドライバーをアンインストールできます。
ドライバー モジュールの生成を仮想マシン上で生成する必要があるのはなぜですか?ラズベリーパイは動かないのですか?
ドライバー モジュールの生成にはコンパイル環境が必要です (Linux ソース コードとコンパイルには、システム バージョンと同じ Linux カーネル ソース コードをダウンロードする必要があります)。Raspberry Pi でもコンパイルできますが、 Raspberry Pi でコンパイルするとより効率的ですが、非常に低いため、非常に長い時間がかかります。この記事では、Raspberry Pi ドライバーのローカル コンパイルについて説明します。
以上がLinux 中級 - 「ドライバー」 ハードウェアを制御するために習得する必要がある低レベルの知識の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。