directory
- An overview of the NIO –
- NIO-Buffer
- NIO-Channel
- Nio-channel interface analysis
- Nio-socketchannel source code analysis
- Nio-filechannel source code analysis
- Nio-selector source code analysis
- NIO-WindowsSelect Impl source code analysis
- Nio-epollselectoripml source code analysis
preface
Originally I wanted to learn Netty, but Netty is a NIO framework, so before learning Netty, I would like to comb through the knowledge of NIO. Understand the design principles of NIO by analyzing the source code.
This series of articles focuses on the source code for JDK1.8.0.161.
The previous articles examined the common functions of Buffer and Channel source code. This article will parse the Selector source code.
What is the Selector
During network transmission, the client connects to the server from time to time, and in high concurrency scenarios, most connections are virtually idle. Therefore, in order to improve the performance of network transmission with high concurrency, various I/O models have emerged to optimize CPU processing efficiency. Different selectors implement different I/O model algorithms. Synchronous I/O has an EPoll model on Linux, a KQueue model on MAC, and a Select model on Windows.
For details about the I/O model, see High-performance Network Communication Principles.
In order to know which connection has been ready, in the beginning we need regularly polling Socket to receive a new connection, at the same time we also monitor whether receives the data connection has been established, with most cases, most of the network connection is actually free, so each time to iterate through all of the client, then with the increase of concurrency, Performance overhead also increases linearly.
So with the Selector, we can have it do the monitoring for us, and when it’s monitoring the connection and receiving the data, we just have to read the data out, and that improves performance dramatically. To do the “monitor” action for us, we need to tell it which channels we need to monitor.
Note that you only need to monitor a channel with a Selector when you’re communicating with a network. From the code, the Channel AbstractSelectableChannel must succeed.
Create a Selector
First we need to create a Selector from the static method Selector. Open ().
Selector selector = Selector.open();
Copy the code
Note that a Channel must be non-blocking, and we need to manually set the Channel to non-blocking. Call Channel instance methods SelectableChannel. ConfigureBlocking (Boolean block).
Registered channel
You need to tell the Selector which channels to monitor, and use channel.register to register the channels you want to monitor with the Selector
Registration are implemented in AbstractSelectableChannel, when register new channels with the Selector to create a SelectionKey, and save it to the SelectionKey [] keys in the cache.
public final SelectionKey register(Selector sel, int ops, Object att) throws ClosedChannelException
{
synchronized (regLock) {
if(! isOpen())throw new ClosedChannelException();
// Whether the current Channel supports operations
if((ops & ~validOps()) ! =0)
throw new IllegalArgumentException();
// Blocking is not supported
if (blocking)
throw new IllegalBlockingModeException();
SelectionKey k = findKey(sel);
if(k ! =null) {
// If it already exists, register it for supported operations
k.interestOps(ops);
// Save the parameters
k.attach(att);
}
if (k == null) {
// New registration
synchronized (keyLock) {
if(! isOpen())throw new ClosedChannelException();
/ / register
k = ((AbstractSelector)sel).register(this, ops, att);
// Add to cacheaddKey(k); }}returnk; }}Copy the code
New SelectionKey will call to AbstractSelector. The register, will first create a SelectionKeyImpl first, and then calls the method implRegister perform the actual registration, This functionality is implemented in the SelectorImpl implementation class for each platform.
k = ((AbstractSelector)sel).register(this, ops, att);
protected final SelectionKey register(AbstractSelectableChannel ch, int ops, Object attachment)
{
if(! (chinstanceof SelChImpl))
throw new IllegalSelectorException();
/ / create a SelectionKey
SelectionKeyImpl k = new SelectionKeyImpl((SelChImpl)ch, this);
k.attach(attachment);
synchronized (publicKeys) {
/ / register
implRegister(k);
}
// Set the event
k.interestOps(ops);
return k;
}
Copy the code
After the SelectionKey is created, it is added to the keys cache. When the keys cache is insufficient, it is doubled in size.
private void addKey(SelectionKey k) {
assert Thread.holdsLock(keyLock);
int i = 0;
if((keys ! =null) && (keyCount < keys.length)) {
// Find empty element of key array
for (i = 0; i < keys.length; i++)
if (keys[i] == null)
break;
} else if (keys == null) {
keys = new SelectionKey[3];
} else {
// Double the size
int n = keys.length * 2;
SelectionKey[] ks = new SelectionKey[n];
for (i = 0; i < keys.length; i++)
ks[i] = keys[i];
keys = ks;
i = keyCount;
}
keys[i] = k;
keyCount++;
}
Copy the code
SelectorProvider
Before we talk about how a Selector works, let’s take a look at how a Selector is created. We create a Selector using the Selector. Open () static method. Internal actual is through SelectorProvider openSelector () method to create the Selector.
public static Selector open(a) throws IOException {
return SelectorProvider.provider().openSelector();
}
Copy the code
Create SelectorProvider
Through SelectorProvider provider () static method, get SelectorProvider, for the first time when they get through configuration methods such as injection, if no configuration, use the DefaultSelectorProvider generated.
public static SelectorProvider provider(a) {
synchronized (lock) {
if(provider ! =null)
return provider;
return AccessController.doPrivileged(
new PrivilegedAction<SelectorProvider>() {
public SelectorProvider run(a) {
/ / by configuring Java. Nio. Channels. The spi. Into custom SelectorProvider SelectorProvider values
if (loadProviderFromProperty())
return provider;
// Inject through ServiceLoad and get the first service configured
if (loadProviderAsService())
return provider;
provider = sun.nio.ch.DefaultSelectorProvider.create();
returnprovider; }}); }}Copy the code
If we did not do any special configuration, we would create the SelectorProvider using the default DefaultSelectorProvider. The implementation of DefaultSelectorProvider varies from platform to platform. Can be found in the JDK \ SRC \ [macosx | | solaris Windows] \ classes \ sun \ nio \ ch find realize DefaultSelectorProvider Java. Here is the implementation of SelectorProvider.
//windows
public class DefaultSelectorProvider {
private DefaultSelectorProvider(a) {}public static SelectorProvider create(a) {
return newsun.nio.ch.WindowsSelectorProvider(); }}//linux
public class DefaultSelectorProvider {
private DefaultSelectorProvider(a) {}@SuppressWarnings("unchecked")
private static SelectorProvider createProvider(String cn) {
Class<SelectorProvider> c;
try {
c = (Class<SelectorProvider>)Class.forName(cn);
} catch (ClassNotFoundException x) {
throw new AssertionError(x);
}
try {
return c.newInstance();
} catch (IllegalAccessException | InstantiationException x) {
throw newAssertionError(x); }}public static SelectorProvider create(a) {
String osname = AccessController
.doPrivileged(new GetPropertyAction("os.name"));
if (osname.equals("SunOS"))
return createProvider("sun.nio.ch.DevPollSelectorProvider");
if (osname.equals("Linux"))
return createProvider("sun.nio.ch.EPollSelectorProvider");
return newsun.nio.ch.PollSelectorProvider(); }}Copy the code
Create a Selector
Now that I’ve got the SelectorProvider, I’ve created the Selector. Through SelectorProvider. OpenSelector instance () method creates a Selector
//windows
public class WindowsSelectorProvider extends SelectorProviderImpl {
public AbstractSelector openSelector(a) throws IOException {
return new WindowsSelectorImpl(this); }}//linux
public class EPollSelectorProvider
extends SelectorProviderImpl
{
public AbstractSelector openSelector(a) throws IOException {
return new EPollSelectorImpl(this); }... }Copy the code
Under Windows created WindowsSelectorImpl, founded EPollSelectorImpl under Linux.
All XXXSelectorImpl inherited from SelectorImpl, can be in JDK \ SRC \ [macosx | | Windows solaris | share] \ classes \ sun \ nio \ ch find realize XXXSelectorImpl Java. The inheritance relationship is shown in the following figure.
Next, we’ll discuss the main features that Selector provides, and then we’ll look at the implementation of Selector for Windows and Linux.
SelectorImpl
The SelectorImpl is created by initializing two hashSets, publicKeys for all registered Selectionkeys, and selectedKeys for ready selectionkeys.
protected SelectorImpl(SelectorProvider sp) {
super(sp);
keys = new HashSet<SelectionKey>();
selectedKeys = new HashSet<SelectionKey>();
if (Util.atBugLevel("1.4")) {
publicKeys = keys;
publicSelectedKeys = selectedKeys;
} else {
// Create a collection that cannot be modified
publicKeys = Collections.unmodifiableSet(keys);
// Create a collection that can only be deleted but cannot be addedpublicSelectedKeys = Util.ungrowableSet(selectedKeys); }}Copy the code
I found an article on util. atBugLevel that mentions this method. It seems to be related to a null pointer exception in EPoll. This bug was introduced in NIO bugLevel=1.4 and existed in JDK1.5 until JDK1.7.
Having registered the channels with the Selector earlier, we now need to call the Selector.select() instance method to load the ready file descriptor from system memory.
public int select(a) throws IOException {
return select(0);
}
public int select(long timeout)
throws IOException
{
if (timeout < 0)
throw new IllegalArgumentException("Negative timeout");
return lockAndDoSelect((timeout == 0)? -1 : timeout);
}
private int lockAndDoSelect(long timeout) throws IOException {
synchronized (this) {
if(! isOpen())throw new ClosedSelectorException();
synchronized (publicKeys) {
synchronized (publicSelectedKeys) {
returndoSelect(timeout); }}}}protected abstract int doSelect(long timeout) throws IOException;
Copy the code
The result is a call to doSelect with a specific SelectorImpl that essentially does two things internally
- Call native methods to get ready file descriptors.
- call
updateSelectedKeys
Update the ready eventSelectorKey
Once we get the selectionkeys ready, we can walk through them. The specific logic that needs to be executed depends on the event type of the SelectionKey.
// Get the ready Key to traverse
Set<SelectionKey> selectKeys = selector.selectedKeys();
Iterator<SelectionKey> it = selectKeys.iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
// Handle events.
if(key.isAcceptable()){
doAccept(key);
}
else if(key.isReadable()) { doRead(key); }... it.remove(); }Copy the code
conclusion
This article analyzes the creation of Selector and SelectorProvider. The general process can be seen in the following figure
The EpollArrayWarpper() of the following steps will be explained in the SelectorImpl platform implementation. The following will be divided into two WindowsSelectorImpl and EpollSelectorImpl analysis.
Related literature
- ServiceLoader,
- SelectorImpl analysis
- NIO source code Analysis (1)
- Wechat scan the QR code to follow the subscription number Jie Ge technology share
- Source: www.cnblogs.com/Jack-Blog/p…
- Author: Jay is very busy
- This article uses the “CC BY 4.0” authoring sharing protocol. Welcome to reprint, please give the source and link in the obvious place.