js css html

Linux之select、poll、epoll讲解

2023-01-11  本文已影响0人  上善若泪

1 select、poll、epoll

1.1 引言

操作系统在处理io的时候,主要有两个阶段:

我们一般将上述过程简化理解为:

selectpollepoll都是IO多路复用的机制。I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但selectpollepoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间

1.2 IO和Linux内核发展

1.2.1 整体概述

整体关系流程:


请添加图片描述

查看进程文件描述符:

获取pid进程号
ps -ef 
查看文件描述符
cd  /proc/进程号/fd ; ll

或者查看当前进程的fd  
$$ 表示 Shell 本身的 PID  (ProcessID)
cd  /proc/$$/fd ; ll

1.2.2 阻塞IO

计算机是有内核(kernel)的,内核向下连接很多的客户端,内核向上连接进程或线程,早先内核通过read命令读取文件描述符(fd),在这个时期socketblocking(阻塞的)BIO

如下图所示:线程通过内核读取文件fd8,读取到用户空间后,在通过内核写入文件fd9,如果fd8阻塞了,它会阻挡后面的操作


请添加图片描述

1.2.3 非阻塞IO

socket fd nonblock(非阻塞),进程/线程用一个,用循环遍历文件描述符(轮询发生在用户空间),这个时期是同步非阻塞时期NIO
这是由于内核socket本身就是nio,同步非阻塞IO

请添加图片描述

1.2.4 select

如果有1000个文件描述符fd,代表用户进程轮询调用1000次内核(kernel),造成成本很大的问题。于是在内核中增加了一个系统调用select,用户空间调用新的系统调用,统一将所有的文件描述符传给select,内核监控文件描述符的完成度,文件描述符完成之后返回,返回之后还有系统调用,再调用read(有数据的文件描述符),这个叫多路复用NIO,在这个时期,文件描述符考来考去成为累赘;

在这里插入图片描述

1.2.5 共享空间

共享空间是进程用户空间一部分,也是内核空间的一部分
引入一个共享空间mmap,将文件描述符放在共享空间里,文件描述符放在共享空间的红黑树里,将资源齐全的文件描述符放到链表

在这里插入图片描述

1.2.6 零拷贝

什么是零拷贝

在操作系统中,使用传统的方式,数据需要经历几次拷贝,还要经历用户态/内核态切换

  1. 从磁盘复制数据到内核态内存;
  2. 从内核态内存复制到用户态内存;
  3. 然后从用户态内存复制到网络驱动的内核态内存;
  4. 最后是从网络驱动的内核态内存复制到网卡中进行传输。
    在这里插入图片描述
    所以,可以通过零拷贝的方式,减少用户态与内核态的上下文切换和内存拷贝的次数,用来提升I/O的性能。零拷贝比较常见的实现方式是mmap,这种机制在Java中是通过MappedByteBuffer实现的。
    在这里插入图片描述
    sendfile,是完成零拷贝的命令,两个参数一个写出io,一个读入io
    在之前是先读取文件到用户空间,再写到内核中去,有了sendfile后,用这一个命令就可以了,不用读取写入
    在这里插入图片描述

1.3 select

1.3.1 简介

单个进程就可以同时处理多个网络连接的io请求(同时阻塞多个io操作)。基本原理就是程序呼叫select,然后整个程序就阻塞状态,这时候,kernel内核就会轮询检查所有select负责的文件描述符fd,当找到其中那个的数据准备好了文件描述符,会返回给selectselect通知系统调用,将数据从kernel内核复制到进程缓冲区(用户空间)

在这里插入图片描述
下图为select同时从多个客户端接受数据的过程
虽然服务器进程会被select阻塞,但是select会利用内核不断轮询监听其他客户端的io操作是否完成
在这里插入图片描述

1.3.2 select缺点

select的几大缺点:

1.4 poll介绍

1.4.1 与select差别

poll的原理与select非常相似,差别如下:

1.4.2 poll缺点

poll的几大缺点:

1.5 epoll

1.5.1 epoll相关函数

epoll:提供了三个函数:

1.5.2 epoll优点

epoll解决的问题:

这个准备就绪list链表是怎么维护的呢?

当我们执行epoll_ctl时,除了把socket放到epoll文件系统里file对象对应的红黑树上之外,还会给内核中断处理程序注册一个回调函数,告诉内核,如果这个句柄的中断到了,就把它放到准备就绪list链表里;当一个socket上有数据到了,内核在把网卡上的数据copy到内核中后就来把socket插入到准备就绪链表里了

一颗红黑树,一张准备就绪句柄链表,少量的内核cache,就帮我们解决了大并发下的socket处理问题。执行epoll_create时,创建了红黑树就绪链表,执行epoll_ctl时,如果增加socket句柄,则检查在红黑树中是否存在,存在立即返回,不存在则添加到树干上,然后向内核注册回调函数,用于当中断事件来临时向准备就绪链表中插入数据。执行epoll_wait时立刻返回准备就绪链表里的数据即可

上一篇 下一篇

猜你喜欢

热点阅读