多路复用I/O模型之select
2017-06-16 17:08
267 查看
1 .所谓I/O多路复用是指内核一旦发现进程指定的一个或者多个I/O条件准备读取,它就通知该进程。I/O多路复用适用如下场合:
(1)当客户处理多个描述字时(一般是交互式输入和网络套接口),必须使用I/O复用。
(2)当一个客户同时处理多个套接口时,而这种情况是可能的,但很少出现。
(3)如果一个TCP服务器既要处理监听套接口,又要处理已连接套接口,一般也要用到I/O复用。
(4)如果一个服务器即要处理TCP,又要处理UDP,一般要使用I/O复用。
(5)如果一个服务器要处理多个服务或多个协议,一般要使用I/O复用。
2 .在多路I/O复用的场合中,应用程序需要同时处理多路输入流和输出流,若我们采用阻塞的模式,则达不到预期的目的;若采用非阻塞的模式,对多个输入进行轮询,会很浪费cpu 的时间;若采用多进程/多线程来处理这样的问题,会使程序变得更加复杂,并且不易维护;因此最好的方法就是采用“多路I/O复用”。
3 .select函数
该函数允许进程指示内核等待多个事件中的任何一个发生,并只在有一个或多个时间发生或经历一段指定的时间后才唤醒;
该函数原型为:
函数说明:
select()函数用来等待文件描述符状态的改变。参数nfds代表最大的文件描述符+1,参数readfds、writefds、execptfds称为“描述符组”,用来传回描述符的读、写或或异常的状况;
select中各参数含义:
nfds:表示集合中需要检查的号码最高的文件描述符+1
readfds:由select()函数监听的读文件描述符集合
writefds:由select()函数监听的写文件描述符集合
execptfds:由select()函数监视的异常处理文件描述符集合
timeout:
对于timeout;它是一个结构体指针,原型为:struct timeval *timeout;这个时间结构体的精确度可以设置到微妙级。该结构体如下:
该结构体的变量设置有3种情况:
select函数是一个阻塞的函数,当select函数返回时,也就是有文件描述符可以读写数据的时候,此时可以用读写函数完成数据的读写;其返回值表示有多少个文件有消息;和阻塞模式相比,多路复用技术的高级之处就在于能够同时去等待多个文件描述符,当这些文件描述符中的任意一个进入就绪状态时,select函数就可以返回。
结论:select函数是一个“阻塞”函数,当该函数执行时,必定会有下面2个事件中的一个;
1.select监测的文件I/O集合中,有文件有消息
2. 时间超时,通过select的第5个参数指定超时的时间
4 .在使用select函数的时候需要对文件描述符进行分类别处理,当文件描述符的处理主要涉及到4个宏函数;分别是:
5 .select函数的原理为:
从set集合中挑选出有数据的fd,一旦set集合中有文件描述符发送状态改变,则select立刻退出阻塞状态,然后继续向下执行相应的操作。
6 .示例 1
linux的/dev/input/下分别有一个mice鼠标读写事件文件和event1键盘读写事件文件;当用open打开这两个文件,并且对其进行读操作时,都是一个阻塞的操作;即当键盘的按键被按下,或者鼠标被点击的时候,才会解除阻塞,开始读写;现在用select的i/o复用模型知识来解决这个问题;用一个进程(主程序)来处理,当有键盘读写时候,立刻去调用个键盘打印函数;若有鼠标读写时,立刻执行鼠标的打印函数;
示例2:
(1)当客户处理多个描述字时(一般是交互式输入和网络套接口),必须使用I/O复用。
(2)当一个客户同时处理多个套接口时,而这种情况是可能的,但很少出现。
(3)如果一个TCP服务器既要处理监听套接口,又要处理已连接套接口,一般也要用到I/O复用。
(4)如果一个服务器即要处理TCP,又要处理UDP,一般要使用I/O复用。
(5)如果一个服务器要处理多个服务或多个协议,一般要使用I/O复用。
2 .在多路I/O复用的场合中,应用程序需要同时处理多路输入流和输出流,若我们采用阻塞的模式,则达不到预期的目的;若采用非阻塞的模式,对多个输入进行轮询,会很浪费cpu 的时间;若采用多进程/多线程来处理这样的问题,会使程序变得更加复杂,并且不易维护;因此最好的方法就是采用“多路I/O复用”。
3 .select函数
该函数允许进程指示内核等待多个事件中的任何一个发生,并只在有一个或多个时间发生或经历一段指定的时间后才唤醒;
该函数原型为:
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); 所需要头文件: /* According to POSIX.1-2001 */ #include <sys/select.h> /* According to earlier standards */ #include <sys/time.h> #include <sys/types.h> #include <unistd.h>
函数说明:
select()函数用来等待文件描述符状态的改变。参数nfds代表最大的文件描述符+1,参数readfds、writefds、execptfds称为“描述符组”,用来传回描述符的读、写或或异常的状况;
select中各参数含义:
nfds:表示集合中需要检查的号码最高的文件描述符+1
readfds:由select()函数监听的读文件描述符集合
writefds:由select()函数监听的写文件描述符集合
execptfds:由select()函数监视的异常处理文件描述符集合
timeout:
对于timeout;它是一个结构体指针,原型为:struct timeval *timeout;这个时间结构体的精确度可以设置到微妙级。该结构体如下:
struct timeval *timeout{ long tv_sec; /* second 秒 */ long tv_unsec; /* and microseconds 微秒 */ }
该结构体的变量设置有3种情况:
1. NULL:表示“永远等待”。直到捕捉到信号或文件描述符已经准 备好为止。如果捕捉到一个信号,则select返回-1,errno 设置为EINTR; 2. timeout->tv_sec == 0 && timeout->tv_usec == 0; 表示“完全不等待”;测试所有已指定的描述符并立即返回。这是 得到多个描述符的状态而不阻塞select函数的轮询方法; 3. timeout->tv_sec != 0 && timeout->tv_usec != 0; 表示“等待指定的秒数和微秒数”。当指定的描述符之一(readfds 、writefds或execptfds)已经准备好,或当指定的时间值已经 超过了的时候则立即返回。如果在超时时候还没有一个文件描述符 已经准备好,则返回值为0;
select函数是一个阻塞的函数,当select函数返回时,也就是有文件描述符可以读写数据的时候,此时可以用读写函数完成数据的读写;其返回值表示有多少个文件有消息;和阻塞模式相比,多路复用技术的高级之处就在于能够同时去等待多个文件描述符,当这些文件描述符中的任意一个进入就绪状态时,select函数就可以返回。
结论:select函数是一个“阻塞”函数,当该函数执行时,必定会有下面2个事件中的一个;
1.select监测的文件I/O集合中,有文件有消息
2. 时间超时,通过select的第5个参数指定超时的时间
4 .在使用select函数的时候需要对文件描述符进行分类别处理,当文件描述符的处理主要涉及到4个宏函数;分别是:
select文件描述符集处理宏 /* 清除一个文件描述符集合 */ FD_ZERO(fd_set *set) /* 将一个文件描述符加入文件描述符集合中 */ FD_SET(int fd, fd_set *set) /* 将一个文件描述符从文件描述符集合中清除*/ FD_CLR(int fd, fd_set *set) /* 测试该集合中的一个文件描述符有无发送变化 */ FD_ISSET(int fd, fd_set *set) 通常,在使用select函数之前,首先应该调用FD_ZERO 和FD_SET 来初始化文件描述符集,在使用了select函数时,可以循环使用F_ISSET来测试哪一个文件描述符就绪,在执行完相关的文件描述符 之后,可使用FD_CLR来清除该文件描述符。
5 .select函数的原理为:
从set集合中挑选出有数据的fd,一旦set集合中有文件描述符发送状态改变,则select立刻退出阻塞状态,然后继续向下执行相应的操作。
6 .示例 1
linux的/dev/input/下分别有一个mice鼠标读写事件文件和event1键盘读写事件文件;当用open打开这两个文件,并且对其进行读操作时,都是一个阻塞的操作;即当键盘的按键被按下,或者鼠标被点击的时候,才会解除阻塞,开始读写;现在用select的i/o复用模型知识来解决这个问题;用一个进程(主程序)来处理,当有键盘读写时候,立刻去调用个键盘打印函数;若有鼠标读写时,立刻执行鼠标的打印函数;
有两个注意的地方: (1) 需要修改/dev/input/mice 和 /dev/input/event1这 两个文件的权限 (2) 在打印鼠标响应事件和键盘响应事件的地方,每次把set里面 的值取出来,不然每次来判断里面都有数据,会陷入一个死循环中; 结果:当有鼠标被按下的时候,打印“鼠标被响应....”;当有键盘被 按下的时候,打印“键盘被响应....”;
/************************************************************************* * File Name: select.c * Author: The answer * Function: Other * Mail: 2412799512@qq.com * Created Time: 2017年06月16日 星期五 16时14分19秒 ************************************************************************/ #include<stdio.h> #include<stdlib.h> #include<string.h> #include<sys/types.h> #include<errno.h> #include<sys/stat.h> #include<fcntl.h> #include<signal.h> #include<time.h> #include<unistd.h> void sys_err(const char *ptr,int num) { perror(ptr); exit(num); } int main(int argc,char **argv) { int fd_mice = open("/dev/input/mice",O_RDONLY); if(fd_mice < 0) sys_err("open mice",-1); int fd_key = open("/dev/input/event1",O_RDONLY); if(fd_key < 0) sys_err("open keyboard",-2); while(1) { struct timeval timeout; fd_set set; int maxfd; /* 取出两个文件描述符中的最大者 */ maxfd = fd_mice > fd_key ? fd_mice : fd_key; /* 初始化集合set */ FD_ZERO(&set); /* 在set集合中加入相应的描述集 */ FD_SET(fd_mice,&set); FD_SET(fd_key, &set); /* 设置select阻塞等待最长的时间(秒数) */ timeout.tv_sec = 5; timeout.tv_usec = 0; /* select有准备就绪的文件描述符就返回,否则等待 * 5秒后报错 * */ int ret = select(maxfd + 1,&set,NULL,NULL,&timeout); if(ret == 0 ) { printf("time out\n"); continue; //表示被信号中端给打断 }else if(ret == -1 && errno == EINTR){ printf("signal inturrupt\n"); continue; }else if(ret == -1) { printf("bad error\n"); break; }else if(ret > 0) { char buf[1024]; /* bzero将buf中的sizeof(buf)字节空间全部清零 */ bzero(buf,sizeof(buf)); if(FD_ISSET(fd_key,&set)) { printf("键盘有消息....\n"); /* 记得将数据读出,不然会死循环 */ read(fd_key,buf,sizeof(buf)); } if(FD_ISSET(fd_mice,&set)) { puts("鼠标有消息....\n"); /* 记得将数据读出,不然会死循环 */ read(fd_mice,buf,sizeof(buf)); } } } }
示例2:
相关文章推荐
- linux下多路复用模型之Select模型
- Linux下的socket编程实践(七) I/O多路复用技术之select模型
- 多路IO复用模型 select epoll 等
- 多路IO复用模型 select epoll
- IO模型及多路复用IO(select、poll、epoll)
- Linux网络通信编程(套接字模型TCP\UDP与IO多路复用模型select\poll\epoll)
- 多路复用I/O模型select() 模型 代码实现
- 多路IO复用模型 select epoll 等
- select的socket server多路复用模型
- Linux网络通信编程(套接字模型TCP\UDP与IO多路复用模型select\poll\epoll)
- C/S通信---服务器IO多路复用模型之select的使用
- I/O多路复用之select模型
- 从零开始写select和epoll I/O多路复用网络模型
- Linux下的socket编程实践(七) I/O多路复用技术之select模型
- linux下多路复用模型之Select模型
- linux下多路复用模型之Select模型
- 多路复用机制——SELECT
- Linux :IO多路复用模型
- Linux--高级I/O多路复用之select
- I/O多路复用之select()简要总结分析一(错误之处请指出)