博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
file_operations 与 驱动实现要求
阅读量:4285 次
发布时间:2019-05-27

本文共 8181 字,大约阅读时间需要 27 分钟。

file_ops 中 最重要的概念莫过于 同步异步,阻塞非阻塞 概念及其实现.
  • 阻塞 非阻塞
同步和异步关注的是消息通信机制 (synchronous communication/ asynchronous communication)所谓同步,就是在发出一个*调用*时,在没有得到结果之前,该*调用*就不返回。但是一旦调用返回,就得到返回值了。换句话说,就是由*调用者*主动等待这个*调用*的结果。而异步则是相反,*调用*在发出之后,这个调用就直接返回了,所以没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在*调用*发出后,*被调用者*通过状态、通知来通知调用者,或通过回调函数处理这个调用。---阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态.阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。其实如果是循环的只等待消息结果,就跟调度出去(挂起)没什么两样.也被称作阻塞.(私人理解)非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。
  • 宏观上的概念
同步,	只有ops->read直接监听的,都是同步 // 什么时候读确定异步	不只有 ops->read 直接监听的,或者根本没有 ops_>read 参与的,都是异步 // 什么时候读不确定阻塞,	进程会换出,进程会循环卡死到某处非阻塞	进程不会因为api 被换出同步非阻塞	read NON_BLOCK同步阻塞	read BLOCK异步阻塞	select,poll方案(阻塞) (异步通知方案)加 read NON_BLOCK ,并不是直接read监听的,都是异步非阻塞 // 也被称作多路复用	异步非阻塞	epoll 方案		epoll 方案是将poll 方案针对 socket 进行的优化,一般用于socket fd 的监听	fasync (非阻塞) 加 read NON_BLOCK(在信号中) // 也被称作 信号驱动IO	aio 方案 // 也被称作异步IO // aio方案有三种(https://my.oschina.net/yangpeng/blog/631905 http://blog.sina.com.cn/s/blog_533074eb01013zdp.html)当前介绍的是glibc 实现的aio方案		aio_read aio_error (循环检测,阻塞,没有换出) , aio_return // aio_return 得到读出的结果 // 其实是 异步阻塞方案		aio_read aio_suspend (阻塞,换出) , aio_return // aio_return 得到读出的结果 //异步非阻塞方案		填充 aio_sigevent (注册信号),aio_read , 有信号来时,执行信号函数(在函数中,aio_error,aio_return ) // 异步非阻塞方案		注意: aio_return 为 非阻塞函数.
  • 阻塞
阻塞// 读	fifo is empty		if(file->f_flags & O_NONBLOCK)			return _EAGAIN;		wait_event_interruptible(read_queue)// 其他路径	wake_up(read_queue);
  • 非阻塞
非阻塞// 读	fifo is empty		if(file->f_flags & O_NONBLOCK)			return _EAGAIN;
  • 异步阻塞 poll
