Linux 网络编程中的I/O多路复用

I/O多路复用的基本思想:

1、先构造一张有关描述符的表, 然后调用一个函数。当这些文件描述符中的一个或者多个已经准备好进行I/O操作时函数时才返回。

2、函数返回时告诉进程那个描述符已就绪, 可以进行I/O操作。

下面分别对I/O多路复用用到的3个重要函数进行了简单的介绍:

*************************************************

#include <sys/select.h>

int select(int n, fd_set  *read_fds, fd_set *write_fds, fd_set *except_fds, struct timeval *timeout);

int pselect(int nfds, fd_set *readfds, fd_set *writefds,  fd_set *exceptfds, const struct timespec *timeout,  const sigset_t *sigmask);

 DESCRIPTION

select() and  pselect()  allow  a  program  to  monitor  multiple  file descriptors,  waiting  until one or more of the file descriptors become "ready" for some class of I/O operation (e.g., input possible).  A file descriptor  is considered ready if it is possible to perform the corresponding I/O operation (e.g., read(2)) without blocking.

struct timeval  {
               long    tv_sec;         /* seconds */
               long    tv_usec;        /* microseconds */      
           };

struct timespec {
               long    tv_sec;         /* seconds */
               long    tv_nsec;        /* nanoseconds */
           };

select()函数和pselect()函数的用途基本一样, 不同点就在于

       (i)    select() 使用 timeout 参数是struct timeval (with seconds and microseconds), 而 pselect() 的超时时间参数是 struct timespec  (with seconds and nanoseconds).
       (ii)   select()  may  update  the timeout argument to indicate how much time was left.  pselect() does not change this argument.
       (iii)  select() has no  sigmask  argument,  and  behaves  as  pselect() called with NULL sigmask.

pselect()函数存在的价值在于防止竟态的产生。

The reason that pselect() is needed is that if one wants to wait for either a signal or for a file descriptor to become ready, then an atomic test is needed to prevent race conditions.  (Suppose the signal handler sets a global flag and returns.  Then a test of this global flag followed  by  a  call of select() could hang indefinitely if the signal arrived just after the test but just before the call.  By contrast, pselect() allows one to first block signals, handle the signals that have come in, then call pselect() with the desired  sigmask,  avoiding  the race.)

************************************************

#include <poll.h>

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

struct pollfd {
               int   fd;         /* file descriptor */
               short events;     /* requested events */
               short revents;    /* returned events */
           };

The caller should specify the number of items in the fds array in nfds.
The field fd contains a file descriptor for an open file.
The  field  events  is  an  input  parameter, a bit mask specifying the events the application is interested in.

The field revents is an output parameter, filled by the kernel with the events  that  actually  occurred.   The  bits  returned  in revents can include any of those specified in events, or one of the values POLLERR,POLLHUP,  or POLLNVAL.  (These three bits are meaningless in the events field, and will be set in the revents field whenever the corresponding condition is true.)

 If  none of the events requested (and no error) has occurred for any of the file descriptors, then  poll()  blocks  until  one  of  the  events occurs.
The  timeout  argument  specifies  an upper limit on the time for which  poll() will block, in milliseconds.  Specifying  a  negative  value  in  timeout means an infinite timeout.

*******************************************

 #include <sys/epoll.h>

Description:  epoll is a variant of poll(2) that can be used either as an edge-triggered or a level-triggered interface and scales well to large numbers of
       watched file descriptors. 

下面是Linux中给出epoll()使用案例:

#define MAX_EVENTS 10
           struct epoll_event ev, events[MAX_EVENTS];
           int listen_sock, conn_sock, nfds, epollfd;

           /* Set up listening socket, 'listen_sock' (socket(),
              bind(), listen()) */

           epollfd = epoll_create(10);
           if (epollfd == -1) {
               perror("epoll_create");
               exit(EXIT_FAILURE);
           }

           ev.events = EPOLLIN;
           ev.data.fd = listen_sock;
           if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1) {
               perror("epoll_ctl: listen_sock");
               exit(EXIT_FAILURE);
           }

           for (;;) {
               nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
               if (nfds == -1) {
                   perror("epoll_pwait");
                   exit(EXIT_FAILURE);
               }

               for (n = 0; n < nfds; ++n) {
                   if (events[n].data.fd == listen_sock) {
                       conn_sock = accept(listen_sock,
                                       (struct sockaddr *) &local, &addrlen);
                       if (conn_sock == -1) {
                           perror("accept");
                           exit(EXIT_FAILURE);
                       }
                       setnonblocking(conn_sock);
                       ev.events = EPOLLIN | EPOLLET;
                       ev.data.fd = conn_sock;
                       if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock,
                                   &ev) == -1) {
                           perror("epoll_ctl: conn_sock");
                           exit(EXIT_FAILURE);
                       }
                   } else {
                       do_use_fd(events[n].data.fd);
                   }
               }
           }
note:When used as an edge-triggered interface, for performance reasons, it is possible to add the  file  descriptor  inside  the  epoll  interface (EPOLL_CTL_ADD)  once by specifying (EPOLLIN|EPOLLOUT).  This allows you to avoid continuously switching between EPOLLIN and EPOLLOUT calling epoll_ctl(2) with EPOLL_CTL_MOD.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值