Linux的I/O模型有下面几种:
1. 同步阻塞I/O: 用户进程进行I/O操作,一直阻塞到I/O操作完成为止。
2. 同步非阻塞I/O: 用户程序可以通过设置文件描述符的属性O_NONBLOCK,I/O操作可以立即返回,无数据准备好时,返回一个错误,一般采用轮询方式,查看某个操作是否就绪。
3.I/O复用: 用户进程可以对I/O事件进行阻塞,但是I/O操作并不阻塞。通过select/poll/epoll等函数调用来达到此目的。

4.信号驱动式I/O:让内核在描述符就绪时,发送SIGIO信号给用户进程。 对于UDP而言,数据报文到达,套接字发生错误时,会产生SIGIO信号。

对于TCP而言,产生SIGIO信号的条件(太多所以基本不会用这种方式)如下:

  1)监听套接字上,某个连接完成;

  2) 某个断连接请求已经发起;

  3) 某个断连接已经完成;

4)数据到达套接字,即TCP缓冲区;

  5)数据从套接字缓冲区发送走,即TCP缓冲区有可写空间;

  6)  发生异步错误;

信号驱动I/O,要求进程执行一下三个步骤:

1) 建立SIGIO的信号处理函数

2)设置套接字的属主,通常为fcntl函数的F_SETOWN设置

3)开启该套接字的信号驱动式I/O, fcntl的F_SETFL命令打开 O_ASYNC

5. 异步事件非阻塞I/O: 也叫做异步I/O(AIO),用户程序可以通过向内核发出I/O请求命令,不用等带I/O事件真正发生,可以继续做另外的事情,等I/O操作完成,

内核会通过函数回调或者信号机制通知用户进程。这样很大程度提高了系统吞吐量。

前4种I/O,都由用户进程,调用I/O函数,实现数据在内核和用户进程缓冲器间的拷贝,数据拷贝期间用户进程或多或少会阻塞一段时间(数据拷贝)。

而异步I/O,是内核完成数据拷贝之后才会通知用户进程 ,在此期间用户进程可以进行其他的操作。

下面就AIO做详细介绍:
要使用aio的功能,需要include头文件aio.h,在编译连接的时候需要加入POSIX实时扩展库rt.下面就aio库的使用做介绍。
1. AIO整个过程所使用的数据存放在一个结构体中,struct aiocb,aio control block.看看头文件中的定义:

/* Asynchronous I/O control block.  */
struct aiocb
{
  int aio_fildes;                /* File desriptor.  */ 
  int aio_lio_opcode;       /* Operation to be performed.*/
  int aio_reqprio;

volatile void *aio_buf;    /* Location of buffer.  */
  size_t aio_nbytes;        /* Length of transfer.  */ 
  struct sigevent aio_sigevent; /* Signal number and value.  */

}

struct sigevent {

  int sigev_notify; //Notification type.

  int sigev_signo; //Signal number.

  union sigval sigev_value; //Signal value.

  void (*sigev_notify_function)(union sigval); //Notification function.

  pthread_attr_t *sigev_notify_attributes; //Notification attributes.   // 指针,用于说明创建的线程属性

};

union sigval

{

  int sival_int;

  void *sival_ptr;

};

a)  sigev_notify 取值:

  SIGEV_NONE 什么也不做 ;

  SIGEV_SIGNAL 事件发生时,将sigev_signo 指定的信号(A queued signal)发送给指定的进程. ;

  SIGEV_THREAD 事件发生时,内核会(在此进程内)以sigev_notification_attributes为线程属性创建一个线程,

并且让它执行sigev_notify_function,传入sigev_value作为为一个参数

b) sigev_value

在sigev_notify = SIGEV_THREAD 时使用,作为sigev_notify_function 的参数

c)*sigev_notify_function)(union sigval)

函数指针(指向通知执行函数),在sigev_notify = SIGEV_THREAD 时使用, 其他情况下置为NULL.

d)sigev_notify_attributes

指向线程属性的指针,在sigev_notify = SIGEV_THREAD 时使用,指定创建线程的属性, 其他情况下置为NULL.

2. int aio_read(struct aiocb *aiocbp);

异步读操作,向内核发出读的命令,传入的参数是一个aiocb的结构,比如
struct aiocb myaiocb;
memset(&aiocb , 0x00 , sizeof(myaiocb));
myaiocb.aio_fildes = fd;
myaiocb.aio_buf = new char[1024];
myaiocb.aio_nbytes = 1024;
if (aio_read(&myaiocb) != 0)
{
  printf("aio_read error:%s/n" , strerror(errno));
  return false;
}

