国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統(tǒng) > Linux > 正文

Linux 簡單字符設(shè)備驅(qū)動程序 (自頂向下)

2024-06-28 13:25:42
字體:
供稿:網(wǎng)友
linux 簡單字符設(shè)備驅(qū)動程序 (自頂向下)

第零章:扯扯淡

  特此總結(jié)一下寫的一個簡單字符設(shè)備驅(qū)動程序的過程,我要強調(diào)一下“自頂向下”這個介紹方法,因為我覺得這樣更容易讓沒有接觸過設(shè)備驅(qū)動程序的童鞋更容易理解,“自頂向下”最初從《計算機網(wǎng)絡(luò) 自頂向下方法》這本書學(xué)到的,我覺得有時候這是一種很好的方式。



第一章:測試程序

  咦?你怎么跟別人的思路不一樣???自頂向下嘛,我就直接從測試程序來說啦,這樣那個不是更熟悉嗎?看看下面的測試程序的代碼,是不是很熟悉?

 1 #include <stdio.h> 2 #include <unistd.h> 3 #include <fcntl.h>  4  5 #define MY_CDEV_NAME "/dev/mychardev" 6 #define BUF_LEN 16 7  8 int main(void) 9 {10     int fd;11     int ret,i;12     char buf[BUF_LEN];13     14     /*打開設(shè)備*/15     fd=open(MY_CDEV_NAME,O_RDWR | O_NONBLOCK);16     if(fd<0)17     {18         PRintf("open %s fail!/n",MY_CDEV_NAME);19         return -1;20     }21     printf("open %s success!/n",MY_CDEV_NAME);22     23     /*設(shè)置buf的數(shù)據(jù),以后會寫進設(shè)備*/24     for(i=0;i<BUF_LEN;i++)25     {26         buf[i]=i+65;27     }28     29         30     /*寫設(shè)備*/31     if((ret=write(fd,buf,BUF_LEN))<0)32     {33         printf("write %s fail!/n",MY_CDEV_NAME);34     }35     else36     {37         printf("write %s success! Write totoal:%d/n",MY_CDEV_NAME,ret);38     }39     40     /*把文件偏移量設(shè)置為文件開始處*/41     if((ret=lseek(fd,0,SEEK_SET))<0)42     {43         printf("lseek %s fail!/n",MY_CDEV_NAME);44     }45     else46     {47         printf("lseek %s success! Now position:%d/n",MY_CDEV_NAME,ret);48     }49     50     /*讀設(shè)備*/51     if((ret=read(fd,buf+BUF_LEN/2,BUF_LEN))<0)52     {53         printf("read %s fail!/n",MY_CDEV_NAME);54     }55     else56     {57         printf("read %s success! Read totoal:%d/n",MY_CDEV_NAME,ret);58     }59     60     for(i=0;i<BUF_LEN;i++)61     {62         printf("buf[%d]:%c/n",i,buf[i]);63     }64     65     close(fd);66     67     return 0;    68 }
最終測試代碼

這里其實不就是Unix環(huán)境打開一個普通的文件嘛,我打開的是/dev/mychardev ,雖然是一個字符設(shè)備文件,但是操作系統(tǒng)統(tǒng)一了接口,所以和打開一個普通文件沒有區(qū)別,先open(),再向文件write(),再把當前文件偏移量設(shè)置為文件的開始處lseek(),再讀讀看read(),最后關(guān)閉close()。寫寫這些代碼越發(fā)覺得Linux真是厲害,基本上把所有的設(shè)備都當做文件給處理掉了,系統(tǒng)提供統(tǒng)一的接口給上層,酷斃了!

  到這里基本上沒有問題,唯一的問題是/dev/mychardev 這個文件怎么來的???



第二章:設(shè)備文件怎么來的

第0節(jié)、Linux文件類型簡單介紹:

  linux系統(tǒng)將設(shè)備基本分為3類:字符設(shè)備、塊設(shè)備、網(wǎng)絡(luò)設(shè)備。詳情請搜搜,我不會告訴你我理解不夠深刻……

第1節(jié)、先看看這個文件屬性:

看最前面是“c”,說明是一個字符設(shè)備文件。我不會告訴你這個文件是我自己創(chuàng)建的,O(∩_∩)O哈哈~

第2節(jié)、Linux創(chuàng)建一個字符設(shè)備文件很簡單,只要mknod命令即可:

說得很清楚,創(chuàng)建字符或塊特殊設(shè)備文件。所以要創(chuàng)建一個字符設(shè)備很簡單啊:

 sudo mknod /dev/mychardev c 248 0

看,這不就創(chuàng)建了一個字符設(shè)備文件嘛,sudo要獲得權(quán)限;/dev/mychardev 為文件名,其實也指定了文件路徑;c表明創(chuàng)建的是字符設(shè)備文件,塊設(shè)備文件就是b啦;最后重要的是 major 和 minor 這兩個參數(shù),我創(chuàng)建時用248和0替換的,這個不是小打小鬧瞎搞的,有來頭!

第3節(jié)、為什么在/dev目錄:

  linux的/dev目錄里,存放的是系統(tǒng)里的各種設(shè)備文件,每個設(shè)備對應(yīng)一個設(shè)備文件,而我們就是通過這個設(shè)備文件訪問和使用這個設(shè)備的,即打開這個文件相當于打開設(shè)備了,向文件里面寫數(shù)據(jù)相當于把數(shù)據(jù)寫到設(shè)備了,讀文件相當于從設(shè)備中讀數(shù)據(jù)了。咱們不是要創(chuàng)建一個字符設(shè)備嘛,雖然不知道這個設(shè)備具體是什么鳥樣,但是總有一個設(shè)備文件來對應(yīng)這個設(shè)備。

第4節(jié)、設(shè)備文件主次設(shè)備號:

  從上一節(jié)可以看到,/dev目錄下是各種雜七雜八的設(shè)備文件,這些設(shè)備文件是怎么樣對應(yīng)設(shè)備的呢?它們兩個好基友肯定要一一對應(yīng)嘛。為了管理這些設(shè)備,操作系統(tǒng)為設(shè)備編了號,每個設(shè)備號又分為主設(shè)備號和次設(shè)備號。

  從設(shè)備文件與設(shè)備來講:主設(shè)備號用來區(qū)分不同種類的設(shè)備;而次設(shè)備號用來區(qū)分同一類型的多個設(shè)備。

  從設(shè)備文件與設(shè)備驅(qū)動來講:主設(shè)備號用來標識與設(shè)備文件相連的驅(qū)動程序,用來反映設(shè)備類型;次設(shè)備號被驅(qū)動程序用來辨別操作的是哪個設(shè)備,用來區(qū)分同類型的設(shè)備。

  上面兩個角度意思一樣。舉個例子:假如我的電腦連了2臺打印機,打印機類型完全一樣,那么按照前面講的,在/dev目錄下肯定會有2個設(shè)備文件來對應(yīng)這2個打印機,比如有/dev/printer1和/dev/printer2,對這兩個文件的讀寫其實就是對打印機1和2的讀寫操作。但是由于這兩個打印機類型一樣,所以它們的驅(qū)動程序不也是一樣的嗎?沒必要把2份一樣的驅(qū)動代碼加載到操作系統(tǒng)的內(nèi)核吧?所以就讓這兩個文件的主設(shè)備號一樣就可以了,比如都是248。好了,我們加載了一個驅(qū)動程序模塊到內(nèi)核里面,下面這個驅(qū)動程序怎么知道是向哪個打印機發(fā)送數(shù)據(jù)呢?這時候就是次設(shè)備號起作用了,比如給它們分配1和2次設(shè)備號,這樣不久區(qū)分了嘛。所以我就可以這樣:sudo mknod /dev/printer1 c 248 0 和sudo mknod /dev/printer2 c 248 1。這樣就創(chuàng)建了2設(shè)備文件以對應(yīng)2設(shè)備。

  好了,到這里設(shè)備文件是怎么來的解決了:自己創(chuàng)建的唄。那么唯一的問題是主設(shè)備號與次設(shè)備號怎么來的???



第三章:向系統(tǒng)裝載設(shè)備驅(qū)動模塊

  這里沒有介紹上一章主設(shè)備號與次設(shè)備號怎么來的問題,因為是程序執(zhí)行過程中從系統(tǒng)獲得的,具體要看代碼,但是在把驅(qū)動程序裝載到系統(tǒng)會執(zhí)行一個函數(shù),一般會在這個函數(shù)里面向系統(tǒng)要設(shè)備號,所以可以在這個函數(shù)里面打印獲得設(shè)備號。所以在把驅(qū)動模塊裝載到系統(tǒng)是可以看到設(shè)備號的,前提是你一定寫了一個打印函數(shù)。

  這一章講的是如果你已經(jīng)把具體的設(shè)備驅(qū)動程序?qū)懞昧酥螅銘?yīng)該怎么把它裝載到系統(tǒng)。看下面的文件夾:

我寫的驅(qū)動程序的源文件只有一個:myCDev.c,就這個,還有就是Makefile,另外test.c和a是測試源文件和生成的測試程序。其余的都是執(zhí)行make命令時生成的,也就是編譯生成的文件。其中有個.ko的文件,向系統(tǒng)裝載驅(qū)動和這個密切相關(guān)。

  既然我們是自頂向下的,假設(shè)下層已經(jīng)提供了源文件,也已經(jīng)編譯好了,現(xiàn)在向系統(tǒng)裝載驅(qū)動程序只要一個命令:

sudo insmod myCDev.ko

insmod就是干這個事情的。相對應(yīng):

sudo rmmod myCDev

是卸載模塊的,模塊不用了可以直接從內(nèi)核去掉的,注意insmod有.ko后綴,這個沒有。

  這里有兩個命令;dmesg和cat /proc/kmsg 可以查看你在源代碼的中printk()函數(shù)的輸出,因為一般會在模塊裝載和卸載時調(diào)用特定的函數(shù)。

  好了,這一章解決了如果我們已經(jīng)寫好了驅(qū)動程序并編譯好了,如何裝載的問題。



第四章:正式編寫驅(qū)動程序源代碼文件

第0節(jié)、虛擬字符驅(qū)動設(shè)備大概原理:

  如果這個設(shè)備不是虛擬的話,我向它寫數(shù)據(jù)之類的是會寫到這個設(shè)備的,如打印機,向打印機寫數(shù)據(jù)直接通過連線寫到了打印機,打印機再看著辦。但是是虛擬的設(shè)備,我向它寫數(shù)據(jù)寫到哪里呢?要從這個設(shè)備哪里讀數(shù)據(jù)呢?總不能寫到空氣中吧?于是只要在內(nèi)存開辟一塊存儲空間,向這個空間寫的相當于寫到了具體設(shè)備,要從設(shè)備讀數(shù)據(jù)也就只要從這個內(nèi)存讀就可以了。

  弄清了這個就好辦了。

第1節(jié)、需要包含的頭文件位置:

  寫程序嘛,少不了包含頭文件,也就是別人寫好的東西。linux內(nèi)核基本是用c寫的,所以也是用include的。但是程序是在內(nèi)核態(tài)運行的而非用戶態(tài),所以需要包含的頭文件不一樣了。看:

 1 /*包含我的電腦中已有的linux內(nèi)核源代碼,注意版本,我的路徑:/usr/src/linux-headers-3.13.0-30/include*/ 2 #include <linux/cdev.h> 3 #include <linux/module.h> 4 #include <linux/types.h> 5 #include <linux/init.h> 6 #include <linux/errno.h> 7 #include <linux/mm.h> 8 #include <linux/sched.h> 9 #include <linux/init.h>10 #include <linux/fs.h>11 #include <linux/kdev_t.h>12 #include <linux/slab.h>
頭文件

這些頭文件按需要包含,我的系統(tǒng)自己就有這個操作系統(tǒng)的源代碼,上面說明了。其實在/usr/src文件夾下還有其他版本的內(nèi)核源代碼,因為我的Ubuntu總是更新到最新。

第2節(jié)、模塊裝載與卸載時會調(diào)用的函數(shù):

  一個模塊裝載到內(nèi)核時會執(zhí)行一個函數(shù)的,這個函數(shù)是你自己寫的,然后告訴系統(tǒng)一下就行了,就是說這樣:嗨,系統(tǒng)兄弟,把我這個模塊加載到內(nèi)核時執(zhí)行的函數(shù)是這個哦,記住哈。同樣把模塊從內(nèi)核卸載也會調(diào)用一個特定函數(shù),你只要寫好告訴它一下就好了。函數(shù)原型如下:

int my_init(void);void my_exit(void);

