組み込みLinux-ueventメカニズム:uevent原理の分析



Embedded Linux Uevent Mechanism



前書き:

この記事では、ueventメカニズムとは何か、およびueventメカニズムを使用してコード分析を通じてデバイスノードを生成する方法に焦点を当てます。この記事は2つのパートに分かれています。最初のパートでは、いくつかの予備知識とueventの原理を紹介し、2番目のパートでは、ueventメカニズムを使用してデバイスノードを作成する方法を紹介するコードを使用します。



Linuxカーネル:linux-2.6.22.6

使用した開発ボード:JZ2440 V3(S3C2440A)



ステートメント:

この記事は主に魏東山先生のビデオを読むためのものであり、いくつかのブログコンテンツと組み合わされているため、記事に他の記事がある可能性があります。私の記事があなたを侵害していると感じたら、私がその記事を投稿することを教えてください。訂正します。テキストの場所が間違っている可能性がある場合は、訂正してください。ありがとうございました。

パート1:準備知識とueventの原則



以下では、準備知識について説明します。説明する前に、ueventメカニズムを紹介するフレームワーク図を見てみましょう。

写真はからです: Linuxデバイスモデル(3)_Uevent

私たちの多くは知らないかもしれません:ueventメカニズムとは何ですか?ueventメカニズムは何をしますか?彼の仕事のどの側面を研究する価値がありますか?

最初にドライバーを学習したときは、ueventメカニズムを使用しませんでした。つまり、プログラムでは使用しませんでした。 class_create class_device_create ユーザースペースにデバイスドライバーのデバイスノードを自動的に作成する機能。当時、ユーザースペースでmknodコマンドを使用してデバイスノードを手動で作成する必要がありました。また、class_create関数とclass_device_create関数を使用すると、手動で行う代わりに、ユーザースペースにデバイスノードが作成されます。そして、これはueventメカニズムのマクロ理解です。また、デバイスノードがデバイスドライバー用に作成され、デバイスデバイスとドライバードライバーがリンクリストの形式でバスバスに接続されており、バスのdevice-driver-upperレイヤーがsysfsレイヤーであることがわかります。 。 。したがって、これにより、組み合わせを導入することになります。 sysfs + mdev 。そして、この組み合わせのペアは、私たちにとってueventメカニズムの原理を説明します。まずsysfsを理解しましょう。

Sysfsは、カーネルによって提供されるメモリベースの仮想ファイルシステムです。 / sysディレクトリにマウントします (マウントで表示 / sysタイプsysfsのsysfs(rw、nosuid、nodev、noexec、relatime)) 直感的なデバイスおよびドライバー情報をデバイスツリーの形式でユーザー名前空間に提供する責任があります 。同時に、sysfsは、現在のシステムに接続されているデバイスをさまざまな観点から表示します。

  • / sys / block 歴史的な問題、保管 ブロックデバイス 、デバイス名(sdaなど)へのシンボリックリンクを/ sys / devicesに提供します
  • / sys / busバスタイプによって分類され、バスに接続されているデバイスへのシンボリックリンクは、/ sys / devicesを指すバスディレクトリの下にあります。バスディレクトリの下のdriversディレクトリには、カーネルのstruct bus_typeに対応する、バスに必要なすべてのドライバのシンボリックリンクが含まれています。
  • / sys / class/ sys / class / inputの下の入力デバイス、/ sys / class / graphicsの下のグラフィックスデバイスなどのデバイス機能によって分類されるのは、カーネルのstructクラスに対応する/ sys / devicesディレクトリ内の対応するデバイスへのシンボリックリンクです。
  • / sys / devデバイスドライバー(キャラクターデバイス/ブロックデバイス)によって階層化され、提供されますmajor:minorを使用した/ sys / devicesへのシンボリックリンクカーネルのstructdevice_driverに対応
  • / sys / devicesさまざまなバスに登録されていることが判明したさまざまな物理デバイスがすべて含まれています。プラットフォームデバイスとシステムデバイスを除いて、すべての物理デバイスはバス上のトポロジの観点から表示されます。プラットフォームデバイスは通常、チップの内部高速または低速バスにぶら下がっているさまざまなコントローラおよび周辺機器であり、CPUによって直接アドレス指定できます。システムデバイスは周辺機器ではなく、CPU、タイマーなどのチップのコア構造であり、通常、関連するドライバーはありませんが、対応する構造体デバイスを構成するためのアーキテクチャ関連のコードがいくつかあります。カーネル

