免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
一步步設(shè)計自己的驅(qū)動程序
一步步設(shè)計自己的驅(qū)動程序

實驗?zāi)康模?br>通過一個簡單的設(shè)備驅(qū)動的實現(xiàn)過程。學(xué)會Linux中設(shè)備驅(qū)動程序的編寫


實驗內(nèi)容:
設(shè)計和實現(xiàn)一個虛擬命名管道(FIFO)的字符設(shè)備。寫一個模塊化的字符設(shè)備驅(qū)動程序


實驗提示:
一、設(shè)備的功能
    設(shè)計和實現(xiàn)一個虛擬命名管道(FIFO)的字符設(shè)備。我們知道,管道是進程間通信的一種
方式:一個進程向管道中寫數(shù)據(jù),另一個進程從管道中讀取數(shù)據(jù),先寫入的數(shù)據(jù)先讀出。我
們的驅(qū)動程序要實現(xiàn)N(N=4)個管道,每個管道對應(yīng)兩個設(shè)備,次設(shè)備號是偶數(shù)的設(shè)備是只
寫設(shè)備,次設(shè)備號是奇數(shù)的是只讀設(shè)備。寫入設(shè)備i(i是偶數(shù))的字符可以從設(shè)備i+1讀出。
這樣,我們一共就需要2N 個次設(shè)備號。
    我們的目標是寫一個模塊化的字符設(shè)備驅(qū)動程序。設(shè)備所使用的主設(shè)備號可以從尚未分
配的主設(shè)備號中任選一個,/Documentation/devices.txt 記錄了當(dāng)前版本內(nèi)核的主設(shè)備號分配
情況。如果設(shè)備文件系統(tǒng)(devfs)尚未激活,我們在加載模塊之后,還必須用mknod 命令創(chuàng)
建相應(yīng)的設(shè)備文件節(jié)點。
    如果FIFO 的寫入端尚未打開,F(xiàn)IFO 中就不會有數(shù)據(jù)可讀,所以此時試圖從FIFO 中讀
取數(shù)據(jù)的進程應(yīng)該返回一個錯誤碼。如果寫入端已經(jīng)打開,為了保證對臨界區(qū)的互斥訪問,
調(diào)用讀操作的進程必須被阻塞。如果存在被阻塞的讀者,在寫操作完成后(或者關(guān)閉一個寫
設(shè)備時)必須喚醒它。
    如果寫入的數(shù)據(jù)太多,超出了緩沖區(qū)中空閑塊的大小,調(diào)用寫操作的進程必須睡眠,以
等待緩沖區(qū)中有新的空閑塊。


二、設(shè)備的實現(xiàn)
1. 數(shù)據(jù)結(jié)構(gòu):
首先,我們要包含一些必要的頭文件、宏和全局變量。
vfifo.c
#ifndef __KERNEL__
#define __KERNEL__
#endif
#ifndef MODULE
#define MODULE
#endif
#define __NO_VERSION__
#include<linux/config.h>
#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/malloc.h>
#include<linux/fs.h>
#include<linux/proc_fs.h>
#include<linux/errno.h>
#include<linux/types.h>
#include<linux/fcntl.h>
#include<linux/init.h>
#include<asm/system.h>
#include<asm/uaccess.h>
#ifndef VFIFO_MAJOR
#define VFIFO_MAJOR 241
#endif
#ifndef VFIFO_NR_DEVS
#define VFIFO_NR_DEVS 4
#endif
#ifndef VFIFO_BUFFER
#define VFIFO_BUFFER 4000
#endif
#include<linux/devfs_fs_kernel.h>
devfs_handle_t vfifo_devfs_dir;
struct file_operations vfifo_fops;
int vfifo_major=VFIFO_MAJOR;
int vfifo_nr_devs=VFIFO_NR_DEVS;
int vfifo_buffer=VFIFO_BUFFER;
MODULE_PARM(vfifo_major,"i");
MODULE_PARM(vfifo_nr_devs,"i");
MODULE_PARM(vfifo_buffer,"i");
MODULE_AUTHOR("EBUDDY");


每個實際的FIFO 設(shè)備都對應(yīng)于一個Vfifo_Dev{ }結(jié)構(gòu)體。其中,rdq 是阻塞讀的等待
隊列,wrq 是阻塞寫的等待隊列,base 是所分配緩沖區(qū)的起始地址,buffersize 是緩沖區(qū)的
大小,len表示管道中已有數(shù)據(jù)塊的長度,start 表示當(dāng)前應(yīng)該讀取的緩沖區(qū)位置相對于base
的偏移量,即緩沖區(qū)起始數(shù)據(jù)的偏移量,readers和writers分別表示VFIFO 設(shè)備當(dāng)前的讀者
個數(shù)和寫者個數(shù),sem是用于互斥訪問的信號量,r_handle和w_handle用于保存設(shè)備文件系
統(tǒng)的注冊句柄,r_handle對應(yīng)的是只讀設(shè)備,w_handle對應(yīng)的是同一管道的只寫設(shè)備。具體
的定義如下所示:
vfifo.c
typedef struct Vfifo_Dev{
    wait_queue_head rdq,wrq;
    char* base;
    unsigned int buffersize;
    unsigned int len;
    unsigned int start;
    unsigned int readers,writers;
    struct semaphore sem;
    devfs_handle_t r_handle,w_handle;
}Vfifo_Dev;


2.設(shè)備操作接口
(1).注冊與注銷
    注冊時,我們必須考慮到兩種管理方式(傳統(tǒng)方式與devfs方式)的兼容性。在這里,
我們用條件編譯來解決這個問題。由于許多主設(shè)備號已經(jīng)靜態(tài)地分配給了公用設(shè)備,Linux
提供了動態(tài)分配機制以獲取空閑的主設(shè)備號。傳統(tǒng)方式下,如果調(diào)用devfs_register_chrdev( )
時的major 為零的話,它所調(diào)用的register_chrdev( )函數(shù)就會選擇一個空閑號碼作為返回值
返回。主設(shè)備號總是正的,因此不會和錯誤碼混淆。在devfs方式下,如果devfs_register( )
的flags 參數(shù)值為DEVFS_FL_AUTO_DEVNUM,注冊時就會自動生成設(shè)備號。
    動態(tài)分配的缺點是:由于分配的主設(shè)備號不能保證總是一樣的,無法事先創(chuàng)建設(shè)備節(jié)點。
但是這并不是什么問題,因為一旦分配了設(shè)備號,我們就可以從/proc/devices 讀到。為了加
載一個設(shè)備驅(qū)動程序,我們可以用一個簡單的腳本替換對insmod的調(diào)用,它通過/proc/devices
獲得新分配的主設(shè)備號,并創(chuàng)建節(jié)點。加載動態(tài)分配主設(shè)備號驅(qū)動程序的腳本可以利用awk
這類工具從/proc/devices 中獲取信息,并在/dev中創(chuàng)建文件。在我們的實例程序中,為了簡
單起見,仍然使用靜態(tài)分配的主設(shè)備號。
    你也許會注意到我們并沒有使用統(tǒng)一的函數(shù)名init_module( )和cleanup_module( ),這是
由于內(nèi)核編程風(fēng)格的變化。自從2.3.13 版的內(nèi)核以來,Linux提供了兩個宏module_init( )和
module_exit( )來顯式地命名模塊的注冊和注銷函數(shù)。通常在源文件的末尾寫上這兩個宏,例
如:
module_init(vfifo_init_module);
module_exit(vfifo_exit_module);
注意,在使用這兩個宏之前必須先包含頭文件<linux/init.h>。這樣做的好處是,內(nèi)核中
的每一個注冊和注銷函數(shù)都有一個唯一的名字,有助于調(diào)試。我們知道驅(qū)動程序既可以設(shè)計
成模塊,又可以靜態(tài)地編譯進內(nèi)核,用了這兩個宏后就能更方便地支持這兩種方式。實際上,
對于模塊來說,它們所做的工作僅僅是把給出的函數(shù)名重新命名為 init_module( )和
cleanup_module( )。當(dāng)然,如果你已使用了init_module( )和cleanup_module( )作為函數(shù)名,
那就沒必要再使用這兩個宏了。
    在函數(shù)名之前,我們可以看到一個表示屬性的詞“__init”,加了這個屬性之后,系統(tǒng)
