本文簡要介紹ruby語言中 Kernel.select
的用法。
用法
select(read_array [, write_array [, error_array [, timeout]]]) → array or nil
調用 select(2) 係統調用。它監視給定的 IO
對象數組,等待一個或多個 IO
對象準備好讀取、準備好寫入並分別有未決異常,並返回一個包含這些 IO
對象數組的數組。如果給出可選的timeout
值並且在timeout
秒內沒有 IO
對象準備好,它將返回nil
。
IO.select
查看 IO
對象的緩衝區以測試可讀性。如果 IO
緩衝區不為空, IO.select
會立即通知可讀性。此 “peek” 僅發生在 IO
對象上。對於 IO-like 對象(例如 OpenSSL::SSL::SSLSocket
)不會發生這種情況。
使用 IO.select
的最佳方法是在 read_nonblock、write_nonblock 等非阻塞方法之後調用它。這些方法引發了一個由 IO::WaitReadable
或 IO::WaitWritable
擴展的異常。模塊通知調用者應該如何等待 IO.select
。如果引發 IO::WaitReadable
,調用者應該等待讀取。如果引發 IO::WaitWritable
,調用者應該等待寫入。
因此,可以使用 read_nonblock 和 IO.select
模擬阻塞讀取(readpartial),如下所示:
begin
result = io_like.read_nonblock(maxlen)
rescue IO::WaitReadable
IO.select([io_like])
retry
rescue IO::WaitWritable
IO.select(nil, [io_like])
retry
end
特別是,非阻塞方法和 IO.select
的組合對於 IO
類對象(如 OpenSSL::SSL::SSLSocket
)是首選。它具有to_io 方法來返回底層 IO
對象。 IO.select
調用to_io獲取要等待的文件說明符。
這意味著 IO.select
通知的可讀性並不意味著 OpenSSL::SSL::SSLSocket
對象的可讀性。
最可能的情況是 OpenSSL::SSL::SSLSocket
緩衝了一些數據。 IO.select
看不到緩衝區。因此,當 OpenSSL::SSL::SSLSocket#readpartial
不阻塞時, IO.select
可以阻塞。
但是,存在幾種更複雜的情況。
SSL 是一種記錄序列的協議。記錄由多個字節組成。因此,SSL 的遠程端發送部分記錄, IO.select
通知可讀性但 OpenSSL::SSL::SSLSocket
無法解密字節並且 OpenSSL::SSL::SSLSocket#readpartial
將阻塞。
此外,遠程端可以請求 SSL 重新協商,這會強製本地 SSL 引擎寫入一些數據。這意味著 OpenSSL::SSL::SSLSocket#readpartial
可能會調用 write 係統調用並且它可以阻塞。在這種情況下, OpenSSL::SSL::SSLSocket#read_nonblock
引發 IO::WaitWritable
而不是阻塞。因此,調用者應該等待準備好寫,如上例所示。
當多個進程從一個流中讀取時,非阻塞方法和 IO.select
的組合對於 tty、管道套接字套接字等流也很有用。
最後,Linux 內核開發人員不保證 select(2) 的可讀性意味著即使對於單個進程也可以遵循 read(2) 的可讀性。請參閱 GNU/Linux 係統上的 select(2) 手冊。
在 IO#readpartial
之前調用 IO.select
可以正常工作。但是,這不是使用 IO.select
的最佳方式。
select(2) 通知的可寫性不顯示可寫的字節數。 IO#write
方法阻塞,直到給定的整個字符串被寫入。因此,在 IO.select
通知可寫性後,IO#write(two or more bytes)
可以阻塞。需要 IO#write_nonblock
以避免阻塞。
可以使用 write_nonblock 和 IO.select
模擬阻塞寫入(寫入),如下所示: IO::WaitReadable
還應該在 OpenSSL::SSL::SSLSocket
中為 SSL 重新協商而拯救。
while 0 < string.bytesize
begin
written = io_like.write_nonblock(string)
rescue IO::WaitReadable
IO.select([io_like])
retry
rescue IO::WaitWritable
IO.select(nil, [io_like])
retry
end
string = string.byteslice(written..-1)
end
參數
示例
rp, wp = IO.pipe
mesg = "ping "
100.times {
# IO.select follows IO#read. Not the best way to use IO.select.
rs, ws, = IO.select([rp], [wp])
if r = rs[0]
ret = r.read(5)
print ret
case ret
when /ping/
mesg = "pong\n"
when /pong/
mesg = "ping "
end
end
if w = ws[0]
w.write(mesg)
end
}
產生:
ping pong
ping pong
ping pong
(snipped)
ping
相關用法
- Ruby Kernel.set_trace_func用法及代碼示例
- Ruby Kernel.syscall用法及代碼示例
- Ruby Kernel.sprintf用法及代碼示例
- Ruby Kernel.srand用法及代碼示例
- Ruby Kernel.system用法及代碼示例
- Ruby Kernel.sleep用法及代碼示例
- Ruby Kernel.spawn用法及代碼示例
- Ruby Kernel.local_variables用法及代碼示例
- Ruby Kernel.Integer用法及代碼示例
- Ruby Kernel.binding用法及代碼示例
- Ruby Kernel.frozen?用法及代碼示例
- Ruby Kernel.`cmd`用法及代碼示例
- Ruby Kernel.autoload用法及代碼示例
- Ruby Kernel.loop用法及代碼示例
- Ruby Kernel.Hash用法及代碼示例
- Ruby Kernel.caller用法及代碼示例
- Ruby Kernel.exit!用法及代碼示例
- Ruby Kernel.trap用法及代碼示例
- Ruby Kernel.String用法及代碼示例
- Ruby Kernel.then用法及代碼示例
- Ruby Kernel.Pathname用法及代碼示例
- Ruby Kernel.yield_self用法及代碼示例
- Ruby Kernel.BigDecimal用法及代碼示例
- Ruby Kernel.raise用法及代碼示例
- Ruby Kernel.test用法及代碼示例
注:本文由純淨天空篩選整理自ruby-lang.org大神的英文原創作品 Kernel.select。非經特殊聲明,原始代碼版權歸原作者所有,本譯文未經允許或授權,請勿轉載或複製。