目录
传统I/O模型(没有多路复用之前)
当一个文件描述符上发生I/O事件时,服务器会处理这个事件,这可能包括读取或写入数据、接受新的客户端连接、关闭客户端连接等操作。
传统的I/O模型中,每个I/O操作都是阻塞的,这意味着当一个I/O操作正在进行时,程序不能执行任何其他操作,必须等待I/O操作完成才能继续执行其他任务。每个I/O操作都会阻塞线程或进程,需要为每个客户端连接启动一个单独的线程或进程,从而限制了服务器的处理能力。
为每个客户端连接启动一个线程或进程也存在一些问题。首先,每个线程或进程都需要占用系统资源,包括内存和CPU时间,如果同时处理的客户端连接数量很大,会导致系统资源的浪费。其次,线程或进程之间的切换开销也会导致性能下降。
为什么需要多路复用机制
在某些情况下,程序需要同时处理多个I/O操作。例如,在一个网络服务器中,需要同时处理多个客户端连接,每个连接都需要进行I/O操作。如果使用传统的I/O模型,程序将无法同时处理多个客户端连接,而需要等待每个客户端连接的I/O操作完成后才能处理下一个客户端连接。
多路复用机制解决了这个问题,它使用一个线程或进程同时监听多个文件描述符,当文件描述符上发生I/O事件时,线程或进程可以立即处理这些事件,而不需要等待其他线程或进程完成。这样可以大大减少系统资源的使用,同时也可以避免线程或进程之间的切换开销。
多路复用机制和线程都是用于提高服务器的并发性能的技术,但是它们的实现方式和目的不同。多路复用机制通过一个线程或进程来同时监听多个文件描述符,以便在文件描述符上发生I/O事件时进行处理,从而避免为每个客户端连接启动一个线程或进程的开销。而线程是用于并发执行多个任务,从而提高程序的并发性能
多路复用机制
在服务器使用多路复用机制时,它通常会创建一个或多个套接字(socket)用于监听客户端连接。每个套接字都会被分配一个文件描述符,服务器使用多路复用机制来同时监听这些文件描述符。当一个文件描述符上发生I/O事件时,服务器会处理这个事件。
使用多路复用机制,服务器可以同时监听多个文件描述符,这意味着可以同时处理多个客户端连接的I/O操作。当一个客户端连接上发生I/O事件时,服务器就可以通过相应的文件描述符进行处理,而不需要为每个客户端连接启动一个单独的线程或进程。
多路复用机制发展历程
最早的多路复用机制是使用select
函数来实现的。随着计算机系统的发展,出现了更多的I/O事件,select
函数的性能开始变得越来越低。为了解决这个问题,出现了poll
函数,它的性能比select
更好。随着计算机系统的发展,poll
函数也开始变得性能不足,导致了新的多路复用机制的出现,其中最著名的是epoll
机制。
select,poll |
|
epoll |
|
当有一个或多个文件描述符准备好读取或写入数据时,select
和poll
函数会通知程序进行相应的I/O操作。但是,它们都存在以下一些问题:
- 当描述符集合较大时,它们的性能会受到影响,因为必须遍历整个集合。
- 它们在内核中使用了线性数据结构,导致操作效率低下。
为了解决这些问题,epoll
机制被引入到Linux内核中。与select
和poll
不同,epoll
机制使用一个文件描述符来代表所有被监视的文件描述符,从而减少了内核数据结构的大小,提高了操作效率。