国产毛片a精品毛-国产毛片黄片-国产毛片久久国产-国产毛片久久精品-青娱乐极品在线-青娱乐精品

查看: 2857|回復(fù): 0
打印 上一主題 下一主題

linux驅(qū)動(dòng)程序的數(shù)據(jù)結(jié)構(gòu)

[復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
發(fā)表于 2017-5-4 11:09:56 | 只看該作者 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式
一、linux驅(qū)動(dòng)程序的數(shù)據(jù)結(jié)構(gòu)

設(shè)備驅(qū)動(dòng)程序?qū)嵸|(zhì)上是提供一組供應(yīng)用程序操作設(shè)備的接口函數(shù)。
各種設(shè)備由于功能不同,驅(qū)動(dòng)程序提供的函數(shù)接口也不相同,但linux為了能夠統(tǒng)一管理,規(guī)定了linux下設(shè)備驅(qū)動(dòng)程序必須使用統(tǒng)一的接口函數(shù) file_operations 。
所以,一種設(shè)備的驅(qū)動(dòng)程序主要內(nèi)容就是提供這樣的一組file_operations接口函數(shù)。
那么,linux是如何管理種類繁多的設(shè)備驅(qū)動(dòng)程序呢?

linux下設(shè)備大體分為塊設(shè)備和字符設(shè)備兩類。
內(nèi)核中用2個(gè)全局?jǐn)?shù)組存放這2類驅(qū)動(dòng)程序。
#define MAX_CHRDEV   255
#define MAX_BLKDEV   255
struct device_struct {
    const char * name;
    struct file_operations * fops;
};
static struct device_struct chrdevs[MAX_CHRDEV];

static struct {
    const char *name;
    struct block_device_operations *bdops;
} blkdevs[MAX_BLKDEV];
//此處說(shuō)明一下,struct block_device_operations是塊設(shè)備驅(qū)動(dòng)程序內(nèi)部的接口函數(shù),上層文件系統(tǒng)還是通過(guò)struct file_operations訪問的。

哈哈,現(xiàn)在明白了吧?你的驅(qū)動(dòng)程序調(diào)用 int register_chrdev(unsigned int major, const char * name, struct file_operations *fops) 就是將你提供的接口函數(shù)fops存放到chrdevs[MAX_CHRDEV]這個(gè)數(shù)組中,數(shù)組下標(biāo)就是你的驅(qū)動(dòng)的主設(shè)備號(hào),數(shù)組內(nèi)容包括驅(qū)動(dòng)名稱和驅(qū)動(dòng)接口函數(shù),這樣,內(nèi)核就能看到你的驅(qū)動(dòng)程序了。BTW,如果你將major設(shè)為0,系統(tǒng)會(huì)自動(dòng)給你分配一個(gè)空閑的主設(shè)備號(hào)。
那么?次設(shè)備號(hào)呢?別急,馬上就出現(xiàn)了:)
二、設(shè)備節(jié)點(diǎn)如何產(chǎn)生?

    驅(qū)動(dòng)程序運(yùn)行在內(nèi)核空間,應(yīng)用程序訪問驅(qū)動(dòng)程序通常是通過(guò)系統(tǒng)調(diào)用文件系統(tǒng)接口函數(shù)的,也就是說(shuō),在linux下,和磁盤文件一樣,設(shè)備也是文件,只是他們的文件屬性不同而已,應(yīng)用程序只能通過(guò)文件名來(lái)訪問設(shè)備的驅(qū)動(dòng)程序。
所以,文件系統(tǒng)中必須要有一個(gè)代表你的設(shè)備的文件,應(yīng)用程序才能訪問你的設(shè)備驅(qū)動(dòng)程序。
    為了便于理解,我們可以將設(shè)備文件換個(gè)名字,叫做設(shè)備節(jié)點(diǎn)。
    設(shè)備節(jié)點(diǎn)在哪里?設(shè)備節(jié)點(diǎn)存在于你的文件系統(tǒng)中,通常在/dev目錄下,當(dāng)然,你也可以在其它地方創(chuàng)建。一般說(shuō)來(lái),我們?cè)谥谱魑募到y(tǒng)映像時(shí)就已經(jīng)將可能用到的設(shè)備節(jié)點(diǎn)都創(chuàng)建好了。
    你可以打開/dev目錄看一下,它下面的設(shè)備節(jié)點(diǎn)的數(shù)量會(huì)讓你吃驚的:)

       如何創(chuàng)建設(shè)備節(jié)點(diǎn)?。
