热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

I/O复用及编程模型

原文:IO多路复用和Socket由于IO操作涉及到系统调用,涉及到用户空间和内核空间的切换,所以理解系统的IO模型,对于需要进入到系统调用层面进行编程来说是很重要的。阻塞IO和非阻

原文:I/O多路复用和Socket

由于IO操作涉及到系统调用,涉及到用户空间和内核空间的切换,所以理解系统的IO模型,对于需要进入到系统调用层面进行编程来说是很重要的。

阻塞IO和非阻塞IO

从程序编写的角度来看,I/O就是调用一个或多个系统函数,完成对输入输出设备的操作。输入输出设置可以是显示器、字符终端命令行、网络适配器、磁盘等。操作系统在这些设备与用户程序之间完成一个衔接,称为驱动程序,驱动程序向下驱动硬件,向上提供抽象的函数调用入口。

一般来说I/O操作是需要时间的,因为这涉及到系统、硬件等计算器模块的互相配合,所以必然不像普通的函数调用那样能够按照既定的方式立即返回。从用户代码的角度,I/O操作的系统调用分为“阻塞”和“非阻塞”两种。

  • “阻塞”的调用会在I/O调用完成前,挂起调用线程,即CPU会不再执行后续代码,而是等到I/O完成后再回来继续执行,在用户代码看来,线程停止执行了,在调用处等待了。

  • “非阻塞”的调用则不同,I/O调用基本上是立即返回,而且往往实际上I/O此时并没有完成,所以需要用户的程序轮询结果。

那么我们以网络IO为例,看一下对于一个服务器,“阻塞”和“非阻塞”两种模式,该如何设计。由于服务器要同时服务多个客户端,所以需要同时操作多个Socket。

可以看到,如果使用阻塞的IO方式,因为每个Socket都会阻塞,为了同时服务多个客户端,需要多个线程同时挂起;而如果采用非阻塞的调用方式,则需要在一个线程中不断轮训每个客户端是否有数据到来。

显然纯粹阻塞式的调用不可取,非阻塞式的调用看起来不错,但是仍不够好,因为轮询实际也是通过某种系统调用完成的,相当于在用户空间进行的,效率不高,如果能够在内核空间进行这种类似轮询,然后让内核通知用户空间哪个IO就绪了,就更好了。于是引出接下来的概念:IO多路复用

IO多路复用

IO多路复用是一种系统调用,内核能够同时对多个IO描述符进行就绪检查。当所有被监听的IO都没有就绪时,调用将阻塞;当至少有一个IO描述符就绪时,调用将返回,用户代码可通过检查究竟是哪个IO就绪来进一步处理业务。显然,IO多路复用是解决系统里面存在N个IO描述符的问题的,这里必须明确IO复用和IO阻塞与否并不是一个概念,IO复用只检测IO是否就绪(读就绪或者写就绪等),具体的数据的输入输出还是需要依靠具体的IO操作完成(阻塞操作或非阻塞操作)。最典型的IO多路复用技术有selectpollepoll等。select具有最大数量描述符限制,而epoll则没有,并且在机制上,epoll也更为高效。select的优势仅仅是跨平台支持性,所有平台和较低版本的内核都支持select模式,epoll则不是。

在IO相关的编程中,IO复用起到的作用相当于一个阀门,让后续IO操作更为精准高效。

编程模型

综上讨论,我们在进行实际的Socket编程的时候,无论是客户端还是服务端,大致有几种模式可以选择:

  1. 阻塞式。纯采用阻塞式,这种方式很少见,基本只会出现在demo中。多个描述符需要用多个进程或者线程来一一对应处理。

  2. 非阻塞式。纯非阻塞式,对IO的就绪与否需要在用户空间通过轮询来实现。

  3. IO多路复用+阻塞式。仅使用一个线程就可以实现对多个描述符的状态管理,但由于IO输入输出调用本身是阻塞的,可能出现某个IO输入输出过慢,影响其他描述符的效率,从而体现出整体性能不高。此种方式编程难度比较低。

  4. IO多路复用+非阻塞式。在多路复用的基础上,IO采用非阻塞式,可以大大降低单个描述符的IO速度对其他IO的影响,不过此种方式编程难度较高,主要表现在需要考虑一些慢速读写时的边界情况,比如读黏包、写缓冲不够等。

