NIO(四):源碼分析_Register部分

  • 註冊,就是將當前Channel註冊到Selector上,是NIO源碼的核心部分

1,Channel.register()部分

  • serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT):註冊事件
public final SelectionKey register(Selector sel, int ops, Object att) throws ClosedChannelException {
    // 用註冊鎖保證同步
    synchronized (regLock) {
        if (!isOpen())
            throw new ClosedChannelException();
        if ((ops & ~validOps()) != 0)
            throw new IllegalArgumentException();
        if (blocking)
            throw new IllegalBlockingModeException();
        // 從當前Channel自身的註冊列表中獲取數據
        SelectionKey k = findKey(sel);
        // 獲取到數據後,只對SelectionKey狀態進行變更
        if (k != null) {
            k.interestOps(ops);
            k.attach(att);
        }
        // key爲空,說明該任務爲新任務,加鎖後註冊
        if (k == null) {
            synchronized (keyLock) {
                if (!isOpen())
                    throw new ClosedChannelException();
                k = ((AbstractSelector)sel).register(this, ops, att);
                // 註冊完成後,將新註冊的SelectionKey添加的Channel的註冊列表中
                addKey(k);
            }
        }
        return k;
    }
}
  • AbstractSelectableChannel.findKey():從ChannelSelectionKey[]數組中獲取存在數據
private SelectionKey findKey(Selector sel) {
    synchronized (keyLock) {
        if (keys == null)
            return null;
        for (int i = 0; i < keys.length; i++)
            // 一個Channel跟一個Selector是強綁定的,可以通過Selector匹配獲取註冊的Key
            // Channel和Selector之間是多對多的關係
            if ((keys[i] != null) && (keys[i].selector() == sel))
                return keys[i];
        return null;
    }
}
  • 如果 Channel 的註冊列表中不存在該數據,則直接轉發到 Selector 進行該 Channel 註冊
  • AbstractSelectableChannel.addKey() :註冊完成後,添加到 Channel 的註冊列表中
