Chinaunix首页 | 论坛 | 博客
  • 博客访问: 4036137
  • 博文数量: 251
  • 博客积分: 11197
  • 博客等级: 上将
  • 技术积分: 6862
  • 用 户 组: 普通用户
  • 注册时间: 2008-12-05 14:41
个人简介

@HUST张友东 work@taobao zyd_com@126.com

文章分类

全部博文(251)

文章存档

2014年(10)

2013年(20)

2012年(22)

2011年(74)

2010年(98)

2009年(27)

分类: LINUX

2011-11-15 22:45:16

循环read(write)

在网络应用程序中,通常需要重复的调用read/write来读取到指定数量的数据,如下例:

int wrapper_read(int fd, char *buf, int size)

{

int ridx = 0;

int rlen = 0;

while(ridx < size) {
         rlen = read(fd, buf + ridx, size – ridx);

if(rlen < 0) {
         return rlen;

}

         ridx += rlen;

}

return ridx;

}

 

首先了解下read的返回值:

1.       ret > 0,表示读到ret字节的数据;

2.       ret = 0,表示对端关闭连接(调用了close)。

3.       ret = -1,出错,errno被设置。

 

为什么在读取磁盘文件时即使请求很大数量的数据,也只需要调用一次read,而读取网络数据时需要重复读写呢?这是由磁盘、网络的特性的决定的。

 

读取磁盘文件,数据都在本地,只需要定位到读取位置,便能读到需求的数据(尽量满足需求,如果read返回值小于指定的大小,则说明数据不足,如已经读到文件末尾);但网络上的数据都是由socket的对端发送过来的,什么时候到,一次到多少都是不确定的,如果没有数据到达socket缓冲区read便会阻塞(默认为阻塞模式,可通过fcntl设置为nonblock模式),当有数据时,read就会读取并返回,此时的数据可能并不完整,还有部分仍在传输中,故要想获取到指定量的数据,则需不断的调用read,直到读到需要的所有数据。

 

EPOLL LT vs ET

epoll有两种模式,水平触发(LT)和边沿触发(ET),其主要区别是,对事件触发的定义不同,以EPOLLIN事件来说,在LT模式下,只要缓冲区有数据可读,就认为事件EPOLLIN事件发生;而ET模式下,只有在缓冲区由无数据转可读转换成有数据可读时(类比电平的0-1跳转),才认为EPOLLIN事件发生。

 

如某一个连接的缓冲区最开始是空的,接着收到2K数据,在ETLT模式下,epoll调用时会告知该连接上EPOLLIN事件发生,之后缓冲区的数据被读取了1K,则在LT模式下,epoll再次调用时,仍会触发EPOLLIN事件(只要缓冲区有数据可读),而在ET模式下,则不会触发(不满足从无数据变到有数据这个条件)。

 

LT模式加上多线程的环境下,需要对epoll的事件做些特殊处理。如当某个事件(请求)到达时,服务器创建一个线程为其服务,而主线程继续调用epoll_wait,如果服务线程不能及时消费掉缓冲区的数据,则该连接上的EPOLLIN事件会再次被触发;特别地,在最后对端close后,触发EPOLLIN,然后服务线程为其服务,如果在服务线程确认对端关闭连接并closeepoll_wait被再次调用,EPOLLIN将再次被触发,服务线程将再次被触发,如果前一个服务线程已将连接关闭,这是read会返回EBADF(此时文件描述符已失效)。为了简化处理逻辑并提供性能,服务器应让epoll处理尽可能少的文件描述符,在确定某个文件描述符需要关闭时就应主动将其从epoll事件中移除,并调用close,而不是等待read返回0时被动调用close。

 

ET模式下,在读取连接上的数据时也需要做些特殊处理,首先需将socket设置为nonblock,然后read直到返回EAGAIN,即消费掉所有的数据(非阻塞模式下,无数据时会立即返回EAGAIN)

 

ETLT模式在内核中的实现原理是一样的,ET相比LT的优势在于减少了每次需要返回的 I/O句柄数量,在并发量极多的时候能加快epoll_wait 的处理; ET在处理请求时,因需要处理完所有的数据,通常会增加额外的处理逻辑及资源开销,总体收益如何只有经过实际测试方能确定。

阅读(2647) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~