内核等待队列
等待队列
在linux驱动程序设计中,可以使用等待队列来实现进程的阻塞,等待队列可看作保存进程的容器,在阻塞进程时,将进程放入等待队列,当唤醒进程时,从等待队列中取出进程。
linux2.6内核提供了如下关于等待队列的操作:
1.定义等待队列
wait_queue_head_t my_queue
2.初始化等待队列
init_waitqueue_head(&my_queue)
3.定义并初始化等待队列(直接代替第1、2两个操作)
DECLARE_WAIT_QUEUE_HEAD(my_queue)
4.有条件睡眠
——wait_event(queue, condition)
当condition(一个布尔表达式)为真时,立即返回;否则让进程进入TASK_UNINTERRUPTIBLE模式的睡眠,并挂载queue参数所指定的等待队列上
——wait_event_interruptible(queue, condition)
当condition(一个布尔表达式)为真时,立即返回;否则让进程进入TASK_INTERRUPTIBLE的睡眠,并挂载queue参数所指定的等待队列上
——int wait_event_killable(wait_queue_t queue, condition)
当condition(一个布尔表达式)为真时,立即返回;否则让进程进入TASK_KILLABLE的睡眠,并挂载queue参数所指定的等待队列上(TASK_UNINTERRUPTIBLE在中断和给它发送信号的时候都不会被唤醒;TASK_KILLABLE也如此,不过在发送kill信号的时候能被唤醒)
5.无条件睡眠(老版本,不建议使用)
——sleep_on(wait_queue_head_t *q)
让进程进入不可中断的睡眠,并把它放入等待队列q
——interruptible_sleep_on(wait_queue_head_t *q)
让进程进入可中断的睡眠,并把它放入等待队列q
6.从等待队列中唤醒进程
——wake_up(wait_queue _t *q)
从等待队列q中唤醒状态为TASK_UNINTERRUPTIBLE,TASK_INTERRUPTIBLE,TASK_KILLABLE的所有进程(唤醒:将进程从睡眠状态改为TASK_RUNNING状态,不过只是就绪状态而非执行态)
——wake_up_interruptible(wait_queue_t *q)
从等待队列q中唤醒状态为TASK_INTERRUPTIBLE的进程
阻塞型字符设备驱动
1、阻塞型字符设备驱动的功能
当一个设备无法立刻满足用户的读写请求时。例如:调用read时没有数据可读, 但以后可能会有;或者一个进程试图向设备写入数据,但是设备暂时没有准备好接收数据。应用程序通常不关心这种问题,应用程序只是调用 read 或 write 并得到返回值。驱动程序应当(缺省地)阻塞进程,使它进入睡眠,直到请求可以得到满足。
2、阻塞方式
1)在阻塞型驱动程序中,Read实现方式如下:如果进程调用read,但设备没有数据或数据不足,进程阻塞。当新数据到达后,唤醒被阻塞进程。
2)在阻塞型驱动程序中,Write实现方式如下:如果进程调用了write,但设备没有足够的空间供其写入数据,进程阻塞。当设备中的数据被读走后,缓冲区中空出部分空间,则唤醒进程。
3、非阻塞方式
阻塞方式是文件读写操作的默认方式,但应用程序员可通过使用O_NONBLOCK标志来人为的设置读写操作为非阻塞方式(该标志定义在<linux/fcntl.h>中,在打开文件时指定)。
如果设置了O_NONBLOCK标志,read和write的行为是不同的。如果进程在没有数据就绪时调用了read,或者在缓冲区没有空间时调用了write,系统只是简单地返回-EAGAIN,而不会阻塞进程。
例子
在.c中添加
bool have_data = false;
在头文件.h中添加 等待队列
读函数添加 检查是否有数据 并添加到等待队列
static ssize_t mem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
unsigned long p = *ppos;
unsigned int count = size;
int ret = 0;
struct mem_dev *dev = filp->private_data; /*获得设备结构体指针*/
/*判断读位置是否有效*/
if (p >= MEMDEV_SIZE)
return 0;
if (count > MEMDEV_SIZE - p)
count = MEMDEV_SIZE - p;
while (!have_data) /* 没有数据可读 ,考虑为什么不用if,而用while。因为wait_event_interruptible函数,除了收到数据会跳出外,中断也会跳出,中断唤醒等待队列,如果使用if就会往下运行但此时并没有数据可读会出错,故次用while和interruptible配合的原因 */
{
/*判断用户是否设置了非阻塞方式*/
if (filp->f_flags & O_NONBLOCK)
return -EAGAIN; /*设置了非阻塞方式*/
/* 当设置了阻塞方式*/
wait_event_interruptible(dev->inq,have_data);/**当have_data为真时,立即返回,否则让进程进入TASK_KILL的睡眠 并挂在dev->inq队列上*/
}
/*读数据到用户空间*/
if (copy_to_user(buf, (void*)(dev->data + p), count))
{
ret = - EFAULT;
}
else
{
*ppos += count;
ret = count;
printk(KERN_INFO "read %d bytes(s) from %d\n", count, p);
}
have_data = false; /* 表明不再有数据可读 */
return ret;
}
写函数添加 有数据标志,并移出等待队列
static ssize_t mem_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
{
unsigned long p = *ppos;
unsigned int count = size;
int ret = 0;
struct mem_dev *dev = filp->private_data; /*获得设备结构体指针*/
/*分析和获取有效的写长度*/
if (p >= MEMDEV_SIZE)
return 0;
if (count > MEMDEV_SIZE - p)
count = MEMDEV_SIZE - p;
/*从用户空间写数据*/
if (copy_from_user(dev->data + p, buf, count))
ret = - EFAULT;
else
{
*ppos += count;
ret = count;
printk(KERN_INFO "written %d bytes(s) from %d\n", count, p);
}
have_data = true; /* 有新的数据可读 */
/* 唤醒读进程*/
wake_up(&(dev->inq));
return ret;
}
添加等待队列初始化函数
static int memdev_init(void)
{
int result;
int i;
dev_t devno = MKDEV(mem_major, 0);
/* 静态申请设备号*/
if (mem_major)
result = register_chrdev_region(devno, 2, "memdev");
else /* 动态分配设备号 */
{
result = alloc_chrdev_region(&devno, 0, 2, "memdev");
mem_major = MAJOR(devno);
}
if (result < 0)
return result;
/*初始化cdev结构*/
cdev_init(&cdev, &mem_fops);
cdev.owner = THIS_MODULE;
cdev.ops = &mem_fops;
/* 注册字符设备*/
cdev_add(&cdev, MKDEV(mem_major, 0), MEMDEV_NR_DEVS);
/* 为设备描述结构分配内存*/
mem_devp = kmalloc(MEMDEV_NR_DEVS * sizeof(struct mem_dev), GFP_KERNEL);
if (!mem_devp) /*申请失败*/
{
result = - ENOMEM;
goto fail_malloc;
}
memset(mem_devp, 0, sizeof(struct mem_dev));
/*为设备分配内存*/
for (i=0; i < MEMDEV_NR_DEVS; i++)
{
mem_devp[i].size = MEMDEV_SIZE;
mem_devp[i].data = kmalloc(MEMDEV_SIZE, GFP_KERNEL);
memset(mem_devp[i].data, 0, MEMDEV_SIZE);
/*初始化等待队列*/
init_waitqueue_head(&(mem_devp[i].inq));
}
return 0;
fail_malloc:
unregister_chrdev_region(devno, 1);
return result;
}
测试函数
写函数