Linux内核发家史
作者:互联网
Kernel:操作系统内核
fd:文件描述符,Linux一切皆文件,不管是文本文件还是网络 Socket 都有其文件描述符。其实就是文件的一个索引值,任何一个进程都有文件描述符
一、BIO
客户端连接过来,假设是文件描述符 fd 8,用户空间的一个线程过去read fd 8 阻塞等着处理,有数据就处理,没数据就阻塞等待;这个弊端很明显,线程很可能空闲着,浪费资源。socket 在这个时期是 blocking 的
二、NIO
为了避免浪费资源,现在假设我们的 CPU 只有一颗,可以在多个客户端连接过来的时候(他们都有不同的文件描述符,Linux 一切皆文件),用户空间让一个进程去轮询查看所有的文件描述符是不是有数据需要处理,如果有需要 read 的就告诉 Kernel 要去 read 哪一个文件描述符,这个线程跟上面的对比,就是没有阻塞等待,而是可以轮询着处理很多的Client,这就是非阻塞
那什么是同步异步呢,其实这里的用户空间一直干活这个线程,肾么事都要自己肝,就是同步,同步异步说的就是一个线程要去做一件事情,是调用一下让别人做呢,还是自己做
这里的轮询,尽管解决了 Blocking 的问题,但是依然有很明显的缺陷,就是这个线程太累了,一直轮询消耗 CPU 资源
三、多路复用
为了避免无限循环带来的性能消耗,用户空间将文件描述符一次性都给了 Kernel 内核空间的 Selector,这个 Selector 负责筛选有哪些 fd 是需要 read 的,然后再写回去用户空间,用户空间告诉 Kernel 这些 fd 要 read,然后内核才去读取文件描述符,这就是多路复用(epoll — event poll)
这样的多路复用也有一个明显的缺陷,就是文件描述符需要再用户态和内核态间烤来烤去的,很耗时
四、共享空间
现在问题就是拷贝耗时,解决办法就是在内核态和用户态间共享一片空间,文件描述符都放到红黑树里,然后内核将需要读取的放到链表里
零拷贝
这里的共享空间并不等同于零拷贝,零拷贝说的是 sendfile(out, in) ,是另一种系统调用
在 sendfile 出现之前,有两个系统调用,一个是 read(fd),一个是 write(fd)。现在有两个 socket 过来内核,我们想做的就是,将一个是磁盘文件通过内核,写到网卡去,它们的 fd 分别是 fd4,fd3。这个过程是这样的:用户空间调用了 read fd3,内核先把文件拿到内核的缓冲区 Buffer,把fd3读到了用户空间,然后又 write fd4 拷回来内核,然后再写到网卡。这里又拷贝的过程
sendfile 对其改进就是零拷贝:用户空间调用 sendfile,内核拿文件到缓冲区,然后直接就写出去到网卡了
最佳实践Kafka
Kafka 由Scala 和 Java 混合编写,编译成字节码执行在 JVM 上
数据通过网卡,进入到 Linux 内核,然后进入到 Kafka,通过 mmap 内存映射,Kafka 得以快速将数据写入文件。消费者(Client)调用 sendfile,也可以快速 read 数据
标签:文件,read,用户,描述符,fd,内核,Linux,发家史 来源: https://blog.csdn.net/qq_38238041/article/details/114557210