赞
踩
linux在内核层面实现对外设的访问控制共有五种I/O模型,分别如下:
1、阻塞 : 当某进程在执行设备操作时,若不能获得资源,则挂起进程直到满足可操作的条件后再进行操作。
2、非阻塞 : 当进程不能进行设备操作时,并不挂 起,而由开发者决定是放弃还是不停轮询,直到可以进行操作为止。
3、多路复用 :I/O多路复用就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。如select,poll,epoll 。
4、信号驱动的异步通知:在信号驱动IO模型中,当用户线程发起一个IO请求操作,会给对应的socket注册一个信号函数,然后用户线程会继续执行,当内核数据就绪时会发送一个信号给用户线程,用户线程接收到信号之后,便在信号函数中调用IO读写操作来进行实际的IO请求操作。
5、异步I/O :异步I/O,和信号驱动有点类似,也是发出I/O请求后就不再等待或阻塞,可以去完成其它工作。不同的是,内核去负责该请求工作,并在内核完成数据的访问,只把结果提交到请求者。而信号驱动还需要由请求者自已完成I/O工作。
由于内容较多,将会分几个篇幅完成这五种I/O的解读。
阻塞是指在某进程在执行设备操作时,若不能获得资源,则挂起进程直到满足可操作的条件后再进行操作。被挂起的进程进入睡眠状态,被系统从调度器的运行队列移到等待队列,直到等待的条件被满足。
而非阻塞操作的进程在不能进行设备操作时,并不挂起,由开发者决定是放弃还是不停查询,直至可以进行操作为止。
因此,在内核层面,驱动通常要提供这样的能力:当应用程序进行read() write()等系统调用时,若设备的资源不能获取,而用户又希望以阻塞的方式访问设备,驱动程序应在设备驱动 xxx_read()、xxx_write()等操作中将进程阻塞直到资源可以获取,此后,应用程序的read()、write()等调用才返回,整个过程仍然进行了正确的设备访问,用户并没有感知到;
若用户以非阻塞的方式访问设备文件,则当设备资源不可获取时,设备驱动的xxx_read()、xxx_write()等操作应立即返回,read()、write()等系统调用也随即被返回,应用程序收到-EAGAIN返回值。
从应用层的角度看。当recvfrom发出后,不能及时得到数据,则进程实际是进入阻塞,也即进入了睡眠态,这时进程是不占用cpu的时间片的。
非阻塞:不能操作就返回错误。应用层应根据出错提示而采用不断的轮询来询问内核是否有数据了。非阻塞方式使调用程序不断被调度,占用了CPU的时间片
在linux内核中,链表是一个非常重要的数据结构。与通常做法中的链表不同,Linux内核方式与众不同,它不是将数据结构塞入链表,而是将链表节点塞入数据结构。这样链表就可以链接在任意的数据结构上。
linux链表的基本结构为一个有着双各指针的链表节点数据结构list_head,其定义如下:
struct list_head{
struct list_head *prev,*next;
}
该节点结构可以被与入任意的数据结构内,当成该数据结构的一个元素,如下:
struct data{
int a;
struct spinlock lock;
char *c;
struct list_head head;
};
list_head节点之间前后串联,形成一个数据链表,如示意图,示意中只出现了单向循环。
这样,当取得一个list_head的地址后,就可以通过container_of()宏而取得整个数据结构的地址。具体是通过list_entry()宏取得的。
#define list_entry( ptr , type , member) container_of(ptr , type , member)
以上简单介绍完了linux内核链表机制,而linux内核的等待队列就是链表机制的一个应用。
linux五种状态(运行态、浅睡眠态、深睡眠态、暂停态、僵死态)的调度,靠的是链表队列实现在,具体从运行态到睡眠态的调度示意如下:
linux 通过结构体task_struct维护所有运行的线程,进程。 不同状态的任务,会由不同的队列进行维护, schedule()函数就负责根据这些状态的变化调度这些任务。
因此,需要阻塞的任务或进程,需要创建一个等待队列,并调用内核函数把这个等待队列放入系统调度的等待队列池内,再调用schedule()函数使任务或进程进入睡眠态。具体步骤如下:
头文件
include/linux/wait.h
include/linux/sched.h
原码定义:
struct __wait_queue_head {
spinlock_t lock;
struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;
定义变量例:
wait_queue_head_t my_queue;
头文件:
include/linux/wait.h
include/iinux/sched.h
原码定义:
#include <include/linux/wait.h>
#define init_waitqueue_head(q) \
do { \
static struct lock_class_key __key; \
__init_waitqueue_head((q), #q, &__key); \
} while (0)
定义文件 kernel/sched/wait.c
void __init_waitqueue_head(wait_queue_head_t *q, const char *name, struct lock_class_key *key)
{
spin_lock_init(&q->lock); //初始化等待队列头的自旋锁
lockdep_set_class_and_name(&q->lock, key, name);
INIT_LIST_HEAD(&q->task_list); //初始化列表头,使列表头指针指向了自已
}
使用:
init_waitqueue_head(&my_queue)
宏DECLARE_WAIT_QUEUE_HEAD(name)可以同时完成wait_queue_head_t等待队列头的创建以及初始化。因此会更便捷一些。其中的参数name为等待队列头变量名。
参考原码如下:
#define __WAIT_QUEUE_HEAD_INITIALIZER(name) { \
.lock = __SPIN_LOCK_UNLOCKED(name.lock), \
.task_list = { &(name).task_list, &(name).task_list } }
#define DECLARE_WAIT_QUEUE_HEAD(name) \
wait_queue_head_t name = __WAIT_QUEUE_HEAD_INITIALIZER(name)
头文件:
#include <linux/wait.h>
wait_queue_t数据结构参考原码:
typedef int (*wait_queue_func_t)(wait_queue_t *wait, unsigned mode, int flags, void *key); int default_wake_function(wait_queue_t *wait, unsigned mode, int flags, void *key); struct __wait_queue { unsigned int flags; #define WQ_FLAG_EXCLUSIVE 0x01 void *private; wait_queue_func_t func; struct list_head task_list; }; typedef struct __wait_queue wait_queue_t; flags: 可以设置为 WQ_FLAG_EXCLUSIVE,表示等待的进程应该独占资源(解决惊群现象)。 private: 一般用于保存等待进程的进程描述符 task_struct。 func: 唤醒函数,一般设置为 default_wake_function() 函数,当然也可以设置为自定义的唤醒函数。 task_list: 用于连接其他等待资源的进程。
DECLARE_WAITQUEUE原码参考:
#define DECLARE_WAITQUEUE(name, tsk) \
wait_queue_t name = __WAITQUEUE_INITIALIZER(name, tsk)
#define __WAITQUEUE_INITIALIZER(name, tsk) { \
.private = tsk, \
.func = default_wake_function, \
.task_list = { NULL, NULL }
}
其中,
name:是要生成的wait_queue_t 等待队列元素项的变量名。
tsk:等待进程的进程描述符 task_struct。
使用:
参考原码:
定义文件 kernel/sched/wait.c void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait) { unsigned long flags; wait->flags &= ~WQ_FLAG_EXCLUSIVE; spin_lock_irqsave(&q->lock, flags); __add_wait_queue(q, wait); spin_unlock_irqrestore(&q->lock, flags); } EXPORT_SYMBOL(add_wait_queue); #include <linux/wait.h> static inline void __add_wait_queue(wait_queue_head_t *head, wait_queue_t *new) { list_add(&new->task_list, &head->task_list); }
void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)
{
unsigned long flags;
spin_lock_irqsave(&q->lock, flags);
__remove_wait_queue(q, wait);
spin_unlock_irqrestore(&q->lock, flags);
}
EXPORT_SYMBOL(remove_wait_queue);
#include <linux/wait.h
static inline void __remove_wait_queue(wait_queue_head_t *head, wait_queue_t *old)
{
list_del(&old->task_list);
}
头文件
include/linux/wait.h
include/linux/sched.h
wait_event(wq,condition)
//宏 ,把当前进程加入等待队列头,并使当前进程处于深度睡眠
wait_event_interruptible(wq,condition)
//宏,把当前进程加入等待队列头,并使当前进程处于浅度睡眠
还有其它宏:
wait_event_timeout(wq , condition , timeout);
wait_event_interruptible_timeout(wq , condition , timeout);
功能:
参数:
返回值:
举个例子:
wait_queue_head_t wq; // 定义等待队列
// 进程A
wait_event(wq, condition); // 等待condition成立
// 进程B
condtion = true; // 改变condition
wake_up(&wq); // 唤醒等待队列wq中的进程
进程A调用wait_event()进入睡眠,加入等待队列wq。
进程B设置condition为真,然后调用wake_up()唤醒wq中的进程。
这时,进程A被唤醒,wait_event()返回。
所以wait_event()通过等待队列实现了进程间的同步和通信。
头文件
include/linux/wait.h
include/linux/sched.h
wake_up(wait_queue_head_t *pwq) 唤醒深度睡眠
另外,wake_up()有两个变种:
wake_up_all(wait_queue_head_t *wq);wake_up_all(wait_queue_head_t *wq);函数会继续唤醒wq等待队列上的进程,直到队列为空为止,所以它会唤醒全部进程。
wake_up 它的功能是:
知道了内核层面的阻塞与非阻塞的概念和实现的函数。但从应用层角度来看,如何设定操作的阻塞与非阻塞?
应用层: open时由O_NONBLOCK指示read、write时是否阻塞
open()系统调用用于打开一个文件,它的原型是:
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
flags参数中与阻塞相关的选项主要有:
应此如果不加O_NONBLOCK标志,则open后,read与write等操作是处于允许阻塞状态的,因此对应的驱动程序要对应有阻塞的代码
同时,应用层open的flag标志是通过前几章说到的struct file 结构体传递给了驱动的。
wait_event()和add_wait_queue()都是用于进程间同步和通信的,但用法有区别:
wait_event():
// wait_event()
wait_queue_head_t wq;
// 进程A
wait_event(wq, condition);
// 进程B
condition = true;
wake_up(&wq);
// add_wait_queue()
// 进程A
add_wait_queue(&wq, &wait);
while (!condition) {
set_current_srtate(TASK_INTERRUPTIBLE);
schedule(); // 睡眠
}
// 唤醒后,condition为真,继续执行
// 进程B
condition = true;
wake_up(&wq);
所以:
/include/linux/sched.h
#define __set_current_state(state_value) \
do { current->state = (state_value); } while (0)
#define set_current_state(state_value) \
set_mb(current->state, (state_value))
这两个宏的区别
__set_current_state()和set_current_state()都是用于设置当前进程的状态,但有一定区别:
__set_current_state():
还是之前的以内存缓冲虚拟设备来实现字符驱动对设备的读写等操作。如果遇到缓冲满或空,则驱动的写与读应处于阻塞状态。使对应
采用wait_event
/************************************************************************* > File Name:block-memory.c 驱动程序根据应用层的flag标志决定是否采用阻塞或非阻塞的工作方式 ************************************************************************/ #include <linux/kernel.h> #include <linux/module.h> #include <linux/cdev.h> #include <linux/fs.h> #include <linux/slab.h> #include <linux/uaccess.h> #include <linux/wait.h> /*1、定义重要的变量及结构体*/ #define MEM_SIZE 500 //每个虚拟设备内存大小 #define DEV_NUM 3 //创建的设备总个数 struct mem_dev_t{ struct cdev my_dev; //cdev设备描述结构体变量 char mem[MEM_SIZE]; //fifo内存池,当成虚拟设备 int curpos; //内存当前数据最后位置指示,从0开始记 struct semaphore sem; //信号量 /********************************************************/ wait_queue_head_t write_queue; //写等待队列 wait_queue_head_t read_queue; //读等待队列 /********************************************************/ }; struct mem_dev_t *mem_dev; /*所有驱动函数声明*/ ssize_t read (struct file *, char __user *, size_t, loff_t *); ssize_t write (struct file *, const char __user *, size_t, loff_t *); int open (struct inode *, struct file *); int release (struct inode *, struct file *); //驱动操作函数结构体,成员函数为需要实现的设备操作函数指针 //简单版的模版里,只写了open与release两个操作函数。 struct file_operations fops={ .open = open, .release = release, .read = read, .write = write, }; /*3、初始化 cdev结构体,并将cdev结构体与file_operations结构体关联起来*/ /*这样在内核中就有了设备描述的结构体cdev,以及设备操作函数的调用集合file_operations结构体*/ static int cdev_setup(struct mem_dev_t *mem_dev , dev_t devno ){ int unsucc =0; cdev_init(&mem_dev->my_dev , &fops); mem_dev->my_dev.owner = THIS_MODULE; /*4、注册cdev结构体到内核链表中*/ unsucc = cdev_add(&mem_dev->my_dev , devno , 1); if (unsucc){ printk("driver : cdev add faild \n"); return -1; } sema_init( &mem_dev->sem,1); //初始化信号量,为1 mem_dev->curpos = 0; //初始化缓冲数据位置为0 /********************************************************/ init_waitqueue_head(&mem_dev->write_queue); init_waitqueue_head(&mem_dev->read_queue); /********************************************************/ return 0; } static int __init my_init(void){ int major , minor; dev_t devno; int unsucc =0; int i=0; mem_dev = kzalloc(sizeof(struct mem_dev_t)*DEV_NUM , GFP_KERNEL); if (!mem_dev){ printk(" driver : allocating memory is failed"); return -1; } /*2、创建 devno */ unsucc = alloc_chrdev_region(&devno , 0 , DEV_NUM , "block_memory"); if (unsucc){ printk(" driver : creating devno is failed\n"); return -1; }else{ major = MAJOR(devno); minor = MINOR(devno); printk("diver : major = %d ; minor = %d\n",major,minor); } /*3、 初始化cdev结构体,并联cdev结构体与file_operations.*/ /*4、注册cdev结构体到内核链表中*/ for (i=0;i<DEV_NUM;i++){ devno = MKDEV(major , i); if (cdev_setup(mem_dev+i , devno) == 0){ printk("deiver : the driver block_memory[%d] initalization completed\n", i); } else printk("deiver : the driver block_memory[%d] initalization failed\n", i); } return 0; } static void __exit my_exit(void) { int i=0; dev_t devno; devno = mem_dev->my_dev.dev; for (i=0 ; i<DEV_NUM ; i++){ cdev_del(&(mem_dev+i)->my_dev); } unregister_chrdev_region(devno , DEV_NUM); printk("***************the driver operate_memory exit************\n"); } /*5、驱动函数的实现*/ /*file_operations结构全成员函数.open的具体实现*/ int open(struct inode *pnode , struct file *pf){ int minor = MINOR(pnode->i_rdev); int major = MAJOR(pnode->i_rdev); struct mem_dev_t *p = container_of(pnode->i_cdev , struct mem_dev_t , my_dev); pf->private_data = p; //把全局变量指针放入到struct file结构体里 if (pf->f_flags & O_NONBLOCK){ //非阻塞 printk("driver : block_memory[%d , %d] is opened by nonblock mode\n",major , minor); }else{ printk("driver : block_memory[%d , %d] is opened by block mode\n",major,minor); } return 0; } /*file_operations结构全成员函数.release的具体实现*/ int release(struct inode *pnode , struct file *pf){ printk("block_memory is closed \n"); return 0; } /*file_operations结构全成员函数.read的具体实现*/ ssize_t read (struct file * pf, char __user * buf, size_t size , loff_t * ppos){ //本例中,因为是fifo,所以ppos参数不用。 struct mem_dev_t *pdev = pf->private_data; int count = 0; //存储读到多少数据 down(&pdev->sem); if (pdev->curpos == 0){ up(&pdev->sem); //退出前释放信号量,V操作 if ((pf->f_flags & O_NONBLOCK) == 0){ /********************************************************/ wait_event_interruptible(pdev->read_queue , pdev->curpos > 0); /********************************************************/ //当前没有数据,进入阻塞睡眠 }else{ return 0; } }else{ up(&pdev->sem); } down(&pdev->sem); //信号量P操作 //判断能够读到的字节数量 if (size > pdev->curpos){ count = pdev->curpos; }else{ count = size; } //copy_from_user返回值大于0失败 if ( copy_to_user(buf , &pdev->mem , count )){ //读取失败 up(&pdev->sem); //退出前释放信号量,V操作 return 0; }else{ //成功读取 memcpy(&pdev->mem , &pdev->mem[count] , pdev->curpos-count); pdev->curpos -= count; up(&pdev->sem); //退出前释放信号量,V操作 /********************************************************/ wake_up_interruptible(&pdev->write_queue); //唤醒可能睡眠的write /********************************************************/ return count; } } /*file_operations结构全成员函数.write的具体实现*/ ssize_t write (struct file * pf, const char __user *buf, size_t size , loff_t *ppos){ struct mem_dev_t *pdev = pf -> private_data; int count =0; down(&pdev->sem); if (pdev->curpos == (MEM_SIZE -1)){ up(&pdev->sem); if ((pf->f_flags & O_NONBLOCK) == 0){ /********************************************************/ wait_event_interruptible(pdev->write_queue , pdev->curpos < (MEM_SIZE -1)); /********************************************************/ }else{ return 0; } }else{ up(&pdev->sem); } down(&pdev->sem); if (size > (MEM_SIZE-pdev->curpos)){ count = MEM_SIZE-pdev->curpos; }else{ count = size; } if (copy_from_user(&pdev->mem[pdev->curpos],buf,count)){ up(&pdev->sem); return 0; }else{ pdev->curpos +=count; up(&pdev->sem); /********************************************************/ wake_up_interruptible(&pdev->read_queue); /********************************************************/ return count; } } module_init(my_init); module_exit(my_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("");
采用add_wait_queue
/************************************************************************* > File Name:block-memory-1.c 驱动程序根据应用层的flag标志决定是否采用阻塞或非阻塞的工作方式 本例用的是add_wait_queue函数 ************************************************************************/ #include <linux/kernel.h> #include <linux/module.h> #include <linux/cdev.h> #include <linux/fs.h> #include <linux/slab.h> #include <linux/uaccess.h> #include <linux/wait.h> /*1、定义重要的变量及结构体*/ #define MEM_SIZE 500 //每个虚拟设备内存大小 #define DEV_NUM 3 //创建的设备总个数 struct mem_dev_t{ struct cdev my_dev; //cdev设备描述结构体变量 char mem[MEM_SIZE]; //fifo内存池,当成虚拟设备 int curpos; //内存当前数据最后位置指示,从0开始记 struct semaphore sem; //信号量 wait_queue_head_t write_queue; //写等待队列 wait_queue_head_t read_queue; //读等待队列 }; struct mem_dev_t *mem_dev; /*所有驱动函数声明*/ ssize_t read (struct file *, char __user *, size_t, loff_t *); ssize_t write (struct file *, const char __user *, size_t, loff_t *); int open (struct inode *, struct file *); int release (struct inode *, struct file *); //驱动操作函数结构体,成员函数为需要实现的设备操作函数指针 //简单版的模版里,只写了open与release两个操作函数。 struct file_operations fops={ .open = open, .release = release, .read = read, .write = write, }; /*3、初始化 cdev结构体,并将cdev结构体与file_operations结构体关联起来*/ /*这样在内核中就有了设备描述的结构体cdev,以及设备操作函数的调用集合file_operations结构体*/ static int cdev_setup(struct mem_dev_t *mem_dev , dev_t devno ){ int unsucc =0; cdev_init(&mem_dev->my_dev , &fops); mem_dev->my_dev.owner = THIS_MODULE; /*4、注册cdev结构体到内核链表中*/ unsucc = cdev_add(&mem_dev->my_dev , devno , 1); if (unsucc){ printk("driver : cdev add faild \n"); return -1; } sema_init( &mem_dev->sem,1); //初始化信号量,为1 mem_dev->curpos = 0; //初始化缓冲数据位置为0 init_waitqueue_head(&mem_dev->write_queue); init_waitqueue_head(&mem_dev->read_queue); return 0; } static int __init my_init(void){ int major , minor; dev_t devno; int unsucc =0; int i=0; mem_dev = kzalloc(sizeof(struct mem_dev_t)*DEV_NUM , GFP_KERNEL); if (!mem_dev){ printk(" driver : allocating memory is failed"); return -1; } /*2、创建 devno */ unsucc = alloc_chrdev_region(&devno , 0 , DEV_NUM , "block_memory"); if (unsucc){ printk(" driver : creating devno is failed\n"); return -1; }else{ major = MAJOR(devno); minor = MINOR(devno); printk("diver : major = %d ; minor = %d\n",major,minor); } /*3、 初始化cdev结构体,并联cdev结构体与file_operations.*/ /*4、注册cdev结构体到内核链表中*/ for (i=0;i<DEV_NUM;i++){ devno = MKDEV(major , i); if (cdev_setup(mem_dev+i , devno) == 0){ printk("deiver : the driver block_memory[%d] initalization completed\n", i); } else printk("deiver : the driver block_memory[%d] initalization failed\n", i); } return 0; } static void __exit my_exit(void) { int i=0; dev_t devno; devno = mem_dev->my_dev.dev; for (i=0 ; i<DEV_NUM ; i++){ cdev_del(&(mem_dev+i)->my_dev); } unregister_chrdev_region(devno , DEV_NUM); printk("***************the driver operate_memory exit************\n"); } /*5、驱动函数的实现*/ /*file_operations结构全成员函数.open的具体实现*/ int open(struct inode *pnode , struct file *pf){ int minor = MINOR(pnode->i_rdev); int major = MAJOR(pnode->i_rdev); struct mem_dev_t *p = container_of(pnode->i_cdev , struct mem_dev_t , my_dev); pf->private_data = p; //把全局变量指针放入到struct file结构体里 if (pf->f_flags & O_NONBLOCK){ //非阻塞 printk("driver : block_memory[%d , %d] is opened by nonblock mode\n",major , minor); }else{ printk("driver : block_memory[%d , %d] is opened by block mode\n",major,minor); } return 0; } /*file_operations结构全成员函数.release的具体实现*/ int release(struct inode *pnode , struct file *pf){ printk("block_memory is closed \n"); return 0; } /*file_operations结构全成员函数.read的具体实现*/ ssize_t read (struct file * pf, char __user * buf, size_t size , loff_t * ppos){ //本例中,因为是fifo,所以ppos参数不用。 struct mem_dev_t *pdev = pf->private_data; int count = 0; //存储读到多少数据 int ret = 0; /*******************************************************/ DECLARE_WAITQUEUE(wait , current); //定义等待队列项目元素 /*******************************************************/ down(&pdev->sem); /*******************************************************/ add_wait_queue(&pdev->read_queue , &wait); //把元素加入读等待队列 /*******************************************************/ while (pdev->curpos == 0){ if ((pf->f_flags & O_NONBLOCK) == 0){ //当前没有数据,进入阻塞睡眠 /*******************************************************/ set_current_state(TASK_INTERRUPTIBLE); //设置当前进程为可中断睡眠态 up(&pdev->sem); //退出前释放信号量,V操作 schedule(); //调度程序 /*******************************************************/ }else{ ret = 0; goto out; } down(&pdev->sem); } //判断能够读到的字节数量 if (size > pdev->curpos){ count = pdev->curpos; }else{ count = size; } //copy_from_user返回值大于0失败 if ( copy_to_user(buf , &pdev->mem , count )){ //读取失败 ret = 0; goto out; }else{ //成功读取 memcpy(&pdev->mem , &pdev->mem[count] , pdev->curpos-count); pdev->curpos -= count; up(&pdev->sem); //退出前释放信号量,V操作 /*******************************************************/ wake_up_interruptible(&pdev->write_queue); //唤醒可能睡眠的write /*******************************************************/ ret = count; } out: up(&pdev->sem); //退出前释放信号量,V操作 /********************************************************/ remove_wait_queue(&pdev->read_queue , &wait); set_current_state(TASK_RUNNING); /*******************************************************/ return ret; } /*file_operations结构全成员函数.write的具体实现*/ ssize_t write (struct file * pf, const char __user *buf, size_t size , loff_t *ppos){ struct mem_dev_t *pdev = pf -> private_data; int count =0; int ret = 0; /*******************************************************/ DECLARE_WAITQUEUE(wait , current); //定义等待队列项目元素 /*******************************************************/ down(&pdev->sem); /*******************************************************/ add_wait_queue(&pdev->write_queue , &wait); //把元素加入读等待队列 /*******************************************************/ while (pdev->curpos == (MEM_SIZE -1)){ if ((pf->f_flags & O_NONBLOCK) == 0){ /*******************************************************/ set_current_state(TASK_INTERRUPTIBLE); up(&pdev->sem); schedule(); /*******************************************************/ }else{ ret = 0; goto out; } down(&pdev->sem); } if (size > (MEM_SIZE-pdev->curpos)){ count = MEM_SIZE-pdev->curpos; }else{ count = size; } if (copy_from_user(&pdev->mem[pdev->curpos],buf,count)){ ret = 0; goto out; }else{ pdev->curpos +=count; /*******************************************************/ wake_up_interruptible(&pdev->read_queue); /*******************************************************/ ret = count; } out: up(&pdev->sem); /*******************************************************/ remove_wait_queue(&pdev->write_queue, &wait); set_current_state(TASK_RUNNING); /*******************************************************/ return ret; } module_init(my_init); module_exit(my_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("");
测试过程:
0、在ubuntu上打开一个终端
1、编译make
2、加载驱动 sudo insmod block-memory.ko
3、加载时可以查看分配的主设备号 cat /proc/devices | grep block-memory
4、创建设备文件,其中主设备号是第3步里查看到的 sudo mknod /dev/block-memory-0 c 主设备号 0
5、chmod 777 /dev/block-memoruy-0
6、cat /dev/block-memory-0 这时会出现阻塞
7、打开ubuntu另一个终端,输入命令 echo “hello” > /dev/block-memory-0
8、这时,上一个终端阻塞状态会解除,并输出hello
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。