Linuxデバイスモデル(9)_デバイスリソース管理
1 はじめに
すべての Linux ドライバー エンジニアは、時間をかけてこの記事を読むことをお勧めします。
この記事で紹介するトピックは非常に実用的で、いくつかの混乱を解決し、コードをシンプルかつ簡潔にすることもできます。まず例を見てみましょう:
リーリーLinux ドライバーを作成したことのあるすべてのエンジニアは、プローブ機能で同様の問題に遭遇したことがあると思います。複数のリソース (IRQ、クロック、メモリ、エリア、ioremap、DMA など) を特定の順序で適用する必要があるということです。リソースのアプリケーションが失敗した場合は、以前に適用されたすべてのリソースをロールバックして解放する必要があります。したがって、通常、関数の最後には多数の goto ラベル (exit_free_irq、exit_free_dma など) があり、リソースの適用が失敗した場合に、慎重に正しいラベルにジャンプして、適用されたリソースを解放できます。
ただし、このようなコードは、コードが繰り返される大きなセクションでいっぱいで、「if (条件) { err = xxx; goto xxx; }」ロジックが多数含まれており、時間の無駄であるだけでなく、エラーが発生しやすくなります。しかし見苦しい。ただし、混乱している場合は、改善の余地があります。最終的に、Linux デバイス モデルはデバイス リソース管理の助けを借りてこの問題を解決します: ドライバーはリソースを適用するだけでよく、リソースの解放方法について心配する必要はありません。デバイス モデルがそれを処理します。最終的には、次の方法でドライバー コードを簡素化できます。
リーリー ###どうやってするの?上の「devm_」で始まるインターフェイスに注目してください。答えはそこにあります。これらの従来のリソース アプリケーション インターフェイスは使用せず、代わりに devm_xxx インターフェイスを使用してください。互換性を維持するために、これらの新しいインターフェイスのパラメータは、名前の前に「devm_」が追加され、追加の構造体デバイス ポインタが追加されることを除いて、古いインターフェイスのパラメータと同じままです。
2.devm_xxx
以下に、一般的に使用されるリソース アプリケーション インターフェイスをいくつか示します。これらは、デバイス リソース管理に基づいてさまざまなフレームワーク (クロック、レギュレーター、GPIO など) によって実装されます。これを使用する場合、「devm_」プレフィックスを無視するだけで、残りの部分はドライバー エンジニアにとって馴染みのあるものになります。 1 つだけ覚えておいてください。ドライバーは適用することしかできませんが、解放することはできません。デバイス モデルがドライバーの解放に役立ちます。ただし、厳密にしたい場合は、ドライバーが削除されたときに積極的にリリースすることもできます (ここには記載されていない対応するインターフェイスもあります)。リーリー
3.「デバイスリソース」とは
デバイスが動作するには、電源、クロックなどの多くの外部条件に依存します。これらの外部条件はデバイス リソースと呼ばれます。最新のコンピュータのアーキテクチャの場合、考えられるリソースは次のとおりです。
#「以前のカーネルでは、システムは特に複雑ではなく、各フレームワークもまだ形になっていなかったため、ほとんどのリソースはドライバー自体によって維持されていました。しかし、システムの複雑さが増すにつれて、ドライバー間で共有されるリソースがますます多くなり、電源管理の必要性がますます高まっています。そこでカーネルは各リソースの管理権を取り戻し、「デバイスリソース管理」フレームワークに基づいて、各フレームワークが割り当てやリサイクルなどを一元的に管理します。」 a) 電源、電源。
b) 時計、時計。c) メモリ。メモリは通常、カーネル内の kzalloc を使用して割り当てられます。
」
d) GPIO、ユーザー、CPU は簡単な制御、ステータス、その他の情報を交換します。
e) IRQ、割り込みをトリガーします。
f) DMA、CPU の関与なしのデータ送信。
g) 仮想アドレス空間は通常、ioremap、request_region などを使用して割り当てられます。
h) 待ってください
#Linux カーネルの観点から見ると、「リソース」の定義は PWM、RTC、リセットなどのより広範なものであり、これらはすべてドライバーが使用するリソースに抽象化できます。
4. デバイスリソース管理のためのソフトウェアフレームワーク
デバイス リソース管理は「drivers/base/devres.c」にあり、その実装は非常に簡単です。リソースにはさまざまな種類や表現があり、それらすべてを把握することはできませんので、具体的な配分やリサイクルを行うことはできません。したがって、devres ができること (そしてその唯一の機能) は次のとおりです:
“
提供一种机制,将系统中某个设备的所有资源,以链表的形式,组织起来,以便在driver detach的时候,自动释放。
”
而更为具体的事情,如怎么抽象某一种设备,则由上层的framework负责。这些framework包括:regulator framework(管理power资源),clock framework(管理clock资源),interrupt framework(管理中断资源)、gpio framework(管理gpio资源),pwm framework(管理PWM),等等。
其它的driver,位于这些framework之上,使用它们提供的机制和接口,开发起来就非常方便了。
5. 代码分析
5.1 数据结构
先从struct device开始吧!该结构中有一个名称为“devres_head”的链表头,用于保存该设备申请的所有资源,如下:
1: struct device { 2: ... 3: spinlock_t devres_lock; 4: struct list_head devres_head; 5: ... 6: } 7:
那资源的数据结构呢?在“drivers/base/devres.c”中,名称为struct devres,如下:
1: struct devres { 2: struct devres_node node; 3: /* -- 3 pointers */ 4: unsigned long long data[]; /* guarantee ull alignment */ 5: };
咋一看非常简单,一个struct devres_node的变量node,一个零长度数组data,但其中有无穷奥妙,让我们继续分析。
node用于将devres组织起来,方便插入到device结构的devres_head链表中,因此一定也有一个list_head(见下面的entry)。另外,资源的存在形式到底是什么,device resource management并不知情,因此需要上层模块提供一个release的回调函数,用于release资源,如下:
1: struct devres_node { 2: struct list_head entry; 3: dr_release_t release; 4: #ifdef CONFIG_DEBUG_DEVRES 5: const char *name; 6: size_t size; 7: #endif 8: };
抛开用于debug的变量不说,也很简单,一个entry list_head,一个release回调函数。看不出怎么抽象资源啊!别急,奥妙都在data这个零长度数组上面呢。
注1:不知道您是否注意到,devres有关的数据结构,是在devres.c中定义的(是C文件哦!)。换句话说,是对其它模块透明的。这真是优雅的设计(尽量屏蔽细节)!
5.2 一个无关话题:零长度数组
零长度数组的英文原名为Arrays of Length Zero,是GNU C的规范,主要用途是用来作为结构体的最后一个成员,然后用它来访问此结构体对象之后的一段内存(通常是动态分配的内存)。什么意思呢?
以struct devres为例,node变量的长度为3个指针的长度,而struct devres的长度也是3个指针的长度。而data只是一个标记,当有人分配了大于3个指针长度的空间并把它转换为struct devres类型的变量后,我们就可以通过data来访问多出来的memory。也就是说,有了零长度数组data,struct devres结构的长度可以不定,完全依赖于你分配的空间的大小。有什么用呢?
以本文的应用场景为例,多出来的、可通过data访问的空间,正是具体的device resource所占的空间。资源的类型不同,占用的空间的多少也不同,但devres模块的主要功能又是释放资源所占的资源。这是就是零长度数组的功能之一,因为整个memory空间是连续的,因此可以通过释devres指针,释放所有的空间,包括data所指的那片不定长度的、具体资源所用的空间。
零长度数组(data[0]),在不同的C版本中,有不同的实现方案,包括1长度数组(data[1])和不定长度数组(data[],本文所描述就是这一种),具体可参考GCC的规范:
https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html
5.3 向上层framework提供的接口:devres_alloc/devres_free、devres_add/devres_remove
先看一个使用device resource management的例子(IRQ模块):
1: /* include/linux/interrupt.h */ 2: static inline int __must_check 3: devm_request_irq(struct device *dev, unsigned int irq, irq_handler_t handler, 4: unsigned long irqflags, const char *devname, void *dev_id) 5: { 6: return devm_request_threaded_irq(dev, irq, handler, NULL, irqflags, 7: devname, dev_id); 8: } 9: 10: 11: /* kernel/irq/devres.c */ 12: int devm_request_threaded_irq(struct device *dev, unsigned int irq, 13: irq_handler_t handler, irq_handler_t thread_fn, 14: unsigned long irqflags, const char *devname, 15: void *dev_id) 16: { 17: struct irq_devres *dr; 18: int rc; 19: 20: dr = devres_alloc(devm_irq_release, sizeof(struct irq_devres), 21: GFP_KERNEL); 22: if (!dr) 23: return -ENOMEM; 24: 25: rc = request_threaded_irq(irq, handler, thread_fn, irqflags, devname, 26: dev_id); 27: if (rc) { 28: devres_free(dr); 29: return rc; 30: } 31: 32: dr->irq = irq; 33: dr->dev_id = dev_id; 34: devres_add(dev, dr); 35: 36: return 0; 37: } 38: EXPORT_SYMBOL(devm_request_threaded_irq); 39: 40: void devm_free_irq(struct device *dev, unsigned int irq, void *dev_id) 41: { 42: struct irq_devres match_data = { irq, dev_id }; 43: 44: WARN_ON(devres_destroy(dev, devm_irq_release, devm_irq_match, 45: &match_data)); 46: free_irq(irq, dev_id); 47: } 48: EXPORT_SYMBOL(devm_free_irq);
前面我们提过,上层的IRQ framework,会提供两个和request_irq/free_irq基本兼容的接口,这两个接口的实现非常简单,就是在原有的实现之上,封装一层devres的操作,如要包括:
1)一个自定义的数据结构(struct irq_devres),用于保存和resource有关的信息(对中断来说,就是IRQ num),如下:
1: /* 2: * Device resource management aware IRQ request/free implementation. 3: */ 4: struct irq_devres { 5: unsigned int irq; 6: void *dev_id; 7: };
2)一个用于release resource的回调函数(这里的release,和memory无关,例如free IRQ),如下:
1: static void devm_irq_release(struct device *dev, void *res) 2: { 3: struct irq_devres *this = res; 4: 5: free_irq(this->irq, this->dev_id); 6: }
“
因为回调函数是由devres模块调用的,由它的参数可知,struct irq_devres变量就是实际的“资源”,但对devres而言,它并不知道该资源的实际形态,因而是void类型指针。也只有这样,devres模块才可以统一的处理所有类型的资源。
”
3)以回调函数、resource的size为参数,调用devres_alloc接口,为resource分配空间。该接口的定义如下:
1: void * devres_alloc(dr_release_t release, size_t size, gfp_t gfp) 2: { 3: struct devres *dr; 4: 5: dr = alloc_dr(release, size, gfp); 6: if (unlikely(!dr)) 7: return NULL; 8: return dr->data; 9: }
调用alloc_dr,分配一个struct devres类型的变量,并返回其中的data指针(5.2小节讲过了,data变量实际上是资源的代表)。alloc_dr的定义如下:
1: static __always_inline struct devres * alloc_dr(dr_release_t release, 2: size_t size, gfp_t gfp) 3: { 4: size_t tot_size = sizeof(struct devres) + size; 5: struct devres *dr; 6: 7: dr = kmalloc_track_caller(tot_size, gfp); 8: if (unlikely(!dr)) 9: return NULL; 10: 11: memset(dr, 0, tot_size); 12: INIT_LIST_HEAD(&dr->node.entry); 13: dr->node.release = release; 14: return dr; 15: }
看第一句就可以了,在资源size之前,加一个struct devres的size,就是total分配的空间。除去struct devres的,就是资源的(由data指针访问)。之后是初始化struct devres变量的node。
4)调用原来的中断注册接口(这里是request_threaded_irq),注册中断。该步骤和device resource management无关。
5)注册成功后,以设备指针(dev)和资源指针(dr)为参数,调用devres_add,将资源添加到设备的资源链表头(devres_head)中,该接口定义如下:
1: void devres_add(struct device *dev, void *res) 2: { 3: struct devres *dr = container_of(res, struct devres, data); 4: unsigned long flags; 5: 6: spin_lock_irqsave(&dev->devres_lock, flags); 7: add_dr(dev, &dr->node); 8: spin_unlock_irqrestore(&dev->devres_lock, flags); 9: }
从资源指针中,取出完整的struct devres指针,调用add_dr接口。add_dr也很简单,把struct devres指针挂到设备的devres_head中即可:
1: static void add_dr(struct device *dev, struct devres_node *node) 2: { 3: devres_log(dev, node, "ADD"); 4: BUG_ON(!list_empty(&node->entry)); 5: list_add_tail(&node->entry, &dev->devres_head); 6: }
6)如果失败,可以通过devres_free接口释放资源占用的空间,devm_free_irq接口中,会调用devres_destroy接口,将devres从devres_head中移除,并释放资源。这里就不详细描述了。
5.4 向设备模型提供的接口:devres_release_all
这里是重点,用于自动释放资源。
先回忆一下设备模型中probe的流程(可参考“Linux设备模型(5)_device和device driver”),devres_release_all接口被调用的时机有两个:
1)probe失败时,调用过程为(就不详细的贴代码了):
__driver_attach/__device_attach–>driver_probe_device—>really_probe,really_probe调用driver或者bus的probe接口,如果失败(返回值非零,可参考本文开头的例子),则会调用devres_release_all。
2)deriver dettach时(就是driver remove时)
driver_detach/bus_remove_device–>__device_release_driver–>devres_release_all
devres_release_all的实现如下:
1: int devres_release_all(struct device *dev) 2: { 3: unsigned long flags; 4: 5: /* Looks like an uninitialized device structure */ 6: if (WARN_ON(dev->devres_head.next == NULL)) 7: return -ENODEV; 8: spin_lock_irqsave(&dev->devres_lock, flags); 9: return release_nodes(dev, dev->devres_head.next, &dev->devres_head, 10: flags); 11: }
以设备指针为参数,直接调用release_nodes:
1: static int release_nodes(struct device *dev, struct list_head *first, 2: struct list_head *end, unsigned long flags) 3: __releases(&dev->devres_lock) 4: { 5: LIST_HEAD(todo); 6: int cnt; 7: struct devres *dr, *tmp; 8: 9: cnt = remove_nodes(dev, first, end, &todo); 10: 11: spin_unlock_irqrestore(&dev->devres_lock, flags); 12: 13: /* Release. Note that both devres and devres_group are 14: * handled as devres in the following loop. This is safe. 15: */ 16: list_for_each_entry_safe_reverse(dr, tmp, &todo, node.entry) { 17: devres_log(dev, &dr->node, "REL"); 18: dr->node.release(dev, dr->data); 19: kfree(dr); 20: } 21: 22: return cnt; 23: }
release_nodes会先调用remove_nodes,将设备所有的struct devres指针从设备的devres_head中移除。然后,调用所有资源的release回调函数(如5.3小节描述的devm_irq_release),回调函数会回收具体的资源(如free_irq)。最后,调用free,释放devres以及资源所占的空间。
以上がLinuxデバイスモデル(9)_デバイスリソース管理の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ユーザーアカウント管理のLinuxとWindowsの主な違いは、許可モデルと管理ツールです。 Linuxは、UNIXベースの権限モデルとコマンドラインツール(useradd、usermod、userdelなど)を使用し、Windowsは独自のセキュリティモデルとグラフィカルユーザーインターフェイス(GUI)管理ツールを使用します。