上記はsysディレクトリを示しています バス、デバイス、ドライバー、クラスに対応するファイル、およびそれらの関係は次のとおりです。

  • デバイスは、すべてのデバイス情報を格納するさまざまなデバイスを説明するために使用されます。
  • ドライバーは、デバイスを駆動するために使用されます。デバイスは、それによって駆動できるデバイスのすべてのリストを保持します。
  • バスはCPUとデバイス間のブリッジであり、CPUにマウントされているすべてのリンクされたデバイスのリストと、それらのデバイスを駆動するドライバーのリストを保持します。
  • クラスは、デバイスのクラスを説明するために使用されますこのクラスのデバイスのすべてのデバイスリストを保存します

以下に紹介しますバス、デバイス、ドライバー、クラスは構造の次の層です。sysfsの機能は、Linuxの統合デバイスモデルに基づいています。これは、kobject、kset、ktypeの構造​​を持っています。同時に、上のブロック図から、ueventがkobject構造に基づいて実装されていることがわかります。

Kobject:統合デバイスモデルの最も基本的なオブジェクト。

  1. struct kobject {
  2. const char *name //name, the name of the Kobject, which is also the name of the directory in sysfs.
  3. //Because Kobject is added to Kernel, it needs to be registered to sysfs by name, and then you can't modify this field directly.
  4. / / If you need to modify the name of the Kobject, you need to call the kobject_rename interface, the interface will actively deal with sysfs related matters.
  5. struct list_head entry //entry, used to add Kobject to list_head in Kset.
  6. struct kobject *parent //parent, pointing to the parent kobject, to form a hierarchy (in sysfs it appears as a directory structure).
  7. struct kset *kset //kset, the Kset to which the kobject belongs. Can be NULL.
  8. / / If there is no parent specified, Kset will be used as parent
  9. // (Don't forget that Kset is a special Kobject).
  10. struct kobj_type *ktype //ktype, the kobj_type to which the Kobject belongs. Each Kobject must have a ktype, or Kernel will prompt an error.
  11. struct sysfs_dirent *sd //sd, the representation of the Kobject in sysfs.
  12. struct kref kref //kref, a variable of type 'struct kref' (defined in include/linux/kref.h), is a reference count that can be used for atomic operations.
  13. unsigned int state_initialized:1 //state_initialized, indicating whether the Kobject has been initialized,
  14. / / Perform an exception check when Kopen's Init, Put, Add, etc. operations.
  15. unsigned int state_in_sysfs:1 //state_in_sysfs, indicating whether the Kobject has been rendered in sysfs to be removed from sysfs when it is automatically logged out.
  16. unsigned int state_add_uevent_sent:1 // state_add_uevent_sent/state_remove_uevent_sent, logging whether ADD uevent has been sent to user space,
  17. //If there is, and no remove uevent is sent, REMOVE uevent is reissued when it is automatically logged out.
  18. / / In order to allow user space to handle correctly.
  19. unsigned int state_remove_uevent_sent:1
  20. unsigned int uevent_suppress:1 //uevent_suppress, if this field is 1, it means that all reported uevent events are ignored.
  21. }

注意: Ueventは、「ユーザースペース通知」の関数実装を提供します。この機能により、カーネル内にKobjectの追加、削除、変更などがあった場合、ユーザースペースに通知されます。

