写点什么

浅谈 NIO 和 Epoll 实现原理

  • 2022 年 3 月 17 日
  • 本文字数:1512 字

    阅读完需:约 5 分钟

1 前置知识

1.1 socket 是什么?

就是传输控制层 tcp 连接通信。

1.2 fd 是什么?fd 既 file descriptor-文件描述符。Java 中用对象代表输入输出流等...在 Linux 系统中不是面向对象的,是一切皆文件的,拿文件来代表输入输出流。其实是一个数值,例如 1,2,3,4...0 是标准输入,1 是标准输出,2 是错误输出...

相关视频讲解:

C++后台开发学习视频

支撑亿级IO的底层基石 epoll实战揭秘

任何进程在操作系统中都有自己 IO 对应的文件描述符,参考如下:

[root@iZj6c1dib207c054itjn30Z ~]# ps -ef | grep redisroot     20688     1  0 Nov23 ?        00:01:01 /opt/redis/bin/redis-server 127.0.0.1:6379root     20786     1  0 Nov23 ?        00:01:00 /opt/redis/bin/redis-server 127.0.0.1:6380root     30741 30719  0 12:26 pts/1    00:00:00 grep --color=auto redis[root@iZj6c1dib207c054itjn30Z ~]# cd /proc/20688/fd[root@iZj6c1dib207c054itjn30Z fd]# lltotal 0lrwx------ 1 root root 64 Nov 23 21:50 0 -> /dev/nulllrwx------ 1 root root 64 Nov 23 21:50 1 -> /dev/nulllrwx------ 1 root root 64 Nov 23 21:50 2 -> /dev/nulllr-x------ 1 root root 64 Nov 23 21:50 3 -> pipe:[27847377]l-wx------ 1 root root 64 Nov 23 21:50 4 -> pipe:[27847377]lrwx------ 1 root root 64 Nov 23 21:50 5 -> anon_inode:[eventpoll]lrwx------ 1 root root 64 Nov 23 21:50 6 -> socket:[27847384]
复制代码

2 从 BIO 到 epoll

2.1 BIO

计算机有内核,客户端和内核连接产生 fd,计算机的进程或线程会读取相应的 fd。因为 socket 在这个时期是 blocking 的,线程读取 socket 产生的文件描述符时,如果数据包没到,读取命令不能返回,会被阻塞。导致会有更多的线程被抛出,单位 CPU 在某一时间片只能处理一个线程,出现数据包到了的线程等着数据包没到的线程的情况,造成 CPU 资源浪费,CPU 切换线程成本巨大。

例如早期 Tomcat7.0 版默认就是 BIO。


2.2 早期 NIO

socket 对应的 fd 是非阻塞的

单位 CPU 只用一个线程,就一颗 CPU 只跑一个线程,没有 CPU 切换损耗,在用户空间轮询遍历所有的文件描述符。从遍历到取数据都是自己完成,所以是同步非阻塞 IO。

问题是如果有 1000 个 fd,代表用户进程轮询调用 1000 次 kernel,

用户空间查询一次文件描述符就得调用一次系统调用,让后内核态用户态来回切换成本很大。

2.3 多路复用 NIO

内核向前发展,轮询放到内核里,内核增加一个系统调用 select 函数,统一把一千个 fd 传给 select 函数,内核操作 select 函数,fd 准备好后返回给线程,拿着返回的文件描述符找出 ready 的再去调 read。如果 1000 个 fd 只有 1 个有数据,以前要调 1000 次,现在只调 1 次,相对前面在系统调用上更加精准。还是同步非阻塞的,只是减少了用户态和内核态的切换。

注意 Linux 只能实现 NIO,不能实现 AIO。问题:用户态和内核态沟通的 fd 相关数据要来回拷贝。

2.4 epoll

内核通过 mmap 实现共享空间,用户态和内核态有一个空间是共享的,文件描述符 fd 存在共享空间实现用户态和内核态共享。epoll 里面有三个调用,用户空间先 epoll_create 准备一个共享空间 mmap,里面维护一个红黑树,内核态将连接注册进红黑树,epoll_ctl 写入。当有数据准备好了,调用 epoll_wait 中断阻塞,取链表 fd,再单独调用 read。

mmap 应用:kafka 实现数据通过 socket 存到服务器文件上的过程也是 mmap。

epoll 应用:redis 和 nginx 的 worker 进程等等。

想学习 C++工程化、高性能及分布式、深入浅出。性能调优、TCP,协程,Nginx 源码分析 Nginx,ZeroMQ,MySQL,Redis,MongoDB,ZK,Linux 内核,P2P,K8S,Docker,TCP/IP,协程,DPDK 学习资料视频获取点击:C++架构师学习资料

C++后台开发视频链接:C/C++Linux服务器开发高级架构师/Linux后台架构师​


用户头像

Linux服务器开发qun720209036,欢迎来交流 2020.11.26 加入

专注C/C++ Linux后台服务器开发。

评论

发布
暂无评论
浅谈NIO和Epoll实现原理_网络编程_Linux服务器开发_InfoQ写作平台