第6章 I/O复用:select和poll函数

6.1概述

在5.12节中,我们看到TCP客户同时处理两个输入:标准输入和TCP套接口。我们遇到的问题是客户阻塞于(标准输入上的)fgets调用,而服务器进程又被杀死。服务器TCP虽正确地给客户TCP发了一个FIN,但客户进程正阻塞于从标准输入读入,它直到从套接口读时才能看到此文件结束符(可能已经过了很长时间)。我们需要这样的能力:如果一个或多个I/O条件满足(例如,输入已准备好被读,或者描述字可以承接更多的输出)时,我们就被通知到。这个能力被称为I/O复用,是由函数select和poll支持的,我们也对较新的Posix.lg的变种(称为pselect)作介绍。

I/O复用典型地用在下列网络应用场合:

  • 当客户处理多个描述字时(一般是交互式输入和网络套接口),必须使用I/O复用,这在前一段中已做了描述。
  • 一个客户同时处理多个套接口是可能的,但很少出现。在15.5节一个Web客户的上下文中,我们给出使用select的例子。
  • 如果一个TCP服务器既要处理监听套接口,又要处理已连接套接口,一般也要用到I/O复用,如6.8节所述。
  • 如果一个服务器既要处理TCP,又要处理UDP,一般也要使用I/O复用,8.15节中我们也要给出这样一个例子。
  • 如果一个服务器要处理多个服务或者多个协议(例如,我们将在12.5节描述的inetd守护进程),一般要使用I/O复用。

I/O复用并非只限于网络编程,许多正式应用程序也需要使用这项技术。

6.2 I/O模型

在介绍函数select和poll之前,我们需要回过头来看看

  • 阻塞 I/O
  • 非阻塞 I/O
  • I/O 复用(select和poll)
  • 信号驱动 I/O(SIGIO)
  • 异步 I/O(Posix.1 的 aio_系列函数)

你在第一次阅读时,可能想略读到本节,到后面的章节中详细介绍不同的I/O模型时才回来看。

正如本节我们所给出的所有例子所述,一个输入操作一般有两个不同的阶段:

  1. 等待数据准备好。
  2. 从内核到进程拷贝数据。

对于一个套接口上的输入操作,第一步一般是等待数据到达网络,当分组到达时,它被拷贝到内核中的某个缓冲区,第二步是将数据从内核缓冲区拷贝到应用缓冲区。

阻塞I/O模型

最流行的I/O模型是阻塞I/O模型,本书中到目前为止的所有例子都使用此模型。缺省时,所有套接口都是阻塞的。以数据报套接口作为例子,我们有示例图6.1中的情形。

此例中,我们用UDP而不是TCP,因为对于UDP来说,数据准备好的概念要简单些:整个数据报是否已接收,而对于TCP则要复杂得多,需考虑诸如套接口的低潮限度(lowwater mark)这样的许多附加变量。

在本节的例子中,我们将行数recvfrom视为系统调用,因为我们正考虑应用进程与内核的区别。不论函数recvfrom如何实现(在源自Berkeley的内核中作为系统调用,在系统V内核中作为调用系统调用getmsg的函数)。一般都有一个从一个应用进程中运行到内核中运行的切换,一段时间后再跟一个返回到应用的进程的切换。

在图6.1中,进程调用recvfrom,此系统调用直到数据报到达且拷贝到应用缓冲区或是出错才返回。最常见的错误是系统调用被信号中断,如5.9节所述。我们所说进程阻塞的整段时间是指从调用recvfrom开始到它返回的这段时间,当进程返回成功指示时,应用进程开始处理数据报。

非阻塞I/O模型

当我们把一个套接口设置成非阻塞方式时,即通知内核:当请求的I/O操作非得让进程睡眠不能完成时,不要让进程睡眠,而应返回一个错误。我们将在第15章节详细介绍非阻塞I/O,但为了说明我们所考虑的例子,在图6.2中作一个小结性描述。

前三次调用recvfrom时仍无数据返回,因此内核立即被返回一个 EWOULDBLOCK 错误,第四次调用recvfrom时,数据报已准备好,被拷贝到应用缓冲区,recvfrom返回成功指示,接着就是我们处理数据。

当一个应用进程像这样对一个非阻塞描述字循环调用recvfrom时,我们称此过程为轮询(polling)。应用进程连续不断地查询内核,看看某操作是否准备好,这对CPU时间是极大的浪费,但这种模式指示偶尔才遇到,一般只是专门提供某种功能的系统中才有。

