异步io是kernel帮你的线程盯着该线程所要的数据是否可用,而线程可以去做别的事情。当数据可用时kernel会把数据拷贝到指定的内存地址然后通知通知你的线程。需要利用事件等机制来完成。
https://www.ibm.com/developerworks/cn/linux/l-cn-edntwk/
2014-11-24 18:25:15
1.select ,poll,epoll都是同步非阻塞的函数
对于select而言:
应用程序在open的时候需要设置O_NONBLOCK标志,这样在read的时候如果无数据可读,则会直接返回。
所以这种情况下,需要先调用select函数,对应驱动中的poll函数,如果驱动中无数据可读,驱动会将此线程加入等待队列,有数据可读时(和其他一些情况)就会唤醒对应线程。线程继续执行就直接调用read函数去读取数据就可以读到数据。
linux下socket没有实现异步
linux下串口没有实现异步
但是都实现了同步非阻塞,如下是serial_core.c中的poll接口
2017-6-16
对于io复用的使用有一个套路,叫reactor模式。使用这种框架写代码和直接使用io复用函数相比,主要是把业务作为回调函数去注册,这样事件发生时调用回调函数,从而实现业务代码和基础设施代码分离。
当然,reactor框架一般会把操作系统的大多数io事件都会集成进来.比如libev就实现了文件变化事件,网络io事件,定时器事件等
根据reator框架用多少个线程去执行回调函数,还可以初步分为单线程reactor和多线程reactor
和reactor相对的是proactor框架,他是把操作系统的异步函数用回调的机制实现出来。
同步阻塞的内核实现:
同步非阻塞的额内核实现:
2.异步io模型
先看一个例子
异步的内核实现:
同步io是你的线程自己去向内核查询所要的数据是否可用。在查询的时候,数据不可用的话:
如果内核将这个线程塞进等待队列,直到数据可用时,内核将线程加进可运行队列,并将可用数据给它,则是阻塞型io;
如果内核不将其塞进等待队列,而是直接给他一个error代码表示数据不可用,则是非阻塞io。执行非阻塞型io的线程需要一遍一遍的去询问内核数据是否可用,否则它可能读不到可用的数据。
可见-同步 异步 阻塞 非阻塞的关系。
①异步同步是指线程对数据获取的方式而言的,
异步:内核主动通知线程读写已经完成,
同步:线程主动询问内核读写就绪,可以去读或写了。
②而阻塞非阻塞是在线程主动询问内核时,内核对线程的不同处理方式。所以阻塞非阻塞都是在同步的情况下发生的。
即
|----异步
|----同步|-----阻塞
|-----非阻塞
执行同步io时,就是直接向内核索取数据,非阻塞io(r如read函数)会立即返回,阻塞io(如read函数)则在数据可用时返回。线程中需要对返回值检查,返回值一般是数据不可用时的错误代码或可用数据。
执行异步io时,就像向内核提交一个"申请当数据可用时的事件通知"的服务(所以可以立即返回),所以需要向内核提交一个事件参数,当数据可用时,内核自动读写数据到指定的内存地址并触发该事件。
当然执行异步io的谈不上阻塞非阻塞,因为他在执行异步io(比如read)时内核没机会没理由将它阻塞。
https://www.ibm.com/developerworks/cn/linux/l-cn-edntwk/
2014-11-24 18:25:15
1.select ,poll,epoll都是同步非阻塞的函数
对于select而言:
应用程序在open的时候需要设置O_NONBLOCK标志,这样在read的时候如果无数据可读,则会直接返回。
所以这种情况下,需要先调用select函数,对应驱动中的poll函数,如果驱动中无数据可读,驱动会将此线程加入等待队列,有数据可读时(和其他一些情况)就会唤醒对应线程。线程继续执行就直接调用read函数去读取数据就可以读到数据。
linux下串口没有实现异步
但是都实现了同步非阻塞,如下是serial_core.c中的poll接口
static const struct tty_operations uart_ops = {
.open = uart_open,
.close = uart_close,
.write = uart_write,
.put_char = uart_put_char,
.flush_chars = uart_flush_chars,
.write_room = uart_write_room,
.chars_in_buffer= uart_chars_in_buffer,
.flush_buffer = uart_flush_buffer,
.ioctl = uart_ioctl,
.throttle = uart_throttle,
.unthrottle = uart_unthrottle,
.send_xchar = uart_send_xchar,
.set_termios = uart_set_termios,
.set_ldisc = uart_set_ldisc,
.stop = uart_stop,
.start = uart_start,
.hangup = uart_hangup,
.break_ctl = uart_break_ctl,
.wait_until_sent= uart_wait_until_sent,
#ifdef CONFIG_PROC_FS
.proc_fops = &uart_proc_fops,
#endif
.tiocmget = uart_tiocmget,
.tiocmset = uart_tiocmset,
.get_icount = uart_get_icount,
#ifdef CONFIG_CONSOLE_POLL
.poll_init = uart_poll_init,
.poll_get_char = uart_poll_get_char,
.poll_put_char = uart_poll_put_char,
#endif
};
2017-6-16
对于io复用的使用有一个套路,叫reactor模式。使用这种框架写代码和直接使用io复用函数相比,主要是把业务作为回调函数去注册,这样事件发生时调用回调函数,从而实现业务代码和基础设施代码分离。
当然,reactor框架一般会把操作系统的大多数io事件都会集成进来.比如libev就实现了文件变化事件,网络io事件,定时器事件等
根据reator框架用多少个线程去执行回调函数,还可以初步分为单线程reactor和多线程reactor
和reactor相对的是proactor框架,他是把操作系统的异步函数用回调的机制实现出来。
同步阻塞的内核实现:
同步非阻塞的额内核实现:
2.异步io模型
先看一个例子
异步的内核实现: