信号驱动式I/O是指进程预先告知内核,使得当某个描述符发生某事时,内核使用信号通知相关进程。

套接字的信号驱动式I/O

针对一个套接字使用信号驱动式I/O(SIGIO)要求进程执行以下3个步骤:

1.建立SIGIO信号的信号处理函数

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

3.开启该套接字的信号驱动式I/O,通常通过使用fcntl的F_SETFL命令打开O_ASYNC标志完成

对于UDP套接字的SIGIO信号

在UDP上使用信号驱动式I/O是简单得。SIGIO信号在发生以下事件时产生:

1.数据报到达套接字

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

因此当捕获对于某个UDP套接字的SIGIO信号时,我们调用recvfrom读入到达的数据报或者获取发生的异步错误。

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

下面是构建一个UDP服务器的两种方式,我们的程序使用的右面的方式

1.全局声明

SIGIO信号处理函数把到达的数据放入一个队列。该队列是一个DG数据数组,我们把它作为一个环形缓冲区处理

每个DG结构包括指向所收取数据报的一个指针、该数据包的长度、指向含有客户协议地址的某个套接字地址结构的一个指针、该协议地址的大小。

iget是主循环将处理的下一个数组元素的下标,iput是信号处理函数将存放到下一个数组元素的下标,nqueue是队列中共主循环处理的数据报的总数

 #include    "unp.h"

 static int        sockfd;

 #define    QSIZE       8        /* size of input queue */
#define MAXDG 4096 /* max datagram size */ 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; /* length of sockaddr{} */
} DG;
static DG dg[QSIZE]; /* queue of datagrams to process */
static long cntread[QSIZE+]; /* diagnostic counter */ 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);

2.dg_echo函数

 void
