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在高并发量的处理上表现更优秀。
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在高并发量的处理上表现更优秀。
相关文章推荐
- 马哥-51CTO-Linux培训-0910-程序包管理
- 几种查询linux内核的方法
- Synchronise time with ntp server in Linux
- 修复linux密码
- CentOS6.5下安装xampp
- Centos自动调整错误路径
- Linux_使用Linux之安装jdk 7
- linux线程5(百度知道上一道题)
- CentOS 6.5系统搭建SVN服务
- linux线程4(google面试题)
- 史上最详细的虚拟机上安装Linux教程
- linux命令行安装matlab
- Linux 环境下思源黑体字体与 Java 之间的兼容性问题的解决
- linux下使用ramdisk的一点认识
- centos7下使用yum安装mysql
- Linux鸟哥私房菜学习笔记--第九章
- Linux 下安装ffmpeg(xlib264 yams)
- Linux 下ffmpeg的安装(xlib264 yams)
- CentOS 安装python MySQLdb模块
- 左右GNU Linux企业加密文件系统 eCryptfs简介