当数据不可用时,用户可能调用read,或缓冲区满了,用户调用write,在这些情况下,驱动程序应该阻塞该操作,将其置入休眠状态直到可以执行操作。
休眠:当一个进程进入休眠状态,会被标记为一种特殊状态并从调度器运行队列移走。直到状态被修改,进程才被cpu调度。休眠的进程被搁置在一边,等待某个事件的发生。
休眠的两条规则:一、永远不要在原子上下文中进入休眠;二、当一个休眠的进程被唤醒时,必须再次检查它所等待的条件是否的确为真。
等待队列(简单休眠):
等待队列就是一个进程链表,其中包括了等待某个特定事件的所有进程。在linux中,一个等待队列通过一个等待队列头来管理,类型为wait_queue_head_t,定义在<linux/wait.h>。
初始化:
init_waitqueue_head(wait_queue_head_t *);
wait_event:
在条件为真之前,进程保持休眠,休眠的同时,也检查进程等待的条件。
wait_event(queue, condition)
wait_event_interruptible(queue, condition) 可以被信号中断;可返回一个整数值,非零值表示休眠被信号中断
wait_event_timeout(queue, condition, timeout) 等待给定的时间,超时返回0
wait_event_interruptible_timeout(queue, condition, timeout)
wake_up:
void wake_up(queue) 唤醒等待在给定queue上的所有进程
void wake_up_interruptible(queue) 只唤醒那些执行可中断休眠的进程
使用wait_event时使用wake_up,使用wait_event_interruptible使用wake_up_interruptible
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#define MAX_BUF_LEN 256
static int major = 277;
static int minor = 0;
static dev_t helloNum;
struct class *helloClass = NULL;
struct device *helloDev = NULL;
wait_queue_head_t readWq, writeWq;
static int haveData = 0;
static char helloBuf[MAX_BUF_LEN] = {0};
int hello_open(struct inode *pinode, struct file *pfile)
{
printk("hello_open, minor:%d, major:%d\n", iminor(pinode), imajor(pinode));
return 0;
}
int hello_release(struct inode *pinode, struct file *pfile)
{
printk("hello_release, minor:%d, major:%d\n", iminor(pinode), imajor(pinode));
return 0;
}
ssize_t hello_read(struct file *filep, char __user *buf, size_t size, loff_t *pos)
{
int copySize;
if (size > MAX_BUF_LEN)
{
copySize = MAX_BUF_LEN;
}
else
{
copySize = size;
}
wait_event_interruptible(readWq, haveData == 1);
if(copy_to_user(buf, helloBuf, copySize))
{
return -EFAULT;
}
printk("read:%s\n",helloBuf);
haveData = 0;
wake_up_interruptible(&writeWq);
return copySize;
}
ssize_t hello_write(struct file *filep, const char __user *buf, size_t size, loff_t *pos)
{
int copySize;
if(size > MAX_BUF_LEN)
{
copySize = MAX_BUF_LEN;
}
else
{
copySize = size;
}
wait_event_interruptible(writeWq, haveData == 0);
memset(helloBuf, 0, MAX_BUF_LEN);
if(copy_from_user(helloBuf, buf, copySize))
{
return -EFAULT;
}
printk("write:%s\n",helloBuf);
haveData = 1;
wake_up_interruptible(&readWq);
return copySize;
}
static struct file_operations hello_ops = {
.open = hello_open,
.release = hello_release,
.read = hello_read,
.write = hello_write,
};
static int hello_init(void)
{
int ret = 0;
printk("hello_init\n");
ret = register_chrdev( major, "hello", &hello_ops);
if(ret < 0)
{
printk("register_chrdev failed.\n");
return ret;
}
helloClass = class_create(THIS_MODULE, "hellocls");
if (IS_ERR(helloClass))
{
printk("class_create failed.\n");
ret = PTR_ERR(helloClass);
goto error_exit1;
}
helloNum = MKDEV(major,minor);
printk("major:%d, minor:%d\n", MAJOR(helloNum), MINOR(helloNum));
helloDev = device_create(helloClass, NULL, helloNum, NULL, "hello0");
if (IS_ERR(helloDev))
{
printk("device_create failed.\n");
ret = PTR_ERR(helloDev);
goto error_exit2;
}
init_waitqueue_head(&readWq);
init_waitqueue_head(&writeWq);
return 0;
error_exit2:
class_destroy(helloClass);
error_exit1:
unregister_chrdev(major,"hello");
return ret;
}
static void hello_exit(void)
{
printk("hello_exit\n");
device_destroy(helloClass, helloNum);
class_destroy(helloClass);
unregister_chrdev(major,"hello");
}
MODULE_LICENSE("GPL");
module_init(hello_init);
module_exit(hello_exit);