• 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套接字几乎没用。

  • 相关阅读:
    模拟登陆江西理工大学教务系统
    python3爬虫 -----华东交大校园新闻爬取与数据分析
    以selenium模拟登陆12306
    PAT (Basic Level) Practice (中文)1076 Wifi密码 (15 分)
    PAT (Basic Level) Practice (中文)1047 编程团体赛 (20 分)
    PAT (Basic Level) Practice (中文)1029 旧键盘 (20 分)
    PAT (Basic Level) Practice (中文)1016 部分A+B (15 分)
    PAT (Basic Level) Practice (中文)1031 查验身份证 (15 分)
    PAT (Basic Level) Practice (中文)1041 考试座位号 (15 分)
    PAT (Basic Level) Practice (中文)1037 在霍格沃茨找零钱 (20 分)
  • 原文地址:https://www.cnblogs.com/runnyu/p/4670358.html
Copyright © 2020-2023  润新知