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

(六):IO复用:select和poll函数

IO复用使用场景:-当客户处理多个描述符(通常是交互式输入和网络套接字)时,必须使用IO复用。-一个客户同时处理多个套接字是可能的,不过比较少见。-如果一个TCP服务器既要处

I/O复用使用场景:

-当客户处理多个描述符(通常是交互式输入和网络套接字)时,必须使用I/O复用。

-一个客户同时处理多个套接字是可能的,不过比较少见。

-如果一个TCP服务器既要处理监听套接字,又要处理已连接套接字,一般就要使用I/O复用。

-如果一个TCP服务器既要处理TCP又要处理UDP,一般就要使用I/O复用。

-如果一个服务器要处理多个服务或者多个协议,一般就要使用I/O复用。

 

I/O模型

5种模型:

-阻塞式I/O

-非阻塞式I/O

-I/O复用(select 和 poll)

-信号驱动式I/O

-异步I/O

 

一个输入操作通常包含两个不同的阶段:

-等待数据准备好

-从内核向进程复制数据

对于套接字上的输入操作:第一步等待数据从网络中到达。当所等待分组到达时,它被复制到内核中的某个缓冲区。第二步就是把数据从内核数据区复制到应用进程缓冲区。

 

 

1.阻塞式I/O模型

最流行的I/O模型就是阻塞式I/O模型,默认情况下,所有套接字都是阻塞的。

以数据报套接字(UDP)为例

             

进程调用recvfrom,其系统调用直到数据报到达且被复制到应用进程的缓冲区中或者发生错误才返回。

 

2.非阻塞式I/O模型

进程把一个套接字设置成非阻塞是在通知内核:当所请求的I/O操作非得把本进程投入睡眠才能完成时,不要把本进程投入睡眠,而是返回一个错误。


前三次调用recvfrom时没有数据可返回,因此内核转而立即返回一个错误。第四次调用recvfrom时已有一个数据报准备好,它被复制到应用进程缓冲区,于是recvfrom成功返回。

当一个应用程序对一个非阻塞描述符循环调用recvfrom时,我们称之为轮询(polling)。应用进程持续轮询内核,以查看某个操作是否就绪。

 

3.I/O复用模型

调用select和poll,阻塞在这两个系统调用中的某一个之上,而不是阻塞在真正的I/O系统调用上。

            

阻塞于select调用,等待数据报套接字变为可读。当select返回套接字可读这一条件时,调用recvfrom把所读数据报复制到应用进程缓冲区。

使用select的优势在于我们可以等待多个描述符就绪。

 

 

4.信号驱动式I/O模型

内核在描述符就绪时发送SIGIO信号通知,称这种模型为信号驱动式I/O.

           

当数据报准备好读取时,内核就为该进程产生一个SIGIO信号。随后既可以在信号处理函数中调用recvfrom读取数据报,并通知主循环数据已准备好待处理,也可以立即通知主循环,让它读取数据报。

无论如何处理SIGIO信号,这种模型的优势在于等待数据报到达期间进程不被阻塞。主循环可以继续执行。

 

5.异步I/O模型

异步I/O由POSIX规范定义。工作机制是:告知内核启动莫格操作,并让内核在整个操作完成后通知我们。

与信号驱动式的区别在于:信号驱动式由内核通知我们何时可以启动一个I/O操作,而异步I/O模型是由内核通知我们I/O操作何时完成。

                    

调用aio_read函数给内核传递描述符,缓冲区指针,缓冲区大小和文件偏移,并告诉内核当整个操作完成时如何通知我们。该系统调用立即返回,而且在等待I/O完成期间,我们的进程不被阻塞。假设要求内核在操作完成时产生某个信号。该信号直到数据已复制到应用进程缓冲区才产生。

 

 

同步I/O和异步I/O比较

同步I/O操作导致请求进程阻塞,直到I/O操作完成。

异步I/O操作不导致请求进程阻塞。

 

前四种模式-阻塞式I/O模型,非阻塞式I/O模型,I/O复用模型和信号驱动式I/O模型都是同步I/O,因为其中真正的I/O操作(recvfrom)将阻塞进程。


 

select函数

该函数允许进程指示内核等待多个事件中的任何一个发生,并只在有一个或多个事件发生或经历一段指定的时间后才唤醒它。

#include
#include
int select (int maxfdpl, fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timeval *timeout);
                //若有就绪描述符则返回其数目,超时返回0,出错返回-1.

最后一个参数timeout告诉内核等待所指定描述符中的任意一个就绪可花多长事件。timeval结构体指定这段时间的秒数和微秒数。

