全面注冊制總結?内核中所有已分配的字符設備編号都記錄在一個名為 chrdevs 散列表裡該散列表中的每一個元素是一個 char_device_struct 結構,它的定義如下:,下面我們就來聊聊關于全面注冊制總結?接下來我們就一起去了解一下吧!
  内核中所有已分配的字符設備編号都記錄在一個名為 chrdevs 散列表裡。該散列表中的每一個元素是一個 char_device_struct 結構,它的定義如下:
	
	static struct char_device_struct {
        struct char_device_struct *next;    // 指向散列沖突鍊表中的下一個元素的指針
        unsigned int major;                 // 主設備号
        unsigned int baseminor;             // 起始次設備号
        int minorct;                        // 設備編号的範圍大小
        char name[64];                      // 處理該設備編号範圍内的設備驅動的名稱
        struct file_operations *fops;       // 沒有使用
        struct cdev *cdev;                  // 指向字符設備驅動程序描述符的指針
    } *chrdevs[CHRDEV_MAJOR_HASH_SIZE];
注 意,内核并不是為每一個字符設備編号定義一個 char_device_struct 結構,而是為一組對應同一個字符設備驅動的設備編号範圍定義一個 char_device_struct 結構。chrdevs 散列表的大小是 255,散列算法是把每組字符設備編号範圍的主設備号以 255 取模插入相應的散列桶中。同一個散列桶中的字符設備編号範圍是按起始次設備号遞增排序的。
注冊内核提供了三個函數 來注冊一組字符設備編号,這三個函數分别是 register_chrdev_region()、alloc_chrdev_region() 和 register_chrdev()。這三個函數都會調用一個共用的 __register_chrdev_region() 函數來注冊一組設備編号範圍(即一個 char_device_struct 結構)。所以下面先來看一下 __register_chrdev_region() 函數的實現代碼。
	
	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);
    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;
    }
    cd->major = major;
    cd->baseminor = baseminor;
    cd->minorct = minorct;
    strncpy(cd->name,name, 64);
    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;
    /* 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;
        }
    }
    cd->next = *cp;
    *cp = cd;
    mutex_unlock(&chrdevs_lock);
    return cd;
out:
    mutex_unlock(&chrdevs_lock);
    kfree(cd);
    return ERR_PTR(ret);
}
函數 __register_chrdev_region() 主要執行以下步驟:
更多linux内核視頻教程文檔資料免費領取後台私信【内核】自行獲取.
分析完 __register_chrdev_region() 後,我們來一個個看那三個注冊函數。首先是 register_chrdev_region()。
	
	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);
}
register_chrdev_region() 函數用于分配指定的設備編号範圍。如果申請的設備編号範圍跨越了主設備号,它會把分配範圍内的編号按主設備号分割成較小的子範圍,并在每個子範圍上調用 __register_chrdev_region() 。如果其中有一次分配失敗的話,那會把之前成功分配的都全部退回。
	
	int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
{
    struct char_device_struct *cd;
    cd = __register_chrdev_region(0, baseminor, count, name);
    if (IS_ERR(cd))
        return PTR_ERR(cd);
    *dev = MKDEV(cd->major, cd->baseminor);
    return 0;
}
alloc_chrdev_region() 函數用于動态申請設備編号範圍,這個函數好像并沒有檢查範圍過大的情況,不過動态分配總是找個空的散列桶,所以問題也不大。通過指針參數返回實際獲得的起始設備編号。
	
	int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops)
{
    struct char_device_struct *cd;
    struct cdev *cdev;
    char *s;
    int err = -ENOMEM;
    cd = __register_chrdev_region(major, 0, 256, name);
    if (IS_ERR(cd))
        return PTR_ERR(cd);
    cdev = cdev_alloc();
    if (!cdev)
        goto out2;
    cdev->owner = fops->owner;
    cdev->ops = fops;
    kobject_set_name(&cdev->kobj, "%s", name);
    for (s = strchr(kobject_name(&cdev->kobj),'/'); s; s = strchr(s, '/'))
        *s = '!';
    err = cdev_add(cdev, MKDEV(cd->major, 0), 256);
    if (err)
        goto out;
    cd->cdev = cdev;
    return major ? 0 : cd->major;
out:
    kobject_put(&cdev->kobj);
out2:
    kfree(__unregister_chrdev_region(cd->major, 0, 256));
    return err;
}
最 後一個 register_chrdev() 是一個老式分配設備編号範圍的函數。它分配一個單獨主設備号和 0 ~ 255 的次設備号範圍。如果申請的主設備号為 0 則動态分配一個。該函數還需傳入一個 file_operations 結構的指針,函數内部自動分配了一個新的 cdev 結構。關于這些,在後續講字符設備驅動的注冊時會說明。
注銷和 注冊分配字符設備編号範圍類似,内核提供了兩個注銷字符設備編号範圍的函數,分别是 unregister_chrdev_region() 和 unregister_chrdev() 。它們都調用了 __unregister_chrdev_region() 函數。由于比較簡單,就不加說明了,隻把代碼貼出來。
	
	static struct char_device_struct * __unregister_chrdev_region(unsigned major, unsigned baseminor, int minorct)
{
    struct char_device_struct *cd = NULL, **cp;
    int i = major_to_index(major);
    mutex_lock(&chrdevs_lock);
    for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)
        if ((*cp)->major == major &&
            (*cp)->baseminor == baseminor &&
            (*cp)->minorct == minorct)
                break;
    if (*cp) {
        cd = *cp;
        *cp = cd->next;
    }
    mutex_unlock(&chrdevs_lock);
    return cd;
}
void unregister_chrdev_region(dev_t from, unsigned count)
{
    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;
        kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));
    }
}
void unregister_chrdev(unsigned int major, const char *name)
{
    struct char_device_struct *cd;
    cd = __unregister_chrdev_region(major, 0, 256);
    if (cd && cd->cdev)
        cdev_del(cd->cdev);
    kfree(cd);
}
注意, unregister_chrdev() 會把 register_chrdev() 中自動分配的 cdev 給注銷掉。
導讀-最新發表 - 内核技術中文網 - 構建全國最權威的内核技術交流分享論壇
轉載地址:淺淺講解字符設備編号的注冊分配! - 圈點 - 内核技術中文網 - 構建全國最權威的内核技術交流分享論壇
更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!