下面以select为例,整理 在select下,socket的阻塞和非阻塞的一些问题。这些细节在编写基于Socket的网络程序时,尤其是底层数据收发时,是十分重要的。

socket读就绪:

  • 【阻/非阻】接收缓冲区有数据,数据量大于SO_RCVLOWAT水位(默认是0)。此时调用recv将返回>0(即读到的字节数)。

  • 【阻/非阻】对端关闭,即收到FIN。此时调用recv将返回=0。

  • 【阻/非阻】accept到一个新的连接,此时accept通常不会阻塞。

  • 【阻/非阻】socket发生某种错误。此时调用recv将返回-1,并应通过getsockopt得到相应的待处理错误。

socket写就绪:

  • 【阻/非阻】发送缓冲区有空余的空间,空间大小大于SO_SNDLOWAT水位(默认是2048)。这种就绪是水平触发的,只要有空间就会触发写就绪,即如果保持对这种套接字的就绪检查将使得select每次都认为有描述符写就绪。所以应当对描述符进行写状态管理,一旦某个描述符可写,应立即停止对该描述符的写状态检查,直到写缓冲区满后,再次select写状态。

  • 【阻/非阻】连接的写半部关闭,此时调用send将产生SIGPIPE信号。

  • 【非阻】connect完成。由于非阻的connect将不会阻塞握手过程,所以,当握手在后续时刻完成后,在此保持写状态检查,将触发一次就绪,表示connect完成。

  • 【阻/非阻】socket发生某种错误。此时调用send将返回-1,并应通过getsockopt得到相应的待处理错误。

补充:

非阻的调用recvsendaccept,分别地,如果收缓冲中无数据、发送缓冲不够空间发、没有外来连接,将立即返回,此时全局errno将得到EWOULDBLOCKEAGIAN,表示“本应阻塞的调用,由于采用了非阻塞模式,而返回”。非阻的调用connect将立即返回,此时全局errno将得到EINPROGRESS,表示连接正在进行。


推荐阅读
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • Java在运行已编译完成的类时,是通过java虚拟机来装载和执行的,java虚拟机通过操作系统命令JAVA_HOMEbinjava–option来启 ... [详细]
  • 本文详细介绍了SQL日志收缩的方法,包括截断日志和删除不需要的旧日志记录。通过备份日志和使用DBCC SHRINKFILE命令可以实现日志的收缩。同时,还介绍了截断日志的原理和注意事项,包括不能截断事务日志的活动部分和MinLSN的确定方法。通过本文的方法,可以有效减小逻辑日志的大小,提高数据库的性能。 ... [详细]
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • 基于事件驱动的并发编程及其消息通信机制的同步与异步、阻塞与非阻塞、IO模型的分类
    本文介绍了基于事件驱动的并发编程中的消息通信机制,包括同步和异步的概念及其区别,阻塞和非阻塞的状态,以及IO模型的分类。同步阻塞IO、同步非阻塞IO、异步阻塞IO和异步非阻塞IO等不同的IO模型被详细解释。这些概念和模型对于理解并发编程中的消息通信和IO操作具有重要意义。 ... [详细]
  • CentOS 7部署KVM虚拟化环境之一架构介绍
    本文介绍了CentOS 7部署KVM虚拟化环境的架构,详细解释了虚拟化技术的概念和原理,包括全虚拟化和半虚拟化。同时介绍了虚拟机的概念和虚拟化软件的作用。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • Nginx使用AWStats日志分析的步骤及注意事项
    本文介绍了在Centos7操作系统上使用Nginx和AWStats进行日志分析的步骤和注意事项。通过AWStats可以统计网站的访问量、IP地址、操作系统、浏览器等信息,并提供精确到每月、每日、每小时的数据。在部署AWStats之前需要确认服务器上已经安装了Perl环境,并进行DNS解析。 ... [详细]
  • 本文介绍了解决Netty拆包粘包问题的一种方法——使用特殊结束符。在通讯过程中,客户端和服务器协商定义一个特殊的分隔符号,只要没有发送分隔符号,就代表一条数据没有结束。文章还提供了服务端的示例代码。 ... [详细]
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
  • 搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的详细步骤
    本文详细介绍了搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的步骤,包括环境说明、相关软件下载的地址以及所需的插件下载地址。 ... [详细]
  • 使用Ubuntu中的Python获取浏览器历史记录原文: ... [详细]
author-avatar
冰凌清泽_712
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有