NIO(三)——NIOの原理と一部のソースコードの解析を深く理解する
15567 ワード
NIO(三)——NIOの原理と一部のソースコードの解析を深く理解する
ようこそ™のソースコードはシリーズを見ます
前の2つのシリーズを見終わった後、NIOに対しても一定の理解があると信じています.次に、ソースコードを深く解読します.私のところはOpenJDK-8 u 60バージョンです.次もideで私と一緒に見たほうが理解しやすいと思います.(ここでは主にSelectorを紹介しますが、Bufferの第1編では少し言及していますが、ChannelもBufferの操作方法にすぎません.ここでは言及しません.興味があれば自分で見てもいいです)
お兄さん、いいですよ.転載して私に言ってくれませんか.転載は気にしませんが、原文のリンクを大きく貼ってください.
open()
ようこそ™のソースコードはシリーズを見ます
前の2つのシリーズを見終わった後、NIOに対しても一定の理解があると信じています.次に、ソースコードを深く解読します.私のところはOpenJDK-8 u 60バージョンです.次もideで私と一緒に見たほうが理解しやすいと思います.(ここでは主にSelectorを紹介しますが、Bufferの第1編では少し言及していますが、ChannelもBufferの操作方法にすぎません.ここでは言及しません.興味があれば自分で見てもいいです)
お兄さん、いいですよ.転載して私に言ってくれませんか.転載は気にしませんが、原文のリンクを大きく貼ってください.
open() // 1. Selector
Selector selector = Selector.open();
まずopenメソッドを分析します.// Selector
public static Selector open() throws IOException {
// provider DefaultSelectorProvider.create(); SelectorProvider
// windows WindowsSelectorProvider,
// Linux EPollSelectorProvider, Linux
// openSelector ( ) EPollSelectorImpl Selector , Selector
return SelectorProvider.provider().openSelector();
}
// EPollSelectorProvider
public AbstractSelector openSelector() throws IOException {
return new EPollSelectorImpl(this);
}
次に、EPollSelectorImplの構造方法を示します.EPollSelectorImpl(SelectorProvider sp) throws IOException {
super(sp);
long pipeFds = IOUtil.makePipe(false);
fd0 = (int) (pipeFds >>> 32);
fd1 = (int) pipeFds;
// , EPollArrayWrapper
pollWrapper = new EPollArrayWrapper();
pollWrapper.initInterrupt(fd0, fd1);
fdToKey = new HashMap<>();
}
// EPollArrayWrapper
EPollArrayWrapper() throws IOException {
// , Linux api:epoll_create, selector,
epfd = epollCreate();
}
だから実はSelectorの方法はほとんどepollをカプセル化していますcreate()メソッド、もちろんepollも呼び出しましたctl()は、serverchannelを登録します.
register() // 5. channel selector ,
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
次に,チャネルをSelectorに登録するregister法を解析する.// 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 att)
throws ClosedChannelException
{
// 212 ,
k = ((AbstractSelector)sel).register(this, ops, att);
}
// SelectorImpl
protected final SelectionKey register(AbstractSelectableChannel ch,
int ops,
Object attachment)
{
if (!(ch instanceof SelChImpl))
throw new IllegalSelectorException();
// SelectorKey hashmap ,
SelectionKeyImpl k = new SelectionKeyImpl((SelChImpl)ch, this);
//attach
k.attach(attachment);
// implRegister ,
synchronized (publicKeys) {
implRegister(k);
}
// option
k.interestOps(ops);
return k;
}
protected void implRegister(SelectionKeyImpl ski) {
if (closed)
throw new ClosedSelectorException();
SelChImpl ch = ski.channel;
// Channel fd, linux socket , fd
int fd = Integer.valueOf(ch.getFDVal());
fdToKey.put(fd, ski);
// pollWrapper add , channel fd
pollWrapper.add(fd);
// HashSet ,keys SelectorImpl
keys.add(ski);
}
呼び出しregister
の方法は、この方法の呼び出しをEpollArrayWrapper
の方法に遅らせたため、epollCtl
のnative方法Select
の呼び出しには関与しない.
select() // channel
int readyChannels = selector.select();
次にselect()メソッドを解析します// 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 {
....
// poll , native epollCtl epollWait
pollWrapper.poll(timeout);
} finally {
....
}
....
// selectedKeys, selectedKeys
int numKeysUpdated = updateSelectedKeys();
....
return numKeysUpdated;
}
// EPollArrayWrapper
int poll(long timeout) throws IOException {
// epoll_ctl() register Channel fd
updateRegistrations();
// epollWait ,
updated = epollWait(pollArrayAddress, NUM_EPOLLEVENTS, timeout, epfd);
. . . .
}
上記のepollCtlとepollWaitの方法は次の章で詳しく説明しますが、ここでは先に説明しません.
要するに、SelectorはLinuxが提供するapi、すなわちepollCreate、epollCtl、epollWaitメソッドをカプセル化していることがわかります.
selectedKeys() // channel
Set selectionKeys = selector.selectedKeys();
次にselectedKeys()メソッドを見てみましょう.// SelectorImpl
// Util.ungrowableSet , ,
private Set publicSelectedKeys;
public Set selectedKeys() {
....
return publicSelectedKeys;
}
おかしいですね.どうしてpublicSelectedKeysに直接戻ったのですか.select関数の実行中にこの変数を修正したことがありますか.publicSelectedKeysこのオブジェクトはselectedKeys変数のコピーです.SelectorImplのコンストラクション関数で2つの関係を見つけることができます.selectのupdateSelectedKeysメソッドを振り返ってみましょう.private int updateSelectedKeys() {
// keys ,
int entries = pollWrapper.updated;
int numKeysUpdated = 0;
for (int i=0; i) {
// channel fd
int nextFD = pollWrapper.getDescriptor(i);
// fd SelectionKey
SelectionKeyImpl ski = fdToKey.get(Integer.valueOf(nextFD));
if (ski != null) {
int rOps = pollWrapper.getEventOps(i);
// selectedKey , channel
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;
}
私たちが先にselect()を呼び出さなければ、直接selectedKeys()はチャネルを取得しません.publicSelectedKeysを更新する方法がないからです.
もう一つ、publicSelectedKeysはselectedKeysの参照なので、新しいオブジェクトを返すたびに、新しいオブジェクトを返すのではなく、その参照を得ることができます.この参照の中のChannelは処理が終わったらremoveを忘れないでください.そうしないと、次はあなたに返されます.
ちなみにここでpublicSelectedKeysはpublicSelectedKeys=Utilを採用する.ungrowableSet(selectedKeys);メソッド名ungrowableSetのように、このメソッドで作成されたsetはaddメソッドを呼び出すことができず、removeしかできません.
なぜNetty自身がnative関連のNIOの下位メソッドを新たに実現したのか.Nettyの創始者がどう言っているのか聞いてみましょうリンク.
Javaのバージョンで使用されるepollのLTモードのため、NettyはETモード(詳細は第4編の2つのトリガモードを参照)を使用することを望んでおり、Javaバージョンではepollの一部の構成項目、例えばTCP_CORKとSO_REUSEPORT.
上一篇:epollの実現原理
参考資料:
https://segmentfault.com/a/1190000017798684?utm_source=tag-newest
// 1. Selector
Selector selector = Selector.open();
// Selector
public static Selector open() throws IOException {
// provider DefaultSelectorProvider.create(); SelectorProvider
// windows WindowsSelectorProvider,
// Linux EPollSelectorProvider, Linux
// openSelector ( ) EPollSelectorImpl Selector , Selector
return SelectorProvider.provider().openSelector();
}
// EPollSelectorProvider
public AbstractSelector openSelector() throws IOException {
return new EPollSelectorImpl(this);
}
EPollSelectorImpl(SelectorProvider sp) throws IOException {
super(sp);
long pipeFds = IOUtil.makePipe(false);
fd0 = (int) (pipeFds >>> 32);
fd1 = (int) pipeFds;
// , EPollArrayWrapper
pollWrapper = new EPollArrayWrapper();
pollWrapper.initInterrupt(fd0, fd1);
fdToKey = new HashMap<>();
}
// EPollArrayWrapper
EPollArrayWrapper() throws IOException {
// , Linux api:epoll_create, selector,
epfd = epollCreate();
}
// 5. channel selector ,
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
次に,チャネルをSelectorに登録するregister法を解析する.
// 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 att)
throws ClosedChannelException
{
// 212 ,
k = ((AbstractSelector)sel).register(this, ops, att);
}
// SelectorImpl
protected final SelectionKey register(AbstractSelectableChannel ch,
int ops,
Object attachment)
{
if (!(ch instanceof SelChImpl))
throw new IllegalSelectorException();
// SelectorKey hashmap ,
SelectionKeyImpl k = new SelectionKeyImpl((SelChImpl)ch, this);
//attach
k.attach(attachment);
// implRegister ,
synchronized (publicKeys) {
implRegister(k);
}
// option
k.interestOps(ops);
return k;
}
protected void implRegister(SelectionKeyImpl ski) {
if (closed)
throw new ClosedSelectorException();
SelChImpl ch = ski.channel;
// Channel fd, linux socket , fd
int fd = Integer.valueOf(ch.getFDVal());
fdToKey.put(fd, ski);
// pollWrapper add , channel fd
pollWrapper.add(fd);
// HashSet ,keys SelectorImpl
keys.add(ski);
}
呼び出し
register
の方法は、この方法の呼び出しをEpollArrayWrapper
の方法に遅らせたため、epollCtl
のnative方法Select
の呼び出しには関与しない.select() // channel
int readyChannels = selector.select();
次にselect()メソッドを解析します// 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 {
....
// poll , native epollCtl epollWait
pollWrapper.poll(timeout);
} finally {
....
}
....
// selectedKeys, selectedKeys
int numKeysUpdated = updateSelectedKeys();
....
return numKeysUpdated;
}
// EPollArrayWrapper
int poll(long timeout) throws IOException {
// epoll_ctl() register Channel fd
updateRegistrations();
// epollWait ,
updated = epollWait(pollArrayAddress, NUM_EPOLLEVENTS, timeout, epfd);
. . . .
}
上記のepollCtlとepollWaitの方法は次の章で詳しく説明しますが、ここでは先に説明しません.
要するに、SelectorはLinuxが提供するapi、すなわちepollCreate、epollCtl、epollWaitメソッドをカプセル化していることがわかります.
selectedKeys() // channel
Set selectionKeys = selector.selectedKeys();
次にselectedKeys()メソッドを見てみましょう.// SelectorImpl
// Util.ungrowableSet , ,
private Set publicSelectedKeys;
public Set selectedKeys() {
....
return publicSelectedKeys;
}
おかしいですね.どうしてpublicSelectedKeysに直接戻ったのですか.select関数の実行中にこの変数を修正したことがありますか.publicSelectedKeysこのオブジェクトはselectedKeys変数のコピーです.SelectorImplのコンストラクション関数で2つの関係を見つけることができます.selectのupdateSelectedKeysメソッドを振り返ってみましょう.private int updateSelectedKeys() {
// keys ,
int entries = pollWrapper.updated;
int numKeysUpdated = 0;
for (int i=0; i) {
// channel fd
int nextFD = pollWrapper.getDescriptor(i);
// fd SelectionKey
SelectionKeyImpl ski = fdToKey.get(Integer.valueOf(nextFD));
if (ski != null) {
int rOps = pollWrapper.getEventOps(i);
// selectedKey , channel
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;
}
私たちが先にselect()を呼び出さなければ、直接selectedKeys()はチャネルを取得しません.publicSelectedKeysを更新する方法がないからです.
もう一つ、publicSelectedKeysはselectedKeysの参照なので、新しいオブジェクトを返すたびに、新しいオブジェクトを返すのではなく、その参照を得ることができます.この参照の中のChannelは処理が終わったらremoveを忘れないでください.そうしないと、次はあなたに返されます.
ちなみにここでpublicSelectedKeysはpublicSelectedKeys=Utilを採用する.ungrowableSet(selectedKeys);メソッド名ungrowableSetのように、このメソッドで作成されたsetはaddメソッドを呼び出すことができず、removeしかできません.
なぜNetty自身がnative関連のNIOの下位メソッドを新たに実現したのか.Nettyの創始者がどう言っているのか聞いてみましょうリンク.
Javaのバージョンで使用されるepollのLTモードのため、NettyはETモード(詳細は第4編の2つのトリガモードを参照)を使用することを望んでおり、Javaバージョンではepollの一部の構成項目、例えばTCP_CORKとSO_REUSEPORT.
上一篇:epollの実現原理
参考資料:
https://segmentfault.com/a/1190000017798684?utm_source=tag-newest
// channel
int readyChannels = selector.select();
// 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 {
....
// poll , native epollCtl epollWait
pollWrapper.poll(timeout);
} finally {
....
}
....
// selectedKeys, selectedKeys
int numKeysUpdated = updateSelectedKeys();
....
return numKeysUpdated;
}
// EPollArrayWrapper
int poll(long timeout) throws IOException {
// epoll_ctl() register Channel fd
updateRegistrations();
// epollWait ,
updated = epollWait(pollArrayAddress, NUM_EPOLLEVENTS, timeout, epfd);
. . . .
}
// channel
Set selectionKeys = selector.selectedKeys();
次にselectedKeys()メソッドを見てみましょう.
// SelectorImpl
// Util.ungrowableSet , ,
private Set publicSelectedKeys;
public Set selectedKeys() {
....
return publicSelectedKeys;
}
おかしいですね.どうしてpublicSelectedKeysに直接戻ったのですか.select関数の実行中にこの変数を修正したことがありますか.publicSelectedKeysこのオブジェクトはselectedKeys変数のコピーです.SelectorImplのコンストラクション関数で2つの関係を見つけることができます.selectのupdateSelectedKeysメソッドを振り返ってみましょう.
private int updateSelectedKeys() {
// keys ,
int entries = pollWrapper.updated;
int numKeysUpdated = 0;
for (int i=0; i) {
// channel fd
int nextFD = pollWrapper.getDescriptor(i);
// fd SelectionKey
SelectionKeyImpl ski = fdToKey.get(Integer.valueOf(nextFD));
if (ski != null) {
int rOps = pollWrapper.getEventOps(i);
// selectedKey , channel
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;
}
私たちが先にselect()を呼び出さなければ、直接selectedKeys()はチャネルを取得しません.publicSelectedKeysを更新する方法がないからです.
もう一つ、publicSelectedKeysはselectedKeysの参照なので、新しいオブジェクトを返すたびに、新しいオブジェクトを返すのではなく、その参照を得ることができます.この参照の中のChannelは処理が終わったらremoveを忘れないでください.そうしないと、次はあなたに返されます.
ちなみにここでpublicSelectedKeysはpublicSelectedKeys=Utilを採用する.ungrowableSet(selectedKeys);メソッド名ungrowableSetのように、このメソッドで作成されたsetはaddメソッドを呼び出すことができず、removeしかできません.
なぜNetty自身がnative関連のNIOの下位メソッドを新たに実現したのか.Nettyの創始者がどう言っているのか聞いてみましょうリンク.
Javaのバージョンで使用されるepollのLTモードのため、NettyはETモード(詳細は第4編の2つのトリガモードを参照)を使用することを望んでおり、Javaバージョンではepollの一部の構成項目、例えばTCP_CORKとSO_REUSEPORT.
上一篇:epollの実現原理
参考資料:
https://segmentfault.com/a/1190000017798684?utm_source=tag-newest