写这篇文章,需要感谢这位老哥提供的资源,好让我较为轻易的了解了select的实现原理和以此带来的弊处
首先,了解一下为什么要在服务器端使用多路复用:
当服务器出现要与多个客户端进行连接时,那么有下面几种办法:
1.多进程
当监听的时候来了一个新的请求时,一旦建立了一个连接,就会有一个已连接 Socket,这时候你可以创建一个子进程,然后将基于已连接 Socket 的交互交给这个新的子进程来做,这样的方式就是每当来了一个新的监听,就创建一个新的进程,这样就可以实现与多个客户的连接;
弊端:每次一个新的请求就创建一个新的子进程,当有很多个请求的时候,那么给创建的进程的资源就会占的越来越多,并且当结束交互的时候,就还要释放资源,这些的开销当请求过多的时候,就会很明显,如果在加上进程间通信,那么又会更麻烦
2.多线程
既然多进程可能会要很大的开销,那么可以想到的时,就用多线程来实现,这里有个比喻,如果创建进程相当于成立新公司,购买新办公家具,而创建线程,就相当于在同一个公司成立项目组。一个项目做完了,那这个项目组就可以解散,组成另外的项目组,办公家具可以共用。线程就避免了创建进程时,独立维护一份新的内存地址空间,它只需要维护一个新的PCB就可以了,而文件描述符,地址空间等等,都是共享的,既每来一个新的请求就创建一个线程
弊端:虽然创建线程只需要多一个pcb,很大的减少了资源的浪费,当时一个进程的内存地址空间是有限的,还是那个问题,当有更大量级的客户请求时,比如1万个连接,那么操作系统估计可能是频繁的进行换进换出,早崩溃了。如果维持 1 亿用户在线需要 10 万台服务器,成本也太高了。那可不可以在节省一点呢?
3.多路复用
刚才是一个项目组管理一个socket,那么可不可以一个项目组管理多个socket呢?那么就引入了多路复用,意思也就很明显了,就是要用尽量少的资源来传递更多的数据,现在使得一个线程管理多个socket(一个进程也可以使用多路复用),这个时候,每个项目组都应该有个项目进度墙(文件描述符列表),将自己组看的项目列在那里,然后每天通过项目墙看每个项目的进度,一旦某个项目有了进展,就读取数据,但是这里会有个问题,因为socket也就是文件描述符,那么当一个socket没有数据时,平时用read读就会发生阻塞,而一个项目组此时有多个socket,如果有一个没有数据可读时,那么此线程就会阻塞,那还能叫一个项目组管理多个项目吗?于是这里的多路复用用非阻塞IO来实现看管多个项目,这里暂且只说select,当它监管的这个fd_set,没有一个数据可读时,就会阻塞此线程,不过它会一直监听fd_set,当有一个fd可读时,那么就会返回一个新的项目进度表给线程来查看,然后线程只需要一个个比对,即可知道那个项目有进展了。
弊端:这里会在之后分析使用select实现多路复用的弊端
先介绍一下linux下select的用法:
int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);
解释一下各个参数的意思:
1.int maxfdp是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,因为文件描述符是从0开始的
2.struct fd_set可以理解为一个集合,这个集合中存放的是文件描述符(file descriptor),即文件句柄。fd_set集合可以通过一些宏由人为来操作。
- FD_ZERO(fd_set *fdset):清空fdset与所有文件句柄的联系。
- FD_S