对设备的同步、异步访问

	对一个设备的同步操作的意思是,调用者(一般是控制器或CPU)对这个设备发出了某些指令操作,只有这个操作完成后,调用者才能去做别的事。	

 
 
同步方式意味着对于一个函数的调用要到所要求的操作完成时才会返回。以读操作为例,就是要到读到了一些数据的时候才返回, 如果一时读不到就使调用者睡眠等待,直到完成所要求的操作 ,对于读操作通常就是多少读到一些数据。返回时可以通过一些参数说明操作的结果和状态,例如究竟读到了多少数据等。
在Linux中对设备的同步访问有4种IO模型:阻塞式IO,非阻塞式, IO复用(select\poll,epoll)和 信号驱动式IO.它们对设备的访问实际上都 是同步的
而异 步方式就不同了。在异步方式中,对于一个函数的调用不必等待其所要求的操作完成,只要启动了某个过程就可以返回,这样调用者就不必睡眠等待,而可以先干点别的事情。以读磁盘操作为例,读磁盘是需要一定时间的,因为需要把磁头移到所需的磁道上并且稳定下来,再等所需的扇区转到磁头下面才可以开始读出。如果是同步方式,启动该过程的线程在此期间就只好睡眠等待(让别的线程活动),直到读出数据。而如果是异步方式,则该线程无须让出CPU,可以先做些别的操作或计算。但是,这个线程怎么能知道所启动的操作确实是完成了或失败了,总之是有了结果呢?换言之,异步操作的启动者与其所启动的过程之间怎么能重新取得同步呢?
linux中使用异步通知的方式实现,应用程序可以在设备可用时收到一个信号(从而在应用程序要用到信号注册函数),而不需要不停地使用轮询来关注设备。p168/LDD3
	调用者对设备进行同步IO访问时,发出IO请求的线程会被挂起。而异步IO时发出请求的线程不会被挂起,它可以继续执行。异步IO请求传给了设备驱动程序,被加入到驱动程序的请求队列中,驱动程序负责实际的IO操作。当设备驱动程序完成了对队列中IO请求的处理,无论成功与否都必须通知应用程序 
     异步IO非常关键的一点就是将IO请求加入驱动程序队列,这是设计高性能可伸缩应用程序的本质所在。
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

3.fafasync简介\来在百科

	异步通知fasync应用于系统调用signal和sigaction函数,简单的说,signal函数就是让一个信号与与一个函数对应,每当接收到这个信号就会调用相应的函数
	那么什么是异步通知?异步通知类似于中断的机制,当设备可写时,设备驱动函数发送一个信号给内核,告知内核有数据可读,在条件不满足之前,并不会造成阻塞。而不像之前学的阻塞型IO和poll,它们是调用函数进去检查,条件不满足时还会造成阻塞。

3.1应用层中启用异步通知机制

其实在应用层启用异步通知只三个步骤:
1)signal(SIGIO, sig_handler);
调用signal函数,让指定的信号SIGIO与处理函数sig_handler对应。
2)fcntl(fd, F_SET_OWNER, getpid());
指定一个进程作为文件的“属主(filp->owner)”,这样内核才知道信号要发给哪个进程。这样属主进程的进程ID号就被保存在filp->f_owner中,这一步是必须的,目的是为了让内核知道应该通知哪个进程。
3)f_flags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, f_flags | FASYNC);
	在设备文件中添加FASYNC标志,驱动中就会调用将要实现的test_fasync函数。
三个步骤执行后,一旦有信号产生,相应的进程就会收到。

3.2驱动中需要实现的异步通知

3.2.1 异步通知内核实现

实现异步通知,内核需要知道几个东西:哪个文件(filp),什么信号(SIGIIO),发给哪个进程(pid),收到信号后做什么(sig_handler)。这些都由上述前两个步骤完成了,而这前两个步骤内核帮忙实现了,所以,我们只需要实现第三个步骤的一个简单的传参。

3.2 fasync_struct结构体

要实现传参,我们需要把一个结构体struct fasync_struct添加到内核的异步队列中,这个结构体用来存放对应设备文件的信息(如fd, filp)并交给内核来管理。一但收到信号,内核就会在这个所谓的异步队列头找到相应的文件(fd),并在filp->owner中找到对应的进程PID,并且调用对应的sig_handler了。

struct fasync_struct {

int magic;

int fa_fd;

struct fasync_struct *fa_next; /* singly linked list */

struct file *fa_file;

};

3.3 内核中我们的工作

上面说了前两个步骤会由内核完成,所以我们只要做两件事情:
1)定义结构体fasync_struct。
struct fasync_struct *async_queue;
2)实现test_fasync,用函数fasync_helper将fd,filp和定义的 结构体传给内核,即把设备登记到内核的异步队列中。
int test_fasync (int fd, struct file *filp, int mode)
{
struct _test_t *dev = filp->private_data;
return fasync_helper(fd, filp, mode, &dev->async_queue);
}
函数fasync_helper的定义为:
int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)
前面的三个参数其实就是teat_fasync的三个参数,所以只要我们定义好的fasync_struct结构体也传进去就可以了。


另外还有两件事:
3)当设备可写时,调用函数kill_fasync发送信号SIGIO给内核。
if (dev->async_queue){
kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
}
讲解一下这个函数:
void kill_fasync(struct fasync_struct **fp, int sig, int band)
sig就是我们要发送的信号。
band(带宽),一般都是使用POLL_IN,表示设备可读,如果设备可写,使用POLL_OUT
4)当设备关闭时,需要将fasync_struct从异步队列中删除:
test_fasync(-1, filp, 0);
删除也是调用test_fasync,不过改了一下参数而已。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值