linux epoll poll select

1.select模型

select所需要的头文件:
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

select函数定义:
int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
struct timeval *timeout);

fd_set操作函数:
FD_CLR(int fd, fd_set *set);    //
用于在文件描述符集合中删除一个文件描述符
FD_ISSET(int fd, fd_set *set);  //
用于检查指定的文件描述符是否在该集合中
FD_SET(int fd, fd_set *set);    //
用于在文件描述符集合中增加一个新的文件描述符。
FD_ZERO(fd_set *set);          //
将指定的文件描述符集清空

timeval的定义如下:
struct timeval {
     long tv_sec; /* seconds */
     long tv_usec; /* 10E-6 second */
};

fd_set   readfds;
FD_ZERO(&readfds);
FD_SET(sock, &readfds);

struct timeval timeout;
timeout.tv_sec  =  2;
timeout.tv_usec =  0;
int ret = select(sock + 1, &readfds, NULL, NULL, &timeout);
// select
函数返回以后会修改readfdstimeout内的值
if (ret > 0)
{
     if (FD_ISSET(sock, &readfds))
    {
         //
可读
    }
}

 select模式的特点:
1.
单个进程监测的fd受限制,默认下是1024个文件描述符;
2.
轮询式检查文件描述符集合中的每个fd可读可写状态,IO效率会随着描述符集合增大而降低;
3.
可以采用一个父进程专门accept,父进程均衡的分配多个子进程分别处理一部分的链接,子进程采用select模型监测自己负责的fd的可读可写。 

2.epoll模型

int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
events
可以是以下几个宏的集合:
EPOLLIN
:表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
EPOLLOUT
:表示对应的文件描述符可以写;
EPOLLPRI
:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
EPOLLERR
:表示对应的文件描述符发生错误;
EPOLLHUP
:表示对应的文件描述符被挂断;
EPOLLET
EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(LevelTriggered)来说的。
EPOLLONESHOT
:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, inttimeout);

LT(level triggered)水平触发,是缺省的工作方式,并且同时支持blockno-block socket。只要buffer里有未被处理的事件,内核会不断通知你就绪未处理的FD

ET(edge-triggered)边缘触发,是高速工作方式,只支持no-block socket。只在buffer大小发生变化时通知,只通知一次。

#include<sys/socket.h>

#include<sys/wait.h>

#include<netinet/in.h>

#include<netinet/tcp.h>

#include<sys/epoll.h>

#include<sys/sendfile.h>

#include<sys/stat.h>

#include<unistd.h>

#include<stdio.h>

#include<stdlib.h>

#include <string.h>

#include<strings.h>

#include<fcntl.h>

#include<errno.h>

 

#define MAX_EVENTS 10

#define PORT 8080

 

//设置socket连接为非阻塞模式

void setnonblocking(intsockfd) {

    int opts;

 

    opts = fcntl(sockfd, F_GETFL);

    if(opts < 0) {

        perror("fcntl(F_GETFL)\n");

        exit(1);

    }

    opts = (opts | O_NONBLOCK);

    if(fcntl(sockfd, F_SETFL, opts) < 0) {

        perror("fcntl(F_SETFL)\n");

        exit(1);

    }

}

 

int main(){

    struct epoll_event ev, events[MAX_EVENTS];

    int addrlen, listenfd, conn_sock, nfds, epfd,fd, i, nread, n;

    struct sockaddr_in local, remote;

    char buf[BUFSIZ];

 

    //创建listen socket

    if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {

        perror("sockfd\n");

        exit(1);

    }

    setnonblocking(listenfd);

    bzero(&local, sizeof(local));

    local.sin_family = AF_INET;

    local.sin_addr.s_addr = htonl(INADDR_ANY);;

    local.sin_port = htons(PORT);

    if( bind(listenfd, (structsockaddr *) &local, sizeof(local)) < 0) {

        perror("bind\n");

        exit(1);

    }

    listen(listenfd, 20);

 

    epfd = epoll_create(MAX_EVENTS);

    if (epfd == -1) {

        perror("epoll_create");

        exit(EXIT_FAILURE);

    }

 

    ev.events = EPOLLIN;

    ev.data.fd = listenfd;

    if (epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd,&ev) == -1) {

        perror("epoll_ctl: listen_sock");

        exit(EXIT_FAILURE);

    }

 

    for (;;) {

        nfds = epoll_wait(epfd, events,MAX_EVENTS, -1);

        if(nfds == -1) {

            perror("epoll_pwait");

            exit(EXIT_FAILURE);

        }

 

        for (i= 0; i < nfds; ++i) {

            fd = events[i].data.fd;

            if (fd== listenfd) {

                while((conn_sock = accept(listenfd,(struct sockaddr *) &remote,

                    (size_t *)&addrlen))> 0) {

                       setnonblocking(conn_sock);

                        ev.events = EPOLLIN |EPOLLET;

                        ev.data.fd = conn_sock;

                        if (epoll_ctl(epfd, EPOLL_CTL_ADD, conn_sock,

                            &ev) == -1) {

                                perror("epoll_ctl: add");

                                exit(EXIT_FAILURE);

                        }

                }

                if(conn_sock == -1) {

                    if(errno != EAGAIN && errno != ECONNABORTED

                        && errno !=EPROTO && errno != EINTR)

                        perror("accept");

                }

                continue;

            } 

            if(events[i].events & EPOLLIN) {

                n = 0;

                while((nread = read(fd, buf + n, BUFSIZ-1)) > 0) {

                    n += nread;

               }

                if(nread == -1 && errno != EAGAIN) {

                    perror("read error");

                }

                ev.data.fd = fd;

                ev.events = events[i].events |EPOLLOUT;

                if(epoll_ctl(epfd, EPOLL_CTL_MOD, fd, &ev) == -1) {

                    perror("epoll_ctl: mod");

                }

            }

            if(events[i].events & EPOLLOUT) {

                sprintf(buf, "HTTP/1.1 200 OK\r\nContent-Length:%d\r\n\r\nHello World", 11);

                intnwrite, data_size = strlen(buf);

                n = data_size;

                while (n> 0) {

                    nwrite = write(fd, buf +data_size - n, n);

                    if(nwrite < n) {

                        if (nwrite == -1 && errno != EAGAIN) {

                            perror("write error");

                        }

                        break;

                    }

                    n -= nwrite;

                }

                close(fd);

            }

        }

    }

 

    return0;

}

  epoll模式的特点: 
1.
支持进程打开的最大文件描述符,很好的解决了C10K问题;
2.IO
效率不随FD数目增加而线性下降,epoll不是通过轮询,而是通过在等待的描述符上注册回调函数,当事件发生时,回调函数负责把发生的事件存储在就绪事件链表中,最后写到用户空间;
3.
使用mmap加速内核与用户空间的消息传递。
4.
内核微调

3.总结

      在连接少而且都活跃的情况下,使用select模型效果很好;而epoll在高并发量的处理上表现更优秀。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值