NIO(三)——NIOの原理と一部のソースコードの解析を深く理解する

15567 ワード

NIO(三)——NIOの原理と一部のソースコードの解析を深く理解する
ようこそ™のソースコードはシリーズを見ます
前の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