I/O复用模型

有了I/O复用,我们就可以调用select或poll,在这两个系统调用中的某一个上阻塞,而不是阻塞于真正的I/O系统调用。图6.3是I/O复用模型的一个小结。

我们阻塞于select调用,等待数据报套接口可读。当select返回套接口可读条件时,我们调用recvfrom将数据报拷贝到应用缓冲区中。

将图6.3与图6.1进行比较,似乎没有显示什么优越性,实际上,因使用了系统调用select,要求两次系统调用不是一次,好像变得还有点差。但是,在本章的后面我们将看到,使用select的好处在于我们可以等待多个描述字准备好。

信号驱动I/O模型

我们也可以用信号,让内核在描述字准备好时用信号SIGIO通知我们,我们将此方法称为信号驱动I/O,图6.4对此作了一个小结。

首先,我们允许套接口进行信号驱动I/O(我们将在22.2节对此进行讨论),并通过系统调用sigaction安装一个信号处理程序。此系统调用立即返回,进程继续工作,它是非阻塞的。当数据报准备好被读时,就为该进程生成一个SIGIO信号。我们随即可以在信号处理程序中调用recvfrom来读数据报,并通知主循环数据已准备好被处理(这正是我们在22.3节中所要做的事情)。也可以通知主循环,让它来读取数据报。

无论我们如何处理SIGIO信号,这种模式的好处是当等待数据报到达时,可以不阻塞。主循环可以继续执行,只是等待信号处理程序的通知;或者数据已准备好被处理,或者数据报已准备好被读。

异步I/O模型

异步I/O是Posix.1的1993版本中的新内容("实时"扩展)。我们让内核启动操作,并在整个操作完成后(包括数据从内核拷贝到我们自己的缓冲区)通知我们。因为这种模型还没有广泛使用,本书不做讨论。这种模型与前一节介绍的信号驱动模型的主要区别在于:信号驱动I/O是由内核通知我们何时可以启动一个I/O操作,而异步是由内核通知我们I/O操作何时完成。图6.5给出一个例子。

我们调用函数aio_read(Posix 异步I/O函数以 aio_ 或 lio_ 开头),给内核传递描述字,缓冲区指针、缓冲区大小(与read相同的三个参数)、文件偏移(与lseek类似),并告诉内核当整个操作完成时如何通知我们。此系统调用立即返回,我们的进程不阻塞与等待I/O操作的完成。在此例子中,我们假设要求内核在操作完成时生成一个信号,此信号知道数据已拷贝到应用缓冲区才产生,这一点是与信号驱动I/O模型不同的。

正如本书说述,很少有系统支持Posix.1的异步I/O模型。例如,我们还不能确定系统是否支持套接口上的这种模型。这儿我们用它,只是作为一个与信号驱动I/O模型进行比较的例子。

各种I/O模型的比较

图6.6示出了上述五种不同I/O模型的比较。它表明:前四种模型的主要区别都在第一阶段,因为前四种魔性的第二阶段基本相同:在数据从内核拷贝到调用者的缓冲区时,进程阻塞于recvfrom调用。然后,异步I/O模型处理的两个阶段都不用于前四个模型。

同步I/O与异步I/O

Posix.1定义这两个术语如下:

  • 同步I/O操作引起请求进程阻塞,知道I/O操作完成。
  • 异步I/O操作不引起请求进程阻塞。

根据上述定义,我们的前四个模型————阻塞I/O模型、I/O复用模型和信号驱动I/O模型都是同步I/O模型,因为真正的I/O操作(recvfrom)阻塞进程,只有异步I/O模型与此异步I/O的定义相匹配。

