一、前言
在第三章中,我們已經討論瞭如何實現驅動程序的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和高級休眠的主要內容。主要是實現在讀取和寫入時,阻塞其中一個進程,防止數據競態,學習了休眠和喚醒方式。