前言:
之前的文章里面,已经学习过了c/s框架模型,但是这个模型有一个缺点,就是服务端处理不了多个客户端的请求,所以今天我们开始来学习select多路复用!
一、一切皆文件:
Linux内核管理的一切东西,都可以看成是文件!那内核来管理什么呢?举简单例子来看-管理硬件,比如管理硬盘、网卡、内存等!在这样的设计理念下,所有的这些硬件都可以被看成文件了。
1、Linux中的文件是什么?
-
狭义:文件系统中物理意义上的文件(逻辑上关联的数据集合)
-
广义:设备、管道、内存,Linux管理的一切对象
2、理解文件描述符(File Descriptor)fd:
- 文件描述符是一个非负整数值,本质上是一个句柄(handle)
- 一切对用户透明的资源标识都可以看作句柄
- 用户使用文件描述符(句柄)与内核交互
- 内核通过文件描述符操作对应资源的数据结构
一般操作文件的操作方式有:
- open
- read
- write
- close
IO设备的文件操作方式:
#include <stdio.h>
#include <unistd.h>
int main()
{
int iofd =0;
char s[] = "mytest\n";
int len =0;
write(0, s, sizeof(s));
len = read(0, s , 2);
s[len] = 0;
printf("%s\n",s);
return 0;
}
3、阻塞、回调、轮询:
-
事件相关函数的分类:
- 阻塞式函数:函数调用后需要等待某个事件发生后才会返回,比如函数:scanf,read,accept
- 非阻塞式函数:函数调用后能够及时返回(仅标记等待的事件);事件发生后以回调方式传递;比如说,在淘宝上买东西,你下单了,不会说一直在那里等待买的东西到来吧,肯定这个时候去干别的事情了,只有快递信息到了,你才会去拿买的东西!这就是所谓的标记! -
阻塞与轮询:
- 轮询指依序询问每一个相关设备是否需要服务的方式
- 轮询可用用于解决阻塞函数导致程序无法继续执行的问题
- 这里还是以刚才的买东西为例,我们每隔3个小时去看看快递是否到了,没有到的话,就干别的事情!
所有解决阻塞函数导致的效率问题,有两种方法:
-
回调的方式
-
轮询的方式
4、神奇的select函数:
- 1、select()函数用于监视指定的文件描述符是否产生事件
- 2、可通过轮询的方式检测目标事件(事件产生则标记发生变化)
- 3、根据事件类型做出具体处理(如:读取数据)
int select(int maxfd,fd_set *readset, fd_set *writeset, fd_set *exceptset,
const struct timeval *timeout);
下面是select()函数的使用步骤流程:
select()相关数据类型及操作:
FD_ZERO(fd_set *fdset);//讲fd_set变量的所有位设置位0
FD_SET(int fd,fd_set *fdset);//讲fd_set中指定需要监听的fd
FD_CLR(int fd , fd_set *fdset); //在fd_set中剔除fd,不再监听
FD_ISSET(int fd, fd_set *fdset); //在fd_set 查看是否包含fd
下面是多路复用实战案例:
#include <stdio.h>
#include <sys/select.h>
#include <sys/time.h>
#include <unisted.h>
int main()
{
int iofd = 0;
char s[] = "linux\n";
fd_set reads = {
0};
fd_set temps = {
0};
struct timeval timeout = {
0};
FD_ZERO(&reads);//将fd_set变量的所有位设置位0
FD_SET(iofd, &reads);//将fd_set中指定需要监听的fd
while(1)
{
int ret = -1;
temps = reads;//这里为什么这样做,主要是为了避免之前设置的reads标记被select函数覆盖了
timeout.tv_sec = 0;
timeout.tv_usec = 50000;
ret = select(1,&temps,0,0,&timeout);
if(ret >0) //轮询的结果有事情发生
{
len = read(iofd, s ,sizeof(s) - 1);//都输入输出设备中的数据
s[len] = 0;
printf("%s\n",s);
}
else if(ret ==0)//轮询的结果没有事件发生,这里可以体现出轮询来,因为没有事件,所以走这里
{
static int count = 0;
usleep(10000);
count++;
if(count >100)
{
printf("do something else\n");
count = 0;
}
}
else//发生了错误
{
}
}
}