select多路复用
1.I/O多路复用(IO Multiplexing)
IO多路复用模型是建立在内核提供的多路分离函数select基础之上的,使用select函数 可以避免同步非阻塞IO模型中轮询等待的问题,此外poll、epoll都是这种模型。在该种模式下,用户首先将需要进行IO操作的 socket添加到select中,然后阻塞等待select系统调用返回。当数据到达时,socket被激活,select函数返回。用户线程正式发起 read请求,读取数据并继续执行。从流程上来看,使用select函数进行IO请求和同步阻塞模型没有太大的区别,甚至还多了添加 监视socket,以及调用select函数的额外操作,效率更差。但是,使用select以后最大的优势是用户可以在一个线程内同时处理 多个socket的IO请求。用户可以注册多个socket,然后不断地调用select读取被激活的socket,即可达到在同一个线程内同时处 理多个IO请求的目的。
2.select()参数解析
select()函数允许进程指示内核等待多个事件(文件描述符)中的任何一个发生,并只在有一个或多个事件发生或经历一段指定时 间后才唤醒它,然后接下来判断究竟是哪个文件描述符发生了事件并进行相应的处理。
给出select()的参数
#include <sys/select.h>
#include <sys/time.h>
struct timeval
{
long tv_sec; //seconds
long tv_usec; //microseconds
};
FD_ZERO(fd_set* fds) //清空集合
FD_SET(int fd, fd_set* fds) //将给定的描述符加入集合
FD_ISSET(int fd, fd_set* fds) //判断指定描述符是否在集合中
FD_CLR(int fd, fd_set* fds) //将给定的描述符从文件中删除
int select(int max_fd, fd_set *readset, fd_set *writeset, fd_set *exceptset, struct timeval *timeout);
- select函数的返回值是就绪描述符的数目,超时时返回0,出错返回-1;
- 第一个参数max_fd指待测试的fd的总个数,它的值是待测试的最大文件描述符加1。Linux内核从0开始到max_fd-1扫描文件描述 符,如果有数据出现事件(读、写、异常)将会返回;假设需要监测的文件描述符是8,9,10,那么Linux内核实际也要监测0~7,此时真 正带测试的文件描述符是0~10总共11个,即max(8,9,10)+1,所以第一个参数是所有要监听的文件描述符中最大的+1。
- 中间三个参数readset、writeset和exceptset指定要让内核测试读、写和异常条件的fd集合,如果不需要测试的可以设置为 NULL;
- 最后一个参数是设置select的超时时间,如果设置为NULL则永不超时;
说明: select监视并等待多个文件描述符的属性发生变化,它监视的属性分3类,分别是readfds(文件描述符有数据到来可读)、 writefds(文件描述符可写)、和exceptfds(文件描述符异常)。调用后select函数会阻塞,直到有描述符就绪(有数据可读、可写、 或者有错误异常),或者超时( timeout 指定等待时间)发生函数才返回。当select()函数返回后,可以通过遍历 fdset,来找到 究竟是哪些文件描述符就绪。
3.示例流程图
下面是使用select()多路复用实现网络socket服务器多路并发的流程图:
4.代码示例
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<sys/types.h>
4 #include<string.h>
5 #include<errno.h>
6 #include<unistd.h>
7 #include<ctype.h>
8 #include<getopt.h>
9 #include<time.h>
10 #include<pthread.h>
11 #include<libgen.h>
12 #include<arpa/inet.h>
13 #include<sys/socket.h>
14 #include<netinet/in.h>
15
16 #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
17
18 static inline void msleep(unsigned long ms);
19 static inline void print_usage(char *progname);
20 int socket_server_init(char *listen_ip,int listen_port);
21
22 int main(int argc, char **argv)
23 {
24 int listenfd;
25 int connfd;
26 int daemon_run = 0;
27 char *progname = NULL;
28 int opt;
29 fd_set rdset;
30 int rv;
31 int i,j;
32 int found;
33 int maxfd = 0;
34 char buf[1024];
35 int fds_array[1024];
36 int serv_port =</