欧美三区_成人在线免费观看视频_欧美极品少妇xxxxⅹ免费视频_a级毛片免费播放_鲁一鲁中文字幕久久_亚洲一级特黄

register_chrdev_region函數(shù)源碼分析

系統(tǒng) 1977 0

本文轉(zhuǎn)載于: http://edsionte.com/techblog/archives/1393

如何找到一個有效的切入點去深入分析內(nèi)核源碼,這是一個令人深思的問題。本文以 前文 中未詳細說明的函數(shù)為切入點,深入分析char_dev.c文件的代碼。如果你已經(jīng)擁有了C語言基礎(chǔ)和一些數(shù)據(jù)結(jié)構(gòu)基礎(chǔ),那么還等什么?Let’s go!

在《字符設(shè)備驅(qū)動分析》一文中,我們說到register_chrdev_region函數(shù)的功能是在已知起始設(shè)備號的情況下去申請一組連續(xù)的設(shè)備號。不過大部分驅(qū)動書籍都沒有去深入說明此函數(shù),可能是因為這個函數(shù)內(nèi)部封裝了__register_chrdev_region(unsigned int major, unsigned int baseminor, int minorct, const char *name)函數(shù)的原因。不過我們不用苦惱,這正好促使我們?nèi)シ治鲞@個函數(shù)。
int register_chrdev_region(dev_t from, unsigned count, const char *name)

{

?????? struct char_device_struct *cd;

?????? dev_t to = from + count;

?????? dev_t n, next; ?

?????? for (n = from; n <\ to; n = next)

?????? {

????????? next = MKDEV(MAJOR(n)+1, 0);

?????????? if (next >\ to)

??????????????? next = to;
??????????????? cd = __register_chrdev_region(MAJOR(n), MINOR(n), next - n, name);
??????????????? if (IS_ERR(cd))

???????????????????? goto fail;

?????? }
?????????? return 0;

fail: to = n;

?????? for (n = from; n <\ to; n = next)

?????? {

??????????? next = MKDEV(MAJOR(n)+1, 0);
??????????? kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));
?????? }

?????? return PTR_ERR(cd);
}

首先值得我們注意的是,這個函數(shù)每次分配的是一組設(shè)備編號。其中from參數(shù)是這組連續(xù)設(shè)備號的起始設(shè)備號,count是這組設(shè)備號的大小(也是次設(shè)備號的個數(shù)),name參數(shù)處理本組設(shè)備的驅(qū)動名稱。另外,當次設(shè)備號數(shù)目過多(count過多)的時候,次設(shè)備號可能會溢出到下一個主設(shè)備。因此我們在for語句中可以看到,首先得到下一個主設(shè)備號(其實也是一個設(shè)備號,只不過此時的次設(shè)備號為0)并存儲于next中。然后判斷在from的基礎(chǔ)上再追加count個設(shè)備是否已經(jīng)溢出到下一個主設(shè)備號。如果沒有溢出(next小于to),那么整個for語句就只執(zhí)行個一次__register_chrdev_region函數(shù);否則當設(shè)備號溢出時,會把當前溢出的設(shè)備號范圍劃分為幾個小范圍,分別調(diào)用__register_chrdev_region函數(shù)。

如果在某個小范圍調(diào)用__register_chrdev_region時出現(xiàn)了失敗,那么會將此前分配的設(shè)備號都釋放。

其實register_chrdev_region函數(shù)還沒有完全說清除設(shè)備號分配的具體過程,因為具體某個小范圍的設(shè)備號是由__register_chrdev_region函數(shù)來完成的。可能你已經(jīng)注意到在register_chrdev_region函數(shù)源碼中出現(xiàn)了struct char_device_struct結(jié)構(gòu),我們首先來看這個結(jié)構(gòu)體:
static struct char_device_struct {
??????? struct char_device_struct *next;

??????? unsigned int major;
??????? unsigned int baseminor;

??????? int minorct;

??????? char name[64];

??????? struct cdev *cdev;????????????? /* will die */
} *chrdevs[CHRDEV_MAJOR_HASH_SIZE];

