IT袋

当前位置:主页 > 经验教程 > 系统教程 >

Linux

Linux I/O模型详解(4)

时间:2024-01-14 17:25:08 来源:IT袋 作者:马勇
导读:Linux,下面是 select 的实现: public void serverStartSelect(){ // 启动服务端监听 ServerSocketChannel server = ServerSocketChannel.open(); server.configureBlocking(false); server.bind(new InetSo

Linux

下面是 select 的实现:

public void serverStartSelect(){
  // 启动服务端监听
  ServerSocketChannel server = ServerSocketChannel.open();
  server.configureBlocking(false);
  server.bind(new InetSocketAddress(8080));
  // register select
  Selector selector = Selector.open(); 
  server.register(selector, SelectionKey.OP_ACCEPT);
  while (true) {  //死循环
    Set<SelectionKey> keys = selector.keys();
    while (selector.select() > 0) {
      // 返回的有状态的 fd 集合
      Set<SelectionKey> selectionKeys = selector.selectedKeys();  
      Iterator<SelectionKey> iter = selectionKeys.iterator();
      // 如果 selectionKeys 不为空,表示存在连接 可读, 可写
      while (iter.hasNext()) {
        SelectionKey key = iter.next();
        iter.remove(); 
        if (key.isAcceptable()) {
          // 有 client 连接进来了, accept 建立连接
          ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
          SocketChannel client = ssc.accept(); 
          client.configureBlocking(false);
          // 将新建立的链接加入 select 管理
          client.register(selector, SelectionKey.OP_READ, buffer);
        } else if (key.isReadable()) {
          // 存在连接可读, 可以 read 读取该连接的数据
          read = client.read();
        }
      }
    }
  }
}

在 select 模式下, 应用程序将所有的连接交给 select 管理,程序中只需要关注 select 的返回数据,处理对应的事件。

这大大降低了程序的复杂度,看起来 select 很好,但在生产上用处依然不多。主要是因为 select 有以下劣势:

  1. 每次调用 select,都需要把 fdset 从用户态拷贝到内核态,在高并发下是个巨大的性能开销(可优化为不拷贝);
  2. 调用 select 阻塞后,用户进程虽然没有轮询,但在内核还是通过遍历的方式来检查 fd 的就绪状态(可通过异步 IO 唤醒的方式);
  3. select 只返回已就绪 fd 的数量,用户线程还得再遍历所有的 fd 查看哪些 fd 已准备好了事件(可优化为直接返回给用户进程数据已就绪的 fd 列表)。
/linux/posix_types.h:
# define __FD_SETSIZE         1024

此外在 Linux 内核中,select 所用到的 FD_SET 是有限的,即内核中有个参数 __FD_SETSIZE 定义了每个 FD_SET 的句柄个数。

所以 select 限制了 maxfd 的大小为 1024,表示只能监听 1024 个 fd 的事件,select 适用于小规模并发连接的应用,如聊天服务器、网络游戏服务器等,而不需要复杂的线程或进程管理,这离 C10k 显然还是有距离的。

poll

poll 的机制其实和 select 一样,唯一比较大的区别其实是把 1024 这个限制给放开了,虽然通过放开限制可以使内核监听上万 socket,但由于以上说的两点劣势,它的性能依然不高。

同 select 一样 poll 适用于小规模的并发连接,通常对于处理百甚至千级并发连接足够。

相关阅读