會在初始化完成之后丟棄初始化函數(shù),收回它所占用的內(nèi)存。這樣可以減小內(nèi)核所占用的內(nèi)
存空間。但它只對內(nèi)建的驅(qū)動程序有用,對于模塊則沒有影響。

vfifo.c
char vfifoname[8];
static int __init vfifo_init_module(void)
{
    int result,i;
    SET_MODULE_OWNER(&vfifo_fops);
    #ifdef CONFIG_DEVFS_FS
        vfifo_devfs_dir=devfs_mk_dir(NULL,"vfifo",NULL);
        if(!vfifo_devfs_dir)
        return -EBUSY;
    #endif
    result=devfs_register_chrdev(vfifo_major,"vfifo",&vfifo_fops);
    if(result<0){
        printk(KERN_WARNING "vfifo: can‘t get major %d",vfifo_major);
        return result;
    }
    if(vfifo_major==0)
    vfifo_major=result;
    vfifo_devices = kmalloc(vfifo_nr_devs*sizeof(Vfifo_Dev),GFP_KERNEL);
    if(!vfifo_devices){
        return -ENOMEM;
    }
    memset(vfifo_devices,0,vfifo_nr_devs*sizeof(Vfifo_Dev));
    for(i=0;i<vfifo_nr_devs;i++){
        init_waitqueue_head(&vfifo_devices[i].rdq);
        init_waitqueue_head(&vfifo_devices[i].wrq);
        sema_init(&vfifo_devices[i].sem,1);
        #ifdef CONFIG_DEVFS_FS
            sprintf(vfifoname,"vfifo%d",2*i);
            vfifo_devices[i].w_handle=
            devfs_register(vfifo_devfs_dir,vfifoname,
            DEVFS_FL_NON,
            vfifo_major,2*i,S_IFCHR|S_IRUGO|S_IWUGO,
            &vfifo_fops,vfifo_device+i);
            sprintf(vfifoname,"vfifo%d",2*i+1);
            vfifo_devices[i].r_handle=
            devfs_register(vfifo_devfs_dir,vfifoname,
            DEVFS_FL_NON,
            vfifo_major,2*i+1,S_IFCHR|S_IRUGO|S_IWUGO,
            &vfifo_fops,vfifo_device+i);
            if(!vfifo_devices[i].r_handle||!vfifo_devices[i].w_handle){
               printk(KERN_WARNING "vfifo: can‘t register vfifo device nr %i\n",i);
            }
        #endif
    }
    #ifdef VFIFO_DEBUG
        create_proc_read_entry("vfifo",0,NULL,vfifo_read_mem,NULL);
    #endif
    return 0;

     注銷的工作相對簡單。需要注意的是在卸載驅(qū)動程序之后要刪除設(shè)備節(jié)點。如果設(shè)備節(jié)
點是在加載時創(chuàng)建的,可以寫一個簡單的腳本在卸載時刪除它們。如果動態(tài)節(jié)點沒有從/dev
中刪除,就可能造成不可預(yù)期的錯誤:系統(tǒng)可能會給另一個設(shè)備分配相同的主設(shè)備號,這樣
在打開設(shè)備時就會出錯。
       我們可以看到在函數(shù)名前標有屬性“__exit”,它的作用類似于“__init”,即使內(nèi)建的
驅(qū)動程序忽略它所標記的函數(shù)。同樣的,它對模塊也沒有影響。
vfifo.c
static void __exit vfifo_cleanup_module(void)
{
    int i;
    devfs_unregister_chrdev(vfifo_major,"vfifo");
    #ifdef VFIFO_DEBUG
        remove_proc_entry("vfifo",NULL);
    #endif
    if(vfifo_devices){
        for(i=0;i<vfifo_nr_devs;i++){
            if(vfifo_devices[i].base)
                kfree(vfifo_devices[i].base);
               devfs_unregister(vfifo_devices[i].r_handle);
               devfs_unregister(vfifo_devices[i].w_handle);
            }
            kfree(vfifo_devices);
            devfs_unregister(vfifo_devfs_dir);
        }
    }
}