3. int aio_write(struct aiocb *aiocbp);
异步写操作,向内核发出写的命令,传入的参数仍然是一个aiocb的结构,当文件描述符的O_APPEND
标志位设置后,异步写操作总是将数据添加到文件末尾。如果没有设置,则添加到aio_offset指定的
地方,比如:
struct aiocb myaiocb;
memset(&aiocb , 0x00 , sizeof(myaiocb));
myaiocb.aio_fildes = fd;
myaiocb.aio_buf = new char[1024];
myaiocb.aio_nbytes = 1024;
myaiocb.aio_offset = 0;
if (aio_write(&myaiocb) != 0)
{
  printf("aio_read error:%s/n" , strerror(errno));
  return false;
}

4. int aio_error(const struct aiocb *aiocbp);
如果该函数返回0,表示aiocbp指定的异步I/O操作请求完成。
如果该函数返回EINPROGRESS,表示aiocbp指定的异步I/O操作请求正在处理中。
如果该函数返回ECANCELED,表示aiocbp指定的异步I/O操作请求已经取消。
如果该函数返回-1,表示发生错误,检查errno。

5. ssize_t aio_return(struct aiocb *aiocbp);
这个函数的返回值相当于同步I/O中,read/write的返回值。只有在aio_error调用后
才能被调用。

6. int aio_cancel(int fd, struct aiocb *aiocbp);
取消在文件描述符fd上的aiocbp所指定的异步I/O请求。
如果该函数返回AIO_CANCELED,表示操作成功。
如果该函数返回AIO_NOTCANCELED,表示取消操作不成功,使用aio_error检查一下状态。
如果返回-1,表示发生错误,检查errno.

7. int lio_listio(int mode, struct aiocb *restrict  list[],int nent, struct sigevent *sig);

使用该函数,在很大程度上可以提高系统的性能,因为再一次I/O过程中,OS需要进行
用户态和内核态的切换,如果我们将更多的I/O操作都放在一次用户太和内核太的切换中,
减少切换次数,换句话说在内核尽量做更多的事情。这样可以提高系统的性能。

用户程序提供一个struct aiocb的数组,每个元素表示一次AIO的请求操作。需要设置struct aiocb
中的aio_lio_opcode数据成员的值,有LIO_READ,LIO_WRITE和LIO_NOP。
nent表示数组中元素的个数。最后一个参数是对AIO操作完成后的通知机制的设置。

8. 设置AIO的通知机制,有两种通知机制:信号和回调
(1).信号机制
 首先我们应该捕获SIGIO信号,对其作处理:
 struct sigaction sig_act;
 sigempty(&sig_act.sa_mask);
 sig_act.sa_flags = SA_SIGINFO;
  sig_act.sa_sigaction = aio_handler;
  
  struct aiocb myaiocb;
  bzero( (char *)&myaiocb, sizeof(struct aiocb) );
  myaiocb.aio_fildes = fd;
  myaiocb.aio_buf = malloc(BUF_SIZE+1);
  myaiocb.aio_nbytes = BUF_SIZE;
  myaiocb.aio_offset = next_offset;
  
  myaiocb.aio_sigevent.sigev_notify = SIGEV_SIGNAL;
  myaiocb.aio_sigevent.sigev_signo = SIGIO;
  myaiocb.aio_sigevent.sigev_value.sival_ptr = &myaiocb;

ret = sigaction( SIGIO, &sig_act, NULL );

信号处理函数的实现:
 void aio_handler( int signo, siginfo_t *info, void *context )
 {
  struct aiocb *req;
  
  if (info->si_signo == SIGIO) {
    req = (struct aiocb *)info->si_value.sival_ptr;
    
    if (aio_error( req ) == 0) {
      ret = aio_return( req );
    }
  }
  return;
}

(2). 回调机制
需要设置:
myaiocb.aio_sigevent.sigev_notify = SIGEV_THREAD
my_aiocb.aio_sigevent.notify_function = aio_handler;

回调函数的原型:
typedef void (* FUNC_CALLBACK)(sigval_t sigval);

AIO机制为服务器端高并发应用程序提供了一种性能优化的手段。加大了系统吞吐量。

https://www.ibm.com/developerworks/cn/linux/l-async/  一个有少量例子的讲解