linux'scommandlinecanbemoresecurethanwindowsifmanaged correctly、butrequiresmoreuserknowledge.1)linux'sourcenatureallowsforquicksecurityupdates.2)MissuturaturecurationCanleadtovulnerabilities.windows'commandlinism command-contontontrollededblessed-blessedlestlessed

このガイドでは、LinuxのブーツにUSBドライブを自動的に取り付け、時間と労力を節約する方法について説明します。 ステップ1:USBドライブを特定します LSBLKコマンドを使用して、すべてのブロックデバイスをリストします。 USBドライブにはラベルが付いている可能性があります /dev /sdb1、 /dev /sdc1など

クロスプラットフォームアプリケーションは、ソフトウェア開発に革命をもたらし、Linux、Windows、MacOなどのオペレーティングシステム間でシームレスな機能を可能にします。 これにより、デバイスに基づいてアプリを切り替える必要性がなくなり、一貫した体験を提供します

人工知能(AI)は、ヘルスケアや金融から芸術や音楽などの創造的な分野に至るまで、多くのセクターを急速に変革しています。 Linuxは、オープンソースの性質、適応性、パフォーマンス機能を備えており、最高のPlatfoとして浮上しています

グラフィカルユーザーインターフェイス(GUI)なしで、高速で最小限で効率的なLinuxディストリビューションをお探しですか? 軽量のガイレスLinuxディストリビューションは、古いハードウェアやサーバーや組み込みシステムなどの特殊なタスクに最適です。彼らはより少ないresを消費します

