为什么要IO多路复用:
1:进程执行过程是线性的,调用低速的系统,比如read write accept等的时候,进程在这里阻塞着,不能执行其他操作;考虑这样一个问题:服务端阻塞调用read等待客户端发送数据,那个直到客户端write数据的时候 才继续往下执行;单对单没有问题;
2:多对单呢?那么服务器这边安排套接字分别去对应客户端,此时服务端阻塞read(sockedfd1),此时另一个客户端数据 服务器安排的sockerfd2,不能处理了啊 你阻塞在了fd1.
3:引出IO多路复用技术,例子 循环去找着几个安排的socket,当其中有一个发送来消息后 从阻塞里面返回;调用read读取收到消息的socketfd,然后有循环回select阻塞;这样就不会因为阻塞在其中一个上而不能处理另一个客户的消息;
定义:实现一个线程可以监视多个文件句柄,一旦某i个就绪 就能够通知应用程序进行对应的读写操作;没有文件句柄时候就会阻塞应用程序,交出CPU。多路指的是网络连接,复用指的是同一个线程。
1:BIO 同步阻塞:单线程 accept一个请求 recv或者send阻塞就傻傻等着吧,无法处理其他请求。无法处理并发
2:多线程 accept一个请求后 开启线程recv 请求书增加 线程数增加 线程切换 开销 资源浪费
同步非阻塞:服务器端当accept一个请求后,加入fds集合,每次轮询一遍fds集合recv(非阻塞)数据,没有数据则立即返回错误,每次轮询所有fd(包括没有发生读写事件的fd)会很浪费cpu
Q:
那这样子,在读取socket1的数据时,如果其它socket有数据来,那么也要等到socket1读取完了才能继续读取其它socket的数据吧。那不是也阻塞住了吗?而且读取到的数据也要开启线程处理吧,那这和多线程IO有什么区别呢?
A:
1.CPU本来就是线性的 不论什么都需要顺序处理 并行只能是多核CPU
2:多路复用本来就是用来解决对多个I/O监听时,一个I/O阻塞影响其他I/O的问题,跟多线程没关系.
3.跟多线程相比较,线程切换需要切换到内核进行线程切换,需要消耗时间和资源. 而I/O多路复用不需要切换线/进程,效率相对较高,特别是对高并发的应用nginx就是用I/O多路复用,故而性能极佳.但多线程编程逻辑和处理上比I/O多路复用简单.而I/O多路复用处理起来较为复杂.
accept函数是新的TCP连接。
现在的做法:
服务器端采用单线程通过select/epoll等系统调用获取fd列表,遍历有事件的fd进行accept/recv/send,使其能支持更多的并发连接请求
fds = [listen_fd]
// 伪代码描述
while(1) {
// 通过内核获取有读写事件发生的fd,只要有一个则返回,无则阻塞
// 整个过程只在调用select、poll、epoll这些调用的时候才会阻塞,accept/recv是不会阻塞
for (fd in select(fds)) {
if (fd == listen_fd) {
client_fd = accept(listen_fd)
fds.append(client_fd)
} elseif (len = recv(fd) && len != -1) {
// logic
}
}
}