您的位置:首页 > 运维架构 > Linux

linux epoll poll select

2015-09-15 14:53 579 查看
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函数返回以后会修改readfds和timeout内的值

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)水平触发,是缺省的工作方式,并且同时支持block和no-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在高并发量的处理上表现更优秀。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: