linux設備驅動程序-第六章2(阻塞IO和高級休眠)學習總結

一、前言
在第三章中,我們已經討論瞭如何實現驅動程序的read和write方法。現在談論另一種重要問題:如何驅動程序無法立即滿足要求,該如何響應?調用程序通常不會關心此類問題,程序員只會簡單調用read和write,然後等待必要的工作結束後返回調用,因此,在這種情況下,我們驅動程序應該(默認)阻塞該進程,將其置入休眠狀態直至請求可繼續。
我麼介紹過的函數可以滿足許多驅動程序的休眠請求。但是在某些情況下,我們需要Linux的等待隊列機制有更深的理解。複雜的鎖定以及性能需求會強制驅動程序用底層的函數實現休眠。
二、阻塞IO和高級休眠
1、休眠的簡單介紹
當一個進程被置入休眠時,它會被標記爲一種特殊狀態並從調度器的運行隊列中移走。休眠中的進程會擱置在一邊,等待將來的某個事件發生。
對linux設備驅動程序來講,讓一個進程進入休眠狀態很容易,需牢記兩個原則:
(1)永遠不要在原子上下文中進入休眠。
(2)當我們被喚醒時,我們永遠無法知道休眠了多長時間,或者休眠期間都發生了什麼事情,因此必須檢查以確保我們等待的條件真正爲真。
2、簡單休眠
linux內核中最簡單的休眠方式爲wait_event的宏,在實現休眠的同時,它也檢查休眠條件。如下:

wait_event(queue, condition)
wait_event_interruptible(queue, condition)
wait_event_timeout(queue, condition, timeout)
wait_event_interruptible_timeout(queue, condition, timeout)

在 Linux 中, 一個等待隊列由一個wait_queue_head_t 結構體來管理,其定義在中:


struct __wait_queue_head {
    spinlock_t lock;
    struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;

喚醒休眠:


void wake_up(wait_queue_head_t *queue);
void wake_up_interruptible(wait_queue_head_t *queue);//驅動中大部分情況使用這個

3、阻塞和非阻塞操作
顯示的非阻塞IO由filp->f_flags的O_NONBLOCK標誌決定。這個標誌在默認時要被清除,因爲等待數據的進程一般是休眠。
只有read、open和write文件操作受非阻塞標誌的影響。
4、高級休眠
(1)手動休眠

/* ①創建並初始化一個等待隊列。通常由宏定義完成:*/
DEFINE_WAIT(my_wait);
/*其中name 是等待隊列入口項的名字. 也可以用下面兩個步驟來做:*/
wait_queue_t my_wait;
init_wait(&my_wait);
/*在實現休眠的循環前放置 DEFINE_WAIT 通常更容易實現*/

/* ②添加等待隊列入口到隊列,並設置進程狀態:*/
void prepare_to_wait(wait_queue_head_t *queue, wait_queue_t *wait, int state); 
/*其中,queue 和 wait 分別地是等待隊列頭和進程入口。state 是進程的新狀態:TASK_INTERRUPTIBLE(可中斷休眠,推薦)或TASK_UNINTERRUPTIBLE(不可中斷休眠,不推薦)。*/

/* ③在調用pre_pare_to_wait之後,若進程仍有必要等待,則調用 schedule*/

schedule();

/* ④一旦schedule 返回,就到了清理時間*/
void finish_wait(wait_queue_head_t *queue, wait_queue_t *wait);
/*⑤代碼測試其狀態,並判斷是否需要重新等待*/

(2)獨佔等待
當一個進程調用 wake_up 在等待隊列上,所有的在這個隊列上等待的進程被置爲可運行的。 這在許多情況下是正確的做法。但有時,可能只有一個被喚醒的進程將成功獲得需要的資源,而其餘的將再次休眠。這時如果等待隊列中的進程數目大,這可能嚴重降低系統性能。爲此,內核開發者增加了一個“獨佔等待”選項。它與一個正常的睡眠有 2 個重要的不同:

①當等待隊列入口設置了 WQ_FLAG_EXCLUSEVE 標誌,它被添加到等待隊列的尾部;否則,添加到頭部。

②當 wake_up 被在一個等待隊列上調用, 它在喚醒第一個有 WQ_FLAG_EXCLUSIVE 標誌的進程後停止喚醒.但內核仍然每次喚醒所有的非獨佔等待。
執行獨佔等待的進程每次只會被喚醒其中一個。但是,內核每次仍然會喚醒所有非獨佔等待進程。

void prepare_to_wait_exclusive(wait_queue_head_t *queue, wait_queue_t *wait, int state);

三、實例
scull_pipe.h

#ifndef _SCULL_H_
#define _SCULL_H_
#include <linux/ioctl.h>

#define DEBUG

//#undef PDEBUG
#ifdef DEBUG
//#  ifdef __KERNEL__
#     define PDEBUG(fmt,args...) printk(KERN_DEBUG "scull:" fmt,## args)
//#  else
//#     define PDEBUG(fmt,args...) fprintf(stderr,fmt,## args)
//#  endif
#else
#  define PDEBUG(fmt,args...)   /*no printf*/
#endif

#ifndef SCULL_MAJOR
#define SCULL_MAJOR 0
#endif

#ifndef SCULL_P_NR_DEVS
#define SCULL_P_NR_DEVS 4
#endif

#ifndef SCULL_P_BUFFER
#define SCULL_P_BUFFER 4000
#endif

#define TYPE(minor)  ((minor)>>4 && 0xff)
#define NUM(minor)   ((minor) & 0xf)

extern int scull_major;   /*from main.c*/

extern int scull_p_buffer; /*for pipe.c*/

int scull_p_init(dev_t dev);
void scull_p_cleanup(void);
int scull_access_init(dev_t dev);
void scull_access_cleanup(void);