所以你可以自己寫自己希望驅(qū)動程序被加載到內(nèi)核和從中卸載時執(zhí)行的函數(shù),我的如下:

 1 /*初始化函數(shù),當模塊裝載時被調(diào)用,如果裝載成功返回0,否則返回非0值*/ 2 static int myCDevInit(void)    //int my_init(void); 3 { 4     int res;    //初始化函數(shù)的返回值 5  6     printk(KERN_EMERG "/n/n/nmyCDevInit() process.../n");       7              8     /*動態(tài)分配設(shè)備號*/ 9     res=alloc_chrdev_region(&myDev,0,1,"MyCharDev");10     if(res<0)    //表示分配設(shè)備號失敗11     {12         return res;13     }14     printk(KERN_EMERG "myCDevInit(): alloc_chrdev_region() success! major:%d,minor:%d/n", MAJOR(myDev), MINOR(myDev));    //打印獲得的主次設(shè)備號15     16     /*為設(shè)備描述結(jié)構(gòu)分配內(nèi)存*/17     pMyCharDev= kmalloc(sizeof(struct MyCharDev), GFP_KERNEL);  18     if(!pMyCharDev)19     {20         res=-ENOMEM;    //系統(tǒng)定義的內(nèi)存不足21         goto failMalloc;22     }    23     memset(pMyCharDev,0,sizeof(struct MyCharDev));24     printk(KERN_EMERG "myCDevInit(): kmalloc() success!/n");25     26     /*下面初始化及注冊字符設(shè)備到系統(tǒng)中*/27     cdev_init(&(pMyCharDev->myCDev),&myCDevOps);    //初始化struct cdev結(jié)構(gòu)28     cdev_add(&(pMyCharDev->myCDev),myDev,1);    //注冊字符設(shè)備29     printk(KERN_EMERG "myCDevInit(): cdev_init() and cdev_add() success!/nmyCDevInit() process success!/n");30     31     return 0;    //一切正常返回032     33 failMalloc:        //內(nèi)存分配不足時跳到這里34     unregister_chrdev_region(myDev,1);35     return res;36 }
模塊初始化函數(shù)
/*退出函數(shù),當模塊從內(nèi)存卸載時被調(diào)用*/static void myCDevExit(void)    //void my_exit(void);{    printk(KERN_EMERG "myCDevExit() process.../n");          cdev_del(&(pMyCharDev->myCDev));    //注銷設(shè)備    kfree(pMyCharDev);    //釋放設(shè)備結(jié)構(gòu)體內(nèi)存    unregister_chrdev_region(myDev,1);    //釋放設(shè)備號        printk(KERN_EMERG "myCDevExit() process success!/n");  }
模塊卸載時調(diào)用

具體在函數(shù)里面干啥了再說,看不懂沒關(guān)系。怎么告訴系統(tǒng)我的這模塊裝載和卸載時是調(diào)用這兩個呢?通過下面的宏:

1 module_init(myCDevInit);    //通過module_init例程把模塊入口點myCDevInit注冊到系統(tǒng)中2 module_exit(myCDevExit);    //由module_exit例程把模塊出口函數(shù)注冊到系統(tǒng)

這樣就告訴操作系統(tǒng)了,這兩個宏也是在某個源代碼文件定義的,我還沒找到……

  下面的一些宏也是告訴系統(tǒng)一些信息的:

1 /*下面是指定模塊版權(quán)、模塊作者、模塊簡要描述信息*/2 MODULE_LICENSE("GPL");3 MODULE_AUTHOR("jiayith");  4 MODULE_DESCRIPTION("jiayith->A simple virtual char device.");  