在register_chrdev_region函數(shù)中,在每個字符設(shè)備號的小范圍上調(diào)用__register_chrdev_region函數(shù),都會返回一個struct char_device_struct類型的指針。因此我們可以得知,struct char_device_struct類型對應(yīng)的并不是每一個字符設(shè)備,而是具有連續(xù)設(shè)備號的一組字符設(shè)備。從這個結(jié)構(gòu)體內(nèi)部的字段也可以看出,這組連續(xù)的設(shè)備號的主設(shè)備號為major,次設(shè)備號起始為baseminor,次設(shè)備號范圍為minorct,這組設(shè)備號對應(yīng)的設(shè)備驅(qū)動名稱為name,cdev為指向這個字符設(shè)備驅(qū)動的指針。

這里要特別說明的是,內(nèi)核中所有已分配的字符設(shè)備編號都記錄在一個名為chrdevs散列表里。該散列表中的每一個元素是一個 char_device_struct結(jié)構(gòu),這個散列表的大小為255(CHRDEV_MAJOR_HASH_SIZE),這是因為系統(tǒng)屏蔽了12位主設(shè)備號的前四位。既然說到散列表,那么肯定會出現(xiàn)沖突現(xiàn)象,因此next字段就是沖突鏈表中的下一個元素的指針。

接下來我們詳細來析__register_chrdev_region函數(shù)。首先為cd變量分配內(nèi)存并用零來填充(這就是用kzalloc而不是kmalloc的原因)。接著通過P操作使得后續(xù)要執(zhí)行的語句均處于臨界區(qū)。

static struct char_device_struct * __register_chrdev_region(unsigned int major, unsigned int baseminor, int minorct, const char *name)

{
?????? struct char_device_struct *cd, **cp;
?????? int ret = 0;

?????? int i;
?????? cd = kzalloc( sizeof ( struct char_device_struct), GFP_KERNEL);
?????? if (cd == NULL)

??????????? return ERR_PTR(-ENOMEM); ?
??????????? mutex_lock(&chrdevs_lock);

如果major為0,也就是未指定一個具體的主設(shè)備號,需要動態(tài)分配。那么接下來的if語句就在整個散列表中為這組設(shè)備尋找合適的位置,即從散列表的末尾開始尋找chrdevs[i]為空的情況。若找到后,那么i不僅代表這組設(shè)備的主設(shè)備號,也代表其在散列表中的關(guān)鍵字。當然,如果主設(shè)備號實現(xiàn)已指定,那么可不去理會這部分代碼。

?????? if (major == 0)

?????? {

?????????? for (i = ARRAY_SIZE(chrdevs)-1; i > 0; i—)

?????????? {
??????????????? if (chrdevs[i] == NULL)

??????????????????? break ;
?????????? } ?
?????????? if (i == 0)

?????????? {

??????????????? ret = -EBUSY;
??????????????? goto out;
?????????? }

?????????? major = i;
?????????? ret = major;
??????? }

接著對將參數(shù)中的值依次賦給cd變量的對應(yīng)字段。當主設(shè)備號非零,即事先已知的話,那么還要通過major_to_index函數(shù)對其進行除模255運算,因此整個散列表關(guān)鍵字的范圍是0~254。

??????? cd->major = major;

??????? cd->baseminor = baseminor;
??????? cd->minorct = minorct;
??????? strlcpy(cd->name, name, sizeof (cd->name)); ?
??????? i = major_to_index(major);

至此,我們通過上面的代碼會得到一個有效的主設(shè)備號(如果可以繼續(xù)執(zhí)行下面代碼的話),那么接下來還不能繼續(xù)分配。正如你所知的那樣,散列表中的沖突是在所難免的。因此我們得到major的值后,我們要去便利沖突鏈表,為當前我們所述的char_device_struct類型的變量cd去尋找正確的位置。更重要的是,我們要檢查當前的次設(shè)備號范圍,即baseminor~baseminor+minorct,是否和之前的已分配的次設(shè)備號(前提是major相同)范圍有重疊。

下面的for循環(huán)就是在沖突鏈表中查找何時的位置,當出現(xiàn)以下三種情況時,for語句會停止。

(1)如果沖突表中正被遍歷的結(jié)點的主設(shè)備號(*(cp)->major)大于我們所分配的主設(shè)備號(major),那么就可以跳出for語句,不再繼續(xù)查找。此時應(yīng)該說設(shè)備號分配成功了,那么cd結(jié)點只需等待被插到?jīng)_突鏈表當中(*cp節(jié)點之前)。