struct timeval {
    long tv_sec;        //seconds
    long tv_usec;       //microseconds
};

这个参数有3种可能:

1.永远等待:仅在有一个描述符准备好I/O时才返回。把该参数设置为空指针。

2.等待一段固定事件:在有一个描述符准备好I/O时返回。但是不超过由该参数指定的时间。

3.根本不等待:检查描述符后立即返回,这称为轮询。为次,该参数执行一个timeval结构体,而且其中的定    时器值为0.

 

中间的三个参数readset,writeset,exceptset指定我们要让内核测试读,写和异常条件的描述符。

目前支持的异常条件只有两个:

-某个套接字的带外数据(OOB)的到达。

-某个已置为分组模式的伪终端存在可从其主端读取的控制状态信息。

 

select使用描述符集,通常是一个整数数组,其中每个整数中的每一位对应一个描述符。 

 

四个宏:


分配一个fd_set数据类型的描述符集,并用这些宏设置或测试该集合中的每一位。

 

举例:以下代码用于定义一个fd_set类型的变量,然后打开描述符1,4,5的对应位。

 

fd_set rset;
FD_ZERO(&rset);             //清除所有位为0
FD_SET(1,&rset);            //打开第一位
FD_SET(4,&rset);            //打开第四位
FD_SET(5,&rset);            //打开第五位

描述符集的初始化非常重要。

select函数的三个参数readset,writeset,exceptset中,如果对某一个的条件不感兴趣,就可以把它设为空指针。

 

maxfdpl参数指定待测试的描述符个数,它的值是待测试的最大描述符加1(描述符0,1,2...一直到maxfdpl-1 均被测试)。

 

select函数修改由指针readset,writeset和exceptset所指向的描述符集,因而这三个参数都是值-结果参数。调用该函数时,指定所关心的描述符的值,函数返回时,结果将指示哪些描述符已就绪。用FD_ISSET宏来测试fd_set数据类型中的描述符,描述符集内任何未就绪描述符对应的位返回时清0。因此,每次重新调用select函数时,都得再次把所有描述符集内所关心的为均置为1。

 

描述符就绪条件

 

 

str_cli函数修订版

str_cli函数完成客户处理循环:从标准输入读入一行文本,写到服务器上,读回服务器对该行的回射,并把回射行写到标准输出上。

#include "unp.h"
#include "myerr.h"