 /*  * Ioctl definitions
 *   */

/* Use 'k' as magic number */
#define SCULL_IOC_MAGIC  'k'
/* Please use a different 8-bit number in your code */

#define SCULL_IOCRESET    _IO(SCULL_IOC_MAGIC, 0)

/*
 *  * S means "Set" through a ptr,
 *  * T means "Tell" directly with the argument value
 *  * G means "Get": reply by setting through a pointer
 *  * Q means "Query": response is on the return value
 *  * X means "eXchange": switch G and S atomically
 *  * H means "sHift": switch T and Q atomically
 *  */
#define SCULL_IOCSQUANTUM _IOW(SCULL_IOC_MAGIC,  1, int)
#define SCULL_IOCSQSET    _IOW(SCULL_IOC_MAGIC,  2, int)
#define SCULL_IOCTQUANTUM _IO(SCULL_IOC_MAGIC,   3)
#define SCULL_IOCTQSET    _IO(SCULL_IOC_MAGIC,   4)
#define SCULL_IOCGQUANTUM _IOR(SCULL_IOC_MAGIC,  5, int)
#define SCULL_IOCGQSET    _IOR(SCULL_IOC_MAGIC,  6, int)
#define SCULL_IOCQQUANTUM _IO(SCULL_IOC_MAGIC,   7)
#define SCULL_IOCQQSET    _IO(SCULL_IOC_MAGIC,   8)
#define SCULL_IOCXQUANTUM _IOWR(SCULL_IOC_MAGIC, 9, int)
#define SCULL_IOCXQSET    _IOWR(SCULL_IOC_MAGIC,10, int)
#define SCULL_IOCHQUANTUM _IO(SCULL_IOC_MAGIC,  11)
#define SCULL_IOCHQSET    _IO(SCULL_IOC_MAGIC,  12)

/*
 *  * The other entities only have "Tell" and "Query", because they're
 *   * not printed in the book, and there's no need to have all six.
 *    * (The previous stuff was only there to show different ways to do it.
 *     */
#define SCULL_P_IOCTSIZE _IO(SCULL_IOC_MAGIC,   13)
#define SCULL_P_IOCQSIZE _IO(SCULL_IOC_MAGIC,   14)
/* ... more to come */

#define SCULL_IOC_MAXNR 14

#endif

scull_pipe.c

//#include <linux/config.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/module.h>

#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <linux/fcntl.h>
#include <linux/poll.h>
#include <linux/seq_file.h>
#include <linux/cdev.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/signal.h>

#include <asm/system.h>
#include <asm/uaccess.h>

#include <linux/device.h>

#include "scull_pipe.h"

struct scull_pipe {
    wait_queue_head_t inq,outq;//等待隊列
    char *buffer,*end;//緩衝起始區
    int buffersize;//總長度
    char *rp,*wp;//讀寫指針地址
    int nreaders,nwriters;//讀寫者個數
    struct fasync_struct *async_queue;
    struct semaphore sem;
    struct cdev cdev;
    int data;
};

/*parameter*/
int scull_major = SCULL_MAJOR;
int scull_minor = 0;
static int scull_p_nr_devs = SCULL_P_NR_DEVS;
int scull_p_buffer = SCULL_P_BUFFER;
struct class *scull_class;
dev_t scull_p_devno;

module_param(scull_p_nr_devs,int,S_IRUGO);
module_param(scull_p_buffer,int,S_IRUGO);

struct scull_pipe *scull_p_devices;

static int scull_p_fasync(int fd,struct file *filp,int mode);
static int spacefree(struct scull_pipe *dev);


/*若設備以讀寫方式打開,它的長度截零,未鎖定信號量*/
int scull_p_open(struct inode *inode,struct file *filep)
{
    struct scull_pipe *dev;
    dev = container_of(inode->i_cdev,struct scull_pipe,cdev);
    filep->private_data = dev;

    if(down_interruptible(&dev->sem))
        return -ERESTARTSYS;
//    if(!dev->buffer){
//        /*allocate the buffer*/
//        dev->buffer = kmalloc(scull_p_buffer,GFP_KERNEL);
//        if(!dev->buffer){
//            up(&dev->sem);
//            return -ENOMEM;
//        }
//    }
//    dev->buffersize = scull_p_buffer;
//    dev->end = dev->buffer + dev->buffersize;
//    dev->rp = dev->wp = dev->buffer;/*rd and wr from beginning*/

    /*use f_mode,not f_flags:it's cleaner*/
    if(filep->f_mode & FMODE_READ)
        dev->nreaders++;
    if(filep->f_mode & FMODE_WRITE)
        dev->nwriters++;
    up(&dev->sem);
    return 0;
}/*open*/

int scull_p_release(struct inode *inode,struct file *filep)
{
    struct scull_pipe *dev = filep->private_data;

    scull_p_fasync(-1,filep,0);
    down(&dev->sem);
    if(filep->f_mode & FMODE_READ)
        dev->nreaders--;
    if(filep->f_mode & FMODE_WRITE)
        dev->nwriters--;
   //if(dev->nreaders + dev->nwriters == 0){
   //     kfree(dev->buffer);
   //     dev->buffer = NULL;
   //}
    up(&dev->sem);
    return 0;
}/*close*/

ssize_t scull_p_read(struct file *filep,char __user *buf,size_t count,loff_t *f_pos)
{
    struct scull_pipe *dev = filep->private_data;
    
    if(down_interruptible(&dev->sem))  //信號量
        return -ERESTARTSYS;
    
    while(dev->rp == dev->wp){ /*nothing to read*/
        up(&dev->sem);                 //釋放信號量
        if(filep->f_flags & O_NONBLOCK)
            return -EAGAIN;
        PDEBUG("\"%s\" reading:going to sleep\n",current->comm);/*當前進程執行的程序名*/
        if(wait_event_interruptible(dev->inq,(dev->rp != dev->wp)))
            return -ERESTARTSYS;/*非零,表示信號被打斷。等待讀取信號,同時通知fs層做相應處理*/
        if(down_interruptible(&dev->sem))
            return -ERESTARTSYS;/*否則循環,但首先獲取鎖*/
    }
    /*ok,data is these,return something*/
    if(dev->wp>dev->rp)               //確認需要寫入的大小
        count = min(count,(size_t)(dev->wp-dev->rp));
    else/*寫入指針回捲,返回數據dev->end*/
        count = min(count,(size_t)(dev->end-dev->rp));
    if(copy_to_user(buf,dev->rp,count)){
        up(&dev->sem);
        return -EFAULT;
    }
    dev->rp += count;
    if(dev->rp == dev->end)
        dev->rp = dev->buffer;/*restart*/
    up(&dev->sem);

    /*finally,sawark any write and return*/
    wake_up_interruptible(&dev->outq);  //喚醒寫入
    PDEBUG( "\"%s\"Dubug scull_read length is %ld\n",current->comm,(long)count);
    return count;
}

/*wait for space for writing;caller must hold device semaphore.
 * on error the semaphore will be released before returning*/
static int scull_getwritespace(struct scull_pipe *dev,struct file *filep)
{
    //等待可用於寫入的空間,調用者必須擁有設備信號量
    while(spacefree(dev) == 0){/*full空的*/
        //如果緩衝區有可用的空間,則不進入while循環,直接還回0
        DEFINE_WAIT(wait);//初始化等待隊列入口

        up(&dev->sem);//休眠前,必須釋放信號量
        if(filep->f_flags & O_NONBLOCK)
            return -EAGAIN;
        PDEBUG("\"%s\"writing:going to sleep\n",current->comm);
        prepare_to_wait(&dev->outq,&wait,TASK_INTERRUPTIBLE);//將等待隊列加入,不賒賬進程狀態
        if(spacefree(dev)==0)//重新判斷是否有可用空間,若還是沒有,則調用schedule,讓出CPU,進入休眠
            schedule();//????爲什麼需要進程調度,唯一的喚醒機會,避免休眠
        finish_wait(&dev->outq,&wait);//一旦schedule退出,則清理等待隊裏
        if(signal_pending(current))
            return -ERESTARTSYS;/*信號,告訴fs做相應處理*/
        if(down_interruptible(&dev->sem))
            return -ERESTARTSYS;
    }
    return 0;
}

/*有多少空間可釋放,有多少可用空間*/
static int spacefree(struct scull_pipe *dev)
{
    if(dev->rp == dev->wp)//空間最大
        return dev->buffersize-1;
    return ((dev->rp+dev->buffersize-dev->wp)%dev->buffersize)-1;
}

ssize_t scull_p_write(struct file *filep,const char __user *buf,size_t count,loff_t *f_pos)
{
    struct scull_pipe *dev = filep->private_data;
    int result;

    if(down_interruptible(&dev->sem))
        return -ERESTARTSYS;

    /*make sure there'sspace to write*/
    result = scull_getwritespace(dev,filep);
    if(result)
        return result;

    /*ok,space is there accept something*/
    count = min(count,(size_t)spacefree(dev));
    if(dev->wp >= dev->rp)
        count = min(count,(size_t)dev->end-(size_t)dev->wp);
    else
        count = min(count,(size_t)(dev->rp-dev->wp-1));//取能寫入最小值
    PDEBUG("Going to accept %li bytes to %p from %p\n",(long)count,dev->wp,buf);
    if(copy_from_user(dev->wp,buf,count)){
        up(&dev->sem);//讀取失敗
        return -EFAULT;
    }
    dev->wp+=count;
    if(dev->wp == dev->end)
        dev->wp = dev->buffer;
    up(&dev->sem);

    /*完成,喚醒讀取*/
    wake_up_interruptible(&dev->inq);

    /*異步讀寫信號*/
    if(dev->async_queue)
        kill_fasync(&dev->async_queue,SIGIO,POLL_IN);
    PDEBUG("\"%s\"did write %ld bytes\n",current->comm,(long)count);
    return count; 
}/*end result*/

/*scull_llseekn法用來修改文件的當前可讀位置,並將新位置作爲返回值返回*/
loff_t scull_p_llseek(struct file *filep,loff_t off,int whence)
{
    struct scull_pipe *dev = filep->private_data;
    loff_t newpos;

    switch(whence){
        case 0:  /*seek_set*/
            newpos = off;
            break;

        case 1:  /*seek_cur*/
            newpos = filep->f_pos + off;
            break;

        case 2:  /*seek_end*/
            newpos = dev->buffersize + off;
            break;

        default: /*no default*/
            return -EINVAL;
    }

    if(newpos < 0) return -EINVAL;
    filep->f_pos = newpos;
    return newpos;
}

long scull_p_ioctl(struct file *filep,unsigned int cmd,unsigned long arg)
{
    int err=0,retval=0;
    int temp;
    struct scull_pipe *dev = filep->private_data;

    //判斷命令幻數是否匹配
    if(_IOC_TYPE(cmd) != SCULL_IOC_MAGIC)
        return -ENOTTY;
    //判斷命令序號是否非法
    if(_IOC_NR(cmd) > SCULL_IOC_MAXNR)
        return -ENOTTY;
    //判斷空間是否可訪問
    if(_IOC_DIR(cmd) & _IOC_READ)
        err = !access_ok(VERIFY_WRITE,(void *)arg,_IOC_SIZE(cmd));
    if(_IOC_DIR(cmd) & _IOC_WRITE)
        err = !access_ok(VERIFY_READ,(void *)arg,_IOC_SIZE(cmd));
    if(err)
        return -EFAULT;

    switch(cmd){
        case SCULL_IOCRESET://數據清零
            dev->data = 0;
            PDEBUG("\"%s\"SCULL_IOCRESET\n",current->comm);
            break;
        case SCULL_IOCSQUANTUM://指針賦值
            if(!capable(CAP_SYS_ADMIN))
                return -EPERM;
            retval=__get_user(dev->data,(int __user *)arg);
            PDEBUG("\"%s\"SCULL_IOCSQUANTUM\n",current->comm);
            break;
        case SCULL_IOCTQUANTUM://數值賦值
            if(!capable(CAP_SYS_ADMIN))
                return -EPERM;
            dev->data=arg;
            PDEBUG("\"%s\"SCULL_IOCTQUANTUM\n",current->comm);
            break;
        case SCULL_IOCGQUANTUM://讀取指針
            retval=__put_user(dev->data,(int __user *)arg);
            PDEBUG("\"%s\"SCULL_IOCGQUANTUM\n",current->comm);
            break;
        case SCULL_IOCQQUANTUM://讀取數值
            PDEBUG("\"%s\"SCULL_IOCQQUANTUM\n",current->comm);
            return dev->data;
        case SCULL_IOCXQUANTUM://交換“s”和“g”
            if(!capable(CAP_SYS_ADMIN))
                return -EPERM;
            temp = dev->data;
            retval = __get_user(dev->data,(int __user *)arg);
            if(0 == retval)//返回成功
                retval = __put_user(temp,(int __user *)arg);
            PDEBUG("\"%s\"SCULL_IOCXQUANTUM\n",current->comm);
            return retval;
            case SCULL_IOCHQUANTUM://交換"t"和“q”
            if(!capable(CAP_SYS_ADMIN))
                return -EPERM;
            temp = dev->data;
            dev->data = arg;
            arg = temp;
            PDEBUG("\"%s\"SCULL_IOCHQUANTUM\n",current->comm);
            return temp;
            default://其餘暫時去除
            PDEBUG("\"%s\"SCULL_DEFAULT\n",current->comm);
            return -ENOTTY;
    }
        return retval;
}


static unsigned int scull_p_poll(struct file *filep,poll_table *wait)
{
    struct scull_pipe *dev = filep->private_data;
    unsigned int mask = 0;

    /*
     *The buffer is circular;its is considered full
     *if "wp" is right behind "rp" and empty if the
     *two are equal
     * */
    down(&dev->sem);
    poll_wait(filep,&dev->inq,wait);
    poll_wait(filep,&dev->outq,wait);
    if(dev->rp != dev->wp)
        mask |= POLLIN | POLLRDNORM;/*讀取使能*/
    if(spacefree(dev))
        mask |= POLLIN | POLLWRNORM;/*寫入使能*/
    up(&dev->sem);
    return mask;
}

static int scull_p_fasync(int fd,struct file *filep,int mode)
{
    struct scull_pipe *dev = filep->private_data;

    return fasync_helper(fd,filep,mode,&dev->async_queue);
}

//scull設備的文件操作
struct file_operations scull_pipe_fops = {
    .owner = THIS_MODULE,
    .llseek = scull_p_llseek,
    .read = scull_p_read,
    .write = scull_p_write,
    .unlocked_ioctl = scull_p_ioctl,
    .open = scull_p_open,
    .release = scull_p_release,
    .fasync = scull_p_fasync,
};

void scull_p_cleanup_module(void)
{
    int i;
    dev_t devno = MKDEV(scull_major,scull_minor);

    if(scull_p_devices){
        for(i=0;i<scull_p_nr_devs;i++)
        {
            cdev_del(&scull_p_devices[i].cdev);
        }
        kfree(scull_p_devices);
    }
    device_destroy(scull_class,MKDEV(scull_major,0));
    class_destroy(scull_class);
    unregister_chrdev_region(devno,scull_p_nr_devs);
    scull_p_devices = NULL;
}

static void scull_p_setup_cdev(struct scull_pipe *dev,int index)
{
    int err,devno = MKDEV(scull_major,scull_minor+index);

    /*從open函數移到此處,實現文件本身有數據時,打開能讀取數據*/
    if(!dev->buffer){
        /*allocate the buffer*/
        dev->buffer = kmalloc(scull_p_buffer,GFP_KERNEL);
        if(!dev->buffer){
            up(&dev->sem);
            return -1;//-ENOMEM;
        }
    }
    dev->buffersize = scull_p_buffer;
    dev->end = dev->buffer + dev->buffersize;
    dev->rp = dev->wp = dev->buffer;/*rd and wr from beginning*/
    
    cdev_init(&dev->cdev,&scull_pipe_fops);
    dev->cdev.owner = THIS_MODULE;
    dev->cdev.ops = &scull_pipe_fops;
    err = cdev_add(&dev->cdev,devno,1);

    if(err)
        printk(KERN_EMERG "Error %d addingscull %d!\n",err,index);
}

int scull_p_init_module(void)
{
    int result,i;
    dev_t dev = 0;

    printk(KERN_EMERG "Debug is scull_init module!\n");
    //獲取主設備數號
    if(scull_major){
        dev = MKDEV(scull_major,scull_minor);
        result = register_chrdev_region(dev,scull_p_nr_devs,"scullp");
    }else{
        result = alloc_chrdev_region(&dev,scull_minor,scull_p_nr_devs,"scullp");
        scull_major = MAJOR(dev);
    }
    if(result < 0){
        printk(KERN_EMERG "scull:can't get major %d/n",scull_major);

        return result;
    }

    scull_p_devices = kmalloc(scull_p_nr_devs * sizeof(struct scull_pipe),GFP_KERNEL);
    if(!scull_p_devices){
        result = -ENOMEM;
        goto fail;
    }
    memset(scull_p_devices,0,scull_p_nr_devs * sizeof(struct scull_pipe));
    printk(KERN_EMERG "the length of scull_dev is %ld\n",sizeof(struct scull_pipe));

    for(i=0;i<scull_p_nr_devs;i++){
        init_waitqueue_head(&(scull_p_devices[i].inq));
        init_waitqueue_head(&(scull_p_devices[i].outq));
        init_MUTEX(&scull_p_devices[i].sem);
        //用於udev/mdev自動創建節點
        scull_p_setup_cdev(&scull_p_devices[i],i);
       }

    scull_class = class_create(THIS_MODULE,"scull_pipe");
    device_create(scull_class,NULL,dev,NULL,"scull_pipe");
    dev=MKDEV(scull_major,scull_minor+scull_p_nr_devs);
    return 0;

fail:
    scull_p_cleanup_module();
    return result;
}

module_init(scull_p_init_module);
module_exit(scull_p_cleanup_module);

MODULE_LICENSE("GPL");

Makefile

# Comment/uncomment the following line to disable/enable debugging
DEBUG = y


# Add your debugging flag (or not) to CFLAGS
ifeq ($(DEBUG),y)
  DEBFLAGS = -O -g -DEBUG # "-O" is needed to expand inlines
else
  DEBFLAGS = -O2
endif

#CFLAGS += $(DEBFLAGS)
#CFLAGS += -I..

ifneq ($(KERNELRELEASE),)
# call from kernel build system
  obj-m := scull_pipe.o
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD       := $(shell pwd)
default:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif

clean:
	rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions

#depend .depend dep:
#	$(CC) $(CFLAGS) -M *.c > .depend


#ifeq (.depend,$(wildcard .depend))
#include .depend
#endif

四、實驗步驟
1、首先先在主目錄下make,編譯模塊,生成.ko文件。
出現如下:

# make
make -C /lib/modules/2.6.35.3-571-gcca29a0/build M=/root/BLOCK modules
make[1]: 正在進入目錄 `/usr/src/linux-2.6.35.3'
  CC [M]  /root/BLOCK/scull_pipe.o
/root/BLOCK/scull_pipe.c: 在函數‘scull_p_setup_cdev’中:
/root/Device_Driver/Cdev_Driver/Ldd-6/BLOCK/scull_pipe.c:377:13: 警告: 在無返回值的函數中,‘return’帶返回值 [默認啓用]
/root/Device_Driver/Cdev_Driver/Ldd-6/BLOCK/scull_pipe.c: 在文件作用域:
/home/fengweilong/桌面/Linux_Pro/Device_Driver/Cdev_Driver/Ldd-6/BLOCK/scull_pipe.c:310:21: 警告: ‘scull_p_poll’定義後未使用 [-Wunused-function]
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /root/Cdev_Driver/Ldd-6/BLOCK/scull_pipe.mod.o
  LD [M]  /root/Cdev_Driver/Ldd-6/BLOCK/scull_pipe.ko
make[1]:正在離開目錄 `/usr/src/linux-2.6.35.3'

2、編譯模塊到內核,裝載設備

# insmod scull_pipe.ko

3、參考第三章操作,使用兩個界面來驗證驅動是否可用
界面1(發送)

# echo "hello" > /dev/scull_pipe
# echo "the end" > /dev/scull_pipe
# echo "Good" > /dev/scull_pipe

界面2(顯示)

# cat /dev/scull_pipe
hello
the end
Good

五、總結
以上就是《linux設備驅動程序》第六章第二部分——阻塞IO和高級休眠的主要內容。主要是實現在讀取和寫入時,阻塞其中一個進程,防止數據競態,學習了休眠和喚醒方式。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章