信号驱动式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. ORA-12012: 自动执行作业 "SYS"."ORA$AT_OS_OPT_SY_21" 出错

    oracle 12.2.0.1版本报错: Errors in file /u01/app/oracle/diag/rdbms/easdb/easdb2/trace/easdb2_j000_27520. ...

  2. 【bzoj4589】Hard Nim FWT

    题目描述 Claris和NanoApe在玩石子游戏,他们有n堆石子,规则如下: 1. Claris和NanoApe两个人轮流拿石子,Claris先拿. 2. 每次只能从一堆中取若干个,可将一堆全取走, ...

  3. AutoDispose代替RxLifecycle优雅的解决RxJava内存泄漏问题

    使用过Rxjava的小伙伴都知道,在使用RxJava时如果处理不当,很可能会产生内存泄漏的问题. 我们使用rxjava最大的原因是响应式编程使我们的异步操作代码变得很优雅,在Android中,也使线程 ...

  4. pat Public Bike Management (30)

    There is a public bike service in Hangzhou City which provides great convenience to the tourists fro ...

  5. java根据开始时间结束时间计算中间间隔日期

    public static void main(String[] args) throws Exception { String beginDate = "2016-07-16"; ...

  6. dockerfile 镜像构建

    1.镜像的构建有手动与自动方式,这里我们介绍自动化的构建方式 ,dockerfile常用指令如下 2.构建指令build Usage: docker image build [OPTIONS] PAT ...

  7. Ubuntu中vim添加lua支持

    系统:Ubuntu 15.10/16.04 因为Ubuntu15.10系统自带vim不支持lua,所以得自己编译安装. $ sudo apt install vim-nox vim-nox可以让vim ...

  8. Codeforces 815 C Karen and Supermarket

    On the way home, Karen decided to stop by the supermarket to buy some groceries. She needs to buy a ...

  9. JDBC-oracle(登陆)

    题目: 第一步:创建用户表,并插入数据(插入后记得commit) create table users ( name ), password ) ); '); '); 第二步:编写登陆界面(index ...

  10. Jenkins集成java非maven/ant项目的打包思路

    打包的思路如下: 1.使用javac命令对代码进行编译,比如递归编译整个项目的java代码.(注意:需要一一对应引用的jar包) 2.输出并整理war包的文件夹结构目录,参考:http://www.c ...