(2). 打開與釋放
打開設(shè)備主要是完成一些初始化工作,以及增加引用計數(shù),防止模塊在設(shè)備關(guān)閉前被注
銷。我們知道內(nèi)核用主設(shè)備號區(qū)分不同類型的設(shè)備,而驅(qū)動程序用次設(shè)備號識別具體的設(shè)備。
利用這一特性,我們可以用不同的方式打開同一個設(shè)備。
vfifo.c
static int vfifo_open(struct inode *inode,struct file *filp)
{
       Vfifo_Dev *dev;
       int num=MINOR(inode->i_rdev);
       /*檢查讀寫權(quán)限是否合法 */
      if((flip->f_mode&FMODE_READ)&&!(num%2)||(filp->f_mode&FMODE_WRITE)&&(num%2))
              return -EPERM;
       if(!filp->private_data){
              if(num>=vfifo_nr_devs*2)
                    return -ENODEV;
              dev=&vfifo_nr_devices[num/2];
              filp->private_data=dev;
       }
       else{
              dev=filp->private_data;
       }
       /*獲得互斥訪問的信號量 */
       if(down_interruptible(&dev->sem))
              return -ERESTARTSYS;
              /*如果尚未分配緩沖區(qū),則分配并初始化 */
       if(!dev->base){
              dev->base=kmalloc(vfifo_buffer,GFP_KERNEL);
              if(!dev->base){
                    up(&dev->sem);
                    return -ENOMEN;
              }
              dev->buffersize=vfifo_buffer;
              dev->len=dev->start=0;
       }
       if(filp->mode&FMODE_READ)
       dev->readers++;
       if(filp->mode&FMODE_WRITE)
       dev->writers++;
       filp->private_data=dev;
       MOD_INC_USE_COUNT;
       return 0;
}

釋放(或關(guān)閉)設(shè)備就是打開設(shè)備的逆過程。
vfifo.c
static int vfifo_release(struct inode *inode,struct file *filp)
{
       Vfifo_Dev *dev=filp->private_data;
       /*獲得互斥訪問的信號量 */
       down(&dev->sem);
       if(filp->f_mode&FMODE_READ)
       dev->readers--;
       if(filp->f_mode&FMODE_WRITE){
              dev->writes--;
              wake_up_interruptible(&dev->sem);
       }
       if((dev->readers+dev->writers==0)&&(dev->len==0)){
              kfree(dev->base);
              dev->base=NULL;
       }
       up(&dev->sem);
       MOD_DEC_USE_COUNT;
       return 0;
}

