http://www.man7.org/linux/man-pages/man7/epoll.7.html
recv/send:http://blog.csdn.net/petershina/article/details/7975798
ET/LT:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&id=4273856&uid=28541347
TCP_DEFER_ACCEPT:http://www.cnblogs.com/KevinT/p/3810824.html
描述符就绪条件(来源于UNPv1 P130):
三次握手(来源于深入理解Nginx P342):
/* Linux Kernel对于每一个epoll都会创建一个eventpoll结构体
epoll对于每一个事件都会创建一个epitem结构体,epitem是宿主,在epitem中寄宿了一棵红黑树和一个双向链表
红黑树保存了所有添加到epoll中的事件,当一个事件发生时,该事件会被添加到双向链表中 */
struct rb_node {
unsigned long __rb_parent_color;
struct rb_node *rb_right;
struct rb_node *rb_left;
} __attribute__((aligned(sizeof(long))));
struct rb_root {
struct rb_node *rb_node;
};
struct list_head {
struct list_head *next, *prev;
};
struct eventpoll {
spinlock_t lock;
struct mutex mtx;
wait_queue_head_t wq;
wait_queue_head_t poll_wait;
struct list_head rdllist; // rdllist.next指向双向链表
struct rb_root rbr; // rbr.rb_node指向红黑树
struct epitem *ovflist;
struct wakeup_source *ws;
struct user_struct *user;
struct file *file;
int visited;
struct list_head visited_list_link;
};
struct epitem {
union {
struct rb_node rbn; // 通过rbn组成一个红黑树
struct rcu_head rcu;
};
struct list_head rdllink; // 通过rdllink组成一个双向链表
struct epitem *next;
struct epoll_filefd ffd;
int nwait;
struct list_head pwqlist;
struct eventpoll *ep;
struct list_head fllink;
struct wakeup_source __rcu *ws;
struct epoll_event event; // epitem是事件的内核态表示,epoll_event是事件的用户态表示
};
typedef union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
struct epoll_event {
uint32_t events;
epoll_data_t data;
};
// 创建一个epoll
int epoll_create(int size);
// 向epoll中添加、修改、删除事件,监控fd上的event
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
// 收集epoll中已经发生的事件
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
// epoll程序基本框架
for (;;) {
nfds = epoll_wait(epfd, events, 100, -1);
for (i = 0; i < nfds; ++i) {
if (events[i].data.fd == listen_sock) { // 监听套接字
conn_sock = accept(listen_sock, (struct sockaddr *) &cliaddr, &addrlen);
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = conn_sock;
epoll_ctl(epfd, EPOLL_CTL_ADD, conn_sock, &ev);
} else if (events[i].events & EPOLLIN) { // 已连接套接字上的读事件
fd = events[i].data.fd;
...
} else if (events[i].events & EPOLLOUT) { // 已连接套接字上的写事件
fd = events[i].data.fd;
} else {
...
}
}
}