i/o多路复用服务器

基于I/O 多路复用技术的并发服务器

1:i/o多路复用技术是为了解决进程或线程阻塞到某个i/o系统调用(即open,write,read,close等函数)而出现的技术,使进程不阻塞于某个特定的i/o系统调用;

2:阻塞i/o就是当某个进程没有收到i/o数据时,进程会睡眠在哪里,不回去运行后面的代码。阻塞i/o的好处是节省CPU,缺点是后面的任务不能被处理,进程的效率不高;

eg:accept()函数默认为阻塞函数,当没有客户请求时,则进程会阻塞在accept函数处。

3:非阻塞i/o就是当进程没有收到i/o数据时,进程不会睡眠在哪里,直接返回运行后面的代码。


在实际的应用中,要求一个服务器能同时处理大量的客户请求,所有这些客户将访问绑
定在某一个特定套接字地址上的服务器。因此,服务器必须满足并发的需求。如果不采用并
发技术,当服务器处理一个客户请求时,会拒绝其他客户端请求,造成其他客户要不断的请
求并长期等待。
在Linux(Unix)系统中并发服务器有三种设计方式:
(1)多进程
进程是执行中的计算机程序,可以认为是一个程序的一次运行。它是一个动态的实体,
是独立的任务。每个单独的进程运行在自己的虚拟地址空间中,并且它只能通过安全的内核
管理机制和其它进程交互。若是一个进程崩溃不会引起其它进程崩溃。
在Linux(Unix)系统中,多个进程可以同时执行相同的代码,从而支持并发。
对于单个CPU 系统而言,CPU 一次只能执行一个进程,但操作系统可通过分时处理,
每个进程在每个时间段中执行,因此对于用户而言,这些进程在同时执行。
(2)多线程
线程与进程类似,也支持并发执行。与进程不同的一点,在同一进程中所有线程共享
相同的全程变量以及系统分配给进程的资源。因此,线程占用较少的系统资源,并且线程之
间切换更快。
(3)I/O 多路复用(select 和poll 函数)
另一种支持并发的方法是I/O 多路复用。select()函数是系统提供的,它可以在多个描
述符中选择被激活的描述符进行操作。
例如:一个进程中有多个客户连接,即存在多个TCP 套接字描述符。select()函数阻塞
直到任何一个描述符被激活,即有数据传输。从而避免了进程为等待一个已连接上的数据而
无法处理其他连接。因而,这是一个时分复用的方法,从用户角度而言,它实现了一个进程
或线程中的并发处理。

I/O 多路复用技术的最大优势是系统开销小,系统不必创建进程、线程,也不必维护这
些进程/线程,从而大大减少了系统的开销。
select()函数用于实现I/O 多路复用,它允许进程指示系统内核等待多个事件中的任何一
个发生,并仅在一个或多个事情发送或经过某指定的时间后才唤醒进程。
它的原型如下,
#include<sys/time.h>
int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set * errorfds, struct timeval *timeout);
ndfs: select() 函数监视描述符数的最大值。根据进程中打开的描述符数而定,一般设为要
监视的描述符的最大数加1。
readfds: select() 函数监视的可读描述符集合。
writefds: select()函数监视的可写描述符集合。
errorfds: select()函数监视的异常描述符集合。
timeout: select()函数超时结束时间
返回值。如果成功返回总的位数,这些位对应已准备好的描述符。否则返回-1,并在errno
中设置相应的错误码。
FD_ZERO(fd_set *fdset):清空fdset 与所有描述符的联系
FD_SET(int fd, fd_set *fdset):建立描述符fd 与fdset 的联系
FD_CLR(int fd, fd_set *fdset):撤销描述符fd 与fdset 的联系
FD_ISSET(int fd,fd_set *fdset) ::检查与fdset 联系的描述符fd 是否可读写,返回非0表示可读写。
采用select()函数实现I/O 多路复用的基本步骤如下:
(1) 清空描述符集合
(2) 建立需要监视的描述符与描述符集合的联系
(3) 调用select()函数
(4) 检查所有需要监视的描述符,利用FD_ISSET 判断是否准备好
(5) 对已准备好的描述符进行I/O 操作

为了解决创建子进程带来的系统资源消耗,人们又想出了多路复用I/O模型. 
首先介绍一个函数select

int select(int nfds,fd_set *readfds,fd_set *writefds,
fd_set *except fds,struct timeval *timeout)
void FD_SET(int fd,fd_set *fdset)
void FD_CLR(int fd,fd_set *fdset)
void FD_ZERO(fd_set *fdset)
int FD_ISSET(int fd,fd_set *fdset)

一般的来说当我们在向文件读写时,进程有可能在读写出阻塞,直到一定的条件满足. 比如我们从一个套接字读数据时,可能缓冲区里面没有数据可读 (通信的对方还没有 发送数据过来),这个时候我们的读调用就会等待(阻塞)直到有数据可读.如果我们不 希望阻塞,我们的一个选择是用select系统调用. 只要我们设置好select的各个参数,那么当文件可以读写的时候select回"通知"我们 说可以读写了. readfds所有要读的文件文件描述符的集合
writefds所有要的写文件文件描述符的集合

exceptfds其他的服要向我们通知的文件描述符

timeout超时设置.

nfds所有我们监控的文件描述符中最大的那一个加1

在我们调用select时进程会一直阻塞直到以下的一种情况发生. 1)有文件可以读.2)有文件可以写.3)超时所设置的时间到.

为了设置文件描述符我们要使用几个宏. FD_SET将fd加入到fdset

FD_CLR将fd从fdset里面清除

FD_ZERO从fdset中清除所有的文件描述符

FD_ISSET判断fd是否在fdset集合中 

[html]  view plain  copy
  1. int use_select(int *readfd,int n)  
  2. {  
  3.    fd_set my_readfd;  
  4.    int maxfd;  
  5.    int i;  
  6.      
  7.    maxfd=readfd[0];  
  8.    for(i=1;i  
  9.     if(readfd[i]>maxfd) maxfd=readfd[i];  
  10.    while(1)  
  11.    {  
  12.         /*   将所有的文件描述符加入   */  
  13.         FD_ZERO(&my_readfd);  
  14.         for(i=0;i  
  15.             FD_SET(readfd[i],*my_readfd);  
  16.         /*     进程阻塞                 */  
  17.         select(maxfd+1,& my_readfd,NULL,NULL,NULL);   
  18.         /*        有东西可以读了       */  
  19.         for(i=0;i  
  20.           if(FD_ISSET(readfd[i],&my_readfd))  
  21.               {  
  22.                   /* 原来是我可以读了  */   
  23.                         we_read(readfd[i]);  
  24.               }  
  25.    }  
  26. }  
  27.   
  28. 使用select后我们的服务器程序就变成了.   
  29.   
  30.   
  31.         初始话(socket,bind,listen);  
  32.           
  33.     while(1)  
  34.         {  
  35.         设置监听读写文件描述符(FD_*);     
  36.           
  37.         调用select;  
  38.           
  39.         如果是倾听套接字就绪,说明一个新的连接请求建立  
  40.              {   
  41.                 建立连接(accept);  
  42.                 加入到监听文件描述符中去;  
  43.              }  
  44.        否则说明是一个已经连接过的描述符  
  45.                 {  
  46.                     进行操作(read或者write);  
  47.                  }  
  48.                           
  49.         }       

猜你喜欢

转载自blog.csdn.net/leikun153/article/details/80011399