第3節(jié)、一些數(shù)據(jù)類型

  首先先不看初始化及卸載函數(shù)都干了啥,先看看定義了什么數(shù)據(jù)類型。看:

 1 /*下面是我的字符設(shè)備驅(qū)動程序的一些定義*/ 2 #ifndef MY_DATA_LEN 3 #define MY_DATA_LEN 8    //自定義設(shè)備描述符中數(shù)據(jù)的長度 4 #endif 5  6 /*自定義的設(shè)備描述結(jié)構(gòu)體*/ 7 struct MyCharDev 8 { 9     struct cdev myCDev;    //struct cdev在<linux/cdev.h>中定義,描述一個字符設(shè)備10     char myData[MY_DATA_LEN];    //以后對設(shè)備的讀寫啊是對這塊內(nèi)存的操作,因為這是一個虛擬的設(shè)備11 };12 13 /*下面是幾個全局變量*/14 struct MyCharDev * pMyCharDev;    //設(shè)備結(jié)構(gòu)體指針,因為是動態(tài)分配15 static dev_t myDev;    //裝獲得的設(shè)備號,因為在多個函數(shù)里面都用到

  我定義了一個結(jié)構(gòu)體struct MyCharDev,第9行struct cdev結(jié)構(gòu)體是內(nèi)核描述字符設(shè)備的,因為內(nèi)核基本用c寫得嘛,沒有類,就用結(jié)構(gòu)體了。第10行我把它與一個字符數(shù)組放一起了,表示這個設(shè)備的存儲數(shù)據(jù)的空間,第0節(jié)不是說了嘛,這是個虛擬的設(shè)備,就用一塊內(nèi)存放設(shè)備的數(shù)據(jù)。這里有一個隱藏的問題的,struct cdev跟設(shè)備驅(qū)動程序關(guān)聯(lián),一個設(shè)備驅(qū)動程序只需要一個struct cdev就可以了,如果有多個設(shè)備怎么辦呢?多個設(shè)備讀寫的空間不是一樣嘛?先不管這個了,目前階段就當作只有一個設(shè)備。

  dev_t,這個數(shù)據(jù)類型表示設(shè)備號的,是個typedef,typedef一個簡單類型。不是說有主次設(shè)備號嘛?怎么就一個變量就可以了?那是因為是按位操作的:32位機中是4個字節(jié),高12位表示主設(shè)備號,低20位表示次設(shè)備號。所以:

  MAJOR(dev_t dev) 這個宏可以獲得主設(shè)備號;

  MINOR(dev_t dev) 這個宏可以獲得次設(shè)備號;

  MKDEV(int major,int minor) 又給定的參數(shù)獲得一個dev_t的類型的設(shè)備號。

第4節(jié)、模塊裝載初始化函數(shù)干了啥:

 1 static int myCDevInit(void)    //int my_init(void); 2 { 3     int res;    //初始化函數(shù)的返回值 4  5     printk(KERN_EMERG "/n/n/nmyCDevInit() process.../n");       6              7     /*動態(tài)分配設(shè)備號*/ 8     res=alloc_chrdev_region(&myDev,0,1,"MyCharDev"); 9     if(res<0)    //表示分配設(shè)備號失敗10     {11         return res;12     }13     printk(KERN_EMERG "myCDevInit(): alloc_chrdev_region() success! major:%d,minor:%d/n", MAJOR(myDev), MINOR(myDev));    //打印獲得的主次設(shè)備號14     15     /*為設(shè)備描述結(jié)構(gòu)分配內(nèi)存*/16     pMyCharDev= kmalloc(sizeof(struct MyCharDev), GFP_KERNEL);  17     if(!pMyCharDev)18     {19         res=-ENOMEM;    //系統(tǒng)定義的內(nèi)存不足20         goto failMalloc;21     }    22     memset(pMyCharDev,0,sizeof(struct MyCharDev));23     printk(KERN_EMERG "myCDevInit(): kmalloc() success!/n");24     25     /*下面初始化及注冊字符設(shè)備到系統(tǒng)中*/26     cdev_init(&(pMyCharDev->myCDev),&myCDevOps);    //初始化struct cdev結(jié)構(gòu)27     cdev_add(&(pMyCharDev->myCDev),myDev,1);    //注冊字符設(shè)備28     printk(KERN_EMERG "myCDevInit(): cdev_init() and cdev_add() success!/nmyCDevInit() process success!/n");29     30     return 0;    //一切正常返回031     32 failMalloc:        //內(nèi)存分配不足時跳到這里33     unregister_chrdev_region(myDev,1);34     return res;35 }
模塊初始化函數(shù)

a:分配設(shè)備號

  int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name);

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name);     /** * alloc_chrdev_region() - register a range of char device numbers * @dev: output parameter for first assigned number * @baseminor: first of the requested range of minor numbers * @count: the number of minor numbers required * @name: the name of the associated device or driver * * Allocates a range of char device numbers.  The major number will be * chosen dynamically, and returned (along with the first minor number) * in @dev.  Returns zero or a negative error code. */
alloc_chrdev_region

這個函數(shù)就是動態(tài)分配設(shè)備號的,設(shè)備號結(jié)果就在第一個參數(shù),其它參數(shù)含義見上。

b:為設(shè)備描述結(jié)構(gòu)分配內(nèi)存

  我不是自己定義了struct MyCharDev嘛,其中的struct cdev要分配內(nèi)存存放,存儲區(qū)域也要分配內(nèi)存,kmallo()就是內(nèi)核態(tài)分配內(nèi)存的調(diào)用。

