ホームページ  >  記事  >  システムチュートリアル  >  Linux システムでのデバイス管理: dev_add 関数から始める

Linux システムでのデバイス管理: dev_add 関数から始める

PHPz
PHPz転載
2024-02-11 13:57:26999ブラウズ

Linux デバイス管理は、Linux システムにおける重要なメカニズムであり、デバイスの作成、削除、検索、変更などの一元的な管理と操作を実現するために使用されます。 Linux デバイス管理には、kobject、kset、kobj_type という 3 つの中心的な概念が含まれます。 kobject は、あらゆるタイプのデバイスを表すために使用できる汎用カーネル オブジェクトです。 kset は、関連する kobject のグループを編成および管理するために使用できるコンテナ オブジェクトです。 kobj_type は、kobject の属性と操作を定義するために使用できる型オブジェクトです。

ここでは、Linux カーネル (4.8.5 カーネルを例にします) がキャラクター デバイスをどのように管理するかについて説明します。つまり、デバイス番号を取得し、cdev 構造体を割り当て、ドライバー操作メソッド セット、最後に cdev_add() が実行されるときにカーネルに正確に何を伝えるのか、カーネルは cdev 構造をどのように管理するのでしょうか? これがこの記事の内容です。話し合う。 。 Linux カーネルのデバイス管理は kobject に基づいていることがわかります (cdev 構造からもわかります)。次に、キャラクター デバイスを操作するための関数が "fs/char_dev.c" に実装されていることがわかります。これらはすべてベースになっています。 **"lib/kobject.c" および "drivers/base/map.c" 内の kobject を操作する関数。さて、cdev_add()** からレイヤーごとに開始します。

cdev_map オブジェクト

リーリー

カーネル内のキャラクタ デバイス操作関数の実装は **"fs/char_dev.c" にあります。このファイルを開くと、最初に気づくのは、この一般的ではない静的なグローバル変数です。カーネル cdev_map**(27) では、ソフトウェアの凝集性を向上させるために、Linux カーネルが設計中に関数間でデータを転送する方法としてグローバル変数の使用を回避しようとすることがわかっており、次のようにすることが推奨されています。仮パラメータ リストをもっと使用し、この構造変数はこのファイルのあらゆる場所で使用されているため、システム内のすべてのキャラクタ デバイスを説明するある種の情報である必要があります。この考えにより、**"drivers/base/ で見つけることができます。 map.c" ##kobj_map** 構造定義: リーリー

このことから、kobj_map のコアは構造体プローブ ポインターの型とサイズ 255 の配列であることがわかります。このプローブ構造体では、最初のメンバー

next(21) が明らかにプローブです。 dev_t 型のメンバ dev は当然デバイス番号であり、get(25) と lock(26 ) は、それぞれ 2 つの関数です。インターフェイス、ここが最後のポイントです。void* は、C 言語のスネークオイル型です。ここでは、cdev 構造体です (次の分析からわかるように)。したがって、この cdev_map は、struct kobj_map タイプです。構造体プローブ ポインター タイプとサイズ 255 の配列を含むポインターです。配列の各要素は、デバイス番号と対応するデバイス オブジェクト (ここでは cdev) をカプセル化するプローブ構造体を指します。この cdev_map は、システム内のすべての cdev 構造体と対応するデバイス番号 (最大 255 文字のデバイス ) をカプセル化していることがわかります。
Linux システムでのデバイス管理: dev_add 関数から始める

cdev_add

cdev_map の機能を理解すると、cdev_add() を探索できます。 リーリー

この関数は非常に短く、(460-461) は前に取得したデバイス番号とデバイス番号の長さを cdev 構造体に埋めるものです。

kobject_get()(468) は何も行いません: リーリー

つまり、コアの作業は明らかに

kobj_map() に引き継がれます。

kobj_map()

この関数はカーネルのデバイス管理において重要な位置を占めています。ここではキャラクターデバイスの観点からのみその機能を分析し、最初に実装します

