linux driver wait queue

本文详细介绍了Linux内核中的等待队列(waitqueue)机制,包括其定义、初始化、使用方法等,并通过一个具体的驱动程序实例展示了如何利用等待队列实现进程间的同步与通信。
在Linux驱动程序中,可以使用等待队列(wait queue)来实现阻塞进程的唤醒。wait queue很早就作为一种基本的功能单位出现在Linux内核里了,它以队列位基础数据结构,与进程调度机制紧密结合,能够用于实现内核中异步事件通知机制。等待队列可以用来同步对系统资源的访问。(信号量在内核中也依赖等待队列来实现).

    Linux-2.6提供如下关于等待队列的操作:
    (1) 定义"等待队列头"
        wait_queue_head_t my_queue;


    (2) 初始化"等待队列头"
        init_waitqueue_head(&my_queue);
        定义和初始化的快捷方式:
        DECLARE_WAIT_QUEUE_HEAD(my_queue);  


    (3) 定义等待队列
        DECLARE_WAITQUEUE(name, tsk);
        定义并初始化一个名为name的等待队列(wait_queue_t);


    (4) 添加/移除等待队列
        void fastcall add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
        void fastcall remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
        add_wait_queue()用于将等待队列wait添加到等待队列头q指向的等待队列链表中,而remove_wait_queue()用于将等待队列wait从附属的等待队列头q指向的等待队列链表中移除。


    (5) 等待事件
        wait_event(queue, condition);
        wait_event_interruptible(queue, condition);
        wait_event_timeout(queue, condition, timeout);
        wait_event_interruptible_timeout(queue, condition, timeout);
        等待第一个参数queue作为等待队列头的等待队列被唤醒,而且第二个参数condition必须满足,否则阻塞。wait_event()和wait_event_interruptible()的区别在于后者可以被信号打断,而前者不能。加上timeout后的宏意味着阻塞等待的超时时间,以jiffy为单位,在第三个参数的timeout到达时,不论condition是否满足,均返回。


    (6) 唤醒队列
        void wake_up(wait_queue_head_t *queue);
        void wake_up_interruptible(wait_queue_head_t *queue);
        上述操作会唤醒以queue作为等待队列头的所有等待队列对应的进程。
        wake_up()               <--->    wait_event()
                                         wait_event_timeout()
        wake_up_interruptible() <--->    wait_event_interruptible()  
                                         wait_event_interruptible_timeout()

        wake_up()可以唤醒处于TASK_INTERRUPTIBLE和TASK_UNINTERRUPTIBLE的进程
        wake_up_interruptble()只能唤醒处于TASK_INTERRUPTIBLE的进程。


    (7) 在等待队列上睡眠
        sleep_on(wait_queue_head_t *q);
        interruptible_sleep_on(wait_queue_head_t *q);
     
        sleep_on()函数的作用就是将当前进程的状态置成TASK_UNINTERRUPTIBLE,定义一个等待队列,并把它添加到等待队列头q,直到支援获得,q引导的等待队列被唤醒。
        interruptible_sleep_on()与sleep_on()函数类似,其作用是将目前进程的状态置成TASK_INTERRUPTIBLE,并定义一个等待队列,之后把它附属到等待队列头q,直到资源可获得,q引导的等待队列被唤醒或者进程收到信号。  

        sleep_on()               <--->   wake_up()
        interruptible_sleep_on() <--->   wake_up_interruptible()


driver部分

#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/wait.h>
#include <asm/semaphore.h>
MODULE_LICENSE("GPL");

#define MAJOR_NUM 254

static ssize_t globalvar_read(struct file *, char *, size_t, loff_t*);
static ssize_t globalvar_write(struct file *, const char *, size_t, loff_t*);

struct file_operations globalvar_fops =
{
	read: globalvar_read, write: globalvar_write,
};

static int global_var = 0;
static struct semaphore sem;
static wait_queue_head_t outq;
static int flag = 0;

static int __init globalvar_init(void)
{
	int ret;
	ret = register_chrdev(MAJOR_NUM, "globalvar", &globalvar_fops);
	if (ret)
	{
  	printk("globalvar register failure");
 	}
	else
	{
  	printk("globalvar register success");
  	init_MUTEX(&sem);
  	init_waitqueue_head(&outq);
 	}
 	return ret;
}

static void __exit globalvar_exit(void)
{
 	int ret;
 	ret = unregister_chrdev(MAJOR_NUM, "globalvar");
 	if (ret)
 	{
  	printk("globalvar unregister failure");
 	}	
 	else
 	{
  	printk("globalvar unregister success");
 	}
}

static ssize_t globalvar_read(struct file *filp, char *buf, size_t len, loff_t *off)
{
 //等待数据可获得
	if (wait_event_interruptible(outq, flag != 0))
	{
		return - ERESTARTSYS;
	}

	if (down_interruptible(&sem))
	{
		return - ERESTARTSYS;
	}

	flag = 0;
	if (copy_to_user(buf, &global_var, sizeof(int)))
	{
		up(&sem);
		return - EFAULT;
	}
	up(&sem);
	return sizeof(int);
}

static ssize_t globalvar_write(struct file *filp, const char *buf, size_t len,loff_t *off)
{
	if (down_interruptible(&sem))
	{
		return - ERESTARTSYS;
	}
	if (copy_from_user(&global_var, buf, sizeof(int)))
	{
		up(&sem);
		return - EFAULT;
	}
	up(&sem);
	flag = 1;
	//通知数据可获得
	wake_up_interruptible(&outq);
	return sizeof(int);
}

module_init(globalvar_init);
module_exit(globalvar_exit);

编写两个用户态的程序来测试,第一个用于阻塞地读/dev/globalvar,另一个用于写/dev/globalvar。只有当后一个对/dev/globalvar进行了输入之后,前者的read才能返回。
读的程序为:

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>
int main(void)
{
	int fd, num;
	fd = open("/dev/globalvar", O_RDWR, S_IRUSR | S_IWUSR);
	if (fd != - 1)
	{
		while (1)
		{
			read(fd, &num, sizeof(int)); //程序将阻塞在此语句,除非有针对globalvar的输入
			printf("The globalvar is %d\n", num);

			//如果输入是0,则退出
			if (num == 0)
			{
				close(fd);
				break;
			}
		}
	}
	else
	{
		printf("device open failure\n");
	}
	return 1;
}

写的程序为:

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>
main()
{
	int fd, num;

	fd = open("/dev/globalvar", O_RDWR, S_IRUSR | S_IWUSR);
	if (fd != - 1)
	{
		while (1)
		{
			printf("Please input the globalvar:\n");
			scanf("%d", &num);
			write(fd, &num, sizeof(int));

			//如果输入0,退出
			if (num == 0)
			{
				close(fd);
				break;
			}
		}
	}
	else
	{
		printf("device open failure\n");
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值