讀寫的操作:3
讀寫設(shè)備也就意味著要在內(nèi)核地址空間和用戶地址空間之間傳輸數(shù)據(jù)。由于指針只能在
當(dāng)前地址空間操作,而驅(qū)動程序運行在內(nèi)核空間,數(shù)據(jù)緩沖區(qū)則在用戶空間,跨空間復(fù)制就
不能通過通常的方法,如利用指針或通過memcpy來完成。在Linux中,跨空間復(fù)制是通過
定義在里的特殊函數(shù)實現(xiàn)的。你既可以用通用的復(fù)制函數(shù),也可以用針對不
同數(shù)據(jù)大小(char,short,int,long)進行了優(yōu)化的復(fù)制函數(shù)。為了能傳輸任意字節(jié)的數(shù)據(jù),
你可以用copy_to_user( )和copy_from_user( )兩個函數(shù)。
盡管上面的兩個函數(shù)看起來很像正常的memcpy函數(shù),但是當(dāng)你在內(nèi)核代碼中訪問用戶
空間時必須額外注意一些問題:正在被訪問的用戶頁面現(xiàn)在可能不在內(nèi)存中,而且缺頁處理
函數(shù)有可能在傳輸頁面的時候讓進程進入睡眠狀態(tài)。例如,當(dāng)必須從交換區(qū)讀取頁面時就會
發(fā)生這種情況。驅(qū)動程序編寫者在設(shè)計時必須注意,任何訪問用戶空間的函數(shù)都必須是可重
入的,而且能夠與驅(qū)動程序內(nèi)的其它函數(shù)并發(fā)執(zhí)行。這就是我們用信號量來控制并發(fā)訪問的
原因。
上述這兩個函數(shù)的作用并不局限于傳輸數(shù)據(jù),它們也可以檢查用戶空間的指針是否有
效。如果指針無效,復(fù)制不會進行;如果在復(fù)制過程中遇到了無效地址,則只復(fù)制部分數(shù)據(jù)。
在這兩種情況下,函數(shù)的返回值都是尚未復(fù)制數(shù)據(jù)的字節(jié)數(shù)。如果你不需要檢查用戶空間指
針的有效性,你可以直接調(diào)用__copy_to_user( )和__copy_from_user( )。例如,你已經(jīng)知道參
數(shù)是有效的,這樣做就可以提高效率。
就實際的設(shè)備操作而言,讀的任務(wù)是把數(shù)據(jù)從設(shè)備復(fù)制到用戶空間(用copy_to_user( )),
而寫操作則必須把數(shù)據(jù)從用戶空間復(fù)制到設(shè)備(用copy_from_user( ))。每一個read或write
系統(tǒng)調(diào)用都會要求傳輸一定字節(jié)數(shù)的數(shù)據(jù),但驅(qū)動程序可以隨意傳輸其中一部分數(shù)據(jù)。
如果有錯誤發(fā)生,read和write都會返回一個負值。一個大于等于零的返回值會告訴調(diào)
用程序成功傳輸了多少字節(jié)的數(shù)據(jù)。如果某個數(shù)據(jù)成功地傳輸了,隨后發(fā)生了錯誤,返回值
必須是成功傳輸?shù)淖止?jié)數(shù),只有到下次函數(shù)被調(diào)用時才會報告錯誤。
雖然內(nèi)核函數(shù)返回一個負值標識錯誤,該數(shù)的數(shù)值表示已發(fā)生的錯誤種類,但是運行在
用戶空間的程序只能看到錯誤返回值-1。只有訪問變量errno,程序才能知道發(fā)生了什么錯
誤。這兩方面的不同行為,一方面是靠系統(tǒng)調(diào)用的POSIX 調(diào)用標準強加的,另一方面是內(nèi)
核不處理errno的優(yōu)點導(dǎo)致的。
具體的read代碼如下:

vfifo.c
static ssize_t vfifo_read(struct file *filp,char *buf,size_t count,loff_t *f_pos)
{
Vfifo_Dev *dev=filp->private_data;
ssize_t read=0;
/*不允許進行定位操作 */
if(f_pos!=&filp->f_pos)
return -ESPIPE;
/*獲得互斥訪問的信號量 */
if(down_interruptible(&dev->sem))
return -ERESTARTSYS;
do_more_read:
/*沒有數(shù)據(jù)可讀,則進入循環(huán)等待 */
while(dev->len==0){
if(!dev->writers){
up(&dev->sem);
return -EAGAIN;
}
up(&dev->sem);
if(filp->f_flags&O_NONBLOCK)
return -EAGAIN;
printk("%s reading:going to sleep\n",current->comm);
if(wait_event_interruptible(dev->rdq,(dev->len>0)))
return -ERESTARTSYS;
printk("%s has been waken up\n",current->comm);
if(down_interruptible(&dev->sem))
return -ERESTARTSYS;
}

/*讀數(shù)據(jù) */
while(count>0&&dev->len){
char *pipebuf=dev->base+dev->start;
/*(buffersize – start)是可以一次性讀取的最大數(shù)據(jù)量 */
ssize_t chars=dev->buffersize-dev->start;
if(chars>count) chars=count;
if(chars>dev->len) chars=dev->len;
if(copy_to_user(buf,pipebuf,chars)){
up(&dev->sem);
return -EFAULT;
}
read+=chars;
dev->start+=chars;
dev->start%=dev->buffersize;
dev->len-=chars;
count-=chars;
buf+=chars;
}
/*Cache behavior optimizition*/
if(!dev->len) dev->start=0;
if(count&&dev->writers&&!(filp->flags&O_NONBLOCK)){
up(&dev->sem);
wake_up_interruptible(&dev->wrq);
if(down_interruptible(&dev->sem))
return -ERESTARTSYS;
goto do_more_read;
}
up(&dev->sem);
wake_up_interruptible(&dev->wrq);
printk("%s did read %li bytes\n",current->comm, (long)read);
return read;
}