Ktype:Kobjectを表します(厳密に言えば、Kobjectのデータ構造を含むデータ構造です)(汎用性のため、複数のKobjectが同じ属性操作のセットを共有する可能性があるため、Ktypeは独立しています)。

  1. struct kobj_type {
  2. void (*release)(struct kobject *kobj) //release, through this callback function, you can release the memory space of the data structure containing this type of kobject.
  3. const struct sysfs_ops *sysfs_ops //sysfs_ops, the sysfs file system interface for this type of Kobject.
  4. struct attribute **default_attrs //default_attrs, the atrribute list of this type of Kobject
  5. // (The so-called attribute is a file in the sysfs file system).
  6. // will be registered to sysfs when Kobject is added to the kernel.
  7. const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj)
  8. //child_ns_type/namespace, related to the namespace of the file system (sysfs)
  9. const void *(*namespace)(struct kobject *kobj)
  10. }

実際、ここでの実装はkobjectの派生に似ており、異なるkobj_typeを含むkobjectは異なるサブクラスと見なすことができます。ポリモーフィズムは、同じ機能を実装することによって実現されます。この設計では、埋め込まれた各Kobjectデータ構造(kset、device、device_driverなど)は、独自のkobj_typeを実装し、コールバック関数を定義する必要があります。

Kset:同様のKobjectをアセンブルするために使用される特別なKobject(したがって、「/ sys /」ファイルシステム内のディレクトリとしても表示されます)(これらのKobjectは同じプロパティまたは異なるプロパティにすることができます)。

  1. struct kset {
  2. struct list_head list //list is used to store a list of all kobjects under the kset.
  3. spinlock_t list_lock //list spin lock
  4. struct kobject kobj //kobj, the kset's own kobject (kset is a special kobject, also in the form of a directory in sysfs).
  5. const struct kset_uevent_ops *uevent_ops //uevent_ops, the set of uevent operations for this kset.
  6. / / When any Kobject needs to report uevent, it must call the uevent_ops of the kset it belongs to, add environment variables,
  7. //Or filter the event (kset can decide which events can be reported).
  8. // Therefore, if a kobject does not belong to any kset, it is not allowed to send uevent.
  9. }

ksetuevent操作のコールバック関数は次のとおりです。

  1. struct kset_uevent_ops {
  2. int (* const filter)(struct kset *kset, struct kobject *kobj)
  3. //filter, when any Kobject needs to report uevent, the kset it belongs to can be filtered through this interface.
  4. / / Block the event that you do not want to report, so as to achieve the purpose of management as a whole.
  5. const char *(* const name)(struct kset *kset, struct kobject *kobj)
  6. //name, the interface can return the name of the kset. If a kset has no legal name,
  7. //All Kobjects under it will not be allowed to report to uvent
  8. int (* const uevent)(struct kset *kset, struct kobject *kobj,
  9. struct kobj_uevent_env *env)
  10. //uevent, when any Kobject needs to report uevent, the kset to which it belongs can use this interface to add environment variables to these events.
  11. //Because many times the environment variables are the same when reporting uevent, so it can be handled uniformly by kset, so you don't need to add each Kobject alone.
  12. }

Ksetとktypeの関連に注意してください Kobjectは、メンバーksetを使用して独自のksetを検索し、独自のktypeをkobj.ktypeに設定します。 ktypeは、ksetメンバーが指定されていない場合に関係を確立するために使用されます。

kobjectは、それが属するksetのueventアクション関数を呼び出すため、ksetはその動作を制御できます。 kobjectがどのksetにも属していない場合、ueventは送信できません。

要約、Ktype、およびKobjectメカニズム全体の理解。

Kobjectのコア機能は、参照カウントを維持することです。カウントが0に減少すると、自動的に解放されます(この記事のkobjectモジュールが担当します)。 Kobjectが占める記憶空間。 これにより、Kobjectを動的に割り当てる必要があることが決定されます(動的に解放される唯一の方法)
Kplayの使用シナリオのほとんどは大規模なデータ構造(Kset、device_driverなど)に埋め込まれているため、これらの大規模なデータ構造も動的に割り当て、動的に解放する必要があります。では、リリースのタイミングは何ですか?埋め込まれたKobjectが解放されたときです。ただし、Kobjectのリリースは、Kobjectモジュールによって自動的に行われます(参照カウントが0の場合)。では、どのようにして独自の大きなデータ構造を解放しますか?
この時点で、Ktypeが役に立ちます。 Ktypeのリリースコールバック関数がKobjectのメモリスペース(Kobjectを含むデータ構造でさえ)をリリースする責任があることはわかっています。では、Ktypeとその内部関数の実装は誰ですか?上位層のデータ構造が配置されているモジュールです!それだけなので、どのデータ構造Kobjectが埋め込まれているかが明確であり、Kobjectポインターとそれ自体のデータ構造タイプを使用して、解放する必要のある上位データ構造のポインターを見つけて解放します。
それに関しては、はるかに明確です。したがって、kset、device、device_driverなどの埋め込まれた各Kobjectデータ構造は、Ktypeを実装し、コールバック関数を定義する必要があります。同様に、sysfs関連の操作は同じであり、sysfsはKobjectを認識し、ファイル操作の実際の本体は埋め込みKobjectの上位レベルのデータ構造であるため、ktypeで転送する必要があります。
ちなみに、KobjectはLinuxカーネルにおけるオブジェクト指向思考の究極の実施形態ですが、C言語の利点はここにはないので、Linuxカーネルは賢い(そして非常に恥ずかしい)必要があります。達成するための手段。

Mdevの原則

上記でsysfsを分析しましたが、mdevの分析を開始しましょう。mdevを分析することで、sysfsとの彼の関係を理解し​​ます。 Mdevは、busyboxのコードパッケージにあります。 busybox / util-linux / mdev.c このファイルでは、彼はuevent_helper関数によって呼び出されます。 mdevで行う主なことは2つあります。

最初のもの:

mdev-sコマンドの実行時にMdevスキャン/ sys / block (ブロックデバイスは/ sys / blockディレクトリに保存されます。カーネルバージョン2.6.25以降、ブロックデバイスは/ sys / class / blockディレクトリにも保存されます。mdevは/ sys / blockをスキャンして下位互換性を確認します) また、/ sys / classディレクトリ内のdev属性ファイルでは、デバイス番号はdev属性ファイルから取得されます。 (dev属性ファイルはデバイス番号を 'major:minor n'の形式で保存します)、 そして、dev属性ファイルを含むディレクトリ名をデバイス名device_nameとして使用します (dev属性ファイルを含むディレクトリはdevice_nameと呼ばれ、/ sys / classとdevice_nameの間の部分はsubsystemと呼ばれます。つまり、各dev属性ファイルのパスは/ sys / class / subsystem / Device_name / devとして表すことができます。 )、 / devディレクトリに対応するデバイスファイルを作成します。 。たとえば、cat / sys / class / tty / tty0 / devは4:0を取得し、サブシステムはttyであり、device_nameはtty0です。

2番目のこと:

mdevがuevnetイベント(以前はhotplugイベントと呼ばれていました)によって呼び出されると、mdevは、ueventイベントによって渡された環境変数(ueventイベントを引き起こしたデバイスアクションとデバイスが配置されているパスデバイスパス)を取得します。次に、ueventイベントを引き起こしたアクションを特定します。そして、アクションに応じて対応する操作を行います。 アクションが追加の場合、新しいデバイスがシステムに追加されます。デバイスが仮想デバイスであるか実際の物理デバイスであるかに関係なく、mdevは、デバイスパスパスのdev属性ファイルを介してデバイス番号を取得し、最後のディレクトリへのデバイスディレクトリパスを使用します。 (つまり、dev属性ファイルを含むディレクトリ)デバイス名として、対応するデバイスファイルを/ devディレクトリに作成します。アクションがリモートの場合、つまりデバイスがシステムから削除されている場合は、デバイスパスの最後のディレクトリ名をファイル名として/ devディレクトリ内のデバイスファイルを削除します。アクションが追加でも削除でもない場合、mdevは何もしません。