unix网络编程第2版(卷1)_第6章_同步_异步的更多相关文章

  1. 【unix网络编程第三版】阅读笔记(五):I/O复用:select和poll函数

    本博文主要针对UNP一书中的第六章内容来聊聊I/O复用技术以及其在网络编程中的实现 1. I/O复用技术 I/O多路复用是指内核一旦发现进程指定的一个或者多个I/O条件准备就绪,它就通知该进程.I/O ...

  2. 【unix网络编程第三版】阅读笔记(三):基本套接字编程

    unp第三章主要介绍了基本套接字编程函数.主要有:socket(),bind(),connect(),accept(),listen()等. 本博文也直接进入正题,对这几个函数进行剖析和讲解. 1. ...

  3. 【UNIX网络编程第三版】阅读笔记(一):代码环境搭建

    粗略的阅读过<TCP/IP详解>和<计算机网络(第五版)>后,开始啃这本<UNIX网络编程卷一:套接字联网API>,目前linux下的编程不算太了解,在阅读的过程中 ...

  4. 【unix网络编程第三版】ubuntu端口占用问题

    <unix网络编程>一书中的代码并不是能直接运行,有时候需要结合各方面的知识来解决,大家在这本书的时候,一定要把代码都跑通,不难你会错过很多学习的机会! 1.问题描述 本人在阅读<U ...

  5. 【unix网络编程第三版】阅读笔记(二):套接字编程简介

    unp第二章主要将了TCP和UDP的简介,这些在<TCP/IP详解>和<计算机网络>等书中有很多细致的讲解,可以参考本人的这篇博客[计算机网络 第五版]阅读笔记之五:运输层,这 ...

  6. 《UNIX网络编程(第3版)》unp.h等源码文件的编译安装

    操作系统:Mac OS X 10.11.5 1.下载书中的源代码:点击下载 2.切换到解压后的目录 unpv13e,先查看下 README,依次执行: ./configure cd lib make ...

  7. 【unix网络编程第三版】阅读笔记(四):TCP客户/服务器实例

    本篇博客主要记录一个完整的TCP客户/服务器实例的编写,以及从这个实例中引发的对僵死进程的处理等问题. 1. TCP客户/服务器功能需求 本实例完成以下功能: (1) 客户从标准输入读入一行文本,并写 ...

  8. UNIX 网络编程第三版

    第五章p102: ps -t  pts/6 -o pid,ppid,tty,stat,args,wchan 在我的系统上运行时出现:TTY not found linux发行版为mint17.1 改用 ...

  9. Unix网络编程第三版源码编译

    配置: $ cd Unix-Network-Programming/ $ chmod 755 configure $ ./configure 主要的工作是检查系统是否有源码编译所依赖的各种资源(系统版 ...

随机推荐

  1. JAVA 将图片转换为Base64编码

    这里使用的jar包是commons-codec-1.10.jar; 示例代码 import java.io.FileInputStream; import java.io.FileOutputStre ...

  2. gitignore样例解析

    # 这是注释行 -- 被忽略 *.a # 忽略所有以 .a 为扩展名的文件 !lib.a # 但是lib.a 文件或目录不要忽略,即使前面设置了对*.a的忽略 /TODO # 只忽略此目录下的TODO ...

  3. Python日志监控系统处理日志(pyinotify)

    前言 最近项目中遇到一个用于监控日志文件的Python包pyinotify,结合自己的项目经验和网上的一些资料总结一下,总的原理是利用pyinotify模块监控日志文件夹,当日志到来的情况下,触发相应 ...

  4. jsp+servlet登录框架模板

    一.建立一个名叫jsp_servlet的工程 二.建立一个AcountBean类和CheckAccount类 1.AcountBean类包含登录名(username)和登录密码(password) p ...

  5. Codeforces 725B Food on the Plane

    B. Food on the Plane time limit per test:2 seconds memory limit per test:256 megabytes input:standar ...

  6. 【NOI2014】魔法森林

    为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为 1…n1…n,边标号为1…m1…m.初始时小E同学在 11 号节点,隐 ...

  7. HDU2243 考研路茫茫――单词情结

    Description 背单词,始终是复习英语的重要环节.在荒废了3年大学生涯后,Lele也终于要开始背单词了. 一天,Lele在某本单词书上看到了一个根据词根来背单词的方法.比如"ab&q ...

  8. c++工程重复编译与重复定义

    #ifndef #define #endif防止的是"重复编译",而不是"重复定义"重复编译可能造成重复定义,但重复定义的来源不只有重复编译从代码变成可执行的程 ...

  9. JavaScript实现职责链模式

    什么是职责链模式 职责链模式的定义是:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止.举个例子:当你从公 ...

  10. 关于layer的坑

    真是自己给自己挖坑,坑死人不偿命啊. 在用layui开发时,遇到这种情况,点击按钮出现一个弹出层,然而我不是用button按钮去实现的,而是用a标签做的,本来a标签也是可以实现的,在这里我无形中给自己 ...