具體的write代碼如下:
vfifo.c
static ssize_t vfifo_write(struct file *filp,const char *buf,size_t count,loff_t *f_pos)
{
Vfifo_Dev *dev=filp->private_data;
ssize_t written=0;
/*不允許進行定位操作 */
if(f_pos!=&filp->f-pos||count==0)
return -ESPIPE;
/*獲得互斥訪問的信號量 */
if(down_interruptible(&dev->sem))
return -ERESTARTSYS;
do_more_write:
/*緩沖區(qū)已滿,則循環(huán)等待 */
while(dev->len==dev->buffersize){
up(&dev->sem);
if(filp->f_flags&O_NONBLOCK)
return -EAGAIN;
printk("%s writting:going to sleep\n",current->comm);
if(wait_event_interruptible(dev->wrq,(dev->lenbuffersize)))
return -ERESTARTSYS;
printk("%s has been waken up\n",current->comm);
if(down_interruptible(&dev->sem))
return -ERESTARTSYS;
}


/*寫數(shù)據(jù) */
while(count>0){
char *pipebuf=dev->base+(dev->len+dev->start)%dev->buffersize;
/*下面兩行計算可以一次性寫入的最大數(shù)據(jù)量 */
ssize_t chars=dev->buffersize-(dev->len+dev->start);
if(chars<0) chars+=dev->start;
if(chars!=0){
if(chars>count) chars=count;
if(copy_from_user(buf,pipebuf,chars)){
up(&dev->sem);
return -EFAULT;
}
written+=chars;
dev->len+=chars;
count-=chars;
buf+=chars;
}
}
if(count&&!(filp->f_flags&O_NONBLOCK)){
up(&dev->sem);
wake_up_interruptible(&dev->rdq);
if(down_interruptible(&dev->sem))
return -ERESTARTSYS;
goto do_more_write;
}
up(&dev->sem);
wake_up_interruptible(&dev->rdq);
printk("%s did write %li bytes\n",current->comm, (long)written);
return written;
}
 

poll方法.4
使用非阻塞型I/O 的應(yīng)用程序經(jīng)常要用到poll 和select 系統(tǒng)調(diào)用。poll 和select 本質(zhì)上
具有相同的功能:它們都允許一個進程決定它是否能無阻塞地從一個或多個打開的文件中讀
數(shù)據(jù),或者向這些文件中寫數(shù)據(jù)。這兩個系統(tǒng)調(diào)用還可用來實現(xiàn)在無阻塞情況下的不同源輸
入的多路復(fù)用。同樣的功能為什么要由兩個不同的函數(shù)提供呢?這是因為它們幾乎是在同一
時間由兩個不同的團體引入Unix 系統(tǒng)中的:BSD Unix引入了select,System V引入了poll。
在Linux 2.0 版本的內(nèi)核中只支持select,從2.1.23 版本的內(nèi)核開始,系統(tǒng)提供了對兩種調(diào)用
的支持。我們的驅(qū)動程序是基于poll系統(tǒng)調(diào)用,因為poll提供了比select更詳細的支持。

poll的實現(xiàn)可以執(zhí)行poll和select兩種系統(tǒng)調(diào)用,它的原型如下:
unsigned int (*poll) (struct file *,poll_table * )

驅(qū)動程序中的poll主要完成兩個任務(wù):

l。 在一個可能在將來喚醒它的等待隊列中將當(dāng)前進程排隊。通常,這意味著同時在輸
入和輸出隊列中對進程排隊。函數(shù)poll_wait( )就用于這個目的,其工作方式說
select_wait( )非常類似。

2。 構(gòu)造一個位掩碼描述設(shè)備的狀態(tài),并將其返回給調(diào)用者。這個位掩碼描述了能立即
被無阻塞執(zhí)行的操作。
這兩個操作通常是很簡單的,在每個驅(qū)動程序中的實現(xiàn)都非常相似。然而,它們依賴于
一些只有驅(qū)動程序才能提供的信息,因此必須在每個驅(qū)動程序中分別實現(xiàn)。


poll_table 結(jié)構(gòu)是在中聲明的,要使用poll 調(diào)用,你必須在源程序中包含
這個頭文件。需要提醒的是,你無需了解它的內(nèi)部結(jié)構(gòu),你只要調(diào)用操作該結(jié)構(gòu)的函數(shù)就行
了。當(dāng)然,如果你想了解的話,你可以自己去看源代碼。