上記からわかるように、デバイスがシステムに追加またはシステムから削除されたときにmdevによってデバイスファイルを自動的に作成および削除する場合は、次の3つのことを行う必要があります。

1. / sys / classのサブシステムディレクトリで、

2.デバイス名device_nameを名前として使用してディレクトリを作成します。

3.また、device_nameディレクトリにはdevプロパティファイルも含まれている必要があります。devプロパティファイルは、デバイス番号を「major:minor n」の形式で出力します。

以上のことから、sysfsがueventメカニズムを事前に準備する、つまり対応するディレクトリを作成し、mdevがsysfsに基づいてsysfsによって作成されたディレクトリまたはファイルを呼び出すことでデバイスノードを作成することがわかります。

2番目の部分:デバイスノードを作成するためのueventメカニズムの使用を紹介するコードと組み合わせる

次に、コードを使用してueventメカニズムを分析する必要があります。このメカニズムを分析するには、class_createとclass_device_createの2つの関数を分析して、このプロセスがどのように実装されているかを分析する必要があります。最初にclass_createを分析します。

ここは class_create関数の階層関係

  1. class_create(THIS_MODULE,'buttonsdrv')
  2. class_register(cls)
  3. kobject_set_name(&cls->subsys.kobj, '%s', cls->name) / / Assign the name of the class led_class to the corresponding kset
  4. subsys_set_kset(cls, class_subsys)
  5. subsystem_register(&cls->subsys)
  6. kset_register(s) / / Create a class device class directory
  7. kset_add(k)
  8. kobject_add(&k->kobj)
  9. kobject_shadow_add(kobj, NULL)
  10. parent = kobject_get(kobj->parent) // parent is class_kset.kobj, which is the directory corresponding to /sysfs/class
  11. list_add_tail(&kobj->entry,&kobj->kset->list)
  12. create_dir(kobj, shadow_parent) / / Create a class device class directory
  13. sysfs_create_dir(kobj, shadow_parent) //This interface is the sysfs file system interface, which means creating a directory and no longer expanding.

上記から、kobjectがsysfsのディレクトリ(dir)に対応していることがわかります。 kobjectを登録するときは、kobject_add(&k-> kobj)を呼び出し、その後、クラスデバイスディレクトリを作成します。同時に、class_create関数がclass_device_create関数の準作業ディレクトリであることがわかります。

記事から紹介した写真から、kobjectの状態が変化すると(追加、削除など)、ユーザースペースに通知され、イベント通知を受信した後、ユーザースペースがイベントを処理できることがわかります。
ueventがイベントをユーザースペースにエスカレーションする方法は2つあります。
1. kmodモジュールを介して、ユーザースペースの実行可能プログラムまたはスクリプトを直接呼び出します。
2.ネットリンク通信メカニズムを介してカーネルスペースからユーザースペースにイベントを渡します。

そして この記事では主に、kmodモジュールを介してユーザースペースを直接呼び出す実行可能プログラムまたはスクリプトについて説明します。 。 kobject.hを使用すると、ueventモジュールは次のAPIを提供します(これらのAPIの実装は「lib / kobject_uevent.c」ファイルにあります)。

  1. int kobject_uevent(struct kobject *kobj, enum kobject_action action)
  2. int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
  3. char *envp[])
  4. int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...)
  5. int kobject_action_type(const char *buf, size_t count,enum kobject_action *type)

