linux socket i/o模型,51CTO博客-专业IT技术博客创作平台-技术成就梦想

在读redis源代码的过程中,我一直在考虑一个问题,就是“为什么单线程的redis能做到如此高效?”。为了弄清楚这个问题,我查阅了一些资料,大概搞清楚了epoll等I/O模型的发展及其原理,以下是一个记录整理。

##I/O模型

###操作系统与网络I/O

de65f436edfdee64c7b0fb2fff89ce81.png

上图来自维基百科,是一个基本的计算机结构。计算机主要完成两个工作,运算和I/O。因为CPU处理效率以及各种设备的I/O效率千差万别,所以协调I/O提高效率是操作系统的一个重要任务。各种I/O模型应运而生。

####同步和异步

计算机任务执行的实体是进程,进程和I/O的关系需要理清。根据执行I/O时进程的状态,可以把I/O操作分为同步和异步两种方式。

* 同步IO操作:引起进程的阻塞直到IO操作完成

* 异步IO操作:IO操作不会引起进程阻塞

其中同步和异步指进程和IO的关系,阻塞和非阻塞指进程的状态。结合操作系统的进程管理,可以认为最优的状态是:a)进程完全不被影响,继续执行直到I/O完成再处理 b)进程完全挂起,把资源交给其他进程,I/O完成后再醒来继续执行。

###网络IO模型

####阻塞IO

最基础的方式,问题在于进程阻塞于一个IO就不能响应其他请求,效率很低。

2121550.png

####非阻塞IO

使用轮询的方式,会占用大量的计算资源,应该只有特殊情况才会使用到。

2121551.png

####IO复用

关键在于复用,一个进程可以一次性等待多个IO。

2121552.png

####信号驱动IO

使用范围很小,在TCP下信号产生的过于频繁难以区分含义,所以只在使用UDP协议的程序中应用。“作者能找到的实际使用信号驱动的I/O程序是基于UDP的NTP服务器程序”。与异步IO的理念区别仅仅在于是否由操作系统自动拷贝数据到内核空间,我不明白为什么不直接发展成异步IO,反而要加入这个模型。

2121553.png

####异步IO

整个过程进程都是非阻塞的。

2121554.png

前4种都是同步的,第5种是异步

##socket

###socket基本概念

socket工作在会话层以上,通过绑定并监听指定端口,接收数据。

59aa87267e880b3a99bf2f8a24b5d86e.png

###socket示例

最简单的accept()方式是阻塞的,在建立连接之前程序挂起。可以使用while循环accept,不过这种方式read()也是阻塞的,所以在第一个连接结束之前,第二个连接无法被处理。

写代码中的两个有趣的点:

* listen函数的backlog有一个magic number 511

* close()之后有一个time_wait的过程,这时候如果再绑定相同端口会失败,可以使用setsockopt()

##改进

###使用多进程改进

在while循环中,每次accept就fork出一个新进程,这样就能同时处理多个连接。这里需要处理父进程与子进程的关系,引用计数,信号处理等问题。而且每次创建销毁进程消耗较大。

###使用select改进

在while循环中,使用select监听一个fd集合,当其中的fd可读/写/异常时从阻塞恢复。可以同时监听多个fd。当然读写也会阻塞,所以要配合多进程/多线程使用。因为不需要每个连接就生成一个新进程,比fork的方式要更优化。

存在的问题是:

* fd数量有限

* 每次都循环检查所有fd效率低

* 用户态和内核态内存拷贝效率低

###使用epoll改进

epoll针对select的问题做了优化:

* 上限是最大可以打开文件的数目

* 只关注“活跃”的链接不用循环检查

* 使用内存共享避免拷贝

使用epoll 主要调用3个API :

int epoll_create(int size); //2.6.8之后size参数被忽略,参考

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

另外特别需要注意的是水平触发(LT)和边缘触发(ET)的概念,以及events的处理(出错,client关闭等等)。

以上几种方式我都简单实现了,代码见【20】。

###性能对比测试

这篇论文【18】  对select/poll/epoll的性能做了对比测试。可以看出在所有I/O都活跃的情况下,select和epoll的性能相近。但存在大量空闲连接时,epoll的性能就明显高于select了,这与epoll的改进思路是相符合的。

在查找资料的过程中,我发现大部分资料都语焉不详或者思路不清晰。所以把IO和socket作为前置知识,结合了一些代码示例。希望应该能更好的理清思路,理解I/O模型为什么要这样设计和实际应用中为什么要这样改进。写的东西不多,干货都在参考文章中。

参考

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值