(2)如果(*cp)結(jié)點和cd結(jié)點的主設(shè)備號相同,但是前者的次設(shè)備號起點比cd結(jié)點的大,那么跳出for語句,等待下一步的范圍重疊的檢測。

(3)如果(*cp)結(jié)點和cd結(jié)點的主設(shè)備號相同,但是cd結(jié)點的次設(shè)備號起點小于(*cp)結(jié)點的次設(shè)備號的終點,那么會跳出for語句。此時很可能兩個范圍的次設(shè)備號發(fā)生了重疊。

由上面的分析可以看出,沖突表中是按照設(shè)備號遞增的順序排列的。

??????? for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next) if ((*cp)->major > major || ((*cp)->major == major && (((*cp)->baseminor >= baseminor) || ((*cp)->baseminor + (*cp)->minorct > baseminor))))
??????????? break ;

接下來檢測當主設(shè)備號相同時,次設(shè)備范圍是否發(fā)生了重疊。首先依次計算出新老次設(shè)備號的范圍,接著進行范圍判斷。第一個判斷語句是檢測新范圍的終點是否在老范圍的之間;第二個判斷語句是檢測新范圍的起點是否在老范圍之間。

??????? /* Check for overlapping minor ranges.? */

?????? if (*cp && (*cp)->major == major)

?????? {
??????????? int old_min = (*cp)->baseminor;
??????????? int old_max = (*cp)->baseminor + (*cp)->minorct - 1;
??????????? int new_min = baseminor;
??????????? int new_max = baseminor + minorct - 1;

? ?????????? /* New driver overlaps from the left.? */
??????????? if (new_max >= old_min && new_max <= old_max)

??????????? {
???????????????? ret = -EBUSY;
???????????????? goto out;
??????????? }
??????????? /* New driver overlaps from the right.? */

??????????? if (new_min <= old_max && new_min >= old_min)

??????????? {
???????????????? ret = -EBUSY;
???????????????? goto out;
??????????? }

??????? }

當一切都正常后,就將char_device_struct描述符插入到中途鏈表中。至此,一次小范圍的設(shè)備號分配成功。并且此時離開臨界區(qū),進行V操作。如果上述過程中有任何失敗,則會跳轉(zhuǎn)到out處,返回錯誤信息。

??????? cd->next = *cp;

??????? *cp = cd;
??????? mutex_unlock(&chrdevs_lock);
??????? return cd;
out: ?? mutex_unlock(&chrdevs_lock);
??????? kfree(cd);
??????? return ERR_PTR(ret);
}

至此,我們已經(jīng)分析完了字符設(shè)備號分配函數(shù)。

register_chrdev_region函數(shù)源碼分析


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯(lián)系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發(fā)表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 日韩视频观看 | 欧美激情精品久久久久久变态 | 亚洲日本国产 | 欧美六九视频 | 国产精品视频一区二区三区不卡 | av电影直播 | 日本高清在线中文字幕网 | 免费播放特黄特色毛片 | 日韩精品一区二区三区 | 亚洲天堂久久 | 久热香蕉精品视频在线播放 | 亚洲香蕉视频 | 欧美精品播放 | 日本九九精品一区二区 | 波多野结衣手机在线播放 | 亚洲国产成人精彩精品 | 人人精品 | 国产精品一区二区三 | 上海一级毛片 | 欧美专区在线 | 亚洲精品久久国产高清 | 国产一毛片 | 日韩欧美一区二区三区久久 | 日韩手机在线观看 | 午夜成人免费视频 | 欧美精品成人一区二区三区四区 | 久久亚 | 国产精品美女久久久久aⅴ国产馆 | 欧美一区免费 | 日本久久精品免视看国产成人 | 亚洲亚色| 日操夜干 | 日日操夜夜爽 | 91热视频在线观看 | 任你干在线视频 | 91视频网| 久久精品 | 人人澡人人澡人人看添欧美 | 久久狠狠色狠狠色综合 | 小明天天看| 国产午夜精品理论片影院 |