以下からです Class_device_create関数 分析を開始し、彼がどのようにしてkobject_uevent関数に到達したかを確認します。 class_device_create関数の階層関係を見てみましょう。

  1. class_device_create(buttonsdrv_class,NULL,MKDEV(auto_major,0),NULL,'buttonsdrv')
  2. class_device_register(class_dev)
  3. class_device_add(class_dev)
  4. class_dev = class_device_get(class_dev)
  5. parent_class = class_get(class_dev->class)
  6. parent_class_dev = class_device_get(class_dev->parent)
  7. kobject_set_name(&class_dev->kobj, '%s', class_dev->class_id)
  8. kobject_add(&class_dev->kobj)
  9. class_device_create_file(class_dev, attr)
  10. class_device_add_groups(class_dev)
  11. make_deprecated_class_device_links(class_dev)
  12. kobject_uevent(&class_dev->kobj, KOBJ_ADD)

前のコードとは異なり、class_createとclass_device_createは多くの同様の作業を行います。つまり、ディレクトリを作成し、kobject_uevent関数に移動すると異なります。分析してみましょう Kobject_uevent関数

  1. /**
  2. * Notify the user layer through terminal events
  3. *
  4. * @action: The events that occurred (usually KOBJ_ADD and KOBJ_REMOVE)
  5. * @kobj: kobject structure for event occurrence
  6. *
  7. */
  8. int kobject_uevent(struct kobject *kobj, enum kobject_action action)
  9. {
  10. return kobject_uevent_env(kobj, action, NULL)
  11. }

彼はkobject_uevent_env関数を呼び出しました。上記の概要では、イベントを送信する必要があることがわかりました。 それで、イベントは何ですか?

私たちは見る linux-3.5 / include / linux / kobject.h

  1. enum kobject_action {
  2. KOBJ_ADD, //ADD/REMOVE, Kobject (or upper data structure) add/remove events.
  3. KOBJ_REMOVE,
  4. KOBJ_CHANGE, //CHANGE, the state or content of the Kobject (or upper data structure) has changed.
  5. //CHANGE, if the device driver needs to report the event is no longer within the scope of the above event,
  6. // or a custom event, you can use the event and carry the corresponding parameters.
  7. KOBJ_MOVE, //MOVE, Kobject (or upper data structure) changes the name or changes the Parent (meaning that the directory structure has been changed in sysfs).
  8. KOBJ_ONLINE, //ONLINE/OFFLINE, the on/off event of Kobject (or upper data structure) is actually enabled.
  9. KOBJ_OFFLINE,
  10. KOBJ_MAX
  11. }