dg_echo(int sockfd_arg, SA *pcliaddr, socklen_t clilen_arg)
{
int i;
const int on = ;
sigset_t zeromask, newmask, oldmask; sockfd = sockfd_arg;
clilen = clilen_arg; for (i = ; 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 = ; Signal(SIGHUP, sig_hup);
Signal(SIGIO, sig_io);
Fcntl(sockfd, F_SETOWN, getpid());
Ioctl(sockfd, FIOASYNC, &on);
Ioctl(sockfd, FIONBIO, &on); Sigemptyset(&zeromask); /* init three signal sets */
Sigemptyset(&oldmask);
Sigemptyset(&newmask);
Sigaddset(&newmask, SIGIO); /* signal we want to block */ Sigprocmask(SIG_BLOCK, &newmask, &oldmask);
for ( ; ; ) {
while (nqueue == )
sigsuspend(&zeromask); /* wait for datagram to process */ /* 4unblock SIGIO */
Sigprocmask(SIG_SETMASK, &oldmask, NULL); Sendto(sockfd, dg[iget].dg_data, dg[iget].dg_len, ,
dg[iget].dg_sa, dg[iget].dg_salen); if (++iget >= QSIZE)
iget = ; /* 4block SIGIO */
Sigprocmask(SIG_BLOCK, &newmask, &oldmask);
nqueue--;
}
}

3.sig_io信号处理函数

因为信号时不排队的,开启信号驱动式I/O的描述符通常也被设置为非阻塞式。

这个前提下,我们把SIGIO信号处理函数编写成一个循环中执行读入操作,直到操作返回EWOULDBLOCK时才结束循环。

 static void
sig_io(int signo)
{
ssize_t len;
int nread;
DG *ptr; for (nread = ; ; ) {
if (nqueue >= QSIZE)
err_quit("receive overflow"); ptr = &dg[iput];
ptr->dg_salen = clilen;
len = recvfrom(sockfd, ptr->dg_data, MAXDG, ,
ptr->dg_sa, &ptr->dg_salen);
if (len < ) {
if (errno == EWOULDBLOCK)
break; /* all done; no more queued to read */
else
err_sys("recvfrom error");
}
ptr->dg_len = len; nread++;
nqueue++;
if (++iput >= QSIZE)
iput = ; }
cntread[nread]++; /* histogram of # datagrams read per signal */
}

4.sig_hup信号处理函数

 static void
sig_hup(int signo)
{
int i; for (i = ; i <= QSIZE; i++)
printf("cntread[%d] = %ld\n", i, cntread[i]);
}

对于TCP套接字的SIGIO信号

因为对于TCP套接字,该信号产生得过于频繁,并且它的出现并没有告诉我们发生了上面事情。因此信号驱动式I/O对于TCP套接字几乎没用。

UNP学习笔记(第二十五章 信号驱动式I/O)的更多相关文章

  1. UNP学习笔记(第五章 TCP客户/服务程序实例)

    我们将在本章使用前一章中介绍的基本函数编写一个完整的TCP客户/服务器程序实例 这个简单得例子是执行如下步骤的一个回射服务器: TCP回射服务器程序 #include "unp.h" ...

  2. 学习笔记 第十五章 JavaScript基础

    第15章   JavaScript基础 [学习重点] 了解JavaScript基础知识 熟悉常量和变量 能够使用表达式和运算符 正确使用语句 能够掌握数据类型和转换的基本方法 正确使用函数.对象.数组 ...

  3. Python学习笔记第二十五周(Django补充)

    1.render_to_reponse() 不同于render,render_to_response()不用包含request,直接写template中文件 2.locals() 如果views文件中 ...

  4. WP8.1学习系列(第二十五章)——控件样式

      XAML 框架提供许多自定义应用外观的方法.通过样式可以设置控件属性,并重复使用这些设置,以便保持多个控件具有一致的外观. 路线图: 本主题与其他主题有何关联?请参阅: 使用 C# 或 Visua ...

  5. JavaScript高级程序设计学习笔记第十五章--使用Canvas绘图

    一.基本用法 1.要使用<canvas>元素,必须先设置其 width 和 height 属性,指定可以绘图的区域大小.能通过 CSS 为该元素添加样式,如果不添加任何样式或者不绘制任何图 ...

  6. [ExtJS5学习笔记]第二十五节 利用window.open()函数实现ExtJS5的登陆页面跳转

    本文地址:http://blog.csdn.net/sushengmiyan/article/details/40427543 mvvm方式实现登陆的博客:http://blog.csdn.net/s ...

  7. 【WPF学习】第二十五章 日期控件

    WPF包含两个日期控件:Calender和DatePicker.这两个控件都被设计为允许用户选择日期. Calendar控件显示日期,在与Windows操作系统中看到的日历(例如,当配置系统日期时看到 ...

  8. “全栈2019”Java多线程第二十五章:生产者与消费者线程详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  9. “全栈2019”Java第二十五章:流程控制语句中循环语句while

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

随机推荐

  1. 【bzoj3930】[CQOI2015]选数 莫比乌斯反演+杜教筛

    题目描述 我们知道,从区间[L,H](L和H为整数)中选取N个整数,总共有(H-L+1)^N种方案.小z很好奇这样选出的数的最大公约数的规律,他决定对每种方案选出的N个整数都求一次最大公约数,以便进一 ...

  2. 用canvas绘制android机器人

    直接上代码: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UT ...

  3. POJ 3683 Priest John's Busiest Day(2-SAT+方案输出)

    Priest John's Busiest Day Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 10010   Accep ...

  4. wooyun

    转自:http://zone.wooyun.org/content/19128 tencent.com baidu.com sina.com.cn sohu.com discuz.net rising ...

  5. bzoj 合集 1079 1791 1876 2208 2306

    1079 记忆化瞎搞吧,[a][b][c][d][e][l]表示当前有能涂1次的油漆a个,能涂2次的b个….前一个颜色为l,再搞下转移就行了. 1791 基环树上找直径 1876 高精度 2208 看 ...

  6. 【08】node 之 fs文件

    var fs = require("fs");//fs 系统文件模块,对文件进行操作.Node.js 文件系统(fs 模块)模块中的方法均有异步和同步版本,例如读取文件内容的函数有 ...

  7. 给gridview增加行链接,点击行任意位置进行跳转

    原文发布时间为:2009-04-14 -- 来源于本人的百度文章 [由搬家工具导入] 可这样,在GridView的RowDataBound输入代码,假如id在第0列,且不是摸板列: C# code p ...

  8. android基本控件学习-----ScrollView

    ScrollView(滚动条)的讲解: 一.对于ScrollView滚动条还是很好理解的,共有两种水平和垂直,ScrollView和HorizontalScrollview,这个里面不知道该总结写什么 ...

  9. 提高Android Studio运行、编译速度方案

    1.安装完成后启动卡死 刚刚打开studio就卡在gradle building的界面再也不动了(去连接墙外的网下载),那么这个时候我们就需要把这个联网下载操作屏蔽掉,找到studio安装目录,找到i ...

  10. AC日记——[国家集训队2010]小Z的袜子 cogs 1775

    [国家集训队2010]小Z的袜子 思路: 传说中的莫队算法(优雅的暴力): 莫队算法是一个离线的区间询问算法: 如果我们知道[l,r], 那么,我们就能O(1)的时间求出(l-1,r),(l+1,r) ...