private void addKey(SelectionKey k) {
    assert Thread.holdsLock(keyLock);
    int i = 0;
    // 首先數據已經初始化且未滿, 正常填充
    if ((keys != null) && (keyCount < keys.length)) {
        for (i = 0; i < keys.length; i++)
            if (keys[i] == null)
                break;
    // 數組未初始化,直接初始化,並制定默認長度3
    } else if (keys == null) {
        keys =  new SelectionKey[3];
    // 數組已經初始化,且數據已滿,
    // 進行二倍擴容和原始數據遷移
    } else {
        // Grow key array
        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++;
}

2,Selector.register()部分

  • ((AbstractSelector)sel).register(this, ops, att):通道未註冊到選擇器,進行註冊
protected final SelectionKey register(AbstractSelectableChannel var1, int var2, Object var3) {
    if(!(var1 instanceof SelChImpl)) {
        throw new IllegalSelectorException();
    } else {
        // 初始化一個註冊器,封裝通道和選擇器
        // 生成的對象真是實例爲SelectionKeyImpl
        SelectionKeyImpl var4 = new SelectionKeyImpl((SelChImpl)var1, this);
        // 附加各位屬性, 會透傳到下一個事件
        var4.attach(var3);
        Set var5 = this.publicKeys;
        // 對註冊的SelectionKey列表進行同步處理
        synchronized(this.publicKeys) {
            // 初始化完成後,進行最終註冊,添加Socket句柄
            this.implRegister(var4);
        }
		// 添加事件類型, 即修改原事件狀態
        var4.interestOps(var2);
        return var4;
    }
}

SelectionKeyImpl(SelChImpl var1, SelectorImpl var2) {
    this.channel = var1;
    this.selector = var2;
}
  • WindowsSelectorImpl.implRegister:註冊
// 註冊
protected void implRegister(SelectionKeyImpl var1) {
    Object var2 = this.closeLock;
    synchronized(this.closeLock) {
        if(this.pollWrapper == null) {
            throw new ClosedSelectorException();
        } else {
            // 對SelectionKeyImpl[] channelArray數組進行二倍擴容, 默認長度爲8
            // 沒增加1024個Channel,則增加一個線程處理
            this.growIfNeeded();
            // 填充到數據的下一個索引位置
            this.channelArray[this.totalChannels] = var1;
            // 設置SelectionKey的索引值, 添加事件時候會用到
            var1.setIndex(this.totalChannels);
            // SelectionKey句柄和對象的映射
            this.fdMap.put(var1);
            // 添加到選擇器的註冊列表中
            this.keys.add(var1);
            // 添加Socket句柄到pollWrapper
            // 註冊此處傳遞的總數,也就代表當前註冊對象索引
            this.pollWrapper.addEntry(this.totalChannels, var1);
            // 表示已註冊的Channel總數
            ++this.totalChannels;
        }
    }
}
  • WindowsSelectorImpl.growIfNeeded:擴容
private void growIfNeeded() {
    if(this.channelArray.length == this.totalChannels) {
        // 數量超過定長,直接進行二倍擴容
        int var1 = this.totalChannels * 2;
        SelectionKeyImpl[] var2 = new SelectionKeyImpl[var1];
        System.arraycopy(this.channelArray, 1, var2, 1, this.totalChannels - 1);
        this.channelArray = var2;
        this.pollWrapper.grow(var1);
    }
	// 數量爲1024的倍數,對線程加1,select()時作爲子線程進行查找事件
    if(this.totalChannels % 1024 == 0) {
        this.pollWrapper.addWakeupSocket(this.wakeupSourceFd, this.totalChannels);
        ++this.totalChannels;
        ++this.threadsCount;
    }

}
  • PollArrayWrapper.addEntry():添加註冊對象的Socket句柄到內存對象中
void addEntry(int var1, SelectionKeyImpl var2) {
    this.putDescriptor(var1, var2.channel.getFDVal());
}

void putDescriptor(int var1, int var2) {
    this.pollArray.putInt(SIZE_POLLFD * var1 + 0, var2);
}
  • SelectionKeyImpl.interestOps:修改註冊事件
public SelectionKey nioInterestOps(int var1) {
    if((var1 & ~this.channel().validOps()) != 0) {
        throw new IllegalArgumentException();
    } else {
        // 進行註冊事件修改
        // 註冊事件修改,主要分析ServerSocketChannel和SocketChannel兩部分
        this.channel.translateAndSetInterestOps(var1, this);
        this.interestOps = var1;
        return this;
    }
}

// ServerSocketChannel
public void translateAndSetInterestOps(int var1, SelectionKeyImpl var2) {
    int var3 = 0;
    if((var1 & 16) != 0) { // 16 表示accept事件
        var3 |= Net.POLLIN;
    }
	// 對事件進行替換
    var2.selector.putEventOps(var2, var3);
}

// SocketChannel
public void translateAndSetInterestOps(int var1, SelectionKeyImpl var2) {
    int var3 = 0;
    if((var1 & 1) != 0) { // 讀事件
        var3 |= Net.POLLIN;
    }

    if((var1 & 4) != 0) { // 寫事件
        var3 |= Net.POLLOUT;
    }

    if((var1 & 8) != 0) { // 連接事件
        var3 |= Net.POLLCONN;
    }
	// 對事件類型進行替換
    var2.selector.putEventOps(var2, var3);
}
  • WindowsSelectorImpl.putEventOps:事件替換
public void putEventOps(SelectionKeyImpl var1, int var2) {
    Object var3 = this.closeLock;
    synchronized(this.closeLock) {
        if(this.pollWrapper == null) {
            throw new ClosedSelectorException();
        } else {
            // 從註冊對象中獲取註冊事件的索引位置
            int var4 = var1.getIndex();
            if(var4 == -1) {
                throw new CancelledKeyException();
            } else {
                // 通過內存對象直接操作地址空間,進行事件類型替換
                this.pollWrapper.putEventOps(var4, var2);
            }
        }
    }
}
  • 註冊源碼分析完成,最終註冊事件添加到 SelectorImpl.keys 和 AbstractSelectableChannel.keys,並在 AllocatedNativeObject 內存對象中存儲,可以通過 select() 進行查找
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章