你可以用mknod命令。如使用以下命令可以創(chuàng)建一個(gè)mtd4的字符設(shè)備節(jié)點(diǎn)。
Mknod  /dev/ mtd4  c MTD_CHAR_MAJOR  4

我們創(chuàng)建一個(gè)普通的磁盤文件,它的內(nèi)容是我們寫入的數(shù)據(jù)。
那么設(shè)備節(jié)點(diǎn)的內(nèi)容是什么?設(shè)備節(jié)點(diǎn)文件沒有數(shù)據(jù),它的文件大小為0,它只有文件屬性,包括設(shè)備類型、主設(shè)備號(hào)、次設(shè)備號(hào)。
沒有別的了?對(duì),就這些,沒別的了。

那設(shè)備節(jié)點(diǎn)和設(shè)備驅(qū)動(dòng)程序是怎么聯(lián)系起來(lái)的啊?

別著急,休息,休息一會(huì)兒:)順便加下我的QQ:2232894713
三、應(yīng)用程序是如何訪問設(shè)備驅(qū)動(dòng)程序的?

舉個(gè)例子:我們要向nor flash的第四個(gè)分區(qū)的起始位置偏移512字節(jié)寫入100字節(jié)的數(shù)據(jù)。
我們是如何做的?主要程序片斷如下:
    fd = open(“/dev/mtd4”, O_RDWR);
    lseek (fd,512, SEEK_SET);
    write (fd , write_buffer, 100);
    close(fd);
上面的代碼比較簡(jiǎn)單,但是似乎沒有看到我們的應(yīng)用程序是如何調(diào)用到驅(qū)動(dòng)程序的。
沒關(guān)系,接下來(lái)我將帶領(lǐng)你們走通這條道路。

應(yīng)用程序調(diào)用Open函數(shù),這是個(gè)系統(tǒng)調(diào)用函數(shù),程序會(huì)進(jìn)入內(nèi)核空間調(diào)用sys_open函數(shù)。
在sys_open,首先會(huì)根據(jù)文件路徑“/dev/mtd4”找到這個(gè)文件節(jié)點(diǎn),這部分工作是屬于VFS(虛擬文件系統(tǒng))的。
“/dev/mtd4”的文件屬性是字符設(shè)備,于是sys_open會(huì)調(diào)用函數(shù)chrdev_open(),在這個(gè)函數(shù)里有一句話:
filp->f_op = get_chrfops(MAJOR(inode->i_rdev), MINOR(inode->i_rdev));
哈!看到了眉目吧!猜也能猜到啊,get_chrfops()里面一定會(huì)返回 chrdevs[major].fops的。
我們終于從文件系統(tǒng)走到驅(qū)動(dòng)程序了,那么,接下來(lái)的事情就是可以理解的了。
Write()最終一定會(huì)調(diào)用到chrdevs[major].fops->write();
Read()最終一定會(huì)調(diào)用到chrdevs[major].fops->read();
各種驅(qū)動(dòng)程序比較特殊的功能函數(shù)都可以通過(guò)ioctl()來(lái)得到調(diào)用。
而次設(shè)備號(hào)也會(huì)作為參數(shù)傳遞給你。
四、為什么要有設(shè)備文件系統(tǒng)?

從前面的章節(jié),我們可以看到以主次設(shè)備號(hào)的形式管理設(shè)備驅(qū)動(dòng)程序存在很大的缺點(diǎn)。
首先,設(shè)備節(jié)點(diǎn)的創(chuàng)建是獨(dú)立于內(nèi)核的,是在建立文件系統(tǒng)時(shí)就把所有要用到的設(shè)備節(jié)點(diǎn)都創(chuàng)建好了的,通常我們不會(huì)去刻意刪除哪些節(jié)點(diǎn),因?yàn)槲覀儾恢老到y(tǒng)將來(lái)會(huì)不會(huì)用到它們。由于每個(gè)設(shè)備節(jié)點(diǎn)代表唯一的主次設(shè)備號(hào),所以每個(gè)可能存在的子設(shè)備都對(duì)應(yīng)一個(gè)設(shè)備節(jié)點(diǎn),可見,這樣的設(shè)備節(jié)點(diǎn)數(shù)量是很大的,這些數(shù)量龐大的設(shè)備節(jié)點(diǎn)都(文件)存在于存儲(chǔ)介質(zhì)中,對(duì)文件系統(tǒng)的效率也是個(gè)影響。
其次,文件系統(tǒng)中存在哪些設(shè)備節(jié)點(diǎn),并不代表內(nèi)核中就有這種設(shè)備的驅(qū)動(dòng)程序,也不代表系統(tǒng)中有這種設(shè)備,因?yàn)樵O(shè)備節(jié)點(diǎn)不是動(dòng)態(tài)創(chuàng)建的,它是制作文件系統(tǒng)時(shí)建立的。因此,/dev目錄下的信息大多對(duì)我們是無(wú)用的,而且那么多的設(shè)備節(jié)點(diǎn)都平鋪在/dev目錄下,閱讀起來(lái)也不直觀。
最后,目前主次設(shè)備號(hào)都是用8位整數(shù)表示的,也就是說(shuō)內(nèi)核最多管理256種字符設(shè)備和256種塊設(shè)備。現(xiàn)在計(jì)算機(jī)外設(shè)種類越來(lái)越多,這樣的限制已經(jīng)不夠了。

