http://www.cnblogs.com/Anker/p/3265058.html
先了解IO的几种模型
http://blog.csdn.net/hguisu/article/details/7453390,这个讲的很好的,收藏
阻塞的IO模型:数据没到达之前,IO一直阻塞,如果数据到达,则会返回。典型的是recvfrom,一般的默认都是阻塞的。
非阻塞的IO模型:和阻塞相反,只要不能得到结果的时候,IO立刻返回。不会阻塞当前线程。
将阻塞的换成非阻塞的,windows下是ioctlsocket(),linux下是fcntl()
IO复用模型:也就是自己要学习的部分。多路复用的意思是,将多路信号合并到一路上进行处理,类似多个管道汇集到一个管道,与之相反的是多路分解。
IO复用模型主要是select,poll,epoll;对一个IO端口,两次调用,两次返回,比阻塞IO并没有什么优越性;关键是能实现同时对多个IO端口进行监听;函数也会使进程阻塞,但是和阻塞I/O所不同的的,这两个函数可以同时阻塞多个I/O操作。而且可以同时对多个读操作,多个写操作的I/O函数进行检测,直到有数据可读或可写时,才真正调用I/O操作函数。就类似让很多个检测人员去站岗,如果有情况,立刻来汇报,然后进行操作。
信号驱动:(暂时未接触到)首先开启套接口信号驱动I/O功能,并通过系统调用sigaction安装一个信号处理函数(此系统调用立即返回,进程继续工作,它是非阻塞的)。当数据报准备好被读时,就为该进程生成一个SIGIO信号。随即可以在信号处理程序中调用recvfrom来读数据报,井通知主循环数据已准备好被处理中。也可以通知主循环,让它来读数据报。理解为非阻塞的处理方式,通过信号作为命令处理。
异步的IO模型:告知内核启动某个操作,并让内核在整个操作完成后(包括将数据从内核拷贝到用户自己的缓冲区)通知我们。这种模型与信号驱动模型的主要区别是:
信号驱动I/O:由内核通知我们何时可以启动一个I/O操作,
异步I/O模型:由内核通知我们I/O操作何时完成。
epoll跟select都能提供多路I/O复用的解决方案。在现在的Linux内核里有都能够支持,其中epoll是Linux所特有,而select则应该是POSIX所规定,一般操作系统均有实现
http://www.cnblogs.com/Anker/archive/2013/08/14/3258674.html
int select(int maxfdp1,fd_set *readset,fd_set *writeset,fd_set *exceptset,const struct timeval *timeout)
返回值:就绪描述符的数目,超时返回0,出错返回-1
函数参数介绍如下:
(1)第一个参数maxfdp1指定待测试的描述字个数,它的值是待测试的最大描述字加1(因此把该参数命名为maxfdp1),描述字0、1、2...maxfdp1-1均将被测试。
(2)中间的三个参数readset、writeset和exceptset指定我们要让内核测试读、写和异常条件的描述字。如果对某一个的条件不感兴趣,就可以把它设为空指针。struct fd_set可以理解为一个集合,这个集合中存放的是文件描述符,可通过以下四个宏进行设置:
void FD_ZERO(fd_set *fdset); //清空集合
void FD_SET(int fd, fd_set *fdset); //将一个给定的文件描述符加入集合之中
void FD_CLR(int fd, fd_set *fdset); //将一个给定的文件描述符从集合中删除
int FD_ISSET(int fd, fd_set *fdset); // 检查集合中指定的文件描述符是否可以读写
(3)timeout告知内核等待所指定描述字中的任何一个就绪可花多少时间。其timeval结构用于指定这段时间的秒数和微秒数。
struct timeval{
long tv_sec; //seconds
long tv_usec; //microseconds
};
这个参数有三种可能:
(1)永远等待下去:仅在有一个描述字准备好I/O时才返回。为此,把该参数设置为空指针NULL。
(2)等待一段固定时间:在有一个描述字准备好I/O时返回,但是不超过由该参数所指向的timeval结构中指定的秒数和微秒数。
(3)根本不等待:检查描述字后立即返回,这称为轮询。为此,该参数必须指向一个timeval结构,而且其中的定时器值必须为0。
这里有详细解释,主要是通过设置和检查相关的存放fd的数据结构进行下一步处理
缺点:fd的数量受限制,维护存放fd的数据结构,采取的是轮询的方案,效率低。
poll:本质上和select没有区别
poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态,如果设备就绪则在设备等待队列中加入一项并继续遍历,如果遍历完所有fd后没有发现就绪设备,则挂起当前进程,直到设备就绪或者主动超时,被唤醒后它又要再次遍历fd。这个过程经历了多次无谓的遍历。
它没有最大连接数的限制,原因是它是基于链表来存储的,但是同样有一个缺点:
1、大量的fd的数组被整体复制于用户态和内核地址空间之间,而不管这样的复制是不是有意义。
2、poll还有一个特点是“水平触发”,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd。
epoll:
epoll支持水平触发和边缘触发,最大的特点在于边缘触发,它只告诉进程哪些fd刚刚变为就需态,并且只会通知一次。还有一个特点是,epoll使用“事件”的就绪通知方式,通过epoll_ctl注册fd,一旦该fd就绪,内核就会采用类似callback的回调机制来激活该fd,epoll_wait便可以收到通知
epoll的优点:
1、没有最大并发连接的限制,能打开的FD的上限远大于1024(1G的内存上能监听约10万个端口);
2、效率提升,不是轮询的方式,不会随着FD数目的增加效率下降。只有活跃可用的FD才会调用callback函数;
即Epoll最大的优点就在于它只管你“活跃”的连接,而跟连接总数无关,因此在实际的网络环境中,Epoll的效率就会远远高于select和poll。
3、 内存拷贝,利用mmap()文件映射内存加速与内核空间的消息传递;即epoll使用mmap减少复制开销。
1、支持一个进程所能打开的最大连接数
select | 单个进程所能打开的最大连接数有FD_SETSIZE宏定义,其大小是32个整数的大小(在32位的机器上,大小就是32*32,同理64位机器上FD_SETSIZE为32*64),当然我们可以对进行修改,然后重新编译内核,但是性能可能会受到影响,这需要进一步的测试。 |
poll | poll本质上和select没有区别,但是它没有最大连接数的限制,原因是它是基于链表来存储的 |
epoll | 虽然连接数有上限,但是很大,1G内存的机器上可以打开10万左右的连接,2G内存的机器可以打开20万左右的连接 |
2、FD剧增后带来的IO效率问题
select | 因为每次调用时都会对连接进行线性遍历,所以随着FD的增加会造成遍历速度慢的“线性下降性能问题”。 |
poll | 同上 |
epoll | 因为epoll内核中实现是根据每个fd上的callback函数来实现的,只有活跃的socket才会主动调用callback,所以在活跃socket较少的情况下,使用epoll没有前面两者的线性下降的性能问题,但是所有socket都很活跃的情况下,可能会有性能问题。 |
3、 消息传递方式
select | 内核需要将消息传递到用户空间,都需要内核拷贝动作 |
poll | 同上 |
epoll | epoll通过内核和用户空间共享一块内存来实现的。 |
总结:
综上,在选择select,poll,epoll时要根据具体的使用场合以及这三种方式的自身特点。
1、表面上看epoll的性能最好,但是在连接数少并且连接都十分活跃的情况下,select和poll的性能可能比epoll好,毕竟epoll的通知机制需要很多函数回调。
2、select低效是因为每次它都需要轮询。但低效也是相对的,视情况而定,也可通过良好的设计改善
下面是相关的select,poll,和epoll的代码(转)服务器select
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <unistd.h>
#include <sys/types.h>
#define IPADDRESS "127.0.0.1"
#define PORT 8787
#define MAXLINE 1024
#define LISTENQ 5
//函数声明
//创建套接字并进行绑定
static int socket_bind(const char* ip,int port);
//IO多路复用select
static void do_select(int listenfd);
//处理多个连接
static void handle_connection(int *connfds,int num,fd_set *prset,fd_set *pallset);
int main(int argc,char *argv[])
{
int listenfd,connfd,sockfd;
struct sockaddr_in cliaddr;
socklen_t cliaddrlen;
listenfd = socket_bind(IPADDRESS,PORT);
listen(listenfd,LISTENQ);
do_select(listenfd);
return 0;
}
static int socket_bind(const char* ip,int port)
{
int listenfd;
struct sockaddr_in servaddr;
listenfd = socket(AF_INET,SOCK_STREAM,0);
if (listenfd == -1)
{
perror("socket error:");
exit(1);
}
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
inet_pton(AF_INET,ip,&servaddr.sin_addr);
servaddr.sin_port = htons(port);
if (bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) == -1)
{
perror("bind error: ");
exit(1);
}
return listenfd;
}
static void do_select(int listenfd)
{
int connfd,sockfd;
struct sockaddr_in cliaddr;
socklen_t cliaddrlen;
fd_set rset,allset;
int maxfd,maxi;
int i;
int clientfds[FD_SETSIZE]; //保存客户连接描述符
int nready;
//初始化客户连接描述符
for (i = 0;i < FD_SETSIZE;i++)
clientfds[i] = -1;
maxi = -1;
FD_ZERO(&allset);
//添加监听描述符
FD_SET(listenfd,&allset);
maxfd = listenfd;
//循环处理
for ( ; ; )
{
rset = allset;
//获取可用描述符的个数
nready = select(maxfd+1,&rset,NULL,NULL,NULL);
if (nready == -1)
{
perror("select error:");
exit(1);
}
//测试监听描述符是否准备好
if (FD_ISSET(listenfd,&rset))
{
cliaddrlen = sizeof(cliaddr);
//接受新的连接
if ((connfd = accept(listenfd,(struct sockaddr*)&cliaddr,&cliaddrlen)) == -1)
{
if (errno == EINTR)
continue;
else
{
perror("accept error:");
exit(1);
}
}
fprintf(stdout,"accept a new client: %s:%d\n", inet_ntoa(cliaddr.sin_addr),cliaddr.sin_port);
//将新的连接描述符添加到数组中
for (i = 0;i <FD_SETSIZE;i++)
{
if (clientfds[i] < 0)
{
clientfds[i] = connfd;
break;
}
}
if (i == FD_SETSIZE)
{
fprintf(stderr,"too many clients.\n");
exit(1);
}
//将新的描述符添加到读描述符集合中
FD_SET(connfd,&allset);
//描述符个数
maxfd = (connfd > maxfd ? connfd : maxfd);
//记录客户连接套接字的个数
maxi = (i > maxi ? i : maxi);
if (--nready <= 0)
continue;
}
//处理客户连接
handle_connection(clientfds,maxi,&rset,&allset);
}
}
static void handle_connection(int *connfds,int num,fd_set *prset,fd_set *pallset)
{
int i,n;
char buf[MAXLINE];
memset(buf,0,MAXLINE);
for (i = 0;i <= num;i++)
{
if (connfds[i] < 0)
continue;
//测试客户描述符是否准备好
if (FD_ISSET(connfds[i],prset))
{
//接收客户端发送的信息
n = read(connfds[i],buf,MAXLINE);
if (n == 0)
{
close(connfds[i]);
FD_CLR(connfds[i],pallset);
connfds[i] = -1;
continue;
}
printf("read msg is: ");
write(STDOUT_FILENO,buf,n);
//向客户端发送buf
write(connfds[i],buf,n);
}
}
}
客户端 select
#include <netinet/in.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/select.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#define MAXLINE 1024
#define IPADDRESS "127.0.0.1"
#define SERV_PORT 8787
#define max(a,b) (a > b) ? a : b
static void handle_connection(int sockfd);
int main(int argc,char *argv[])
{
int sockfd;
struct sockaddr_in servaddr;
sockfd = socket(AF_INET,SOCK_STREAM,0);
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERV_PORT);
inet_pton(AF_INET,IPADDRESS,&servaddr.sin_addr);
connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr));
//处理连接描述符
handle_connection(sockfd);
return 0;
}
static void handle_connection(int sockfd)
{
char sendline[MAXLINE],recvline[MAXLINE];
int maxfdp,stdineof;
fd_set rset;
int n;
FD_ZERO(&rset);
for (; ;)
{
//添加标准输入描述符
FD_SET(STDIN_FILENO,&rset);
//添加连接描述符
FD_SET(sockfd,&rset);
maxfdp = max(STDIN_FILENO,sockfd);
//进行轮询
select(maxfdp+1,&rset,NULL,NULL,NULL);
//测试连接套接字是否准备好
if (FD_ISSET(sockfd,&rset))
{
n = read(sockfd,recvline,MAXLINE);
if (n == 0)
{
fprintf(stderr,"client: server is closed.\n");
close(sockfd);
FD_CLR(sockfd,&rset);
}
write(STDOUT_FILENO,recvline,n);
}
//测试标准输入是否准备好
if (FD_ISSET(STDIN_FILENO,&rset))
{
n = read(STDIN_FILENO,sendline,MAXLINE);
if (n == 0)
{
FD_CLR(STDIN_FILENO,&rset);
continue;
}
write(sockfd,sendline,n);
}
}
}
poll的例子
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
http://man7.org/linux/man-pages/man2/poll.2.html
/*
* Pollserver.c
*
* Created on: 2012-9-9
* Author: sangerhoo
*/
#include<sys/types.h>
#include<ctype.h>
#include<strings.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netdb.h>
#include<arpa/inet.h>
#include<ctype.h>
#include<errno.h>
#include<sys/time.h>
#include<stdio.h>
#include<string.h>
#include<sys/poll.h>
#include<stdlib.h>
#define LISTEN_QUEUE_NUM 5
#define MAX_CONNECT 1024
#define BUFFER_SIZE 256
#define ECHO_PORT 2029
static struct pollfd clipoll[MAX_CONNECT];
int max = 0;
/*initialize poll struct*/
static void poll_init()
{
int i;
for (i = 0; i < MAX_CONNECT; i++)
clipoll[i].fd = -1;
}
/*
find a unused pollfd struct
if found then return its index
else return -1
*/
static int poll_alloc()
{
int i;
for (i = 0; i < MAX_CONNECT; i++)
{
if (clipoll[i].fd < 0)
return i;
}
return -1;
}
/*find used max index in pllfd*/
static void update_max()
{
int i;
max = 0;
for (i = 0; i < MAX_CONNECT; i++)
{
if (clipoll[i].fd > 0)
max = i;
}
}
/*free a pollfd when unused*/
static int poll_free(int i)
{
close(clipoll[i].fd);
clipoll[i].fd = -1;
clipoll[i].events = 0;
clipoll[i].revents = 0;
update_max();
}
/*
fill up pollfd with file descriptable
and event
*/
static int poll_set_item(int fd, uint32_t events)
{
int i;
if ((i = poll_alloc()) < 0)
return -1;
clipoll[i].fd = fd;
clipoll[i].events = events;
clipoll[i].revents = 0;
if (i > max)
max = i;
return 0;
}
int main(int argc, char **argv)
{
struct sockaddr_in servaddr, remote;
int request_sock, new_sock;
int nfound, i, bytesread;
uint32_t addrlen;
char buf[BUFFER_SIZE];
/*setup socket*/
if ((request_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("socket");
return -1;
}
/*fill up ip address structure*/
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = INADDR_ANY;
servaddr.sin_port = htons(ECHO_PORT);
/*bind server ip address structure*/
if (bind(request_sock, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0)
{
perror("bind");
return -1;
}
/*listen client*/
if (listen(request_sock, LISTEN_QUEUE_NUM) < 0)
{
perror("listen");
return -1;
}
poll_init();
poll_set_item(request_sock, POLLIN);
while (1)
{
/*check whether ready file exist ?*/
if ((nfound = poll(clipoll, max + 1, 500)) < 0)
{
if (errno == EINTR)
{
printf("interruptedb system call\n");
continue;
}
perror("poll");
return -1;
}else if(nfound == 0)
{
printf(".");
fflush(stdout);
continue;
}
/*check listen socket if ready or not ?*/
if (clipoll[0].revents & (POLLIN | POLLERR))
{
addrlen = sizeof(remote);
if ((new_sock = accept(request_sock, (struct sockaddr *) &remote, &addrlen)) < 0)
{
perror("accept");
return -1;
}
printf("connection fromm host %s,port %d, socket %d\r\n",
inet_ntoa(remote.sin_addr), ntohs(remote.sin_port), new_sock);
if (poll_set_item(new_sock, POLLIN) < 0)
fprintf(stderr, "Too many connects\r\n");
nfound--;
}
/*check communicating socket if ready or not ? */
for (i = 1; i <= max && nfound > 0; i++)
{
if (clipoll[i].fd >= 0 && clipoll[i].revents & (POLLIN | POLLERR))
{
nfound--;
if((bytesread = read(clipoll[i].fd, buf, sizeof(buf) - 1)) < 0)
{
perror("read");
poll_free(i);
continue;
}
if(bytesread == 0)
{
fprintf(stderr, "server: end of file on %d\r\n", clipoll[i].fd);
poll_free(i);
continue;
}
buf[bytesread] = 0;
printf("%s:%d bytes from %d :%s\n", argv[0], bytesread,
clipoll[i].fd, buf);
if (write(clipoll[i].fd, buf, bytesread) != bytesread)
{
perror("echo");
poll_free(i);
continue;
}
}
}
}
return 0;
}
epoll:
epoll只有epoll_create,epoll_ctl,epoll_wait 3个系统调用。
http://man7.org/linux/man-pages/man7/epoll.7.html
http://blog.csdn.net/xiajun07061225/article/details/9250579
epoll的三个函数
- int epoll_creae(int size);
参数:size为epoll上能关注的最大描述符数
- int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epoll的事件注册函数,它不同于select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。
参数:epfd由epoll_create生成的epoll专用描述符
op操作:EPOLL_CTL_ADD 注册 EPOLL_CTL_MOD修改 EPOLL_DEL删除
fd:关联的文件描述符
evnet告诉内核要监听什么事件
- int epoll_wait(int epfd,struct epoll_event*events,int maxevents,int timeout);
参数:epfd要检测的句柄
events:用于回传待处理时间的数组
maxevents:告诉内核这个events有多大,不能超过之前的size
timeout:为超时时间,-1阻塞,0为立刻返回。
返回值是需要读写的fd的个数
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <errno.h>
#define MAXEVENTS 64
static int
make_socket_non_blocking (int sfd)
{
int flags, s;
flags = fcntl (sfd, F_GETFL, 0);
if (flags == -1)
{
perror ("fcntl");
return -1;
}
flags |= O_NONBLOCK;
s = fcntl (sfd, F_SETFL, flags);
if (s == -1)
{
perror ("fcntl");
return -1;
}
return 0;
}
static int
create_and_bind (char *port)
{
struct addrinfo hints;
struct addrinfo *result, *rp;
int s, sfd;
memset (&hints, 0, sizeof (struct addrinfo));
hints.ai_family = AF_UNSPEC; /* Return IPv4 and IPv6 choices */
hints.ai_socktype = SOCK_STREAM; /* We want a TCP socket */
hints.ai_flags = AI_PASSIVE; /* All interfaces */
s = getaddrinfo (NULL, port, &hints, &result);
if (s != 0)
{
fprintf (stderr, "getaddrinfo: %s\n", gai_strerror (s));
return -1;
}
for (rp = result; rp != NULL; rp = rp->ai_next)
{
sfd = socket (rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (sfd == -1)
continue;
s = bind (sfd, rp->ai_addr, rp->ai_addrlen);
if (s == 0)
{
/* We managed to bind successfully! */
break;
}
close (sfd);
}
if (rp == NULL)
{
fprintf (stderr, "Could not bind\n");
return -1;
}
freeaddrinfo (result);
return sfd;
}
int
main (int argc, char *argv[])
{
int sfd, s;
int efd;
struct epoll_event event;
struct epoll_event *events;
if (argc != 2)
{
fprintf (stderr, "Usage: %s [port]\n", argv[0]);
exit (EXIT_FAILURE);
}
sfd = create_and_bind (argv[1]);
if (sfd == -1)
abort ();
s = make_socket_non_blocking (sfd);
if (s == -1)
abort ();
s = listen (sfd, SOMAXCONN);
if (s == -1)
{
perror ("listen");
abort ();
}
efd = epoll_create1 (0);
if (efd == -1)
{
perror ("epoll_create");
abort ();
}
event.data.fd = sfd;
event.events = EPOLLIN | EPOLLET;
s = epoll_ctl (efd, EPOLL_CTL_ADD, sfd, &event);
if (s == -1)
{
perror ("epoll_ctl");
abort ();
}
/* Buffer where events are returned */
events = calloc (MAXEVENTS, sizeof event);
/* The event loop */
while (1)
{
int n, i;
n = epoll_wait (efd, events, MAXEVENTS, -1);
for (i = 0; i < n; i++)
{
if ((events[i].events & EPOLLERR) ||
(events[i].events & EPOLLHUP) ||
(!(events[i].events & EPOLLIN)))
{
/* An error has occured on this fd, or the socket is not
ready for reading (why were we notified then?) */
fprintf (stderr, "epoll error\n");
close (events[i].data.fd);
continue;
}
else if (sfd == events[i].data.fd)
{
/* We have a notification on the listening socket, which
means one or more incoming connections. */
while (1)
{
struct sockaddr in_addr;
socklen_t in_len;
int infd;
char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
in_len = sizeof in_addr;
infd = accept (sfd, &in_addr, &in_len);
if (infd == -1)
{
if ((errno == EAGAIN) ||
(errno == EWOULDBLOCK))
{
/* We have processed all incoming
connections. */
break;
}
else
{
perror ("accept");
break;
}
}
s = getnameinfo (&in_addr, in_len,
hbuf, sizeof hbuf,
sbuf, sizeof sbuf,
NI_NUMERICHOST | NI_NUMERICSERV);
if (s == 0)
{
printf("Accepted connection on descriptor %d "
"(host=%s, port=%s)\n", infd, hbuf, sbuf);
}
/* Make the incoming socket non-blocking and add it to the
list of fds to monitor. */
s = make_socket_non_blocking (infd);
if (s == -1)
abort ();
event.data.fd = infd;
event.events = EPOLLIN | EPOLLET;
s = epoll_ctl (efd, EPOLL_CTL_ADD, infd, &event);
if (s == -1)
{
perror ("epoll_ctl");
abort ();
}
}
continue;
}
else
{
/* We have data on the fd waiting to be read. Read and
display it. We must read whatever data is available
completely, as we are running in edge-triggered mode
and won't get a notification again for the same
data. */
int done = 0;
while (1)
{
ssize_t count;
char buf[512];
count = read (events[i].data.fd, buf, sizeof buf);
if (count == -1)
{
/* If errno == EAGAIN, that means we have read all
data. So go back to the main loop. */
if (errno != EAGAIN)
{
perror ("read");
done = 1;
}
break;
}
else if (count == 0)
{
/* End of file. The remote has closed the
connection. */
done = 1;
break;
}
/* Write the buffer to standard output */
s = write (1, buf, count);
if (s == -1)
{
perror ("write");
abort ();
}
}
if (done)
{
printf ("Closed connection on descriptor %d\n",
events[i].data.fd);
/* Closing the descriptor will make epoll remove it
from the set of descriptors which are monitored. */
close (events[i].data.fd);
}
}
}
}
free (events);
close (sfd);
return EXIT_SUCCESS;
}
EPOLLIN = 0x001, #define EPOLLIN EPOLLIN EPOLLPRI = 0x002, #define EPOLLPRI EPOLLPRI EPOLLOUT = 0x004, #define EPOLLOUT EPOLLOUT EPOLLRDNORM = 0x040, #define EPOLLRDNORM EPOLLRDNORM EPOLLRDBAND = 0x080, #define EPOLLRDBAND EPOLLRDBAND EPOLLWRNORM = 0x100, #define EPOLLWRNORM EPOLLWRNORM EPOLLWRBAND = 0x200, #define EPOLLWRBAND EPOLLWRBAND EPOLLMSG = 0x400, #define EPOLLMSG EPOLLMSG EPOLLERR = 0x008, #define EPOLLERR EPOLLERR EPOLLHUP = 0x010, #define EPOLLHUP EPOLLHUP EPOLLET = (1 << 31) #define EPOLLET EPOLLET相关的值,只是一些位置上的num