c:初始化及注冊字符設(shè)備到系統(tǒng)中

  給struct MyCharDev分配內(nèi)存了,也就是也給系統(tǒng)描述字符設(shè)備的struct cdev分配內(nèi)存了,但是光分配不行啊,空當當?shù)臎]數(shù)據(jù)啊,所以要初始化struct cdev,cdev_init()就是干這個事情,初始化字符設(shè)備是也綁定了對這個設(shè)備進行讀寫操作時內(nèi)核要調(diào)用什么函數(shù),以后對這個設(shè)備的操作就直接調(diào)用這些函數(shù)就可以了,這個結(jié)構(gòu)體里面的賦值右側(cè)是自己寫的函數(shù)。這就是告訴系統(tǒng)一個綁定嘛。

 1 /*自定義字符設(shè)備的操作函數(shù)的結(jié)構(gòu)體*/ 2 static const struct file_Operations myCDevOps =    3 { 4     .owner    = THIS_MODULE,   5     .llseek   = myCDevLlseek, 6     .read     = myCDevRead, 7     .write    = myCDevWrite, 8     .open     = myCDevOpen, 9     .release  = myCDevRelease10 };
字符設(shè)備的操作函數(shù)

  然后把這個字符設(shè)備添加到系統(tǒng):cdev_add()。函數(shù)具體使用請搜搜。

第5節(jié)、模塊卸載函數(shù)干了啥:

 1 /*退出函數(shù),當模塊從內(nèi)存卸載時被調(diào)用*/ 2 static void myCDevExit(void)    //void my_exit(void); 3 { 4     printk(KERN_EMERG "myCDevExit() process.../n");   5      6     cdev_del(&(pMyCharDev->myCDev));    //注銷設(shè)備 7     kfree(pMyCharDev);    //釋放設(shè)備結(jié)構(gòu)體內(nèi)存 8     unregister_chrdev_region(myDev,1);    //釋放設(shè)備號 9     10     printk(KERN_EMERG "myCDevExit() process success!/n");  11 }

其實就是與裝載時相反的事情啦,分配的內(nèi)存要回收吧?注冊了要注銷吧等。

第6節(jié)、其他的具體操作:

  上面說了我們在內(nèi)存開辟了一個地方代表這是虛擬的字符設(shè)備的,對這個字符設(shè)備的讀寫啊什么的都是針對這個內(nèi)存空間的,我的就是一個字符數(shù)組啦,所以write()、read()什么的就好寫了嘛,直接裝到這個數(shù)組、從這個數(shù)組讀不久可以了嘛?具體看代碼的注釋吧,很詳細了,聰明的你一定會懂。

  里面涉及到用戶態(tài)和內(nèi)核態(tài)的數(shù)據(jù)的交換,系統(tǒng)也給我們了,什么copy_from_user()之類的。

  還有那些函數(shù)的參數(shù),這個系統(tǒng)規(guī)定了。