poll部分標志位的列表如下:
POLLIN 如果設(shè)備可以被無阻塞地讀,那么該位必須被設(shè)置。

POLLRDNORM 如果“普通”數(shù)據(jù)可以被讀,該位必須被設(shè)置。一個可讀設(shè)備返回
(POLLIN | POLLRDNORM)。

POLLOUT 如果設(shè)備可以被無阻塞地寫,則該位在返回值中被設(shè)置。
POLLWRNORM 該位與POLLOUT,有時甚至的確為同一個數(shù)。一個可寫的設(shè)備返回
(POLLOUT | POLLWRNORM)。

具體的poll實現(xiàn)代碼如下:
vfifo.c
unsigned int vfifo_poll(struct file *filp, poll_table *wait)
{
Vfifo_Dev *dev = filp->private_data;
unsigned int mask = 0;
poll_wait(filp, &dev->rdq, wait);
poll_wait(filp, &dev->wrq, wait);
if (dev->len > 0) mask |= POLLIN | POLLRDNORM; /* readable */
if (dev->len != dev->buffersize) mask |= POLLOUT | POLLWRNORM; /* writable */
return mask;
}


三、設(shè)備的安裝
采用下面的命令可以對vfifo.c進行編譯:
#gcc –c vfifo.c –D__KERNEL__ -DMODULE –O2 –g -Wall

如果沒有出錯的話,將會在本目錄下生成一個vfifo.o 文件。
下面的操作必須是以root身份進行的:

先執(zhí)行module的插入操作,
#insmod vfifo.o
如果設(shè)備文件系統(tǒng)已經(jīng)應(yīng)用起來的話,此時在設(shè)備文件系統(tǒng)掛接的目錄(通常是/dev)
下,就可以找到vfifo 文件節(jié)點了。如果沒有應(yīng)用設(shè)備文件系統(tǒng),則需要手工為設(shè)備添加文
件節(jié)點。首先進入dev目錄,再執(zhí)行如下命令:
[root

Linux /dev]#mknod vfifo c 241 0
[root
Linux /dev]#mknod vfifo c 241 1
……
[root
Linux /dev]#mknod vfifo c 241 7
此時就可以對設(shè)備進行讀、寫、ioctl等操作了。

當(dāng)不再需要對設(shè)備進行操作時,可以采用下面的命令卸載module:
[root
Linux /dev]# rmmod vfifo

四、設(shè)備的使用

設(shè)備安裝好之后就可以使用了。你可以用cp、dd等命令以及輸入/輸出重定向機制來測
試這個驅(qū)動程序。為了更清晰地了解程序是如何運行的,你可以在適當(dāng)?shù)奈恢眉尤雙rintk( ),
通過它來跟蹤程序。另外,你還可以用專門的調(diào)試工具如strace來監(jiān)視程序使用的系統(tǒng)調(diào)用。
例如,你可以這樣來寫vfifo設(shè)備:
#strace ls /dev/vfifo* > /dev/vfifo0
#strace cat /dev/vfifo1
到此為止,我們已經(jīng)完成了對Linux設(shè)備驅(qū)動的分析,并且自己設(shè)計了一個與具
體設(shè)備無關(guān)的特殊設(shè)備的驅(qū)動程序。但還有一些我們并沒有涉及到的內(nèi)容,如ioctl、I/O 端
口等,如有興趣可以自己去深入鉆研。
(驅(qū)動程序開發(fā))



本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
在驅(qū)動模塊初始化函數(shù)中實現(xiàn)設(shè)備節(jié)點的自動創(chuàng)建(華清遠見旗下品牌)
驅(qū)動程序用到的頭文件linux/include/linux/init.h(基于linux ...
linux2.6移植+根文件系統(tǒng)實驗手冊
基于優(yōu)龍FS2410開發(fā)板Linux 2.6.14.1 kernel的移植(轉(zhuǎn)載)
devfs_mk_cdev 與devfs_register引用 ARM嵌入式LINUX設(shè)備驅(qū)動設(shè)計入 - 嵌入式驅(qū)動開發(fā) - wuyunzdh
linux內(nèi)核初始化以及啟動
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服