驱动学习-日常笔记day6

【1】IO多路复用
IO多路复用:在同一app应用程序中想同时监听多个文件描述符,
使用select、poll、epoll在监听,如果数据都没有准备好,select
poll、epoll就会阻塞。当硬件的数据准备好的时候就会产生中断,
在驱动的中断处理函数中唤醒select、poll、epoll即可。当他们
被唤醒之后,然后判断文件描述符的集合中哪个文件描述的数据准备
好了,如果数据准备好了,就从硬件将将数据读取到用户空间即可。

fd1 = open("mma",权限);
fd2 = open("mpu",权限);
将fd1和fd2放入到文件描述符的集合中(读表)
select(maxfd+1,&读表,NULL,NULL,NULL);
if(FD_ISSET(fd1,读表)){
	read(fd1,buf,sizeof(buf));
}
if(FD_ISSET(fd2,读表)){
	read(fd2,buf,sizeof(buf));
	
}
------------------------------------------------
vfs:
	sys_select:
	1.在内核空间分配表的内存,然后调用copy_from_user
	将用户空间的文件描述符表拷贝到内核空间。
	
	2.从文件描述符表中取出文件描述符
		fd1 fd2 fd3
		
		fd1-->fd_array[fd1]--->file--->poll(指针)-->driver1_poll()
		ret1  = driver1_poll();
		
		ret1=0 ret2=0 ret3=0 进程休眠
		在虚拟文件系统层,实现了poll_table结构体
		调用driver1和driver2中的poll,如果得到的结果都是
		0,表示两个驱动的数据都没有准备好,如果数据都没
		准备好就将进程休眠。
	
	3.休眠的进程被驱动的wake_up 唤醒
		当某一个时间点有一个或者多个进程同时调用wake_up
		唤醒了这个休眠的进程。然后它会重新全部调用一下
		驱动中的poll函数,获取到返回值,
		fd1 fd2 fd3
		
		fd1-->fd_array[fd1]--->file--->poll(指针)-->driver1_poll()
		ret1  = driver1_poll();
		
		ret1=POLLIN ret2=0 ret3=POLLIN 
		
		将ret1 ret3对应的文件描述符找到fd1 fd3,
		并将fd1 fd3放到文件描述符表中
		
	4.将文件描述符表返回到用户空间copy_to_user。
-------------------------------------------------
驱动:
	  driver1             driver2
	driver1_open         driver2_open
	dirver1_poll         dirver2_poll
	driver1_read         driver2_read
	
	wake_up              wake_up
	
unsigned int (*poll) (struct file *, struct poll_table_struct *);
	
grep ".poll = " * -nR
//搜索内核中已经实现的poll函数,通过内核实现的poll函数
//来完成自己的poll函数
	
通过上述代码参考知道,在poll函数中实现的步骤如下
	1.定义mask=0
	
	2.调用poll_wait,不会阻塞,它只是完成等待队列头的提交
	
	3.当条件为真设置mask
		if(condition == 1){
			mask = POLLIN/POLLOUT;
		}
		
	4.返回mask

【2】epoll的使用:
#include <sys/epoll.h>

 int epoll_create(int size);
 功能:创建一个epoll的实例
 参数:
	@size:已经不再使用,可以填写任意值
 返回值:成功返回epoll的文件描述符,失败返回-1;
 
 int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
 功能:控制箱epfd中添加、修改、删除本次要监听的事件的类型
 参数:
	@epfd:epoll的文件描述符
	@op  :EPOLL_CTL_ADD 添加事件
		  EPOLL_CTL_MOD 修改事件 
		  EPOLL_CTL_DEL 删除事件
	@fd  :文件描述符
	@event:事件的结构体对象
	    struct epoll_event {
           uint32_t     events; //EPOLLIN EPOLLOUT
           epoll_data_t data;        /* User data variable */
       };

 返回值:成功返回0,失败返回-1
  
 int epoll_wait(int epfd, struct epoll_event *revents,
                  int maxevents, int timeout);
 功能:完成阻塞,如果有数据准备好,就返回
 参数:
	@epfd     :epoll的文件描述符
	@revents  :返回的事件结构体
	@maxevents:最大的结构体个数
	@timeout  :超时时间
返回值:1.成功返回准备好的文件描述符的个数
		2.超时返回0
		3.失败返回-1;

【3】面试题:select/poll/epoll的区别?
select:
1.select监听的最大的文件描述符是1024个
2.select每次会清空文件描述符表,每次都需要拷贝用户空间的表到内核空间,效率低
3.select被唤醒之后需要重新轮询一遍驱动的poll函数,效率比较低

poll:
1.poll监听的最大的文件描述符没有个数限制
2.poll不需要重新构造文件描述符表,只需要从用户空间向内核空间拷贝一次数据即可
3.poll被唤醒之后需要重新轮询一遍驱动的poll函数,效率比较低

epoll:(本世纪最好用的io多路复用机制)
1.epoll监听的最大的文件描述符没有个数限制
2.epoll不需要重新构造文件描述符表,只需要从用户空间向内核空间拷贝一次数据即可
3.epoll被唤醒之后epoll直接能拿到唤醒的文件描述符,将文件描述符拷贝到用户空间即可
  不需要轮询,效率高。

【4】异步通知
当硬件的数据没有准备好的时候,应用程序自由执行。
当硬件的数据准备好的时候,硬件会给驱动发送一个中断,
在中断的处理函数中给应用程序发送一个信号,当应用程序
收到这个信号的时候,执行信号处理函数,在信号处理函数
中将数据读取到即可。

user:
	1.注册信号处理函数
		signal(SIGIO,信号处理函数);
	2.调用驱动的fasync函数
		unsigned int flags = fcntl(fd,F_GETFL);
		fcntl(fd,F_SETFL,flags|FASYNC);
	3.指定接收信号的进程
		fcntl(fd,F_SETOWN,getpid());  
------------------------------------------------------
VFS:
	sys_fcntl:
		 err = do_fcntl(fd, cmd, arg, filp);
			switch(cmd)
			case F_SETFL:
			  err = setfl(fd, filp, arg);
			    if (((arg ^ filp->f_flags) & FASYNC) && filp->f_op &&                                          
				filp->f_op->fasync) {
				error = filp->f_op->fasync(fd, filp, (arg & FASYNC) != 0); 

------------------------------------------------------
kernel:fops: .fasync = driver_fasync;
	  driver_fasync();
	1.实现驱动的fasync函数
		grep ".fasync =" * -nR
		struct fasync_struct *fapp;
		int fasync_helper(int fd, struct file * filp, 
			int on, struct fasync_struct **fapp)
		功能:初始异步通知结构体,并把异步通知结构体放入队列。
		

	2. 实现发送信号的过程
		 kill_fasync(&fapp, SIGIO, POLL_IN);

おすすめ

転載: blog.csdn.net/weixin_48430195/article/details/108671820