文章目錄
-
- 一、chrdevs哈希表
- 二、、 __register_chrdev_region 相關函數分析
一、chrdevs哈希表
用于管理裝置号。
定義在
ebf-buster-linux/fs/char_dev.c
。
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];
// #define CHRDEV_MAJOR_HASH_SIZE 255 fs.h
示意圖:
注意上圖中的一個節點可以表示
一個主裝置号,一段次裝置号
。
二、、 __register_chrdev_region 相關函數分析
此函數用于向 linux kernel 注冊一個主裝置号,一個或多個次裝置号。
定義在
char_dev.c
。
/* 參數含義依次為:
* 主裝置号,次裝置号起始,次裝置号數量,裝置名字
*/
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);
/* 若主裝置号為0,由kernel配置設定一個裝置号 */
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;
}
// 儲存傳進來的四個參數
cd->major = major; /* 主裝置号 */
cd->baseminor = baseminor; /* 次裝置号起始 */
cd->minorct = minorct; /* 次裝置号數量 */
strlcpy(cd->name, name, sizeof(cd->name));
/* 計算在哈希表中的位置,就是對 255 進行取餘 */
i = major_to_index(major);
/*
* 周遊此位置所指向的連結清單,知道放在連結清單中的哪個位置。
* 一個連結清單内按主裝置号遞增,次裝置号遞增排序。一個
* 節點可能包含一個主裝置号,一段連續的次裝置号
*/
for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)
if ((*cp)->major > major ||
((*cp)->major == major &&
(((*cp)->baseminor >= baseminor) ||
((*cp)->baseminor + (*cp)->minorct > baseminor))))
break;
/*
* 主裝置号相同的情況下需要判斷次裝置号是否沖突
* 裡面兩個 if 判斷
*/
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;
}
}
/* 連結清單的節點插入操作 */
cd->next = *cp;
*cp = cd;
mutex_unlock(&chrdevs_lock);
return cd;
out:
mutex_unlock(&chrdevs_lock);
kfree(cd);
return ERR_PTR(ret);
}
儲存
新注冊的裝置号
到
chrdevs哈希表
中,防止裝置号沖突
分析結論:
哈希表中每個元素都是一個連結清單。
連結清單中的每一個元素似乎可以表示一個主裝置号,一段次裝置号(多個裝置)
相關的其他函數
int register_chrdev_region(dev_t from, unsigned count, const char *name);
此函數内部會調用 __register_chrdev_region。
申請自己指定的一段裝置号。這段裝置号可能會包含多個主裝置号。
而 __register_chrdev_region 每次調用隻能申請一個主裝置号,一個或多個次裝置号。
是以在此函數内部要進行判斷,分次調用 __register_chrdev_region。
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
const char *name);
由系統配置設定一個裝置号,并調用 __register_chrdev_region 進行注冊。
static struct char_device_struct *
__unregister_chrdev_region(unsigned major, unsigned baseminor, int minorct);
此函數用于登出一段裝置号
在哈希表 chrdevs 中找到對應的節點,删除之。
由于之前是kzalloc動态申請,此時不需要手動free了。