リーリー

この関数の設計も非常に単純です。つまり、

プローブ構造をカプセル化し、そのアドレスをプローブ配列に入れてから、それを cdev_map にカプセル化します。、(48-55)j は以下に基づいています。受信デバイス番号 プローブの数は、デバイス番号と cdev を kmalloc_array によって割り当てられた n 個のプローブ構造に順番にカプセル化します (57-63) NULL の値を持つ要素が見つかるまでプローブ配列を走査します。次に、プローブ アドレスをプローブに保存します。この時点で、cdev をカーネルのデータ構造に組み込みました。もちろん、cdev 内の多数の属性はカーネルによって埋められます。

chrdev_open()

将设备放入的内核,我们再来看看内核是怎么找到一个特定的cdev的,对一个字符设备的访问流程大概是:文件路径=>inode=>chrdev_open=>cdev->fops->my_chr_open。所以只要通过VFS找到了inode,就可以找到chrdev_open(),这里我们就来关注一个chrdev_open()是怎么从内核的数据结构中找到我们的cdev并回调里满的my_chr_open()的。首先,chrdev_open()尝试将inode->i_cdev(一个cdev结构指针)保存在局部变量p中(359),如果p为空,即inode->i_cdev为空(360),我们就根据inode->i_rdev(设备号)通过kobj_lookup搜索cdev_map,并返回与之对应kobj(364),由于kobject是cdev的父类,我们根据container_of很容易找到相应的cdev结构并将其保存在inode->i_cdev中(367),找到了cdev,我们就可以将inode->devices挂接到inode->i_cdev的管理链表中,这样下次就不用重新搜索,直接cdev_get()即可(378)。找到了我们的cdev结构,我们就可以将其中的操作方法集inode->i_cdev->ops传递给filp->f_ops(386-390),这样,我们就可以回调我们的设备打开函数my_chr_open()(392);

//fs/char_dev.c
326 static struct kobject *cdev_get(struct cdev *p)     
327 {
328         struct module *owner = p->owner;
329         struct kobject *kobj;
330         
331         if (owner && !try_module_get(owner))
332                 return NULL;
333         kobj = kobject_get(&p->kobj);
        ...
336         return kobj;
337 }

351 static int chrdev_open(struct inode *inode, struct file *filp)
352 {
353         const struct file_operations *fops;
354         struct cdev *p;
355         struct cdev *new = NULL;
356         int ret = 0;
        ...
359         p = inode->i_cdev;
360         if (!p) {
361                 struct kobject *kobj;
362                 int idx;
            ...
364                 kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx);
            ...
367                 new = container_of(kobj, struct cdev, kobj);
369                 /* Check i_cdev again in case somebody beat us to it while
370                    we dropped the lock. */
371                 p = inode->i_cdev;
372                 if (!p) {
373                         inode->i_cdev = p = new;
374                         list_add(&inode->i_devices, &p->list);
375                         new = NULL;
376                 } else if (!cdev_get(p))
377                         ret = -ENXIO;
378         } else if (!cdev_get(p))
379                 ret = -ENXIO;
            ...
386         fops = fops_get(p->ops);
        ...
390         replace_fops(filp, fops);
391         if (filp->f_op->open) {
392                 ret = filp->f_op->open(inode, filp);
            ...
395         }
396 
397         return 0;
398 
399  out_cdev_put:
400         cdev_put(p);
401         return ret;
402 }

通过本文,我们了解了Linux设备管理的原理和方法。它们可以用来实现对设备的统一管理和操作。我们应该根据实际需求选择合适的方法,并遵循一些基本原则,如使用正确的引用计数,使用正确的锁机制,使用正确的属性文件等。Linux设备管理是Linux系统中最基本的机制之一,它可以实现对设备的抽象和封装,也可以提升系统的可维护性和可扩展性。希望本文能够对你有所帮助和启发。

以上がLinux システムでのデバイス管理: dev_add 関数から始めるの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はlxlinux.netで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。