次に分析します Kobject_uevent_env関数

  1. /**
  2. * Send an event with an environment variable
  3. *
  4. * @action: The event that occurred (usually KOBJ_MOVE)
  5. * @kobj: kobject structure for event occurrence
  6. * @envp_ext: environment variable data pointer
  7. *
  8. */
  9. int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
  10. char *envp_ext[])
  11. {
  12. action_string = action_to_string(action)
  13. /* Finds whether the current kobject or its parent is subordinate to a kset if it is not subordinate to a kset, an error is returned. (Note that if a kobject does not join kset, it will not report uevent) */
  14. top_kobj = kobj
  15. while (!top_kobj->kset && top_kobj->parent) {
  16. top_kobj = top_kobj->parent
  17. }
  18. if (!top_kobj->kset) {
  19. pr_debug('kobject attempted to send uevent without kset! ')
  20. return -EINVAL
  21. }
  22. kset = top_kobj->kset
  23. uevent_ops = kset->uevent_ops
  24. /* If the kset belongs to uevent_ops->filter, the function is called. If the function returns 0, the report is filtered. (kset can filter the event that you do not want to report through the filter interface) */
  25. if (uevent_ops && uevent_ops->filter)
  26. if (!uevent_ops->filter(kset, kobj)) {
  27. pr_debug('kobject filter function caused the event to drop! ')
  28. return 0
  29. }
  30. /* to determine whether the kset belongs to a legal name, if uevent_ops->name exists, use its returned name as the subsystem if uevent_ops->name does not exist, use the kobject name of kset itself as the subsystem if there is no legal name , do not report uevent */
  31. if (uevent_ops && uevent_ops->name)
  32. subsystem = uevent_ops->name(kset, kobj)
  33. else
  34. subsystem = kobject_name(&kset->kobj)
  35. if (!subsystem) {
  36. pr_debug('unset subsytem caused the event to drop! ')
  37. return 0
  38. }
  39. /* Assign an environment variable for this report */
  40. envp = kzalloc(NUM_ENVP * sizeof (char *), GFP_KERNEL)
  41. if (!envp)
  42. return -ENOMEM
  43. /* Assign a buffer for saving environment variables that is reported this time, */
  44. buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL)
  45. if (!buffer) {
  46. retval = -ENOMEM
  47. goto exit
  48. }
  49. /* Get the path of the kobject in sysfs */
  50. devpath = kobject_get_path(kobj, GFP_KERNEL)
  51. if (!devpath) {
  52. retval = -ENOENT
  53. goto exit
  54. }
  55. /* uevent_helper environment variable */
  56. envp[i++] = 'HOME=/'
  57. envp[i++] = 'PATH=/sbin:/bin:/usr/sbin:/usr/bin'
  58. /* Add environment variable */
  59. scratch = buffer
  60. envp [i++] = scratch
  61. scratch += sprintf(scratch, 'ACTION=%s', action_string) + 1
  62. envp [i++] = scratch
  63. scratch += sprintf (scratch, 'DEVPATH=%s', devpath) + 1
  64. envp [i++] = scratch
  65. scratch += sprintf(scratch, 'SUBSYSTEM=%s', subsystem) + 1
  66. for (j = 0 envp_ext && envp_ext[j] j++)
  67. envp[i++] = envp_ext[j]
  68. /* just reserve the space, overwrite it after kset call has returned */
  69. envp[i++] = seq_buff = scratch
  70. scratch += strlen('SEQNUM=18446744073709551616') + 1
  71. /* If uevent_ops->uevent exists, call this interface and add the kset unified environment variable to the env pointer */
  72. if (uevent_ops && uevent_ops->uevent) {
  73. retval = uevent_ops->uevent(kset, kobj,
  74. &envp[i], NUM_ENVP - i, scratch,
  75. BUFFER_SIZE - (scratch - buffer))
  76. if (retval) {
  77. pr_debug ('%s - uevent() returned %d ',
  78. __FUNCTION__, retval)
  79. goto exit
  80. }
  81. }
  82. /* Call the add_uevent_var interface and add the serial number in the format 'SEQNUM=%llu' */
  83. spin_lock(&sequence_lock)
  84. seq = ++uevent_seqnum
  85. spin_unlock(&sequence_lock)
  86. sprintf(seq_buff, 'SEQNUM=%llu', (unsigned long long)seq)
  87. /* Call uevent_helper, subsystem, and the env pointer with the standard environment variable (HOME=/, PATH=/sbin:/bin:/usr/sbin:/usr/bin) as the argument, call the call_usermodehelper function provided by the kmod module, and report it. Uevent. */
  88. if (uevent_helper[0]) {
  89. char *argv [3]
  90. argv [0] = uevent_helper
  91. argv [1] = (char *)subsystem
  92. argv [2] = NULL
  93. call_usermodehelper (argv[0], argv, envp, 0)
  94. }
  95. }

