裝置驅動設計模式¶
本文件描述了裝置驅動程式中常見的一些設計模式。子系統維護者可能會要求驅動程式開發人員遵循這些設計模式。
狀態容器
container_of()
1. 狀態容器¶
雖然核心包含一些裝置驅動程式,它們假設在特定系統(單例)上只會探測 (probe()) 一次,但通常假設驅動程式繫結的裝置會以多個例項出現。 這意味著 probe() 函式和所有回撥需要是可重入的。
實現此目的的最常見方法是使用狀態容器設計模式。 它通常具有以下形式
struct foo {
spinlock_t lock; /* Example member */
(...)
};
static int foo_probe(...)
{
struct foo *foo;
foo = devm_kzalloc(dev, sizeof(*foo), GFP_KERNEL);
if (!foo)
return -ENOMEM;
spin_lock_init(&foo->lock);
(...)
}
這將在每次呼叫 probe() 時在記憶體中建立一個 struct foo 的例項。 這是此裝置驅動程式例項的狀態容器。 當然,有必要始終將此狀態例項傳遞給所有需要訪問該狀態及其成員的函式。
例如,如果驅動程式正在註冊中斷處理程式,您將像這樣傳遞一個指向 struct foo 的指標
static irqreturn_t foo_handler(int irq, void *arg)
{
struct foo *foo = arg;
(...)
}
static int foo_probe(...)
{
struct foo *foo;
(...)
ret = request_irq(irq, foo_handler, 0, "foo", foo);
}
這樣,您始終可以在中斷處理程式中獲得指向 foo 的正確例項的指標。
2. container_of()¶
繼續上面的例子,我們新增一個解除安裝的工作
struct foo {
spinlock_t lock;
struct workqueue_struct *wq;
struct work_struct offload;
(...)
};
static void foo_work(struct work_struct *work)
{
struct foo *foo = container_of(work, struct foo, offload);
(...)
}
static irqreturn_t foo_handler(int irq, void *arg)
{
struct foo *foo = arg;
queue_work(foo->wq, &foo->offload);
(...)
}
static int foo_probe(...)
{
struct foo *foo;
foo->wq = create_singlethread_workqueue("foo-wq");
INIT_WORK(&foo->offload, foo_work);
(...)
}
對於 hrtimer 或類似的東西,設計模式是相同的,它將返回一個單引數,該引數是指向回撥中某個 struct 成員的指標。
container_of() 是在 <linux/kernel.h> 中定義的宏
container_of() 的作用是透過使用標準 C 中的 offsetof() 宏從成員的指標獲取指向包含 struct 的指標,方法是進行簡單的減法,這允許類似於面向物件的行為。 請注意,包含的成員不能是指標,而必須是實際成員才能使其工作。
我們可以在這裡看到,我們避免了以這種方式擁有指向我們的 struct foo * 例項的全域性指標,同時仍然保持傳遞給工作函式的引數數量為單個指標。