原创 多路复用select与poll

2010-6-3 14:07 1827 4 4 分类: MCU/ 嵌入式

 


作者:曹忠明,华清远见嵌入式学院讲师。


在UNIX/linux中有4中IO模型,分别为:
        1、 阻塞IO
        2、 非阻塞IO
        3、 IO多路复用
        4、 信号驱动IO


这几种IO模型,阻塞IO是最长用到的,并且操作相对简单,但是缺点在于效率低下,尤其是在,同时操作多个IO的时候,不能随时的处理各个IO操作。而非阻塞IO可以解决这个问题,但是同样也存在的问题,因为使用非阻塞IO模型的时候,就需要对每个IO操作进行轮训操作,而实际上轮空的几率是很大的,多以非阻塞IO模型是在那种轮空几率相对较小的的时候才会使用。因为轮训是会占用相当多的cpu时间片的。基于这种考虑多路复用模型变产生了。


多路复用模型是对多个IO操作进行检测,返回可操作集合,这样就可以对其进行操作了。这样就避免了阻塞IO不能随时处理各个IO和非阻塞占用系统资源的确定。下面就多路复用中两个常用的系统调用select和poll进行简单的说明。


1、 select


select函数的原型为:


#include <sys/select.h>
        #include <sys/time.h>
        #include <sys/types.h>
        #include <unistd.h>
        int select(int nfds,
                fd_set *readfds,
                fd_set *writefds,
                fd_set *exceptfds,
                struct timeval *timeout);


select的使用需要维护三类文件描述符集合,分别是:
        readfds:用于存放我们要检测的可读的文件描述符。
        writefds:用于存放我们要检测的可写的文件描述符。
        exceptfds:用于存放我们要检测的是否发生异常的文件描述符。


在使用select的时候需要注意,在调用select后者几个集合就会变化,变为是操作的集合,即可读写或发生异常,所以在调用select之前需要保存以前的集合。


系统为我们提供了下面四个宏来操作这些文件描述符集合:


void FD_SET(int fd, fd_set *set);
        void FD_CLR(int fd, fd_set *set);
        I int FD_ISSET(int fd, fd_set *set);
        void FD_ZERO(fd_set *set);


其中FD_SET用来将一个文件描述符加入到文件描述符集合里,FD_CLR是删除,FD_ISSET是判断文件描述符是否在这个集合里,而FD_ZERO是将这个集合清零。


下面我们以一个简单的例子来说明select的使用。


#include <stdio.h>
        #include <stdlib.h>
        #include <unistd.h>
        #include <string.h>
        #include <sys/types.h>
        #include <sys/select.h>
        #include <sys/socket.h>
        #include <netinet/in.h>
        #include <arpa/inet.h>


#define N 64


