UNP Chapter 22 - 信号驱动I/O

22.1. 概述

信号驱动是指当某个描述字上发生了某个事件时,让内核通知进程。

这里描述的信号驱动不是真正的异步I/O。

第15章描述的非阻塞I/O同样不是异步I/O。在非阻塞I/O中,启动I/O操作后内核并不像真正的异步I/O那样立即返回,它只有在进程非得睡眠才能完成操作时才立即返回。

 

22.2. 套接口上的信号驱动I/O

使用套接口上的信号驱动I/O(SIGIO)需要进程执行以下三个步骤:

1. 给SIGIO信号建立信号处理程序

2. 设置套接口属主,通常使用fcntl的F_SETOWN命令

3. 激活套接口的信号驱动I/O,通常使用fcntl的F_SETFL命令打开O_ASYNC标志

 

UDP套接口上的SIGIO信号

  UDP上使用信号驱动I/O是简单的。当下述事件发生时产生SIGIO信号:

  1. 数据报到达套接口

  2. 套接口上发生异步错误

因此,当我们捕获到SIGIO信号时,我们调用recvfrom读取到达的数据报或者获取异步错误。

 

TCP套接口上的SIGIO信号

  不幸的是,信号驱动I/O对TCP套接口几乎是没用的,原因是该信号产生得过于频繁,并且该信号的出现并没有告诉我们发生了什么事情。

  下列条件均可在TCP套接口上产生SIGIO信号(假设信号驱动I/O是使能的):

    1. 在监听套接口上有一个连接请求已经完成

    2. 发起了一个连接拆除请求

    3. 一个连接拆除请求已经完成

    4. 一个连接的一半已经关闭

    5. 数据到达了套接口

    6. 数据已从套接口上发出(即输出缓冲区有空闲时间)

    7. 发生了一个异步错误

例如,如果一个进程既从一个TCP套接口读数据,又向其上写数据,当新数据到达或者以前所写数据得到确认后均会产生SIGIO信号,进程无法在信号处理程序中区分这两种情况。如果在这种情况下使用SIGIO,TCP套接口应该被设置为非阻塞方式以防止read或write发生阻塞。我们应该考虑只在监听TCP套接口上使用SIGIO,因为在监听套接口上产生SIGIO的唯一条件是一个新连接的完成。

 

这里找到的实际使用信号驱动I/O的程序是基于UDP的NTP(网络时间协议)服务器程序。NTP服务器的主循环从客户接收数据报并送回相应,但是每个客户请求都要进行相当数量的处理(比我们简单的回射服务器多得多)。对于服务器来讲,很重要的一点是给每个收到的数据报记录精确的时间戳,因为这个值要返回给客户,客户要用它来计算到达该服务器的来回时间。图22.1展示了构建这样一个UDP服务器的两种方法。

大多数UDP服务器都被设计成图中左边的方式。但是NTP服务器采用了图中右边的技术: 当一个新数据报到达时,SIGIO处理程序读得该数据报,同时记录数据报到达的时刻,然后把它放入进程的另一个队列中,由主服务器循环移走和处理。虽然这种技巧使服务器代码变得复杂,但是它为到达的数据报提供了精确的时间戳。

 

22.3. 使用SIGIO的UDP回射服务器程序

现在举一个类似图22.1.右边的例子:一个使用SIGIO信号接收到达的数据报的UDP服务器程序。

我们使用图8.7和8.8中同样的客户程序以及图8.3中同样的服务器程序main函数。我们做的唯一修改是dg_echo函数,下边四张图给出这些修改,图22.2给出了全局变量声明。

#include "unp.h"
static int sockfd;
#define QSIZE 8 /* size of input queue */
#define MAXDG 4096 /* maximum datagram size */
/* SIGIO信号处理程序将到达的数据报放入一个队列中。该队列是一个DG结构数组,我们将它处理成环形缓冲区。 */
/* 每个DG结构包括一个指向收到的数据报的指针,数据报的长度,一个指向包含客户协议地址的套接口地址结构的指针以及协议地址的大小。 */
/* 静态分配我们QSIZE个DG结构,从图22.4我们将看到dg_echo函数调用malloc给所有的数据报和套接口地址结构分配内存。 */
/* 我们还分配一个诊断用计数器cntread,不久将会解释到。图22.3展示了当第一项指向一个150字节数据报,与其关联的套接口地址结构长度为16时,DG结构数组的内容 */
typedef struct {
void * dg_data; /* ptr to actual datagram */
size_t dg_len; /* length of datagram */
struct sockaddr * dg_sa; /* ptr to sockaddr{} w/client's address */
socklen_t dg_salen; /* lenght of sockaddr{} */
} DG;
static DG dg[QSIZE]; /* the queue of datagrams to process */
static long cntread[QSIZE+1]; /* diagnostic counter */
/* iget是主循环将处理的下一个数组元素的下标 */
/* iput是信号处理程序将要存放的下一个数组元素的下标 */ 
/* nqueue是主循环将要处理的队列中数据报的总数目 */
static int iget;  /* next one for main loop to process */
static int iput; /* next one for signal handler to read into */
static int nqueue; /* #on queue for main loop to process */
static socklen_t clilen; /* max length of sockaddr{} */
static void sig_io(int);
static void sig_hup(int);

 

dg_echo函数:服务器主处理循环

void dg_echo(int sockfd_arg, SA * pcliaddr, socklen_t clilen_arg)
{
int on = 1;
sigset_t zeromask, newmask, oldmask;
sockfd = sockfd_arg;
clilen = clilen_arg;
  /* 套接口描述字保存在一个全局变量中,因为信号处理程序要用到它。已收到数据报队列被初始化 */
for ( i = 0; i < QSIZE; i++) { /* init queue of buffers */
dg[i].dg_data = Malloc(MAXDG);
dg[i].dg_sa = Malloc(clilen);
dg[i].dg_salen = clilen;
}
iget = iput = nqueue = 0;
  /* 给SIGHUP和SIGIO建立信号处理程序 */
Signal(SIGHUP, sig_hup);
Signal(SIGIO, sig_io);
  /* 用fcntl设置套接口属主 */
Fcntl(sockfd, F_SETOWN, getpid());
  /* 用ioctl设置信号驱动和非阻塞I/O标志 */
ioctl(sockfd, FIOASYNC, &on);
ioctl(sockfd, FIONBIO, &on);
  /* 初始化三个信号集:zeromask(从不改变)、oldmask(记录我们阻塞SIGIO时的老信号掩码)和newmask */
Sigemptyset(&zeromask); /* init three signal sets */
Sigemptyset(&oldmask);
Sigemptyset(&newmask);
  /* sigaddset打开newmask中与SIGIO对应的位 */
Sigaddset(&newmask, SIGIO); /* the signal we want to block */
/* sigprocmask将进程当前信号掩码存入oldmask中,然后将newmask与当前的信号掩码进行逻辑或。这将阻塞SIGIO并返回当前的信号掩码 */
  Sigprocmask(SIG_BLOCK, &newmask, &oldmask);
  /* 进入for循环并测试nqueue计数器。只要这个计数器为0,进程就无事可做。这时我们调用sigsuspend。 */  
  /* 因为zeromask是一个空信号集,所有的信号将被解阻塞。sigsuspend在捕获一个信号并在其信号处理程序返回后返回 */
/* 但在返回前,sigsuspend总是将信号掩码恢复为调用它时的信号掩码值,在这里这个掩码值为newmask,所以我们能够保证sigsuspend返回后,SIGIO仍被阻塞 */
  /* 这就是为什么我们能够测试nqueue标志,因为当我们测试时,SIGIO信号不可能被递交。 */
  for( ; ; ) {
while (nqueue == 0)
sigsuspend(&zeromask); /* wait for a datagram to process */
/* unblock SIGIO */
Sigprocmask(SIG_SETMASK, &oldmask, NULL); /* 调用sigprocmask将进程的信号掩码设置为先前保存的oldmask的旧值,从而解除了SIGIO阻塞 */
Sendto(sockfd, dg[iget].dg_data, dg[iget].dg_len, 0, dg[iget].dg_sa, dg[iget].dg_salen);/* 然后调用sendto发送应答 */
if( ++iget >= QSIZE) /* 下标iget加1,如果其值等于数据元素个数,则置iget为0,因为我们把数组当作环形缓冲区对待 */
iget = 0; /* 当修改iget时,我们不需要阻塞SIGIO,因为iget只被主循环使用,信号处理程序永远不会修改它 */
/* block SIGIO */
Sigprocmask(SIG_BLOCK, &newmask, &oldmask); /* 阻塞SIGIO,nqueue减1,我们在修改nqueue是必须阻塞SIGIO,因为主循环和信号处理程序在共享这个变量 */
nqueue--;
}
}

SIGIO处理程序

