I/O多路转接

1.作用

①监视文件描述符的行为
②I/O多路转接技术:先构造一张有关描述符的列表,然后调用一个函数,知道这些描述符中的一个已准备好进行I/O时,给函数才返回。在返回时,它告诉进程哪些描述符已准备好可以进行I/O。

2.select函数

以事件为单位 监视文件描述符。 监视事件单一

int select(int nfds, fd_set *readfds, fd_set *writefds,
           fd_set *exceptfds, struct timeval *timeout);


void FD_CLR(int fd, fd_set *set);//不是清空 指的是删除 从集合中删除某个
int  FD_ISSET(int fd, fd_set *set);//是否在集合中
void FD_SET(int fd, fd_set *set);//增加到集合中
void FD_ZERO(fd_set *set);//清空

①nfds你所指定的文件描述的中最大的那个+1
②fd_set *readfds, fd_set *writefds,fd_set *exceptfds 你所关心的可读 可写 异常的文件描述符
③最长等待时间 可以当做一个安全的sleep函数
不进行超时设置,就是死等,直到你感兴趣的事件发生
当函数返回的时候,这三个集合不会是你存在的监视现场,是当前存放结果的地方
监视结果和监视现场在同一个地方
对以前程序的改进 盲等

void relay(int fd1, int fd2)
{
    
    
    int fd1_save;
    int fd2_save;
    struct fsm_st fsm12, fsm21;
    fd_set rset, wset;//读集合 写集合

    //不能确定哪个是阻塞哪个是非阻塞
    fd1_save = fcntl(fd1,F_GETFL);
    fcntl(fd1, F_SETFL, fd1_save|O_NONBLOCK);

    fd2_save = fcntl(fd2,F_GETFL);
    fcntl(fd2, F_SETFL, fd2_save|O_NONBLOCK);

    fsm12.State = STATE_R;
    fsm12.sfd = fd1;
    fsm12.dfd = fd2;

    fsm21.State = STATE_R;
    fsm21.sfd = fd2;
    fsm21.dfd = fd1;


    while(STATE_T != fsm12.State || STATE_T != fsm21.State)
    {
    
    
            //布置监视任务

            FD_ZERO(&rset);FD_ZERO(&wset);

            if(STATE_R == fsm12.State)
            {
    
    
                    FD_SET(fsm12.sfd, &rset);
            }
            if(STATE_W == fsm12.State)
            {
    
    
                    FD_SET(fsm12.dfd, &wset);
            }

            if(STATE_R == fsm21.State)
            {
    
    
                    FD_SET(fsm21.sfd, &rset);
            }
            if(STATE_W == fsm21.State)
            {
    
    
                    FD_SET(fsm21.dfd, &wset);
            }

            //监视
            if(select(Max(fd1, fd2)+1, &rset, &wset, NULL, NULL)<0)
            {
    
    
                    if(EINTR == errno)
                            continue;
                    perror("select()");
                    exit(1);

            }

            //根据监视结果 来推动状态机

            if(FD_ISSET(fd1, &rset) || FD_ISSET(fd2, &wset))
                    fsm_driver(&fsm12);
            if(FD_ISSET(fd1, &wset) || FD_ISSET(fd2, &rset))
            fsm_driver(&fsm21);
    }

    fcntl(fd1, F_SETFL, fd1_save);
    fcntl(fd2, F_SETFL, fd2_save);


}

3.poll

wait for some event on a file descriptor
以文件描述符为单位,组织事件
感兴趣的事件和发生的事件已经分开存放了

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

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

①struct pollfd *fds 不是给他一个结构体,而是给一个结构体数组的起始地址
②nfds指定数组的大小
③超时时间


void relay(int fd1, int fd2)
{
    
    
   int fd1_save;
   int fd2_save;
   struct fsm_st fsm12, fsm21;
   struct pollfd pfd[2];

   //不能确定哪个是阻塞哪个是非阻塞
   fd1_save = fcntl(fd1,F_GETFL);
   fcntl(fd1, F_SETFL, fd1_save|O_NONBLOCK);

   fd2_save = fcntl(fd2,F_GETFL);
   fcntl(fd2, F_SETFL, fd2_save|O_NONBLOCK);

   fsm12.State = STATE_R;
   fsm12.sfd = fd1;
   fsm12.dfd = fd2;

   fsm21.State = STATE_R;
   fsm21.sfd = fd2;
   fsm21.dfd = fd1;


   pfd[0].fd = fd1;
   pfd[1].fd = fd2;

   while(STATE_T != fsm12.State || STATE_T != fsm21.State)
   {
    
    
           //布置监视任务

           pfd[0].events = 0;
           pfd[1].events = 0;

           if(STATE_R == fsm12.State)
           {
    
    
                   pfd[0].events |= POLLIN;
           }
           if(STATE_W == fsm21.State)
           {
    
    
                   pfd[0].events |= POLLOUT;

           }

           if(STATE_R == fsm21.State)
           {
    
    
                   pfd[1].events |= POLLIN;

           }
           if(STATE_W == fsm12.State)
           {
    
    
                   pfd[1].events |= POLLOUT;
           }

           //监视
           while(poll(pfd, 2 ,-1) < 0)
           {
    
    
                   if(EINTR == errno)
                           continue;
                   perror("select()");
                   exit(1);

           }

           //根据监视结果 来推动状态机

           if(pfd[0].revents & POLLIN || pfd[1].events & POLLOUT)
                   fsm_driver(&fsm12);
           if(pfd[1].revents & POLLIN || pfd[0].events & POLLOUT )
                   fsm_driver(&fsm21);
   }

   fcntl(fd1, F_SETFL, fd1_save);
   fcntl(fd2, F_SETFL, fd2_save);


}

4.epoll

SYNOPSIS
 #include <sys/epoll.h>

DESCRIPTION
The  epoll  API performs a similar task to poll(2):
monitoring multiple file descriptors to see if I/O is possible on 
any of them.  The epoll API can be used either as an  edge-triggered
or  a  level-triggered  interface and scales well to large numbers 
of watched file descriptors.  The following system calls are provided
to create and manage an epoll instance:

 *  epoll_create(2)  creates an epoll instance and returns a file
   descriptor referring to that instance.  (The  more  recent
   epoll_create1(2)  extends  the  functionality  of epoll_create(2).)

 *  Interest in particular file descriptors is then registered via
  epoll_ctl(2).  The set of file descriptors currently registered on
   an epoll instance is sometimes called  an epoll set.

 *  epoll_wait(2) waits for I/O events, blocking the calling thread 
 if no events are cur‐rently available.

epoll_create

int epoll_create(int size);

size随意给一个正数
原来的poll是自己管理一个数组,现在是内核在帮你管理
epoll_ctl

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
typedef union epoll_data {
    
    
    void        *ptr;
    int          fd;
    uint32_t     u32;
    uint64_t     u64;   
} epoll_data_t;

struct epoll_event {
    
    
    uint32_t     events;      /* Epoll events */
    epoll_data_t data;        /* User data variable */
};

刚才获得的实例,操作, 针对哪个文件描述符,什么事件
操作是针对这个文件描述符的哪个事件
epoll_wait

 int epoll_wait(int epfd, struct epoll_event *events,
                      int maxevents, int timeout);

猜你喜欢

转载自blog.csdn.net/ZZHinclude/article/details/119843084