阻塞、非阻塞与异步通知(驱动开发)

阻塞操作

描述:指在执行设备操作时,若不能获得资源则进程睡眠。当满足可操作的条件后,内核唤醒进程继续执行。
采用等待队列的方式实现,等待队列 以队列为基础数据结构,与进程调度机制紧密结合,能够用于实现内核中的异步事件通知机制,也可以用来同步对系统资源的访问(如信号量)

操作步骤:

定义“等待队列头”
wait_queue_head_t my_queue;
初始化“等待队列头”
init_waitqueue_head(&my_queue);
定义等待队列
DECLARE_WAITQUEUE(name, tsk)
添加/移除等待队列
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);
等待事件
wait_event(queue, condition)
wait_event_interruptible(queue, condition) (推荐使用,安全性更高)
wait_event_timeout(queue,condition, timeout)
wait_event_interruptible_timeout(queue, condition,timeout)
唤醒队列
void wake_up(wait_queue_head_t *queue);
void wake_up_interruptible(wait_queue_head_t *queue); (推荐使用,安全性更高)
在等待队列上睡眠
sleep_on(wait_queue_head_t *q );
interruptible_sleep_on(wait_queue_head_t *q );(推荐使用,安全性更高)

例子:

//定义等待队列头
wait_queue_head_t  my_queue;

//在init函数中初始化队列
init_waitqueue_head(&my_queue);

//等待事件(意为当flag大于或等于len时被才执行)
wait_event_interruptible(my_queue,flag>=len);

//唤醒事件
wake_up_interruptible(&my_queue);

非阻塞操作

描述:指进程在不能进行设备操作时并不睡眠而是立刻返回结果。
进程中调用poll和select操作来查询打开的I/O设备文件是否就绪(有资源)
设备驱动操作集合中的 unsigned int (*poll) (struct file *, poll_table *)被调用通过poll_wait可以向驱动向poll_table结构添加一个等待队列驱动的poll函数应该返回设备的当前状态
POLLIN,POLLOUT,POLLRDNORM,POLLERR

操作步骤:
用户空间使用的select函数:
int select(int numfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
其中readfds、writefds、exceptfds分别是被select()监视的读、写和异常处理的文件描述符集合,numfds的值是需要检查的号码最高的文件描述符加1。timeout参数是一个指向struct timeval类型的指针,它可以使select()在等待timeout时间后若没有文件描述符准备好则返回。

poll底层实现函数模板:

static unsigned int xxx_poll(struct file *filp, poll_table *wait)
{ 
	unsigned int mask = 0;
	struct xxx_dev *dev = filp->private_data; /*获得设备结构体指针*/
	... 
	poll_wait(filp, &dev->r_wait, wait);//加读等待队列头
	poll_wait(filp, &dev->w_wait, wait);//加写等待队列头
	if (...)//可读
	{ 
		mask |= POLLIN | POLLRDNORM; /*标示数据可获得*/
	}
	if (...)//可写
	{ 
		mask |= POLLOUT | POLLWRNORM; /*标示数据可写入*/
	}
	... 
	return mask; 
}

底层例子:

static struct file_operations myops={
	.poll    = myPoll
};

unsigned int myPoll(struct file *pf, struct poll_table_struct *wait)
{
	unsigned int mask = 0;
	
	poll_wait(pf,&my_queue,wait);
	
	if(flag>0)
	{
		mask |= POLLIN | POLLRDNORM;
	}
	if(0 == flag)
	{
		mask |= POLLOUT | POLLWRNORM; 
	}
	return mask;
}

应用层例子:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include "mydev.h"

static char rbuf[32] = {0};
static char wbuf[32] = "1232145679";
static int fd = 0;

int main()
{
	int ret = 0;
	fd_set rfd;
	fd_set wfd;
	fd = open("/dev/mydev",O_RDWR);
	if(fd<0)
	{
		printf("open failed.\n");
	}

	while(1)
	{
			FD_ZERO(&rfd);//将指定的文件描述符集清空,初始化操作
			FD_ZERO(&wfd);
			FD_SET(fd,&rfd);//用于在文件描述符集合中增加一个新的文件描述符。
			FD_SET(fd,&wfd);
			select(fd+1,&rfd,&wfd,NULL,0);	
			if(FD_ISSET(fd,&rfd))
			{
					read(fd,rbuf,32);
					printf("read rbuf is %s\n",rbuf);
			}
			if(FD_ISSET(fd,&wfd))
			{
					write(fd,wbuf,32);
					printf("write wbuf ok\n");
			}

	}

	close(fd);
}

异步通知

描述:一旦设备就绪,则主动通知应用程序,这样应用程序根本就不需要查询设备状态,这一点非常类似于硬件上“中断”的概念,比较准确的称谓是“信号驱动的异步I/O”

操作步骤:
在用户程序中,为了捕获信号,可以使用signal()函数来设置对应信号的处理函数,如下所示:

void (*signal(int signum, void (*handler)(int)) (int)

捕获信号范例:

void sigterm_handler(int signo)
{
	printf("Have caught sig N.O. %d\n", signo); exit(0); 
}
int main(void)
{
	 signal(SIGINT, sigterm_handler); 
	 signal(SIGTERM, sigterm_handler); 
	 while(1);
	 return 0;
}

应用程序要指定进程为文件的属主

	fcntl(STDIN_FILENO, F_SETOWN, getpid()); 

应用程序通过fcntl函数在设备中设置FASYNC标志

	oflags = fcntl(fd, F_GETFL);
	fcntl(fd, F_SETFL, oflags | FASYNC);

为了使设备支持异步通知机制,驱动程序中涉及以下3项工作。
支持F_SETOWN命令,能在这个控制命令处理中设置filp->f_owner为对应进程ID。不过此项工作已由内核完成,设备驱动无须处理。
支持F_SETFL命令的处理,每当FASYNC标志改变时,驱动程序中的fasync()函数将得以执行。因此,驱动中应该实现fasync()函数。
在设备资源可获得时,调用kill_fasync()函数激发相应的信号

应用层:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <signal.h>

static char rbuf[32] = {0};
static char wbuf[32] = "123456789";
static int fd = 0;

void handler(int s)
{
	read(fd,rbuf,32);
	printf("rbuf is %s\n",rbuf);
}

int main()
{
	int oflags = 0;
	int ret = 0;
	fd = open("/dev/mydev",O_RDWR);
	if(fd<0)
	{
		printf("open failed.\n");
	}
	
	signal(SIGIO, handler);
	//绑定文件和进程号以便信号准确发送
	fcntl(fd, F_SETOWN, getpid());	
	//在设备中设置FASYNC标志
	oflags = fcntl(fd, F_GETFL); 
	fcntl(fd, F_SETFL, oflags | FASYNC);

	write(fd,wbuf,32);

	sleep(5);

	close(fd);
}

驱动:

//定义信号队列
struct fasync_struct *async_queue;

//定义fasync函数
static struct file_operations myops={
	.fasync   = myfasync
};

//实现fasync函数,初始化信号队列
static int myfasync(int fd, struct file *filp, int mode)
{   
	return fasync_helper(fd, filp, mode, &async_queue);  
}

//发送信号
kill_fasync(&async_queue, SIGIO, POLL_IN); 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值