第7節(jié)、全部代碼:

  1 /*包含我的電腦中已有的linux內(nèi)核源代碼,注意版本,我的路徑:/usr/src/linux-headers-3.13.0-30/include*/  2 #include <linux/cdev.h>  3 #include <linux/module.h>  4 #include <linux/types.h>  5 #include <linux/init.h>  6 #include <linux/errno.h>  7 #include <linux/mm.h>  8 #include <linux/sched.h>  9 #include <linux/init.h> 10 #include <linux/fs.h> 11 #include <linux/kdev_t.h> 12 #include <linux/slab.h> 13  14 /*下面是我的字符設(shè)備驅(qū)動程序的一些定義*/ 15 #ifndef MY_DATA_LEN 16 #define MY_DATA_LEN 8    //自定義設(shè)備描述符中數(shù)據(jù)的長度 17 #endif 18  19 /*自定義的設(shè)備描述結(jié)構(gòu)體*/ 20 struct MyCharDev 21 { 22     struct cdev myCDev;    //struct cdev在<linux/cdev.h>中定義,描述一個字符設(shè)備 23     char myData[MY_DATA_LEN];    //以后對設(shè)備的讀寫啊是對這塊內(nèi)存的操作,因為這是一個虛擬的設(shè)備 24 }; 25  26 /*下面是幾個全局變量*/ 27 struct MyCharDev * pMyCharDev;    //設(shè)備結(jié)構(gòu)體指針,因為是動態(tài)分配 28 static dev_t myDev;    //裝獲得的設(shè)備號,因為在多個函數(shù)里面都用到 29  30  31 /*文件打開函數(shù),無論一個進程何時試圖去打開這個設(shè)備都會調(diào)用這個函數(shù)*/ 32 int myCDevOpen(struct inode* inode, struct file* filp) 33 { 34 } 35  36 /*文件釋放函數(shù),當一個進程試圖關(guān)閉這個設(shè)備特殊文件的時候調(diào)用這個函數(shù)*/ 37 int myCDevRelease(struct inode* inode, struct file* filp) 38 { 39 } 40  41  42 /*讀函數(shù),當一個進程已經(jīng)打開次設(shè)備文件以后并且試圖去讀它的時候調(diào)用這個函數(shù)。從filp的ppos位置讀size個到用戶空間的buf中*/ 43 static ssize_t myCDevRead(struct file *filp, char __user *buf, size_t size, loff_t *ppos) 44 { 45     unsigned long pos =  *ppos;    /*記錄文件指針偏移位置*/   46     unsigned int count = size;    /*記錄需要讀取的字節(jié)數(shù)*/  47     int ret = 0;    /*返回值*/ 48     struct MyCharDev * pDev=filp->private_data;    //獲得這個文件對應(yīng)的相當于私有的數(shù)據(jù) 49      50     /*判斷讀位置是否有效*/ 51     if(pos>=MY_DATA_LEN)    //要讀取的偏移大于設(shè)備的內(nèi)存空間,也就是我自己定義的數(shù)組 52     { 53         return 0; 54     } 55     if(pos+count>MY_DATA_LEN)    //無法滿足讀取這么多個字節(jié) 56     { 57         count=MY_DATA_LEN-pos;    //盡量多地讀取 58     } 59      60     /*讀數(shù)據(jù)到用戶空間:內(nèi)核空間->用戶空間交換數(shù)據(jù)*/ 61     if(copy_to_user(buf,(void*)(pDev->myData+pos),count)) 62     { 63         ret= -EFAULT; 64     } 65     else 66     { 67         *ppos+=count;    //把記錄文件讀取的位置移動到正確的位置,注意,這里可以看出*ppos范圍是[0,MY_DATA_LEN] 68         ret=count;    //返回正確讀到的字符個數(shù) 69          70         printk(KERN_EMERG "myCDevRead():read %d byte(s) from %d position/n",count,pos);       71     } 72      73     return ret; 74 } 75  76  77 /*寫函數(shù),當試圖將數(shù)據(jù)寫入這個設(shè)備文件的時候,這個函數(shù)被調(diào)用。把用戶空間buf開始的size個寫入filp對應(yīng)的文件ppos位置*/ 78 static ssize_t myCDevWrite(struct file *filp, const char __user *buf, size_t size, loff_t *ppos) 79 { 80     unsigned long pos =  *ppos; 81     unsigned int count = size; 82     int ret = 0; 83     struct MyCharDev * pDev = filp->private_data; /*獲得設(shè)備結(jié)構(gòu)體指針*/ 84      85     /*分析和獲取有效的寫長度*/ 86     if(pos>=MY_DATA_LEN) 87     { 88         return 0; 89     } 90     if(pos+count>MY_DATA_LEN) 91     { 92         count=MY_DATA_LEN-pos; 93     } 94      95     /*從用戶空間寫入數(shù)據(jù)*/ 96     if(copy_from_user(pDev->myData+pos,buf,count)) 97     { 98         ret = -EFAULT; 99     }100     else101     {102         *ppos+=count;103         ret=count;104         105         printk(KERN_EMERG "myCDevWrite():write %d byte(s) from %d/n",count,pos);      106     }107     108     return ret;109 }110 111 /*seek文件定位函數(shù),為已經(jīng)打開的設(shè)備文件設(shè)置其偏移。把filp的偏移設(shè)置成從whence開始的加上offset*/112 static loff_t myCDevLlseek(struct file *filp, loff_t offset, int whence)113 {114 }115 116 117 /*自定義字符設(shè)備的操作函數(shù)的結(jié)構(gòu)體*/118 static const struct file_operations myCDevOps =   119 {120     .owner    = THIS_MODULE,  121     .llseek   = myCDevLlseek,122     .read     = myCDevRead,123     .write    = myCDevWrite,124     .open     = myCDevOpen,125     .release  = myCDevRelease126 };127 128 /*初始化函數(shù),當模塊裝載時被調(diào)用,如果裝載成功返回0,否則返回非0值*/129 static int myCDevInit(void)    //int my_init(void);130 {131     int res;    //初始化函數(shù)的返回值132 133     printk(KERN_EMERG "/n/n/nmyCDevInit() process.../n");      134             135     /*動態(tài)分配設(shè)備號*/136     res=alloc_chrdev_region(&myDev,0,1,"MyCharDev");137     if(res<0)    //表示分配設(shè)備號失敗138     {139         return res;140     }141     printk(KERN_EMERG "myCDevInit(): alloc_chrdev_region() success! major:%d,minor:%d/n", MAJOR(myDev), MINOR(myDev));    //打印獲得的主次設(shè)備號142     143     /*為設(shè)備描述結(jié)構(gòu)分配內(nèi)存*/144     pMyCharDev= kmalloc(sizeof(struct MyCharDev), GFP_KERNEL);  145     if(!pMyCharDev)146     {147         res=-ENOMEM;    //系統(tǒng)定義的內(nèi)存不足148         goto failMalloc;149     }    150     memset(pMyCharDev,0,sizeof(struct MyCharDev));151     printk(KERN_EMERG "myCDevInit(): kmalloc() success!/n");152     153     /*下面初始化及注冊字符設(shè)備到系統(tǒng)中*/154     cdev_init(&(pMyCharDev->myCDev),&myCDevOps);    //初始化struct cdev結(jié)構(gòu)155     cdev_add(&(pMyCharDev->myCDev),myDev,1);    //注冊字符設(shè)備156     printk(KERN_EMERG "myCDevInit(): cdev_init() and cdev_add() success!/nmyCDevInit() process success!/n");157     158     return 0;    //一切正常返回0159     160 failMalloc:        //內(nèi)存分配不足時跳到這里161     unregister_chrdev_region(myDev,1);162     return res;163 }164 165 /*退出函數(shù),當模塊從內(nèi)存卸載時被調(diào)用*/166 static void myCDevExit(void)    //void my_exit(void);167 {168     printk(KERN_EMERG "myCDevExit() process.../n");  169     170     cdev_del(&(pMyCharDev->myCDev));    //注銷設(shè)備171     kfree(pMyCharDev);    //釋放設(shè)備結(jié)構(gòu)體內(nèi)存172     unregister_chrdev_region(myDev,1);    //釋放設(shè)備號173     174     printk(KERN_EMERG "myCDevExit() process success!/n");  175 }176 177 178 module_init(myCDevInit);    //通過module_init例程把模塊入口點myCDevInit注冊到系統(tǒng)中179 module_exit(myCDevExit);    //由module_exit例程把模塊出口函數(shù)注冊到系統(tǒng)180 181 /*下面是指定模塊版權(quán)、模塊作者、模塊簡要描述信息*/182 MODULE_LICENSE("GPL");183 MODULE_AUTHOR("jiayith");  184 MODULE_DESCRIPTION("jiayith->A simple virtual char device.");  
