UNP学习笔记(第二十五章 信号驱动式I/O)
信号驱动式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是队列中共主循环处理的数据报的总数
1 #include "unp.h" 2 3 static int sockfd; 4 5 #define QSIZE 8 /* size of input queue */ 6 #define MAXDG 4096 /* max datagram size */ 7 8 typedef struct { 9 void *dg_data; /* ptr to actual datagram */ 10 size_t dg_len; /* length of datagram */ 11 struct sockaddr *dg_sa; /* ptr to sockaddr{} w/client's address */ 12 socklen_t dg_salen; /* length of sockaddr{} */ 13 } DG; 14 static DG dg[QSIZE]; /* queue of datagrams to process */ 15 static long cntread[QSIZE+1]; /* diagnostic counter */ 16 17 static int iget; /* next one for main loop to process */ 18 static int iput; /* next one for signal handler to read into */ 19 static int nqueue; /* # on queue for main loop to process */ 20 static socklen_t clilen;/* max length of sockaddr{} */ 21 22 static void sig_io(int); 23 static void sig_hup(int);
2.dg_echo函数
1 void 2 dg_echo(int sockfd_arg, SA *pcliaddr, socklen_t clilen_arg) 3 { 4 int i; 5 const int on = 1; 6 sigset_t zeromask, newmask, oldmask; 7 8 sockfd = sockfd_arg; 9 clilen = clilen_arg; 10 11 for (i = 0; i < QSIZE; i++) { /* init queue of buffers */ 12 dg[i].dg_data = Malloc(MAXDG); 13 dg[i].dg_sa = Malloc(clilen); 14 dg[i].dg_salen = clilen; 15 } 16 iget = iput = nqueue = 0; 17 18 Signal(SIGHUP, sig_hup); 19 Signal(SIGIO, sig_io); 20 Fcntl(sockfd, F_SETOWN, getpid()); 21 Ioctl(sockfd, FIOASYNC, &on); 22 Ioctl(sockfd, FIONBIO, &on); 23 24 Sigemptyset(&zeromask); /* init three signal sets */ 25 Sigemptyset(&oldmask); 26 Sigemptyset(&newmask); 27 Sigaddset(&newmask, SIGIO); /* signal we want to block */ 28 29 Sigprocmask(SIG_BLOCK, &newmask, &oldmask); 30 for ( ; ; ) { 31 while (nqueue == 0) 32 sigsuspend(&zeromask); /* wait for datagram to process */ 33 34 /* 4unblock SIGIO */ 35 Sigprocmask(SIG_SETMASK, &oldmask, NULL); 36 37 Sendto(sockfd, dg[iget].dg_data, dg[iget].dg_len, 0, 38 dg[iget].dg_sa, dg[iget].dg_salen); 39 40 if (++iget >= QSIZE) 41 iget = 0; 42 43 /* 4block SIGIO */ 44 Sigprocmask(SIG_BLOCK, &newmask, &oldmask); 45 nqueue--; 46 } 47 }