1.非阻塞I/O
非阻塞I/O使我们可以调用不会永远阻塞的I/O操作,例如open,read和write。如果这种操作不能完成,则立即出错返回,表示该操作如继续执行将继续阻塞下去。对于一个给定的描述符有两种方法对其指定非阻塞I / O:
(1) 如果是调用open以获得该描述符,则可指定O_NONBLOCK标志
(2) 对于已经打开的一个描述符,则可调用fcntl打开O)NONBLOCK文件状态标志
 
 
2.I/O多路转接
select
select函数使我们可以执行I/O多路转接。传向select的参数告诉内核:我们所关注的描述符;对于每个描述符我们所关心的状态,以及我们愿意等待的时间。从select返回时,内核告诉我们:以准备好的描述符的数量,对于读、写或异常这三个状态中的每一个,那些描述符已经准备好。
#include <sys/select.h>
int select (int maxfdp1, fd_set *restrict readfds, fd_set *restrict writefds,
fd_set *restrict exceptfds, struct timeval *restrict tvptr);
//返回准备好的描述符的计数,超时返回0,错误返回-1。
  第一个参数maxfdp1的意思是“最大描述符加1”。也可将第一个参数设置为FD_SETSIZE,这是<sys/select.h>中的一个常数,它说明了最大的描述符数(经常是1024)。如果将第三个参数设置为我们所关注的最大描述符编号值加一,内核就只需在此范围内寻找打开的位,而不必在三个描述符集中的数百位内搜索。中间的三个参数readfds、writefds和exceptfds是指向描述符集的指针。这三个描述符集说明了我们关心的可读(readfds)、可写(writefd)或处于异常条件(wxcepfds)的各个描述符。每个描述符集存放在一个fd_set数据类型中。这种结构相当于一个描述符的数组,它为每个可能的描述符设置1位。

select的中间三个参数中的任意一个或全部都可以是空指针,这表示对相应状态不关系。如果所有三个指针都是空指针,则select提供了较sleep更精确的计时器。其等待时间可以小于1秒。
tvptr指定最后等待的时间,它的结构是:
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* and microseconds */
};
a. tvptr==NULL:永远等待。如果捕捉到一个信号则中断此无限等待。当所指定的描述符中的一个已经准备好或捕捉到一个信号则返回。如果捕捉到一个信号,则select返回-1,errno设置为EINTR.
b. tvptr->tv_sec==0&&tvptr_usec==0 完全不等待。测试所有的描述符并立即返回。这是得到多个描述符的状态而不阻塞select函数的轮询方法。
c. tvptr->tv_sec!=0||tvptr_usec!=0 等待指定的秒数或微秒数。当指定的描述符之一已准备好,或当指定的时间值已超过时立即返回。如果在超时还没有一个描述符准备好,则返回值是0
 
select有三个可能的返回值。
a. 返回值-1表示出错。这是可能发生的,例如在所指定的描述符都没有准备好时捕捉到一个信号。
b. 返回值0表示没有描述符准备好。若指定的描述符都没有准备好,而且指定的时间已经超过,则发生这种情况。
c. 返回一个正值说明了已经准备好的描述符数,在这种情况下,三个描述符集中仍旧打开的位是对应于已准备好的描述符位。
 
对fgset数据类型可以进行的处理是: (a)分配一个这种类型的变量, (b)将这种类型的一个变量赋与同类型的另一个变量,或(c)对于这种类型的变量使用下列四个宏:
#include <sys/select.h>
int FD_ISSET(int fd, fd_set *fdset);
//如果fd被设置返回非0,否则返回0。
void FD_CLR(int fd, fd_set *fdset);
void FD_SET(int fd, fd_set *fdset);
void FD_ZERO(fd_set *fdset);
调用FD_ZERO将一个fd_set变量的所有位置为0位。调用FD_SET设置一个fd_set变量的指定位,调用FD_CLR则将一指定位清除。最后,调用FD_ISSET测试一指定位是否设置
 
poll函数
poll函数类似于select,但是其调用形式则有所不同
include <poll.h>
int poll(struct poolfd fdarray[], nfds_t nfds, int timeout);
//返回准备好的描述符的计数,到时返回0,错误返回-1。
与select不同,poll不是为每个条件构造一个描述符集,而是构造一个pollfd结构数组,每个数组元素指定一个描述符编号以及对其所关心的条件。
struct pollfd {
int fd; /* file descriptor to check, or <0 to ignore */
short events; /* event of interest on fd */
short revents; /* event that occurred on fd */
};
在fdarray数组里的元素数由nfds指定。
poll的最后参数指明我们想要等待多久。和select一样,有三种情况
a.timeout == -1:永远等待     b.timeout == 0:不等待     c.timeout > 0
 
常量 说明
POLLIN 普通或优先级带数据可读
POLLRDNORM 普通数据可读
POLLRDBAND 优先级带数据可读
POLLPRI 高优先级数据可读
POLLOUT 普通数据可写
POLLWRNORM 普通数据可写
POLLWRBAND 优先级带数据可写
POLLERR 发生错误
POLLHUP 发生挂起
POLLNVAL 描述字不是一个打开的文件
 
