poll模型:其实是在jvm的native中维护了fd列表。系统调用poll的时候,需要传递fds到kernel中,kernel遍历fds,返回有变化的fds。
epoll模型:Selector#open 通过epoll_create函数,创建一个name为eventpoll的fd,在kernel中创建一个eventpoll结构体,这个结构体包含红黑树和双向链表。Selector#register 向name为eventpoll的fd注册fd,但是不会立马调用,等调用select的时候调用,对应epoll_ctl函数。Selector#select 首先会调用注册事件epoll_ctl函数,然后调用epoll_wait返回有变化fd。
单线程模型下:如果连接过多,服务器可能处理不过来。因为在系统io的时候,cpu中断,程序会停止执行。如果创建新的线程来处理io,那么需要调用key#cancel,把fd从eventpoll中移除,否则下次调用epoll_wait仍会返回该对象。
读事件依赖于Recvq是否有数据(其实是网卡拷贝完数据发送的回调函数把fd加入到rdlist中),写事件依赖于SendQ是否有数据,如果注册了写事件,因为SendQ一直处于可写状态,所以需要在写的时候才注册写事件,否则会出现死循环,cpu空转。
用一个Selector处理accept事件,用其他的Selector处理io读写事件,每个Selector在一个线程中,实际上fd的io的处理还是单线程的
这样做的好处是充分利用cpu,因为在处理io的过程中,cpu会中断,这时候可以让cpu处理别的事情,每个Selector中都有一些fd列表,这样其实在线程中处理fd的时候是线性的。
单线程多路复用器(Select) epoll,客户端发送什么服务器端会写什么事件。
//创建一个用于accept的socket对象fd=4,
//socket(AF_INET6, SOCK_STREAM, IPPROTO_IP) = 4
ServerSocketChannel server = ServerSocketChannel.open();
//绑定9090端口,监听fd=4
// bind(4, {sa_family=AF_INET6, sin6_port=htons(9090), inet_pton(AF_INET6, "::", &sin6_addr), sin6_flowinfo=htonl(0), sin6_scope_id=0}, 28) = 0
// listen(4, 50)
server.bind(new InetSocketAddress(9090));
// 设置非阻塞
// fcntl(4, F_SETFL, O_RDWR|O_NONBLOCK) = 0
server.configureBlocking(false);
// 调用epoll_create创建一个name为eventpoll的fd,eventpoll对象中包含红黑树和双向链表
// epoll_create(256) = 7
Selector selector = Selector.open();
// 此方法不会立马调用 epoll_ctl,会等到调用select方法,为了提高效率。
server.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
// ①注册ServerSocketChannel的fd到 epollevent上,并调用epoll_wait方法
// ①epoll_ctl(7, EPOLL_CTL_ADD, 4, {EPOLLIN, {u32=4, u64=4186726359134896132}}) = 0
// ①epoll_wait(7, [{EPOLLIN, {u32=4, u64=4186726359134896132}}], 4096, -1) = 1
// ②注册fd为8的SocketChannel到 eventpoll上。
// ②epoll_ctl(7, EPOLL_CTL_ADD, 8, {EPOLLIN, {u32=8, u64=4294967304}}) = 0
// ②epoll_wait(7, [{EPOLLIN, {u32=8, u64=4294967304}}], 4096, -1) = 1
// ③修改fd为8的SocketChannel到eventpoll上。
// ③epoll_ctl(7, EPOLL_CTL_MOD, 8, {EPOLLOUT, {u32=8, u64=4294967304}}) = 0
// ③epoll_wait(7, [{EPOLLOUT, {u32=8, u64=4294967304}}], 4096, -1) = 1
while (selector.select() > 0) {
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> iterator = keys.iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
if(key.isAcceptable()) {
// 应用程序接收socket,fd=8
// accept(4, {sa_family=AF_INET6, sin6_port=htons(57572), inet_pton(AF_INET6, "::ffff:10.211.55.5", &sin6_addr), sin6_flowinfo=htonl(0), sin6_scope_id=0}, [28]) = 8
SocketChannel client = ((ServerSocketChannel) key.channel()).accept();
//设置为非阻塞
//fcntl(7, F_SETFL, O_RDWR|O_NONBLOCK) = 0
client.configureBlocking(false);
ByteBuffer buffer = ByteBuffer.allocate(8192);
//此方法不会立马调用epoll_ctl
client.register(key.selector(), SelectionKey.OP_READ, buffer);
} else if(key.isReadable()) {
SocketChannel client = ((ServerSocketChannel) key.channel()).accept();
ByteBuffer buffer = (ByteBuffer) key.attachment();
buffer.clear();
while (true) {
int read = client.read(buffer);
if (read > 0) {
client.register(key.selector(), SelectionKey.OP_WRITE, buffer);
} else if (read == 0) {
break;
} else {
client.close();
break;
}
}
} else if(key.isWritable()) {
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer buffer = (ByteBuffer) key.attachment();
buffer.flip();
while (buffer.hasRemaining()) {
client.write(buffer);
}
buffer.clear();
//此方法不会立马调用epoll_ctl
client.register(key.selector(), SelectionKey.OP_READ, buffer);
}
}
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。