static void sig_io(int signo)
{
ssize_t len;
int nread;
DG *ptr;
for(nread = 0; ; ) {
if(nqueue >= QSIZE) /* 如果队列满,进程就终止 */
err_quit("receive overflow");
ptr = &dg[iput]; /* 在非阻塞的套接口上调用recvfrom,iput做下标的数组项是数据报存储的地方,如果没有可读数据报,则跳出for循环 */
ptr->dg_salen = clilen;
len = recvfrom(sockfd, ptr->dg_data, MAXDG, 0, ptr->dg_sa, &ptr->dg_salen);
if(len<0) {
if(errno == EWOULDBLOCK)
break; /* all done; no more queued to read */
else
err_sys("recvfrom error");
}
ptr->dg_len = len;
nread++; /* nread是一个计数器,记录每个信号读的数据报数 */
nqueue++; /* nqueue是主循环将要处理的数据报数 */
if(++iput >= QSIZE)
iput = 0;
}
cntread[nread]++; /* histogram of #datagrams read per signal */
/* 信号处理程序在返回前,将与每个信号读到的数据报数目对应的计数器加1,当SIGHUP递交后,该数组的内容做为诊断信息输出 */
}

SIGHUP信号处理程序

static void sig_hup(int signo)
{/* SIGHUP信号处理程序,它输出cntread数组的内容,cntread数组统计每个信号读到的数据报数目 */
int i;
for(i = 0; i <= QSIZE; i++)
printf("cntread[%d] = %d\n", i, cntread[i];
}


22.4. 小结

 

转载于:https://www.cnblogs.com/s7vens/archive/2012/03/28/2421602.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/422528.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

php strpos与strrpos,PHP开发之 strpos stripos strrpos strripos的区别

前言在日常PHP开发中strpos stripos strrpos strripos这四个函数是会经常用到的&#xff0c;但是你又掌握了多少呢&#xff1f;在这里将着中就这4个函数做一个详细的解析。正文stripos — 查找字符串首次出现的位置(不区分大小写),应使用 运算符来测试此函数的返回值&#xff…

南京php基础学习,南京玄武区php培训有哪些(学习PHP的优点有哪些)

南京玄武区php培训有哪些&#xff0c;进行选择南京php培训的时候&#xff0c;肯定要知道php培训的学习周期&#xff0c;php乱码怎么解决&#xff0c;学习PHP的优点有哪些&#xff0c;php的性质有哪些。php培训的学习周期一般都是四到五个月和Java培训周期差不多&#xff0c;不过…

php额拍戏,像这种会演戏的演员,给我焊在剧组365天拍戏可以吗?

最近芭姐疯狂 get 到董子健的演技&#xff0c;每晚换台一边《大江大河 2》一边《流金岁月》交叉着看&#xff0c;太直观了&#xff01;《大江大河》中&#xff0c;董子健饰演的杨巡虽然戏份不及宋运辉多&#xff0c;但在有限的笔墨中&#xff0c;董子健凭借到位的演技&#xff…

照相机滤镜使用,优化解码和滤镜导致的预览卡屏现象

这几天看到亚瑟boy的技术连载&#xff0c;也试着做了下带滤镜特效的照相机&#xff0c;效果也出来了&#xff0c;但是发现添加滤镜特效后的预览窗口卡屏现象很严重&#xff0c;于是自己索性试着尝试修改&#xff0c;在亚瑟和其他网友的代码中基本上都是对于照相机data视频流先进…

oracle linux6 u盘安装,U盘安装RHEL6

1)烧录ISO镜像用软碟通写入硬盘镜像rhel-server-6.5-x86_64-boot.iso&#xff0c;然后将rhel-server-6.2-x86_64-dvd.iso复制到U盘根目录备注&#xff1a;如果是烧录DVD那么就直接烧rhel-server-6.2-x86_64-dvd.iso即可&#xff0c;U盘安装linux不注意此处会发生找不到image错误…

网页设计师的最佳设计工具名单出炉

一个网站的基本设计是为了吸引大多数人。因此&#xff0c;要吸引更多的客户&#xff0c;他们需要运用所有可能的图案和花纹&#xff0c;创造出惊人的效果&#xff0c;体现网站自身的美感。这样&#xff0c;设计师就需要使用一些设计工具&#xff0c;以确保有更高的效率。创建3D…

HDOJ1879(继续畅通工程)

题目链接 最小生成树的题。克鲁斯卡尔算法。 View Code 1 #include <stdio.h>2 #define N 1003 #define M 50004 struct node5 {6 int a,b,d;7 }edge[M];8 int n;9 int p[N]; 10 void make_set() 11 { 12 int i; 13 for(i1;i<n;i) p[i]i; 14 } 15 int …

bug是什么PHP,bug是什么

在程序设计中&#xff0c;bug一词&#xff0c;是我们经常听到的&#xff0c;那么bug是什么意思&#xff0c;下面我们来总结一下。什么是bug?在IT中&#xff0c;bug一般表示程序中出现的错误&#xff0c;因为软件运行中出现错误&#xff0c;或者是硬件造成的错误&#xff0c;出…

php 渐变色,段落每行渐变色文本效果