多路复用	poll	    unsigned int mask = 0;	    poll_wait(file, &button_waitq, wait); /* 将进程挂接到button_waitq等待队列下 */ // 该api不会引起阻塞,  // poll_wait把自己挂入某个队列,这个队列也是我们的驱动自己定义的	    /* 根据实际情况,标记事件类型 */	    if (ev_press)	        mask |= POLLIN | POLLRDNORM;		    /* 如果mask为0,那么证明没有请求事件发生;如果非零说明有时间发生 */	    return mask;	//----用户空间	poll	//-----------内核空间		sys_poll			do_sys_poll				do_poll					for(;;)						do_pollfd(pfd, pt)							file->f_op->poll(file, pwait);					schedule_timeout(__timeout);	// --- 内核空间	除了休眠到指定时间被系统唤醒外,还可以被驱动程序唤醒──记住这点,这就是为什么驱动的poll里要调用poll_wait的原因task 1    DECLARE_WAITQUEUE(wait, current);    add_wait_queue(w_wait, wait);    __set_current_state(TASK_INTERRUPTIBLE);    schedule();    //task1 进程休眠    ................//唤醒后从这儿执行task 2 //唤醒task1   wake_up(w_wait);//可唤醒处于TASK_INTERRUPTIBLE和TASK_UNINTERRUPTIBLE状态的进程#define wake_up(x) __wake_up(x, TASK_NORMAL, 1, NULL)//只能唤醒处于TASK_INTERRUPTIBLE状态的进程#define wake_up_interruptible(x) __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)经过以上驱动程序的poll()函数应该返回设备资源的可获取状态,即POLLIN,POLLOUT,POLLPRI,POLLERR,POLLNVAL等宏的位"或"结果.每个宏的含义都表示设备的一种状态,如:常量	说明POLLIN	普通或优先级带数据可读POLLRDNORM	普通数据可读POLLRDBAND	优先级带数据可读POLLPRI	高优先级数据可读POLLOUT	普通数据可写POLLWRNORM	普通数据可写POLLWRBAND	优先级带数据可写POLLERR	发生错误POLLHUP	发生挂起POLLNVAL	描述字不是一个打开的文件SYSCALL_DEFINE3(poll, struct pollfd __user *, ufds, unsigned int, nfds, int, timeout_msecs)	do_sys_poll(ufds, nfds, to);		do_poll(nfds, head, &table, end_time);			poll_table* pt = &wait->pt;			for (;;) {
for (; pfd != pfd_end; pfd++) {
do_pollfd(pfd, pt, &can_busy_loop, busy_flag); } poll_schedule_timeout(wait, TASK_INTERRUPTIBLE, to, slack); } __put_user(fds[j].revents, &ufds->revents)static inline unsigned int do_pollfd(struct pollfd *pollfd, poll_table *pwait, pwait->_key = pollfd->events|POLLERR|POLLHUP; mask = f.file->f_op->poll(f.file, pwait); // 由驱动中设置. mask &= pollfd->events | POLLERR | POLLHUP; pollfd->revents = mask; // 用户空间在poll 返回之后,会在用户空间检查这个值.#define FD_ISSET(fd, fdsetp) __FD_ISSET (fd, fdsetp)#define __FD_ISSET(d, s) \ ((__FDS_BITS (s)[__FD_ELT (d)] & __FD_MASK (d)) != 0)如果当前不可读(先调用驱动.poll确定是否可读,然后继续do_poll),那么在sys_poll->do_poll中当前进程就会睡眠在等待队列上,这个等待队列是由驱动程序提供的(就是poll_wait中传入的那个)。当可读的时候,驱动程序可能有一部分代码运行了(比如驱动的中断服务程序),那么在这部分代码中,就会唤醒等待队列上的进程,也就是之前睡眠的那个,当那个进程被唤醒后do_poll会再一次的调用驱动程序的poll函数,这个时候应用程序就知道是可读的了。
  • 异步非阻塞 fasync
异步IO fasynchandler	main	act.sa_sigaction = handler;	sigaction(SIGIO,&act,&oldact)	open	fcntl() // 设置异步IO所有权	fcntk() // 设置SIGIO信号	fcntl()//获取文件flags	fcntl() // 设置FASYNC	while(1)---fops	.fasync	 = demo_fasyc;demo_fasyc	fasyc_helper(fd,file,on,xxx);write	kill_fasync(xxx,SIGIO,POLLIN);
  • 异步非阻塞 aio
aio 方案	libc		线程和阻塞IO模拟		#include 
linux 内核fops->aio_read 实现 .aio_read = pipe_read #include
libeio 类似libc方案 #include "eio.h"异步IO aio_readhttps://blog.csdn.net/summer_zgh/article/details/82416427http://blog.sina.com.cn/s/blog_6028e2630100y0d1.html
  • linux aio
#include 
#include
#include
#include
#include
#include
#include
#include
#include
int main(){ io_context_t ctx; unsigned nr_events = 10; memset(&ctx, 0, sizeof(ctx)); // It's necessary,这里一定要的 int errcode = io_setup(nr_events, &ctx); if (errcode == 0) printf("io_setup success\n"); else printf("io_setup error: :%d:%s\n", errcode, strerror(-errcode)); // 如果不指定O_DIRECT,则io_submit操作和普通的read/write操作没有什么区别了,将来的LINUX可能 // 可以支持不指定O_DIRECT标志 int fd = open("./direct.txt", O_CREAT|O_DIRECT|O_WRONLY, S_IRWXU|S_IRWXG|S_IROTH); printf("open: %s\n", strerror(errno)); char* buf; errcode = posix_memalign((void**)&buf, sysconf(_SC_PAGESIZE), sysconf(_SC_PAGESIZE)); printf("posix_memalign: %s\n", strerror(errcode)); strcpy(buf, "hello xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); struct iocb *iocbpp = (struct iocb *)malloc(sizeof(struct iocb)); memset(iocbpp, 0, sizeof(struct iocb)); iocbpp[0].data = buf; iocbpp[0].aio_lio_opcode = IO_CMD_PWRITE; iocbpp[0].aio_reqprio = 0; iocbpp[0].aio_fildes = fd; iocbpp[0].u.c.buf = buf; iocbpp[0].u.c.nbytes = page_size;//strlen(buf); // 这个值必须按512字节对齐 iocbpp[0].u.c.offset = 0; // 这个值必须按512字节对齐 // 提交异步操作,异步写磁盘 int n = io_submit(ctx, 1, &iocbpp); printf("==io_submit==: %d:%s\n", n, strerror(-n)); struct io_event events[10]; struct timespec timeout = { 1, 100}; // 检查写磁盘情况,类似于epoll_wait或select n = io_getevents(ctx, 1, 10, events, &timeout); printf("io_getevents: %d:%s\n", n, strerror(-n)); close(fd); io_destroy(ctx); return 0;}
  • glibc aio
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define BUFFER_SIZE 1025 int main(int argc,char **argv){ //定义aio控制块结构体 struct aiocb wr; int ret,fd; char str[20] = { "hello,world"}; //置零wr结构体 bzero(&wr,sizeof(wr)); fd = open("test.txt",O_WRONLY | O_APPEND); if(fd < 0) { perror("test.txt"); } //为aio.buf申请空间 wr.aio_buf = (char *)malloc(BUFFER_SIZE); if(wr.aio_buf == NULL) { perror("buf"); } wr.aio_buf = str; //填充aiocb结构 wr.aio_fildes = fd; wr.aio_nbytes = 1024; //异步写操作 ret = aio_write(&wr); if(ret < 0) { perror("aio_write"); } //等待异步写完成 while(aio_error(&wr) == EINPROGRESS) { printf("hello,world\n"); } //获得异步写的返回值 ret = aio_return(&wr); printf("\n\n\n返回值为:%d\n",ret); return 0;}
  • libeio
https://blog.csdn.net/lanyan822/article/details/7644745linux下主要有两套异步IO,一套是由glibc实现的(以下称之为glibc版本)、一套是由linux内核实现,并由libaio来封装调用接口(以下称之为linux版本)libeio 是 后期 开发的 , 类似 glibc 版本,相比 glibc 的aio 来说bug 比较少,而且架构清晰#include 
#include
#include
#include
#include
#include
#include
#include
#include
#include "eio.h" int respipe [2]; /* * 功能:子线程通知主线程已有回执放入回执队列. */voidwant_poll (void){ char dummy; printf ("want_poll ()\n"); write (respipe [1], &dummy, 1);} /* * 功能:主线程回执处理完毕,调用此函数 */voiddone_poll (void){ char dummy; printf ("done_poll ()\n"); read (respipe [0], &dummy, 1);}/* * 功能:等到管道可读,处理回执信息 */voidevent_loop (void){ // an event loop. yeah. struct pollfd pfd; pfd.fd = respipe [0]; pfd.events = POLLIN; printf ("\nentering event loop\n"); while (eio_nreqs ()) { poll (&pfd, 1, -1); printf ("eio_poll () = %d\n", eio_poll ()); } printf ("leaving event loop\n");} /* * 功能:自定义函数,用户处理请求执行后的回执信息 */intres_cb (eio_req *req){ printf ("res_cb(%d|%s) = %d\n", req->type, req->data ? req->data : "?", EIO_RESULT (req)); if (req->result < 0) abort (); return 0;} intmain (void){ printf ("pipe ()\n"); if (pipe (respipe)) abort (); printf ("eio_init ()\n"); if (eio_init (want_poll, done_poll)) //初始化libeio库 abort (); eio_mkdir ("eio-test-dir", 0777, 0, res_cb, "mkdir"); event_loop (); return 0;}
你可能感兴趣的文章
小蜗牛,慢慢爬
查看>>
Java关键字 -- Super
查看>>
Java -- 入口函数浅析
查看>>
EventBus 的简单使用
查看>>
Banner 浅析
查看>>
Android Crash框架Recovery
查看>>
限制 EditText 最多输入两位小数
查看>>
Android中attrs.xml文件的使用详解
查看>>
TabLayout 解析
查看>>
android获取屏幕尺寸、密度(判断手机屏幕类型)
查看>>
dpi 、 dip 、分辨率、屏幕尺寸、px、density 关系以及换算
查看>>
Android drawable 目录下不同精度 浅析
查看>>
Drawable 文件夹——xml文件
查看>>
OkHttp 新手上路
查看>>
Android 蓝牙如何使用
查看>>
自定义View三个方法的意义
查看>>
Java访问控制的作用
查看>>
横竖屏切换的生命周期
查看>>
广播的使用
查看>>
Python Requests库
查看>>