I/O复用

来源:互联网 发布:数据库导入excel 编辑:程序博客网 时间:2024/05/28 16:24

http://blog.sina.com.cn/s/blog_5f4344bf0100cklt.html

总的来说,I/O 处理的模型有种。http://www.360doc.com/content/12/0426/15/507289_206688004.shtml# ,http://blog.chinaunix.net/uid-24780853-id-2578681.html

· 阻塞 I/O模型在这种模型下,若所调用的I/O 函数没有完成相关的功能就会使进程

挂起,直到相关数据到才会出错返回。如常见对管道设备、终端设备和网络设备进行读写时

经常会出现这种情况。

· 非阻塞模型:在这种模型下,当请求的I/O 操作不能完成时,则不让进程睡眠,

而且返回一个错误。非阻塞I/O 使用户可以调用不会永远阻塞的I/O 操作,如openwrite

read。如果该操作不能完成,则会立即出错返回,且表示该I/O 如果该操作继续执行

就会阻塞。

· I/O多路转接模型:在这种模型下,如果请求的I/O操作阻塞,且它不是真正阻塞I/O

而是让其中的一个函数等待,在这期间,I/O 还能进行其他操作。如本节要介绍的select函数

poll函数,就是属于这种模型。

· 信号驱动 I/O 模型:在这种模型下,通过安装一个信号处理程序,系统可以自动

捕获特定信号的到来,从而启动I/O。这是由内核通知用户何时可以启动一个I/O 操作

决定的。

· 异步 I/O模型:在这种模型下,当一个描述符已准备好,可以启动I/O 时,进程会通

知内核。现在,并不是所有的系统都支持这种模型。


一。select(不需要设置监听的文件阻塞与否,但要注意非阻塞的可写

#include <sys/types.h>

#include <sys/times.h>
#include <sys/select.h>
int select(nfds, readfds, writefds, exceptfds, timeout)
int nfds;
fd_set *readfds, *writefds, *exceptfds;

struct timeval *timeout;

a 文件描述符就绪条件-socket可读

1.socket内核接收缓冲区大于等于低水位标记SO_RCVLOWAT,此时可以无阻塞的读,读操作返回字节数大于0

2.socket通信对方关闭连接,此时读改socket返回0

3.监听socket上有新连接请求

4.socket上有未处理的错误,此时我们可以用getsockopt来去读和清除该错误

b. socket可写的就绪条件

1.socket内核发送缓存区可以字节大于等于低水位标记SO_SENLOWAT.

2.socket的写操作关闭,此时执行写操作触发SIGPIPE信号

3.使用非阻塞connect连接成功或者失败(超时)之后

4.socket上有未处理的错误,此时可以读取和清除该错误

c .socket文件就绪条件---文件异常:socket上有带外数据

http://www.2cto.com/kf/201301/180982.html

   while(1)
   {
           //每次循环都要清空集合,否则不能检测描述符变化(超时情况下)
          FD_ZERO(&fdreads);
           FD_ZERO(&fdwrites);
            FD_ZERO(&fdexcepts);
 
            //添加描述符       
            FD_SET(sock, & fdreads);
            FD_SET(sock, & fdwrites);
            FD_SET(sock, & fdexcepts);
             
            //超时设置为1s
             timeout.tv_sec = 1;
             timeout.tv_usec = 0;
 
           switch(select(sock+1, & fdreads, & fdwrites, &fdexcepts, &timeout) )
          {
              case -1: //select错误,退出
                           perror(“select err”);
                           goto _out;
                case 0: //select超时,再次轮询
                           printf(“time out !\n”);
                           break;
               default:
                        //测试sock是否可读、可写或者fdexcepts异常,可写3个FD_ISSET函数
                      if(FD_ISSET(sock, &fds))
                      {
                          send/recv (sock, buffer, sizeof(buffer), 0); //或者错误处理fdexcepts
                      }
           }
     }

使用非阻塞 connect 需要注意的问是:http://www.cnblogs.com/yuxingfirst/archive/2013/03/08/2950281.html
1. 
connect返回0, connect 建立连接成功。

2. 返回不等于0,只有errno==EINROGRESS才表示连接还在进行,否则出错

Posix 定义了两条与 select 和 非阻塞 connect 相关的规定:
1)连接成功建立时,socket 描述字变为可写。(连接建立时,写缓冲区空闲,所以可写)
2)连接建立失败时,socket 描述字既可读又可写。 (由于有未决的错误,从而可读又可写)
如果只可写,说明连接成功

若描述符既可读又可写,分为两种情况:http://www.cnblogs.com/yuxingfirst/archive/2013/03/08/2950281.html

第一种情况是socket连接出现错误

第二种情况是connect连接成功,socket读缓冲区得到了远程主机发送的数据。需要通过connect连接后返回给errno的值来进行判定,或者通过调用 getsockopt(sockfd,SOL_SOCKET,SO_ERROR,&error,&len)来读取并清除socket上的错误,错误码errno等于0表明成功,否则失败; 

如何判断呢:用select,poll等监听可写事件,当函数返回后调用getsockopt函数,这里存在一个可移植性问题,非阻塞的socket可能导致connect始终失败;select对处于EINPROGRESS状态下的socket可能不起作用;getsockopt返回值不一致-1,或者0 。

fd_set writefds;
FD_ZERO(&writefds);
FD_SET(sockfd,&writefds);
timeout.tv_sec = 1;
timeout.tv_usec = 0;
ret=select(sockfd+1,NULL,&writefds,NULL,&timeout);
if(ret<=0)
return -1;//select超时或者出错
if( !FD_ISSET(sockfd,&writefds) )
{
return -1;//sockfd上没有可写事件
}
int error=0; 
if (getsockopt(c_fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {            cout << "getsockopt error." << endl;            return -1;        }
if(error!=0){
cout<<"connection failed"<<end1;
return -1;
}
     /*成功*/   cout << "success << endl;

二。epoll函数

1.默认是LT模型(不处理下次还会触发提醒),ET(触发事件通知后必须立即处理)需要手动设置,同时因为要立即处理完,所以像读数据就得循环读取,直到全部读取完毕,读取完毕后还不能阻塞了程序,所以还要使用非阻塞的文件描述符

int setnonblocking(int fd)

{

int old=fcntl(fd,F_GETFL);

int new=old|O_NONBLOCK;

fcntl(fd,F_SETFL,new);

return old;

}

epoll_event event;

event.data.fd=fd;

event.events=EPOOLIN|EPOLLET;//监听可读,并设置et

epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&event)

setnonblocking(fd);//非阻塞


2.EPOLLONESHOT事件:避免一个socket上的某次业务因频繁通信而多次触发事件(ET模式时事件也可能被多次触发)。特别是用多线程处理时,若是多次触发事件,就会造成多个线程处理同一个socket的问题。epooloneshot使得最多触发一个可读、可写或者异常事件,且最多触发一次,除非用epoll_ctl重置

event.events=EPOOLIN|EPOLLET|EPOOLONESHOT;//监听可读,并设置et,EPOOLONESHOT

epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&event)

当断定这次业务处理完成后,应该重置,以便epoll_wait 还能捕获提醒某事件

event.events=EPOOLIN|EPOLLET|EPOOLONESHOT;//监听可读,并设置et,EPOOLONESHOT

epoll_ctl(epollfd,EPOLL_CTL_MOD,fd,&event)


select和poll采用轮询的方式,epoll采用回调的方式,当活动连接多的时候,回调函数频繁触发,效率就未必比select和Poll好了,所以epoll适用于连接数据量多,但活动连接少的情况。

0 0
原创粉丝点击