int main(int argc, char *argv[])
        {
                if(argc != 2)
                {
                        printf(“format: server <server ip> <server port>”);
                        exit(0);
                }
                int i, servsock, connfd, maxfd;
                char buf[N];
                fd_set rdfd,rdfd1;


        struct sockaddr_in servaddr;


        if((servsock = socket(PF_INET, SOCK_STREAM, 0)) < 0)
                {
                        perror("socket");
                        exit(1);
                }


        memset(&servaddr, 0, sizeof(servaddr));
                servaddr.sin_family = PF_INET;
                servaddr.sin_addr.s_addr = inet_addr("192.168.1.203");
                servaddr.sin_port = htons(8889);


        if(bind(servsock, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
                {
                        perror("bind");
                        exit(1);
                }


        listen(servsock, 5);
                maxfd = servsock;

                FD_ZERO(&rdfd1);
                FD_SET(0, &rdfd1);
                FD_SET(servsock, &rdfd1);


        for(;;)
                {

                        rdfd = rdfd1;
                        if(select(maxfd+1, &rdfd, NULL, NULL, NULL) < 0)
                        {
                                perror("select");
                                exit(1);
                        }


                for(i = 0; i <= maxfd; i++)
                        {
                                if(FD_ISSET(i, &rdfd))
                                {
                                        if( i == STDIN_FILENO)
                                        {
                                                fgets(buf, N, stdin);
                                                printf("%s", buf);
                                        }
                                        else if(i == servsock)
                                        {
                                                connfd = accept(servsock, NULL, NULL);
                                                printf("New Connection %d is comming\n", connfd);
                                                send(connfd, buf, strlen(buf), 0);
                                                sleep(1);
                                                close(connfd);
                                        }
                                }
                        }
                }


        return 0;
        }


上面这段程序在select会阻塞,这个时候我们可以定义一个时间结构体变量,如下:


struct timeval timeout;
        timeval的原型为:
        struct timeval {
                                long        tv_sec;        /* seconds */
                                long        tv_usec;        /* microseconds */
        };


这个变量可以提供一个微秒级的时间,我们可以使用这个作为超时处理。使用方式就是把这个变量作为select系统调用的最后一个参数。


2、 poll


poll的原型为:


#include <poll.h>
        int poll(struct pollfd *fds, nfds_t nfds, int timeout);


poll的使用不需要分别维护几个表,只需要维护一个结构体数组,这个结构体为:


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


这个结构体几个成员分别是我们要监测的文件描述符,和我们请求的事件及最后返回的事件。


poll事件的类型为:


POLLIN There is data to read.
        POLLPRI There is urgent data to read .
        POLLOUT Writing now will not block.
        POLLERR Error condition (output only).
        POLLHUP Hang up (output only).
        POLLNVAL Invalid request: fd not open (output only).


上面这些事件最下面三种在events中没有用的,只能在revents中才能出现。我们可以调用poll并且查看每个文件描述符的返回状态进行相应的操作。


下面以一个简单的实力说明poll的使用:


#include <stdio.h>
        #include <stdlib.h>
        #include <unistd.h>
        #include <poll.h>
        #include <sys/socket.h>
        #include <netinet/in.h>
        #include <arpa/inet.h>
        #include <sys/types.h>
        #include <errno.h>
        #include <string.h>


#define OPEN_MAX openmax
        #define BUFSIZE 1024


int main(int argc, char **argv)
        {
                if(argc != 3)
                {
                        printf("format: server-poll <server ip> <port>");
                        exit(0);
                }


        int openmax = getdtablesize();


        int listensock, connsock;
                struct sockaddr_in servaddr;
                struct pollfd fds[OPEN_MAX];
                int i, count = 0, ready, reval;
                char readbuf[BUFSIZE];


        for(i = 0; i < OPEN_MAX; i++)
                {
                        fds.fd = -1;
                }


        if((listensock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
                {
                        perror("socket");
                        exit(0);
                }


        memset(&servaddr, 0, sizeof(servaddr));
                servaddr.sin_family = AF_INET;
                servaddr.sin_addr.s_addr = inet_addr(argv[1]);
                servaddr.sin_port = htons(atoi(argv[2]));


        if(bind(listensock, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
                {
                        perror("bind");
                        exit(1);
                }


        listen(listensock, 5);


        fds[0].fd = 0;
                fds[0].events = POLLIN;
                count++;
                fds[1].fd = listensock;
                fds[1].events = POLLIN;
                count++;

                for(;;)
                {
                        memset(readbuf, 0, BUFSIZE);
                        ready = poll(fds, count, 0);
                        for(i = 0; i < count; i++)
                        {
                                if(fds.revents & POLLIN)
                                {
                                        if(fds.fd == 0)
                                        {
                                                fgets(readbuf, BUFSIZE, stdin);
                                                printf("read buf:%s\n", readbuf);
                                        }
                                        else if(fds.fd == listensock)
                                        {
                                                connsock = accept(listensock, NULL, NULL);
                                                printf("client connect\n");
                                                fds[count].fd = connsock;
                                                fds[count].events = POLLIN;
                                                count++;
                                                }
                                                else
                                                {
                                                        reval = recv(fds.fd, readbuf, BUFSIZE, 0);
                                                        printf("recv buf:%s\n", readbuf);
                                                        send(fds.fd, readbuf, reval, 0);
                                                }
                                        }
                                }
                       }
                }
                return 0;
        }

文章评论0条评论)

登录后参与讨论
我要评论
0
4
关闭 站长推荐上一条 /2 下一条