ワイン10.0安定バージョンリリース:LinuxでWindowsアプリケーションをより高いレベルに実行する このオープンソースと無料アプリケーションであるWineは、LinuxユーザーがUNIX/LinuxオペレーティングシステムでWindowsソフトウェアとゲームを実行できるようにし、10.0 Stableバージョンのリリースを紹介します。このバージョンには、ソースコードとバイナリパッケージのダウンロードが提供されており、Linux、Windows、Macなどのさまざまな分布をサポートしています。 このエディションは、1年の努力と8,600を超える改善を具体化し、多くのエキサイティングな改善をもたらします。重要なハイライトは次のとおりです。 Bluetoothデバイスの強化されたサポート。 HID入力デバイスのサポートを改善します。 32ビットおよび64ビットアプリケーションの最適化されたパフォーマンス。

このチュートリアルは、rhel 8.xまたは9.xにSQL Server 2022をインストールし、SQLCMDコマンドラインツール、データベース作成、および基本クエリを介して接続することをガイドします。 前提条件 始める前に、次のことを確認してください サポートされているRHELバージョン(RHEL 8または9)。 sudo


ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

Video Face Swap
完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

人気の記事

ホットツール

SublimeText3 中国語版
中国語版、とても使いやすい

VSCode Windows 64 ビットのダウンロード
Microsoft によって発売された無料で強力な IDE エディター

ドリームウィーバー CS6
ビジュアル Web 開発ツール

Dreamweaver Mac版
ビジュアル Web 開発ツール

SublimeText3 Linux 新バージョン
SublimeText3 Linux 最新バージョン

ホットトピック