ueventモジュールがkmodを介してueventを報告すると、ユーザースペース実行可能ファイル(またはスクリプト、略してueventヘルパー)を呼び出して、call_usermodehelper関数を介してイベントを処理します。 ueventヘルパーへのパスは、uevent_helper配列に格納されます。 CONFIG_UEVENT_HELPER_PATH構成アイテムを使用してカーネルをコンパイルすることにより、ueventヘルパーを静的に指定できます。
ただし、このメソッドは各イベントのプロセスをフォークします。カーネルでサポートされるデバイスの数が増えると、この方法はシステムの起動時に致命的です(メモリオーバーフローを引き起こす可能性があります)待機)。したがって、この方法は以前のカーネルバージョンでのみ使用され、カーネルでは推奨されなくなりました。したがって、カーネルをコンパイルするときは、この構成項目を空白のままにする必要があります。システムの起動後、ほとんどのデバイスの準備が整い、必要に応じてueventヘルパーを再割り当てして、システム操作中のホットプラグイベントを検出できます。
これは、ヘルパーのパスを「/ sys / kernel / uevent_helper」ファイルに書き込むことで実行できます。実際、カーネルは、ユーザースペースプログラムの変更アクセスのために、sysfsファイルシステムの形式でユーザースペースにuevent_helper配列を開きます。詳細については、「。/kernel /ksysfs.c」の対応するコードを参照してください。

echo '/ sbin / mdev'> / proc / sys / kernel / hotplugを/etc/init.d/rcSスクリプトに追加すると、cat / sys / kernel / uevent_helperが/ sbin / mdevであることがわかります。 / proc / sys / kernel / hotplugの実行可能ファイルパスは、最終的に/ sys / kernel / uevent_helperに書き込まれます。 / sys / kernel / uevent_helperのlsmod、rmmod、/ kernel / mainが実行されると、手動で '/ kernel / main'> uevent_helper(以前の/ sbin / mdevが上書きされます)をエコーし​​、イベントがに報告されたことを示します。ユーザースペース。

以下では、Busyboxでデバイスノードを作成する方法を見ていきます。

mdevが表示される番です。前の説明では、sysfsファイルシステムにディレクトリまたはファイルを作成し、アプリケーションがアクセスするデバイスファイルを/ dev /ディレクトリに作成する必要があります。この作業はmdevによって行われます。
mdevの原則は、/ etc / mdev.confファイルで定義されているデバイスファイルに名前を付けるためのルールを説明し、環境変数の要件に従ってルールの下でデバイスファイルを作成することです。 Mdev.confはユーザー層によって指定されるため、より柔軟性があります。この記事は、mdev構成スクリプトの分析を拡張することを意図していません。関連する知識は私の翻訳を見ることができます: Mdev.conf翻訳

mdevに対応するプログラムは、Busybox / util-linux /mdev.cにあります。

  1. int mdev_main(int argc UNUSED_PARAM, char **argv)
  2. xchdir('/dev')
  3. if (argv[1] && strcmp(argv[1], '-s')/ / mdev -s will execute this branch when the system starts
  4. else
  5. action = getenv('ACTION')
  6. env_path = getenv('DEVPATH')
  7. G.subsystem = getenv('SUBSYSTEM')
  8. snprintf(temp, PATH_MAX, '/sys%s', env_path)//to the /sysfs/devices/led directory
  9. make_device(temp, /*delete:*/ 0)
  10. strcpy(dev_maj_min, '/dev') / / Read the dev property file to get the device number
  11. open_read_close(path, dev_maj_min + 1, 64)
  12. ….
  13. mknod(node_name, rule->mode | type, makedev(major, minor)) / / Finally mknod create node

最終的には、mknodにトレースして、/ dev /ディレクトリにデバイスファイルを作成します。

参照:

Sysfs、udev、およびそれらの背後にあるLinux統合デバイスモデル
Linuxデバイスモデル(2)_Kobject
Linuxデバイスファイルの作成とmdev
カーネルはueventAPI、ユーザースペース解決ueventを送信します
Linuxデバイスモデル(3)_Uevent
デバイスモデルのイベントメカニズム