由于目前的主次設(shè)備號(hào)的管理形式有以上幾個(gè)缺點(diǎn),linux內(nèi)核小組在2.4版本以后加入了設(shè)備文件系統(tǒng)來(lái)改進(jìn)這些缺點(diǎn)。
設(shè)備文件系統(tǒng)的思想就是想讓設(shè)備節(jié)點(diǎn)可以動(dòng)態(tài)創(chuàng)建、刪除,這樣系統(tǒng)中有哪些設(shè)備驅(qū)動(dòng)程序就可以一目了然;還要能夠把設(shè)備節(jié)點(diǎn)組織成一棵目錄樹,方便閱讀;最后,希望能夠擴(kuò)大主次設(shè)備號(hào)的限制,不再限制在256種設(shè)備以內(nèi)。
五、設(shè)備文件系統(tǒng)如何實(shí)現(xiàn)?

       要想在內(nèi)核中方便的做到動(dòng)態(tài)創(chuàng)建、刪除設(shè)備文件(在這里,我們把設(shè)備節(jié)點(diǎn)稱為設(shè)備文件會(huì)更恰當(dāng)些),最自然的做法就是在RAM中創(chuàng)建一個(gè)文件系統(tǒng),內(nèi)核啟動(dòng)時(shí)這個(gè)文件系統(tǒng)是空的,以后每加載一種設(shè)備驅(qū)動(dòng)程序,就在這個(gè)文件系統(tǒng)中創(chuàng)建一個(gè)對(duì)應(yīng)的設(shè)備文件;卸載設(shè)備驅(qū)動(dòng)程序時(shí),再刪除這個(gè)設(shè)備文件。而且,我們可以在這個(gè)文件系統(tǒng)中創(chuàng)建目錄,一類設(shè)備文件放在同一個(gè)目錄中,甚至把一種設(shè)備的多個(gè)子設(shè)備文件放在同一個(gè)目錄下,方便閱讀。
    在設(shè)備文件系統(tǒng)中,我們?cè)谧?cè)設(shè)備文件時(shí)可以把設(shè)備驅(qū)動(dòng)程序的ops直接掛到設(shè)備文件的inode中,以后訪問驅(qū)動(dòng)程序就可以擺脫主次設(shè)備號(hào)的限制了,不需要再訪問chrdev[]數(shù)組,這樣就突破了256種設(shè)備的限制。
    我們把設(shè)備文件系統(tǒng)mount到/dev目錄下,這樣,看起來(lái)跟以前的方案就很相似了,也方便老的應(yīng)用程序的移植。

六、如何使用設(shè)備文件系統(tǒng)?

       以前我們寫驅(qū)動(dòng)程序時(shí)要調(diào)用int register_chrdev(unsigned int major, const char * name, struct file_operations *fops)將你提供的接口函數(shù)fops存放到chrdevs[MAX_CHRDEV]這個(gè)數(shù)組中,然后在文件系統(tǒng)中用mknod創(chuàng)建有相同主設(shè)備號(hào)的設(shè)備節(jié)點(diǎn)就可以了。
    那么現(xiàn)在有了設(shè)備文件系統(tǒng),我們的驅(qū)動(dòng)程序該如何寫呢?
    很簡(jiǎn)單,follow me!

