Welcome back to the source to see the explosion Gan ™ Series
After watching the first two series, I believe we have a certain understanding of the NIO, then we take a deep source code to interpret it, here is my version of OpenJDK-8u60, also suggest that you put next years and I ide look would be better understood together. (This introduces the Selector, Buffer first article mentioned that, Channel but also some method of operation Buffer only, not mentioned here, we are interested can see for yourself)
open()
// 1. Create Selector Selector Selector = Selector.open ();
First, we analyze the open method:
// Selector public static Selector Open () throws IOException { // here the static method provider will use DefaultSelectorProvider.create (); method of selecting a system based on SelectorProvider // is WindowsSelectorProvider windows platform, then, // Linux platform is a EPollSelectorProvider, here analysis of major Linux platform // the openSelector method after (a'll see below) will return as a EPollSelectorImpl achieve Selector, we generally refer to the Selector is it return SelectorProvider.provider () the openSelector ().; } // EPollSelectorProvider public AbstractSelector openSelector() throws IOException { return new EPollSelectorImpl(this); }
Followed EPollSelectorImpl constructor:
EPollSelectorImpl(SelectorProvider sp) throws IOException { super(sp); long pipeFds = IOUtil.makePipe(false); fd0 = (int) (pipeFds >>> 32); fd1 = ( int ) pipeFds; // other I also do not understand, we go directly to the constructor of this EPollArrayWrapper pollWrapper = new new EPollArrayWrapper (); pollWrapper.initInterrupt(fd0, fd1); fdToKey = new HashMap<>(); } // EPollArrayWrapper EPollArrayWrapper () throws IOException { // direct look here, here calling out a package of Linux-api: epoll_create, this thing can probably be understood as a selector, detailed in the next chapter we explain epfd = epollCreate () ; }
So in fact Selector method is probably on a package epoll_create () method, of course, also call for a moment epoll_ctl () , I also do not understand why call it, has not found any data, not analyzed
register()
// 5. The channel registered on the selector, listen for connection event serverSocketChannel.register (selector, SelectionKey.OP_ACCEPT);
Next, we analyze the channel registration method to register on Selector
// SelectableChannel public final SelectionKey register(Selector sel, int ops) throws ClosedChannelException { return register(sel, ops, null); } // AbstractSelectableChannel public final SelectionKey register(Selector sel, int ops, Object to) throws ClosedChannelException { // 212 rows, the remaining deleted K = ((the AbstractSelector) SEL) .register ( the this , OPS, ATT); } // SelectorImpl protected final SelectionKey register(AbstractSelectableChannel ch, int ops, Object attachment) { IF ((CH! the instanceof SelChImpl)) the throw new new IllegalSelectorException (); // generate SelectorKey hashmap stored in the total after acquiring SelectionKeyImpl K = new new SelectionKeyImpl ((SelChImpl) CH, the this ); // the attach of the user wants to store Object k.attach (Attachment); // call implRegister subclass, the next go where the synchronized (publickeys) { implRegister(k); } // set the focus of the Option k.interestOps (OPS); return k; }
protected void implRegister(SelectionKeyImpl ski) { if (closed) throw new ClosedSelectorException(); CH SelChImpl = ski.channel; // get Channel corresponding to fd, because under linux socket will be treated as a file, there will be fd int fd = Integer.valueOf (ch.getFDVal ()); fdToKey.put(fd, ski); // call pollWrapper add method to add the channel to the monitoring list of fd pollWrapper.add (fd); // save to the HashSet, keys are the member variable SelectorImpl keys.add (ski); }
Call the register
method does not involve EpollArrayWrapper
native method epollCtl
call, because they will call this method to defer to the Select
process went.
select()
// Get the number of available channel int readyChannels the selector.select = ();
Next, we analyze the select () method
// SelectorImpl public int select(long timeout) throws IOException { . . . . return lockAndDoSelect((timeout == 0) ? -1 : timeout); } // SelectorImpl private int lockAndDoSelect(long timeout) throws IOException { . . . . return doSelect(timeout); }
// EPollSelectorImpl protected int doSelect(long timeout) throws IOException { ..... try { .... // call poll method, called the bottom and the native epollCtl epollWait method pollWrapper.poll (timeout); } finally { .... } .... // update selectedKeys, selectedKeys function after preparing for int numKeysUpdated = updateSelectedKeys (); .... return numKeysUpdated; }
// EPollArrayWrapper int poll ( Long timeout) throws IOException { // realize there is call epoll_ctl () method to register fd and interest previously stored in the register method Channel of event types updateRegistrations (); // Here is the call epollWait method waits generate interest in the event, leading thread blocks Updated = epollWait (pollArrayAddress, NUM_EPOLLEVENTS, timeout, epfd); . . . . }
epollCtl and epollWait method mentioned above, we will talk about in detail in the next chapter, do not speak here first.
In short we can know Selector is actually the api Linux package provided by it, that is epollCreate , epollCtl and epollWait method.
selectedKeys()
// Get the available channel set Set <SelectionKey> selectionKeys = selector.selectedKeys ( );
Next we look at selectedKeys () method:
// SelectorImpl // by Util.ungrowableSet generated, can not be added, can reduce the Private the Set <the SelectionKey> publicSelectedKeys; public the Set <the SelectionKey> the selectedKeys () { .... return publicSelectedKeys; }
Very strange ah, how directly returned publicSelectedKeys , is it in the process of implementation of the select function has been modified this variable it? PublicSelectedKeys this object is actually selectedKeys a copy of the variable, you can SelectorImpl find them both in the constructor relations, we look back at updateSelectedKeys select the method:
Private int updateSelectedKeys () { // update the number of keys, or the number of said generated event int entries It = pollWrapper.updated; int numKeysUpdated 0 = ; for ( int I = 0; I <entries It; I ++ ) { // Channel fd corresponding to the int nextFD = pollWrapper.getDescriptor (I); // find the corresponding through the SelectionKey fd SelectionKeyImpl Ski = fdToKey.get (Integer.valueOf (nextFD)); IF ! (Ski = null ) { int ROPS = pollWrapper.getEventOps (I); //SelectedKey update variables, and notifies the channel response processing in response to do IF (selectedKeys.contains (Ski)) { IF (ski.channel.translateAndSetReadyOps (ROPS, Ski)) { numKeysUpdated++; } } else { ski.channel.translateAndSetReadyOps(rOps, ski); if ((ski.nioReadyOps() & ski.nioInterestOps()) != 0) { selectedKeys.add(ski); numKeysUpdated++; } } } } return numKeysUpdated; }
I do not know if you are aware, if we do not first call to select (), direct selectedKeys () is not going to get any Channel, because there is no method of updating publicSelectedKeys
Why Netty own and from one side to achieve a new relevant NIO underlying native method? Listen founder Netty is how to say it links .
Because the level-triggered mode using the Java version of epoll, and Netty wants to use edge-triggered mode, and the Java version is not exposed epoll part of the configuration items out, for example TCP_CORK and SO_REUSEPORT.
References:
https://segmentfault.com/a/1190000017798684?utm_source=tag-newest