epoll
1. int epoll_create(int size);
创建一个epoll的句柄。自从linux2.6.8之后,size参数是被忽略的。需要注意的是,当创建好epoll句柄后,它就是会占用一个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。
 
2. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epoll的事件注册函数,它不同于select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。
第一个参数是epoll_create()的返回值。
第二个参数表示动作,用三个宏来表示:
  EPOLL_CTL_ADD:注册新的fd到epfd中;
  EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
  EPOLL_CTL_DEL:从epfd中删除一个fd;
第三个参数是需要监听的fd。
第四个参数是
告诉内核需要监听什么事,struct epoll_event结构如下
//保存触发事件的某个文件描述符相关的数据(与具体使用方式有关)
typedef union epoll_data {
void *ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;
//感兴趣的事件和被触发的事件
struct epoll_event {
__uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};
events可以是以下几个宏的集合:
EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里
 
3. int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
收集在epoll监控的事件中已经发送的事件。参数events是分配好的epoll_event结构体数组,epoll将会把发生的事件赋值到events数组中(events不可以是空指针,内核只负责把数据复制到这个events数组中,不会去帮助我们在用户态中分配内存)。maxevents告之内核这个events有多大,这个 maxevents的值不能大于创建epoll_create()时的size,参数timeout是超时时间(毫秒,0会立即返回,-1将不确定,也有说法说是永久阻塞)。如果函数调用成功,返回对应I/O上已准备好的文件描述符数目,如返回0表示已超时。
 
 
效率:select <poll< epoll
主要却别在于内核实现上,select采用数组形式进行用户空间与内核空间的数组交互;poll在内核使用链表队列形式对监听的事件进行处理;epoll则是在内核实现了一个微型的文件系统,负责事件的管理。有兴趣的可以研究下,主要集中在select.c这个文件中。
 
3.存储映射I/O
  存储映射I/O使一个磁盘文件与存储空间中的一个缓存相映射。于是当从缓存中取数据,就相当于读文件中的相应字节。与其类似,将数据存入缓存,则相应字节就自动地写入文件。为了使用这种功能,应首先告诉内核将一个给定的文件映射到一个存储区域中。这是由mmap函数实现的
#include <sys/mman.h>
void *mmap(void *addr, size_t len, int prot, int flag, int filedes, off_t off);
//成功返回被映射区域的开始地址,错误返回MAP_FAILED。
addr参数用于指定映射存储区的起始地址,len是映射的字节数,filedes指定要被映射文件的描述符,off是要映射字节在文件中的起始位移量,prot参数指定映射区域的保护,此函数的返回地址是:该映射区的起始地址。
prot参数取值:

调用mprotect来改变一个已有映射上的权限。
#include <sys/mman.h>
int mprotect(void *addr, size_t len, int prot);
//成功返回0,错误返回-1。
调用msync来冲洗对被映射文件的改变
#include <sys/mman.h>
int msync(void *addr, size_t len, int flags);
//成功返回0,错误返回-1。
调用了munmap之后,存储映射区就被自动去除。
#include <sys/mman.h>
int munmap(caddr_t addr, size_t len);
//成功返回0,错误返回-1

最新文章

  1. docker——容器安装tomcat
  2. WPF仿Win7便笺
  3. JQ的live(),on(),deletage(),bind()几个的区别
  4. HC蓝牙模块测试AT指令搭建外部电路遇到的问题
  5. PHP isset()与empty()的使用区别详解
  6. Intellisense for Xrm.Page in CRM 2011
  7. Linux下的内核测试工具——perf使用简介
  8. Postgres的用户认证
  9. CSS实现限制字数功能
  10. Caliburn Micro框架
  11. php示例代码
  12. go语言学习--内核态和用户态(协程)
  13. LINUX LVM和快照卷配置和管理
  14. jQuery EasyUI 折叠面板accordion的使用实例
  15. js数组去除重复数据
  16. WinForm版图像编辑小程序(实现图像拖动、缩放、旋转、抠图)
  17. MySQL基础之 统计函数总结
  18. ssh连接linux服务器不断开- &quot;Write failed: Broken pipe&quot;
  19. js将UTC时间转化为当地时区时间
  20. linux安装nagios客户端

热门文章

  1. 2017.9.24 基于HTML+JavaScript+CSS的开发案例&amp;&amp;JavaScript+CSS+DIV实现表格变色
  2. 第50章 读写内部FLASH—零死角玩转STM32-F429系列
  3. 项目部署到自己的IIS上
  4. C#中 property 与 attribute的区别?
  5. 在c#中using和new这两个关键字有什么意义?
  6. Docker 日常指令
  7. 如何设置 html 中 select 标签不可编辑、只读
  8. Redis ----------String的操作
  9. form submit 的callback方法
  10. python之微信好友统计信息