1、調(diào)用devfs_handle_t devfs_mk_dir (devfs_handle_t dir, const char *name, void *info)創(chuàng)建設(shè)備文件所在的目錄。Dir是要?jiǎng)?chuàng)建目錄的父目錄句柄,如為NULL,就是設(shè)備文件系統(tǒng)的根目錄(/dev);最后一個(gè)參數(shù)info通常為NULL。
2、調(diào)用devfs_handle_t devfs_register (devfs_handle_t dir, const char *name,
                  unsigned int flags,
                  unsigned int major, unsigned int minor,
                  umode_t mode, void *ops, void *info)
注冊(cè)具體的設(shè)備,并在指定目錄下創(chuàng)建子設(shè)備節(jié)點(diǎn)。
這里的dir目錄名是要?jiǎng)?chuàng)建的設(shè)備文件所在的目錄名,目錄名一般不能為NULL,因?yàn)樽釉O(shè)備文件名name通常是以0、1、2、3等數(shù)字命名的,會(huì)和其它設(shè)備文件沖突。
    3、卸載驅(qū)動(dòng)程序時(shí)調(diào)用void devfs_unregister (devfs_handle_t de)刪除創(chuàng)建的目錄和子設(shè)備文件。

七、具體設(shè)備驅(qū)動(dòng)程序分析

       這節(jié)以mtdchar設(shè)備驅(qū)動(dòng)程序來(lái)具體分析驅(qū)動(dòng)程序的寫法。
    Mtdchar字符設(shè)備是管理flash驅(qū)動(dòng)程序的,是各種flash驅(qū)動(dòng)程序的抽象層。
    Mtdchar的主程序是driver/mtd/mtdchar.c;

1、驅(qū)動(dòng)程序初始化時(shí),要注冊(cè)設(shè)備節(jié)點(diǎn),創(chuàng)建子設(shè)備文件

驅(qū)動(dòng)程序?yàn)榱思嫒菀郧暗姆桨福ǔ?huì)既注冊(cè)設(shè)備節(jié)點(diǎn),又創(chuàng)建子設(shè)備文件,這樣不管內(nèi)核支持不支持設(shè)備文件系統(tǒng),驅(qū)動(dòng)程序都可以工作。

static int __init init_mtdchar(void)
{
    //為了兼容以前的方案,要注冊(cè)mtdchar的主設(shè)備號(hào)、設(shè)備名以及fops
    if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops))
{
       printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",
              MTD_CHAR_MAJOR);
       return -EAGAIN;
    }

    //如果內(nèi)核支持設(shè)備文件系統(tǒng),在這個(gè)函數(shù)里會(huì)創(chuàng)建子設(shè)備文件。
    mtdchar_devfs_init();
    return 0;
}

static inline void mtdchar_devfs_init(void)
{
    //創(chuàng)建設(shè)備節(jié)點(diǎn)的父目錄,/dev/mtd/
    devfs_dir_handle = devfs_mk_dir(NULL, "mtd", NULL);
    //在這個(gè)函數(shù)里會(huì)調(diào)用devfs_register()創(chuàng)建子設(shè)備
    register_mtd_user(¬ifier);
}


notifier定義如下,主要是提供創(chuàng)建和刪除子設(shè)備文件的接口函數(shù)

static struct mtd_notifier notifier = {
    .add   = mtd_notify_add,        //創(chuàng)建一個(gè)子設(shè)備
    .remove    = mtd_notify_remove, //刪除一個(gè)子設(shè)備
};
static void mtd_notify_add(struct mtd_info* mtd)
{
    char name[8];

    if (!mtd)
       return;
    // mtd是一個(gè)子設(shè)備,代表flash上的一個(gè)邏輯分區(qū)
    sprintf(name, "%d", mtd->index);
    //這里調(diào)用devfs_register創(chuàng)建子設(shè)備文件,如/dev/mtd/0
    devfs_rw_handle[mtd->index] = devfs_register(devfs_dir_handle, name,
           DEVFS_FL_DEFAULT, MTD_CHAR_MAJOR, mtd->index*2,
           S_IFCHR | S_IRUGO | S_IWUGO,
           &mtd_fops, NULL);

    //下面注冊(cè)的是只讀子設(shè)備,無(wú)關(guān)緊要。
    sprintf(name, "%dro", mtd->index);
    //創(chuàng)建只讀子設(shè)備,如 /dev/mtd/0ro
    devfs_ro_handle[mtd->index] = devfs_register(devfs_dir_handle, name,
           DEVFS_FL_DEFAULT, MTD_CHAR_MAJOR, mtd->index*2+1,
           S_IFCHR | S_IRUGO,
           &mtd_fops, NULL);
}

static void mtd_notify_remove(struct mtd_info* mtd)
{
    if (!mtd)
       return;
    //刪除mtdchar子設(shè)備文件和mtdchar子設(shè)備文件
    devfs_unregister(devfs_rw_handle[mtd->index]);
    devfs_unregister(devfs_ro_handle[mtd->index]);
}

mtd驅(qū)動(dòng)程序中會(huì)將一片flash劃分為多個(gè)邏輯分區(qū),這樣的每個(gè)邏輯分區(qū)也可以被看做是一個(gè)子設(shè)備,具體flash驅(qū)動(dòng)程序添加邏輯分區(qū)時(shí)會(huì)在數(shù)組mtd_table[]中記錄分區(qū)的位置和大小。

void register_mtd_user (struct mtd_notifier *new)
{
    int i;

    down(&mtd_table_mutex);
    list_add(&new->list, &mtd_notifiers);
    __module_get(THIS_MODULE);

    // mtd_table[]是個(gè)全局?jǐn)?shù)組,每個(gè)元素都是一個(gè)邏輯分區(qū),記錄著分區(qū)的起始位置和大小,我們將每個(gè)邏輯分區(qū)創(chuàng)建為一個(gè)單獨(dú)的mtd子設(shè)備文件
    for (i=0; i< MAX_MTD_DEVICES; i++)
       if (mtd_table)
           new->add(mtd_table);

    up(&mtd_table_mutex);
}

2、驅(qū)動(dòng)程序卸載時(shí)要注銷設(shè)備節(jié)點(diǎn),刪除設(shè)備文件

static void __exit cleanup_mtdchar(void)
{
    mtdchar_devfs_exit();
    //注銷chrdevs[major]
    unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
}


static inline void mtdchar_devfs_exit(void)
{
    //在這個(gè)函數(shù)里會(huì)調(diào)用devfs_unregister()刪除子設(shè)備
    unregister_mtd_user(¬ifier);
    //刪除父目錄
    devfs_unregister(devfs_dir_handle);
}



int unregister_mtd_user (struct mtd_notifier *old)
{
    int i;

    down(&mtd_table_mutex);

    module_put(THIS_MODULE);

    for (i=0; i< MAX_MTD_DEVICES; i++)
     if (mtd_table)
           old->remove(mtd_table);

    list_del(&old->list);
    up(&mtd_table_mutex);
    return 0;
}

本版積分規(guī)則

關(guān)于我們  -  服務(wù)條款  -  使用指南  -  站點(diǎn)地圖  -  友情鏈接  -  聯(lián)系我們
電子工程網(wǎng) © 版權(quán)所有   京ICP備16069177號(hào) | 京公網(wǎng)安備11010502021702
快速回復(fù) 返回頂部 返回列表
主站蜘蛛池模板: 亚洲欧美日韩在线精品一区二区 | 久久久91精品国产一区二区 | 善良朋友的妻子在线观看 | 精品国产一区二区三区国产馆 | 日本一区二区中文字幕 | 日本精品一区二区三区在线 | 韩国伦理妈妈的朋友在线观看 | 国产免费播放一区二区三区 | 亚洲无线码一区二区三区 | 免费精品美女久久久久久久久 | 韩国大尺度女教师未删减在线 | 中文线码中文高清播放中 | 精品国产日韩亚洲一区在线 | a级黄色毛片三个搞一 | 四虎入口 | 日韩在线欧美高清一区 | 午夜影院一区 | 亚天堂 | 亚洲欧美日韩一区超高清 | 欧美日韩一区二区三区色综合 | 欧美日韩国产在线成人网 | 男人的天堂欧美 | 久久久久国产精品免费看 | 日韩欧美亚洲国产高清在线 | 狠狠色狠狠色综合久久一 | 亚洲 欧美 国产 中文 | 亚洲欧美成人网 | www.欧美在线观看 | 啦啦啦高清影视在线观看视频? | 五月婷婷视频在线观看 | 国产一区二区三区在线免费观看 | 欧美日韩国产亚洲一区二区 | 亚洲精品网站在线观看不卡无广告 | 中文字幕免费人成乱码中国 | 日本爽视频 | 中文字幕成人在线 | 日本韩国一免费观看 | 国产伦精一区二区三区视频 | 一个色综合高清在线观看 | 国产精品一区二区在线观看完整版 | 久草视频中文 |