歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> 關於Linux

淺談Linux 中字符設備的注冊

Linux中字符設備的注冊過程是比較簡單的。我們通常可以調用misc_register()函數來注冊一個字符設備。Misc設備是一種字符設備,通過該設備可以將fops請求轉發給注冊的misc設備,從而實現字符設備的功能。用戶調用該接口注冊Misc字符設備時,可以動態分配設備Minor號,當獲取Minor號之後調用class_simple_device_add()或者device_create()函數完成字符設備的創建。Misc字符設備注冊函數如下所示:

int misc_register(struct miscdevice * misc)  
{  
    struct miscdevice *c;  
    dev_t dev;  
    int err = 0;  
     
    INIT_LIST_HEAD(&misc->list);  
     
    mutex_lock(&misc_mtx);      //獲取misc設備信號量  
    list_for_each_entry(c, &misc_list, list) {  //檢查設備是否已經存在  
        if (c->minor == misc->minor) {  
            mutex_unlock(&misc_mtx);  
            return -EBUSY;          //如果設備存在,直接返回  
        }  
    }  
     
    if (misc->minor == MISC_DYNAMIC_MINOR) {    //動態分配分配minor號  
        int i = DYNAMIC_MINORS;  
        while (--i >= 0)  
            if ( (misc_minors[i>>3] & (1 << (i&7))) == 0)  
                break;  
        if (i<0) {  
            mutex_unlock(&misc_mtx);  
            return -EBUSY;  
        }  
        misc->minor = i;  
    }  
     
    if (misc->minor < DYNAMIC_MINORS)  
        misc_minors[misc->minor >> 3] |= 1 << (misc->minor & 7);  
    dev = MKDEV(MISC_MAJOR, misc->minor);  
     
    misc->this_device = device_create(misc_class, misc->parent, dev,  
                      "%s", misc->name);        //創建字符設備(Misc設備)  
    if (IS_ERR(misc->this_device)) {  
        err = PTR_ERR(misc->this_device);  
        goto out;  
    }  
     
    /*  
     * Add it to the front, so that later devices can "override"  
     * earlier defaults  
     */
    list_add(&misc->list, &misc_list);  //將設備保存至misc設備鏈中,設備訪問時需要操作該鏈表  
 out:  
    mutex_unlock(&misc_mtx);  
    return err;  
}

需要注意的是2.6.12內核中創建系統設備時需要調用simple_device的接口class_simple_device_add()。在2.6.23中需要調用device_create()函數完成設備注冊。在3.2內核中,simple_device的接口已經不存在了,所以必須調用device_create函數,另外,3.2內核不支持具有相同minor號的字符設備,在2.6.x內核中是支持的。

系統是如何完成fops函數調用的呢?回答這個問題需要分析Misc設備打開過程。在打開Misc設備驅動的時候,Misc設備驅動會根據訪問設備的Minor號重定向fops函數集,該程序說明如下:

static int misc_open(struct inode * inode, struct file * file)  
{  
    int minor = iminor(inode);  
    struct miscdevice *c;  
    int err = -ENODEV;  
    const struct file_operations *old_fops, *new_fops = NULL;  
     
    mutex_lock(&misc_mtx);  
     
    list_for_each_entry(c, &misc_list, list) {  //通過minor號來匹配對應的fops函數集  
        if (c->minor == minor) {  
            new_fops = fops_get(c->fops);  
            break;  
        }  
    }  
     
    if (!new_fops) {  
        mutex_unlock(&misc_mtx);  
        request_module("char-major-%d-%d", MISC_MAJOR, minor);  
        mutex_lock(&misc_mtx);  
     
        list_for_each_entry(c, &misc_list, list) {  
            if (c->minor == minor) {  
                new_fops = fops_get(c->fops);  
                break;  
            }  
        }  
        if (!new_fops)  
            goto fail;  
    }  
     
    err = 0;  
    old_fops = file->f_op;  
    file->f_op = new_fops;  //重定向fops函數  
    if (file->f_op->open) {     //打開設備  
        err=file->f_op->open(inode,file);  
        if (err) {  
            fops_put(file->f_op);  
            file->f_op = fops_get(old_fops);  
        }  
    }  
    fops_put(old_fops);  
fail:  
    mutex_unlock(&misc_mtx);  
    return err;  
}

很多時候我們不想創建Misc字符設備,想要自己創建一個字符設備類,然後再創建該設備類的字符設備,那麼整個創建過程可以描述如下:

1,調用register_chrdev_region函數注冊設備號區間

2,調用cdev_alloc函數分配一個字符設備

3,調用cdev_add函數添加內核字符設備

4,調用device_create函數通知udev創建設備節點,並且注冊到sysfs中。

register_chrdev_region函數用來分配設備號。在linux系統中維護了一個char_device的Hash表,每個Major占用一個Hash項。通過register_chrdev_region函數就是向全局的char_device Hash表注冊設備號。該函數說明如下:

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);    //一個Major設備類最多可以容納1M個Minor設備  
        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);  
}

當設備號區間分配完成之後,通過cdev_alloc()函數分配一個內核字符設備,然後通過cdev_add函數將該字符設備注冊到內核cdev_map->probes[]數組中,至此,內核的字符設備創建完畢。但是,此時,應用層還無法看到字符設備節點,因此,可以調用device_create函數通知udev去創建設備節點,並且將設備添加到sysfs系統樹中。至此,應用層可以通過設備節點訪問字符設備了。

字符設備是Linux中最簡單的一種設備,其系統注冊過程也相對比較簡單。

本文出自 “存儲之道” 博客,請務必保留此出處http://alanwu.blog.51cto.com/3652632/1123908

Copyright © Linux教程網 All Rights Reserved