源代碼
ifneq ($(KERNELRELEASE),)obj-m := myCDev.oelsePWD  := $(shell pwd)KVER := $(shell uname -r)KDIR := /lib/modules/$(KVER)/buildall:    $(MAKE) -C $(KDIR) M=$(PWD) modulesclean:    rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions modules.*  Module.*endif
makefile

第五章:啰嗦與參考資歷

  看完了如果要寫的話,就倒著看這個博客。相信我解釋清楚了。

  抄襲了:http://www.CUOXin.com/geneil/archive/2011/12/03/2272869.html

  抄襲了:http://blog.chinaunix.net/uid-254237-id-2458604.html

  抄襲了:http://blog.chinaunix.net/uid-11829250-id-337300.html

  抄襲了一大堆,希望大家不要介意哈。


發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 如东县| 岚皋县| 白山市| 古浪县| 泗水县| 海门市| 瓮安县| 和静县| 开原市| 阜新| 惠州市| 建湖县| 洞口县| 台东县| 京山县| 洛川县| 巴马| 和静县| 奉节县| 舞钢市| 舞阳县| 平顺县| 邯郸市| 肃北| 从江县| 清新县| 黄平县| 吉林省| 玉林市| 萍乡市| 赣州市| 玛曲县| 许昌县| 伽师县| 寻乌县| 溆浦县| 新田县| 大余县| 杭州市| 霍州市| 云阳县|