void str_cli(FILE *fp, int sockfd)
{
	int maxfdp1;
	fd_set rset;
	char sendline[MAXLINE],recvline[MAXLINE];

	FD_ZERO(&rset);
	while(1) {
		FD_SET(fileno(fp),&rset);
		FD_SET(sockfd,&rset);
		maxfdp1 = max(fileno(fp),sockfd) +1;
		select(maxfdp1, &rset, NULL,NULL,NULL);

		if (FD_ISSET(sockfd,&rset)) {		//sockfd is readable
			if (read(sockfd, recvline, MAXLINE) == 0)
				err_quit("str_cli: server terminated prematurely");
			fputs(recvline,stdout);
		}

		if (FD_ISSET(fileno(fp), &rset)) {	//input is readable 
			if (fgets(sendline, MAXLINE, fp) == NULL)
				return ;
			write(sockfd, sendline, strlen(sendline));
		}
	}

 

 

shutdown函数

close把描述符的引用计数减1,仅在该计数变为0时关闭套接字。

close终止读和写两个方向的数据传送。TCP连接是全双工的,有时候需要告知对端我们已经完成了数据发送,即使对端仍有数据要发送给我们。

                

 

int shutdown(int sockfd, int howto);

howto参数:

SHUT_RD,关闭连接的读这一半——套接字中不再有数据可接收,而且套接字接收缓冲区中的现有数据都被丢弃。

SHUT_WR,关闭连接的写这一半——对于TCP为套接字,这称为半关闭。当前留在套接字发送缓冲区中的数据将被发送掉,后跟TCP的正常连接终止序列。

SHUT_RDWR,连接的读半部和写半部都关闭

 

 

poll函数 

#include
int poll(struct pollfd *fdarray, unsigned long nfds, int timeout);
    //若有描述符就绪则返回其数目,若超时则为0,若出错则为-1

第一个参数是指向一个结构数组第一个元素的指针。每个数组元素都是一个pollfd结构,用于指定测试某个给定描述符fd的条件。

struct pollfd {
    int fd;
    short events;
    short revents;
}

要测试的条件由events成员指定,函数在相应的revents成员中返回该描述符的状态。这两个成员中的每一个都由指定某个特定条件的一位或多位构成。

第一部分是处理输入的四个常值,第二个部分是处理输出的三个常值,第三部分是处理错误的三个常值,不能在events中设置,只能在revents中返回。

poll时别三类数据:普通,优先级带,高优先级。

 

timeout参数指定poll函数返回前等待多长时间。它是一个指定应等待毫秒数的正值。

                


推荐阅读
  • 域名解析系统DNS
    文章目录前言一、域名系统概述二、因特网的域名结构三、域名服务器1.根域名服务器2.顶级域名服务器(TLD,top-leveldomain)3.权威(Authoritative)域名 ... [详细]
  • SQL Server 2008 到底需要使用哪些端口?
    SQLServer2008到底需要使用哪些端口?-下面就来介绍下SQLServer2008中使用的端口有哪些:  首先,最常用最常见的就是1433端口。这个是数据库引擎的端口,如果 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • 本文介绍了RPC框架Thrift的安装环境变量配置与第一个实例,讲解了RPC的概念以及如何解决跨语言、c++客户端、web服务端、远程调用等需求。Thrift开发方便上手快,性能和稳定性也不错,适合初学者学习和使用。 ... [详细]
  • 本文介绍了如何使用iptables添加非对称的NAT规则段,以实现内网穿透和端口转发的功能。通过查阅相关文章,得出了解决方案,即当匹配的端口在映射端口的区间内时,可以成功进行端口转发。详细的操作步骤和命令示例也在文章中给出。 ... [详细]
  • Python瓦片图下载、合并、绘图、标记的代码示例
    本文提供了Python瓦片图下载、合并、绘图、标记的代码示例,包括下载代码、多线程下载、图像处理等功能。通过参考geoserver,使用PIL、cv2、numpy、gdal、osr等库实现了瓦片图的下载、合并、绘图和标记功能。代码示例详细介绍了各个功能的实现方法,供读者参考使用。 ... [详细]
  • 本文介绍了Windows操作系统的版本及其特点,包括Windows 7系统的6个版本:Starter、Home Basic、Home Premium、Professional、Enterprise、Ultimate。Windows操作系统是微软公司研发的一套操作系统,具有人机操作性优异、支持的应用软件较多、对硬件支持良好等优点。Windows 7 Starter是功能最少的版本,缺乏Aero特效功能,没有64位支持,最初设计不能同时运行三个以上应用程序。 ... [详细]
  • 实现一个通讯录系统,可添加、删除、修改、查找、显示、清空、排序通讯录信息
    本文介绍了如何实现一个通讯录系统,该系统可以实现添加、删除、修改、查找、显示、清空、排序通讯录信息的功能。通过定义结构体LINK和PEOPLE来存储通讯录信息,使用相关函数来实现各项功能。详细介绍了每个功能的实现方法。 ... [详细]
  • 概述H.323是由ITU制定的通信控制协议,用于在分组交换网中提供多媒体业务。呼叫控制是其中的重要组成部分,它可用来建立点到点的媒体会话和多点间媒体会议 ... [详细]
  • java io换行符_Java IO:为什么从stdin读取时,换行符的数字表示出现在控制台上?...
    只是为了更好地理解我在讲座中听到的内容(关于Java输入和输出流),我自己做了这个小程序:publicstaticvoidmain(String[]args)thro ... [详细]
  • 1、概述首先和大家一起回顾一下Java消息服务,在我之前的博客《Java消息队列-JMS概述》中,我为大家分析了:然后在另一篇博客《Java消息队列-ActiveMq实战》中 ... [详细]
  • 刚开始crousera上学习<algorithmspart1>但对JAVA实在是不熟。******************************************** ... [详细]
  • IP双栈环境下网络应用迁移
    IPv4向IPv6迁移有多种途径,在选择具体的迁移方式时,当前环境中运行的应用是否支持IPv6是重要的考量因素之一,同时在编写新的应用时,需要考虑新编写的应用不仅可以适应当前主流的IPv4环境, ... [详细]
  • 796.[APIO2012]派遣在一个忍者的帮派里,一些忍者们被选中派遣给顾客,然后依据自己的工作获取报偿。在这个帮派里,有一名忍者被称之为Master。 ... [详细]
  • 引言随着企业安全意识的增强以及现代化管理水平的提高,对设备的远程监控在工业控制系统中得到了越来越广泛的应用。近年来,Web技术广泛普及,把 ... [详细]
author-avatar
OkzYa-_916
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有