高级io模型

高级IO模型

一般对于底层硬件的中断,一般表示有数据发生,如果要去读取该中断产生的数据值,则需要通过文件io在进程上下文中进行交互(read),文件io交互的方式由分为5种模型:

  1. 非阻塞(O_NONBLOCK,一旦调用立刻执行,若没有数据则返回-EAGAIN错误码,提醒重试)
  2. 阻塞(休眠加入等待队列)
  3. 多路复用(select、poll、epoll)对应驱动fileoperation对象的poll方法
  4. 异步io
  5. 异步通知(fasync)信号实现
  6. 轮询(while循环,占CPU高)

我们应该对通过中断事件类型来选择交互模型

阻塞与非阻塞模型

驱动中需要对应用中打开文件标识(非阻塞与阻塞的标识)进行判断,file对象中记录文件的成员f_flag,通过逻辑与操作进行判断。
注意:休眠下的唤醒操作默认将唤醒指定等待队列中的所有进程。
对于休眠的状态分为以下几种:

  1. interruptible:休眠时可通过信号唤醒
  2. timeout:超时限制
  3. exclusive:具有排他性,唤醒该进程时不会唤醒队列中其他进程。
  4. locked:先获得锁才能唤醒队列。

非阻塞关键代码:

非阻塞且没有数据则返回。
if(fp->f_flags & O_NONBLOCK && 没数据)
{
		return -EAGAIN;
}

阻塞休眠关键代码:

//初始化等待队列的链表头
wait_queue_head_t wq_head; 
init_waitqueue_head(&wq_head); 
volatile int condition;

//当前进程加入等待队列,condition 为0 则阻塞休眠, conditon一般定义为volatile类型
wait_event_interruptible(wq_head, condition);

//中断发生后,产生数据,唤醒进程
condition = 1;
wake_up_interruptible(wq_head);

多路复用模型

进程在休眠中是无法做其他操作,当进程同时对多个设备操作时,一个设备阻塞其他将无法操作,这就是阻塞状态下的缺点,他控制整个进程。
多路复用在单线程与单进程中对多个文件进行交互,类似监控,只要有一个发生则非阻塞执行。
驱动fileoperation对象的poll方法 —对应— 应用层的 int poll(struct pollfd *fds, nfds_t nfds, int timeout);
对于应用层poll用法,读者最好在我的博客linux系统编程中学习,比较全,这里只讲述思路
对于驱动层,将阻塞休眠等待队列注册到poll中进行监控,其中等待队列分为读等待队列和写等待队列,需要注意若没有指定poll方法,应用层调用poll会默认设备驱动有事件发生。

应用层关键代码:

struct pollfd pfd[2];//监控两个设备文件
pfd[0].fd = fd1;
pfd[0].events = POLLIN;
pfd[1].fd = fd2;
pfd[1].events = POLLIN;

maxfd = fd1 > fd2 ? fd1 : fd2;

ret = poll(pfd, 2, -1);
if(!ret){
	perror("poll");
	exit(1);
}else{
	for(i=0;i<2;i++){
		if(pfd[i].revents & POLLIN){
			//有数据可读
			read(pfd[i].fd, &event, sizeof(key_event));
			//对数据进行处理
			xxxxxx
		}
	}
}

驱动关键代码:
因为唤醒默认会对所有等待队列中进程唤醒,因此读和写需分为两个队列。

unsigned int my_poll(struct file *fp, struct poll_table_struct *pts)
{
	//返回mask值,表示是否有数据(0/POLLIN)
	unsigned int mask = 0;
	//poll_wait将当前读事件等待队列注册到系统
	poll_wait(fp, wq_read, pts);

	if(_无数据_){
		mask = 0;
	}

	if(_有数据_){
		mask |= POLLIN | POLLRDNORM;
	}
	
	return mask;
}

异步io

应用程序中对提交完io操作后立即返回,继续做其他操作,具有非阻塞型,底层收到应用层的提交后开始操作,当底层io操作完成给提交者发送信号,或调用注册的回调函数,告知请求者io操作已完成。
这里不做讲述

异步通知

类似于异步io,资源可用时只能向应用层发信号,不能直接调用应用层注册的回调函数。
应用程序不用主动去对设备文件进行读,设备文件发生事件时自己通知应用层的进程去读取。类似于中断与硬件之间的关系,这里是驱动与应用的关系。

应用层:

	//信号处理方法
	void catch_signal(int signo)
	{
		if(signo == SIGIO)
		{
			//读数据
			read(fd, &buf, sizeof(buf));
			//处理操作
		}
	}

	//设置信号处理方法
	signal(SIGIO, catch_signal);
	//将当前进程设置成SIGIO的属主进程
	fcntl(fd, F_SETOWN, getpid());
	//将io设置成异步模式
	int flags = fcntl(fd, F_GETFL);
	fcntl(fd, F_SETFL, flags | FASYNC);//传参给驱动中fasync方法调用

驱动添加文件io方法fasync:

struct fasync_struct *fasync;

//定义fasync接口,用于发送给应用层进程
int my_fasync(int fd, struct file *fp, int on)
{
	//只需要调用一个函数记录信号该发送给谁,和进程相关联
	return fasync_helper(fd, fp, on, &fasync);
}

//当有数据时通过fasync接口向指定进程发生信号
kill_fasync(&fasync, SIGIO, POLLIN);

//release方法中
my_fasync(-1, fp, 0);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值