网络编程之异步IO的更多相关文章

  1. 网络编程之异步IO,rabbitMQ笔记

    对于网络并发编程而言,多线程与多进程算是最常见的需求场景了.毕竟网站开放就是想要更多的流量访问的. 回顾 回顾下之前学过的关于线程,进程和协程的知识点 IO密集型任务--用多线程更好计算密集型任务-- ...

  2. Java网络编程中异步编程的理解

    目录 前言 一.异步,同步,阻塞和非阻塞的理解 二.异步编程从用户层面和框架层面不同角度的理解 用户角度的理解 框架角度的理解 三.为什么使用异步 四.理解这些能在实际中的应用 六.困惑 参考文章 前 ...

  3. 网络编程 - 协议遇到IO自动切换

    一.协议遇到IO自动切换 python网络编程,遇到IO自动切换,通过模块gevent来实现: import gevent,time def g1(): print ("g1 is star ...

  4. IO模式设置网络编程常见问题总结—IO模式设置,阻塞与非阻塞的比较,recv参数对性能的影响—O_NONBLOCK(open使用)、IPC_NOWAIT(msgrcv)、MSG_DONTWAIT(re

    非阻塞IO 和阻塞IO: 在网络编程中对于一个网络句柄会遇到阻塞IO 和非阻塞IO 的概念, 这里对于这两种socket 先做一下说明:       基本概念: 阻塞IO:: socket 的阻塞模式 ...

  5. Python高级编程和异步IO并发编程

    第1章 课程简介介绍如何配置系统的开发环境以及如何加入github私人仓库获取最新源码. 1-1 导学 试看 1-2 开发环境配置 1-3 资源获取方式第2章 python中一切皆对象本章节首先对比静 ...

  6. Python编程-网络编程进阶(IO复用、Socketserver)

    一.认证客户端的链接合法性 如果你想在分布式系统中实现一个简单的客户端链接认证功能,又不像SSL那么复杂,那么利用hmac+加盐的方式来实现. 服务端 from socket import * imp ...

  7. Java网络编程 -- AIO异步网络编程

    AIO中的A即Asynchronous,AIO即异步IO.它是异步非阻塞的,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理,一般我们的业务处理逻辑会变成一个回调函数,等待IO操 ...

  8. JAVA基础知识之网络编程——-关于阻塞IO/非阻塞IO/同步IO/异步IO的一些参考文章

    Java NIO之多个Selector的实现Java NIO类库Selector机制解析(上) Java NIO类库Selector机制解析(下) https://www.zhihu.com/ques ...

  9. 【Unix网络编程】chapter6 IO复用:select和poll函数

    chapter6 6.1 概述 I/O复用典型使用在下列网络应用场合. (1):当客户处理多个描述符时,必须使用IO复用 (2):一个客户同时处理多个套接字是可能的,不过不叫少见. (3):如果一个T ...

随机推荐

  1. HDU 1074 Doing Homework ——(状态压缩DP)

    考虑到n只有15,那么状压DP即可. 题目要求说输出字典序最小的答案的顺序,又考虑到题目给出的字符串本身字典序是递增的,那么枚举i的时候倒着来即可.因为在同样完成的情况下,后选字典序大的,小的字典序就 ...

  2. MySql的Linux版安装

    1,官网下载MySql的Linux版: 下载地址:http://dev.mysql.com//downloads/mysql/ 2.上传到Linux服务器,建议放在/opt/目录下 3.检查当前系统是 ...

  3. Redis启动后基础只是讲解

    1.单进程 epoll是Linux内核为处理大批量文件描述符而作了改进的epoll,是Linux下多路复用IO接口select/poll的增强版本, 它能显著提高程序在大量并发连接中只有少量活跃的情况 ...

  4. MATLAB实现模糊控制

    一.简介 MATLAB软件有提供一个模糊推理系统编辑器,利用模糊工具箱在matlab命令窗口输入Fuzzy命令进入模糊控制编辑环境 二.主要步骤 1.接受输入变量 2.输入变量模糊化 3.利用模糊规则 ...

  5. Java 线程概述

    1 进程与线程基本概念 1.1 进程:执行中的程序 每个进程都有独立的代码和数据空间(进程上下文),进程空间切换会有较大的开销,一个进程包含1-n个线程.进程是资源分配的最小单位. 1.2 线程:进程 ...

  6. What do you do as a DevOps?

    https://ilhicas.com/2019/08/11/What-you-as-a-Devops.html Introduction In this post I'll just explain ...

  7. [SpringBoot/SpringMVC]从Webapp下载一个大文件出现java.lang.OutOfMemoryError: GC overhead limit exceeded怎么办?

    本文示例工程下载:https://files.cnblogs.com/files/xiandedanteng/WebFileDownload20191026.rar 制作一个Webapp,让其中一个网 ...

  8. Linux嵌入式学习过程(转载)

    嵌入式专业是一门实践性非常强的学科,只有多动手,多实践,多编程,多调试,多看书,多思考才能真正掌握好嵌入式开发技术.那么,如何从零开始学习嵌入式开发技术, 进入嵌入式开发大门呢,笔者根据自己的嵌入式学 ...

  9. 2.5 Go语言基础之map

    Go语言中提供的映射关系容器为map, Go中内置类型,其内部使用散列表(hash)实现,为引用类型. 无序键值对(key-value)集合,通过key(类似索引)快速检索数据 必须初始化才能使用. ...

  10. OpenStack Manila发展动态系列--Austin峰会

    1 Manila Mitaka版本概述 在Austin峰会上介绍到,Manila Mitaka发布版本Driver个数达到了18个, M版本新加入14家公司(中国公司继华为之后又有99cloud等公司 ...