等待队列和阻塞的实现——wait queue

等待队列

对于一个进程"睡眠"意味着什么? 当一个进程被置为睡眠, 它被标识为处于一个特殊的状态并且从调度器的运行队列中去除,这个进程将不被在任何 CPU 上调度,因此将不会运行,直到发生某些事情改变了那个状态。
睡眠是“自愿调度”,其实就是将当前进程的状态设置为 TASK_INTERRUPTIBLE 等状态,然后schedule() 让出CPU1,让调度器重新选择一个进程来执行。

堵塞主要就是依赖于等待队列。

//定义头文件:
#include <linux/wait.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 wq;
//初始化(队列头部)
init_waitqueue_head(&wq);
添加和移除等待队列:
void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);

等待队列主要由锁和内核列表构成的;主要用于和wait_event(_XXX)配合,人为设置可控的阻塞。

阻塞接口

wait_event (wq ,condition)
//当condition为假,谁间接调用这个函数谁就被阻塞(阻塞在这个位置),且不会被中断的阻塞
wait_event_timeout (wq ,condition,timeout)
//可以被中断的
wait_event_interruptible (wq ,condition)

condition为条件表达式,当 wake up 后, condition 为真时,唤醒阻塞的进程,为假时,继续睡眠。
举例:
应用程序调用read函数,read函数间接调用了wait_event;condition为0就是阻塞等待。

解决阻塞接口(唤醒)

#define wake_up(x) _wake_up (x, TASK_NORMAL, 1 , NULL)

#define wake_up_interruptible(x) _wake_up (x, TASK_INTERRUPTIBLE, 1 ,NULL)

wake_up唤醒之后,任然会检测一次condition,如果为假继续休眠。

举个栗子:实现阻塞

以字符设备为例,在没有数据的时候,在 read 函数中实现读阻塞,当向内核写入数据时,则唤醒阻塞在该等待队列的所有任务。
在这里插入图片描述


wait_queue_head_t wwq;
wait_queue_head_t rwq;
int havedata = 0;//0 表示没有数据:可以写但不可以读;1表示有数据:可以读不可以写。

static int hello_init(void)
{
//注册设备号
......
	init_waitqueue_head(&wwq);
	init_waitqueue_head(&rwq);
	return 0;

}


static ssize_t hello_write (struct file *filep, const char __user *buf, size_t size, loff_t *pos)
{
	//仅当数据被读取完之后才能再写
	wait_event_interruptible(wwq ,havedata == 0);
	//写入数据,将user_buf存入kernel_buf
	havedata = 1;
	wake_up_interruptible(&rwq);
.......


static ssize_t hello_read (struct file *filep, char __user *buf, size_t size, loff_t *pos)
{
	//仅当数据被读写入之后才能再读
	wait_event_interruptible (rwq ,havedata == 1);
	//读取数据
	havedata = 0;
	wake_up_interruptible(&wwq);
.......  

用户层

while(1)
		{
		printf("before read\n");
		len = read(fd,buf,1024);
		printf("data  come in\n");

		printf("read:%s \n",buf);
		memset(buf,0,1024);
		}

注意: memset(buf,0,1024)会将buf的内存置为0(二进制下的0)所以这个时候strlen(buf)的值为0

与信息量的不同

信息量下导致的阻塞,是需要当一个程序去释放掉信号量时才能唤醒的;wait_event 它的 阻塞和唤醒是完全可控的,这是因为condition是可控的,唤醒也可以由别的程序 控制的一个是需要释放掉自动唤醒,可以是可控的选择唤醒。

设置为非阻塞的实现

没有数据立刻返回

应用层
fd=open("/dev/hello",O_RDONLY O_NONBLOCK);

static ssize_t hello_read (struct file filp,char __user buf,size_t size,loff_t poss)
{
	int ret = 0;
	if( havedate == 0)
	{
		if( filp->f_flags & O_NONBLOCK)
			return -EAGAIN;
		wait_event_interruptible(rwq,flage !=0);
	}
	flages = 0;
	wake_up_interruptible(wwq);
	return size;

通过 filp->f_flags & O_NONBLOCK 的结果去判断f_flags是否被设置成阻塞形式(f_flags在open的时候已经被定义了);当前没有数据的情况下先看一下是否f_flags是否被设置成非阻塞,如果是非阻塞filp->f_flags & O_NONBLOCK结果为真,直接返回-EAGAIN;退出程序。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值