zoukankan      html  css  js  c++  java
  • 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);
    View Code

    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 }
    View Code

    3.sig_io信号处理函数

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

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

     1 static void
     2 sig_io(int signo)
     3 {
     4     ssize_t        len;
     5     int            nread;
     6     DG            *ptr;
     7 
     8     for (nread = 0; ; ) {
     9         if (nqueue >= QSIZE)
    10             err_quit("receive overflow");
    11 
    12         ptr = &dg[iput];
    13         ptr->dg_salen = clilen;
    14         len = recvfrom(sockfd, ptr->dg_data, MAXDG, 0,
    15                        ptr->dg_sa, &ptr->dg_salen);
    16         if (len < 0) {
    17             if (errno == EWOULDBLOCK)
    18                 break;        /* all done; no more queued to read */
    19             else
    20                 err_sys("recvfrom error");
    21         }
    22         ptr->dg_len = len;
    23 
    24         nread++;
    25         nqueue++;
    26         if (++iput >= QSIZE)
    27             iput = 0;
    28 
    29     }
    30     cntread[nread]++;        /* histogram of # datagrams read per signal */
    31 }
    View Code

    4.sig_hup信号处理函数

    1 static void
    2 sig_hup(int signo)
    3 {
    4     int        i;
    5 
    6     for (i = 0; i <= QSIZE; i++)
    7         printf("cntread[%d] = %ld
    ", i, cntread[i]);
    8 }
    View Code

    对于TCP套接字的SIGIO信号

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

  • 相关阅读:
    cout的输出格式初探
    CVPR 2015 papers
    C语言的32个保留字
    读取siftgeo格式文件的matlab程序
    (转)各类排序算法总结
    被除数、除数、商、余数的正负号规律二
    被除数、除数、商、余数的正负号规律一
    FCKEditor上传图片word
    CKEditor上传图片word
    在线编辑器上传图片word
  • 原文地址:https://www.cnblogs.com/runnyu/p/4670358.html
Copyright © 2011-2022 走看看