LDD3原始碼學習日記《四》

2021-06-18 20:45:56 字數 2784 閱讀 3727

日記三不小心被刪了。。。哎,只能在**站裡面自己看了。這部落格80%的功能還是寫給自己看的,其他部分的功能應該是用來練習寫作能力的,留下這一路走來的證據,兩年後回過頭來看看自己當初是怎麼犯低階錯誤的。哈哈。。

第五章是併發與競態,書上的內容講的主要是訊號量和自旋鎖機制,還有completion,不過書上介紹這部分的內容不多,只用了短短的一頁紙。訊號量可以導致休眠,所以一般的可以用於臨界保護區比較大的場合下,而自旋轉鎖鎖住的程序會在原地打轉,所以,如果臨界保護區比較大的話,非常消耗系統資源。ldd3給出的原始碼裡包含了訊號量和completion,訊號量是嵌在之前的**裡面的,一般而言,它總會出現在可能修改保護物件的**前面。在使用訊號量之前,首先要明確什麼是需要用訊號量保護的資源,

然後,我們才能用訊號量保證對這些資源的互斥訪問。對於scull裝置來說,所有的資訊都儲存在scull_dev結構體中,因此,scull_dev就是我們要保護的資源。在main.c檔案在有很多地方使用了訊號量來保證對scull_dev的互斥訪問。或者說,凡是要改變scull_dev結構休內容的地方,都必須加鎖,防止競態。

一、訊號量

訊號量在使用之前必須先初始化,scull在模組初始化函式scull_init_module。中執行下面的迴圈完成對所有scull裝置專用訊號量的初始化,並且要注意,訊號量必須在scull裝置對系統其它部分可用前被初始化。因此,在下面的**裡,在scull_setup_cdev之前使用了iniy_mutex。

for (i = 0; i < scull_nr_devs; i++)
在main.c中,涉及到訊號量的函式有open和write、read函式,down_interruptible都是在一些區域性變數初始化後執行的第乙個動作。

if (down_interruptible(&dev->sem))

return -erestartsys;

呼叫down_interruptible(&dev->sem)進行加鎖,注意,要對down_interruptible的返回值進行檢查,如果返回0,說明說明加鎖成功了,可以開始操作受保護的資源scull_dev,反之,如果down_interruptible返回非0值,說明是在等待過程中被中斷了,這時要退出並返回-erestartsys,交給系統處理。

給訊號量加鎖後,不管scull_write能否完成其工作,都必須釋放訊號量,**如下:

out:

up(&dev->sem);

return retval;

二、completion

completion是一種輕量級機制,它允許乙個執行緒告訴另乙個執行緒某個工作已經完成。它包含在裡面。

如果有多個執行緒在等待同乙個completion事件,complete函式只喚醒乙個等待執行緒,而complete_all函式將喚醒所有等待執行緒。

等待completion使用如下函式:

void wait_for_completion(struct completion *c);

相應的,completion事件可以通過如下函式觸發:

void complete(struct completion *c);

void complete_all(struct completion *c);

ldd3在misc-modules裡給出了completion的演示,**如下:

#include #include #include /* current and everything */

#include /* printk() */

#include /* everything... */

#include /* size_t */

#include module_license("dual bsd/gpl");

static int complete_major = 0;

declare_completion(comp);//註冊completion

ssize_t complete_read (struct file *filp, char __user *buf, size_t count, loff_t *pos)

ssize_t complete_write (struct file *filp, const char __user *buf, size_t count,

loff_t *pos)

struct file_operations complete_fops = ;

int complete_init(void)

void complete_cleanup(void)

module_init(complete_init);

module_exit(complete_cleanup);

在這裡主要需要關注read和write函式的實現。

下面看complete_read的實現:

在列印即將進入睡眠的資訊後,complete_read在呼叫wait_for_completion(&comp),進入睡眠,即等待completion 「comp」。」comp」是用declare_completion(comp)建立的。如果等待的completion發生了,complete_read函式將再次列印已被喚醒相關資訊。也就是說,任何程序讀取模組裝置檔案,都會進入睡眠等待。

再來看complete_write的實現:

首先列印提示資訊,然後在47行呼叫complete(&comp)觸發completion事件,相應會喚醒乙個在等待」comp」的程序。可以有多個程序進行讀操作,這些讀程序都會進入睡眠等待,當有執行寫操作的程序時,只有乙個等待程序會被喚醒,但是哪個程序,不能確定。

LDD3原始碼學習筆記之scull pipe轉

pipe.c 驅動功能分析 本驅動使用環形緩衝作為scull裝置的的具體實現,類似於pipe.其中實現了阻塞的i o讀寫和非同步通知.主函式流程分析 1.定義scull pipe裝置機構體 2.初始化模組module init scull p init 3.退出並登出模組module exit sc...

LDD3原始碼分析之vmalloc

部落格 編譯環境 ubuntu 10.10 核心版本 2.6.32 38 generic pae ldd3原始碼路徑 examples scullv 一 scullv編譯本文分析ldd3第8章中與vmalloc函式相關 對應原始碼是examples scullv目錄下的相關檔案。這裡首先說明一下,s...

LDD3原始碼分析之llseek分析

分類 ldd3原始碼分析 2012 03 28 14 36 201人閱讀收藏 舉報部落格 編譯環境 ubuntu 10.10 核心版本 2.6.32 38 generic pae ldd3原始碼路徑 examples scull main.c 本文分析ldd3第6章的llseek函式。一 使用者空間...