I/O模型以及I/O复用

一个简单的服务器端程序:


#include "unp.h"
int main(int argc,char**argv)
{
int listenfd,connfd;
pid_t childpid;
socklet_t clilen;
struct sockaddr_in cliaddr,servaddr;
listenfd=socket(AF_INET,SOCK_STREAM,0);

bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_addr.s_addr=hton1(INADDR_ANY);
servaddr.sin_port=htons(serv_port);


bind(listenfd,(SA*)&servaddr,sizeof(servaddr));


listen(listenfd,LISTENQ);
for(;;)
{
clilen=sizeof(cliaddr);
connfd=accept(listenfd,(SA*)&cliaddr,&clilen);
if((childpid==fork())==0))
{
close(listenfd);//子进程关闭监听接口

while(n=read(sockfd,buf,maxline)>0)

writen(sockfd,buf,n);

exit(0);
}
close(connfd);//关闭连接
}

}

对于上面的这个简单的服务器程序,如果客户阻塞于read调用期间,服务器进程被杀死了。服务器TCP虽然正确的给客户TCP发送了一个FIN,但是既然客户阻塞在从标准输入读入字节流,它看不到这个EOF,知道可以从套接口读为止。这样的进程需要一种预先告知内核的能力,使得内核一旦发现进程指定的一个或多个IO条件就绪,内核就通知进程读数据,这就是I/O复用,是由select和poll这两个函数支持的。

I/O复用模型,主要用在以下场合:

1、当客户处理多个描述字的时候,必须使用I/O复用

2、一个客户同时处理多个描述字是有可能得,不过比较少见。

3、一个TCP既要处理监听套接口,又要处理已连接的套接口

4、一个服务器既要处理TCP又要处理UDP

5、一个服务器要处理多个进程和协议


在介绍I/O模型之前,首先介绍UNIX下的5种I/O模型

1、阻塞I/O模型:上面的那个服务器简单的例子就是阻塞I/O模型,如果接收不到数据,则进程阻塞在读数据上,直到有数据到达,进程继续执行。

2、非阻塞I/O模型:当应用进程从服务器端读取数据时,若内核无数据准备好,并不是阻塞进程,而是则返回一个错误,过一段时间后,进程再次以轮询的方式读取数据,重复上面步骤,直到数据准备好,返回给用户进程成功读取的信息。这么做使进程从阻塞IO中脱离出来,但是不挺的轮询耗费大量的cpu时间,不过这种模型偶尔也会遇到,通常是在只专门提供某种功能的系统中才有。

3、I/O复用模型,有了I/O复用模型,我们就可以调用色了select和poll函数,阻塞在这两个系统调用的某一个之上,而不是阻塞于真正的I/O系统调用之上。我们阻塞于select调用,等待数据报接口变为可读,当select返回套接口可读这一条件时,我们在吧所有数据读到进程缓冲区。

4、信号驱动I/O:我们可以用信号,让内核在描述字就绪时发送SIGIO信号通知我们,我们称这种模型为信号驱动I/O。我们首先开启套接口的信号驱动I/O功能,并通过sigaction系统调用安装一个信号处理函数,该系统调用立即返回,我们的进程继续工作,也就是说它没有阻塞。当数据报准备好事,内核发送一个signo信号,应用进程捕捉到信号并且进入信号处理函数,在信号处理函数中读取数据报。这个模型的优势在于等待数据报到达期间,进程不被阻塞,主循环可以继续执行。

5、异步I/O模型:由posix规范定义。一般的说,这个模型的工作机制是:告知内核启动某种操作,并让内核在整个操作完成后通知我们。这种模型与信号驱动I/O的主要区别在于:信号驱动I/O是有内核通知我们何时可以启动一个I/O操作,二异步I/O模型是有内核通知我们操作何时完成。


I/O复用中的select函数和poll函数:

int select(int maxfdp1,fd_set*readset,fd_set*write_set,fd_set*exceptset,const struct timeval *timeout)

maxfdp1 : 描述字最大值 
  readset : 读描述字集 
  writeset : 写描述字集 
  exceptset : 异常条件的描述字集 
  timeout : 等待时间 
  
  readset, writeset和exceptset  
  让内核测试读、写和异常条件所需的描述字。 
  为这三个参数的每一个指定一个或多个描述字  
  描述字集,是一个整数数组,每个数中的每一位对应一个描述字。  
  数组的第一个元素对应于描述字0-31,  
  数组户的第二个元素对应于描述字32—63。  

poll 函数 
  原型: 
  
  int poll (struct pollfd *fdarray, unsigned long nfds, int timeout ) 
  
  第一个参数是指向结构数组第一个元素的指针: 
  struct pollfd{ 
  int fd; // descriptor 
  short events // events of interest on fd 
  short revents // events that occured on fd 
  } 
  第二个参数是套接字个数,第三个参数是等待时间 .

一个使用select的回射函数

void str_cli(file*fp,int sockfd)

{

int   maxfdp1;

fdset rset;

char  sendline[maxline],receive[maxline];

FD_ZERO(&rset);

for(;;)

{

fd_set(fileno(fp),&rset);

fd_set(sockfd,&rset);

maxfdp1=max(fileno(fp),sockfd)+1;

select(maxdp1,&rset,null,null,null);

if(fd_isset(sockfd,&rset){//套接口可读

...

}

if(fd_isset(fileno(fp),,&rset)标准输入可读

{

。。。。

}

}

}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值