这次给大家带来段落每行渐变色文本效果&#xff0c;段落每行渐变色文本效果的注意事项有哪些&#xff0c;下面就是实战案例&#xff0c;一起来看一下。今天小编在Codepen上看到一个CSS写的效果。一个段落的每行文本是渐变效果。对于单行文本或单个词实现渐变填充效果并不是什么…

SEO笔记—网页结构优化(四)

网页结构优化是页面优化的重点之一&#xff0c;它是对网页内容布局的规划&#xff0c;合理的网页结构能够有效地提高用户体验和搜索引擎的友好性。网页主要包含导航栏、栏目以及正文三部分组成&#xff0c;对网页结构的主要也是围绕这三部分进行的。 1、页面重要区域分布规律 搜…

linux下spi有哪些函数,linux下怎么快速的使用 SPI 驱动。

ek_spi_devices 数组就在本文件内。/** SPI devices.*/static struct spi_board_info ek_spi_devices[] {#if !(defined(CONFIG_MMC_ATMELMCI) || defined(CONFIG_MMC_AT91)){/* DataFlash chip */.modalias "mtd_dataflash",.chip_select 1,.max_speed_hz 15 * 100…

SQL 查询横表变竖表

首先是三张表&#xff0c; CNo对应的是课程&#xff0c;在这里我就粘贴了。 主表 人名表 按照常规查询 SELECT s.SName, c.CName,s2.SCgrade FROM S s INNER JOIN SC s2 ON s2.SNo s.SNo INNER JOIN C c ON c.CNo s2.CNo 那么结果是这样的 但是这是横表 不是我想…

东芝笔记本linux系统安装驱动,笔记本驱动,详细教您东芝笔记本无线网卡驱动怎么安装...

在品牌的笔记本电脑中&#xff0c;使用东芝笔记本的用户都知道&#xff0c;东芝笔记本电脑具有十分鲜明的特色&#xff0c;其产品十分注重细节与工艺&#xff0c;为用户带去了不错的使用体验。那么如果要下载安装无线网卡驱动&#xff0c;该怎么操作呢&#xff1f;下面&#xf…

asp.net页面绑定数据的方式-----未完待续

**方式一 **方式二转载于:https://www.cnblogs.com/wgx0428/archive/2012/05/10/2493953.html

红帽企业版linux 7.3,红帽推企业Linux 7.3 新功能特性一览

原标题&#xff1a;红帽推企业Linux 7.3 新功能特性一览【IT168 云计算】日前红帽宣布推出企业Linux 7.3版本&#xff0c;作为Linux平台的最新版本&#xff0c;红帽方面透漏本次更新的版本在网络与存储功能两方面做到了强化。同时&#xff0c;该版本还引入了关于Linux容器和物联…

Android游戏开发系统控件-Dialog

Android游戏开发系统控件-Dialog Dialog(对话框)在Android应用开发中经常用到&#xff0c;下面是学习《Android游戏编程从零开始》一书&#xff0c;关于Dialog的初步学习。 创建项目&#xff1a;DialogProject 作者&#xff1a;wwj 日期&#xff1a;2012/5/13 功能&#xff1a…

步进电机编写单4拍或4-8拍方式的汇编或c语言控制程序.,基于SCM和PLC的两种步进电机控制方法...

引 言步进电机位移与输入脉冲信号数相对应&#xff0c;精度高、响应特性好、可靠性高、速度可在较宽范围内平滑调节&#xff0c;是控制系统中一种重要的自动化执行元件。SCM(Single Chip Microcomputer&#xff0c;单片机)是把组成微型计算机的中央处理器、存储器、输入输出接口…

在网上找到一个CSS hack列表,记录在这里以分析css的浏览器兼容性

另外摘抄一份目前的css浏览器兼容问题汇总&#xff0c;记录在这里自己好好学习一下 CSS对浏览器的兼容性有时让人很头疼,或许当你了解当中的技巧跟原理,就会觉得也不是难事,从网上收集了IE7,6与Fireofx的兼容性处理方法并整理了一下.对于web2.0的过度,请尽量用xhtml格式写代码,…

c语言安卓贪吃蛇代码下载,C语言贪吃蛇代码

c语言编写贪吃蛇源代码&#xff0c;简单易懂&#xff0c;文件为VC源代码。如果你正在学习c语言&#xff0c;就来下载吧。很经典的C语言贪吃蛇代码部分#include #include #include#include#define SX 50#define SY 25#define Length 10#define Xnub 50#define Ynub …

android webview 图表,Android WebView 无法正常显示网页图表

1、Android项目中&#xff0c;使用了一个WebView&#xff0c;加载的页面是一个使用百度echart和canvas写的统计图表&#xff0c;现在网页版和IOS版都能正常显示&#xff0c;Android无法正常显示&#xff1a;2、ios和web端&#xff1a;3、android 端&#xff1a;4、Android控制台…