select¶
はじめに¶
selectは、I/Oの監視します。
bsdの場合は、 http://www.nxmnpg.com/ja/2/kevent に詳しくイベントの内容が記載されています。
サンプル¶
import select
from select import kqueue, kevent
import os
import sys
filename = "access.log"
fd = os.open(filename,os.O_RDONLY)
#fd = open(filename)
kq = kqueue()
event = [
kevent(fd,
filter=select.KQ_FILTER_READ,
flags=select.KQ_EV_ADD),
kevent(fd,
filter=select.KQ_FILTER_VNODE,
flags=select.KQ_EV_ADD | select.KQ_EV_CLEAR,
fflags=select.KQ_NOTE_DELETE | select.KQ_NOTE_RENAME)
]
#ke = select.kevent(f, filter=select.KQ_FILTER_VNODE,
# flags=select.KQ_EV_ADD | select.KQ_EV_ENABLE | select.KQ_EV_CLEAR,
# fflags=select.KQ_NOTE_DELETE | select.KQ_NOTE_WRITE)
events = kq.control(event,0,0)
while True:
print "loop"
r_events = kq.control(None,4)
#r_events = kq.control([ke], 1, None)
for event in r_events:
print event
for event in r_events:
if event.fflags & select.KQ_NOTE_DELETE:
print "file was deleted"
elif event.fflags & select.KQ_NOTE_RENAME:
print "file was renamed"
kq.close()
os.close(fd)
サンプルです。
import select as s
import os
file = "./file.txt"
fd = open(file)
kq = s.kqueue()
ke = s.kevent(
fd,
filter=s.KQ_FILTER_VNODE,
flags=s.KQ_EV_ADD | s.KQ_EV_CLEAR,
fflags=s.KQ_NOTE_WRITE
)
events = kq.control([ke], 0, 0)
while True:
for e in kq.control(None, 4):
if e.fflags & s.KQ_NOTE_WRITE:
print("file was updated")
kq.close()
fd.close()
指定出来るファイルハンドル¶
ファイル記述子を表す整数値か、引数なしで整数を返すメソッド fileno() を持つオブジェクトであれば、使用可能です。
pythonでは、以下のファイルハンドルがそのまま使用出来ます。
- sys.stdin
- open
- os.open
- socket.socket
ノンブロッキング¶
ブロッキングとは、関数が返ってこない事を表します。 例えば、readはデータを受信して関数が戻ってきます。 言い方を変えると、データを受信するまでブロックしています。
os.read()
として、いつまでも出力を待ち続けてしまい、他の動作をブロックしてしまうことです。
select(2) / poll(2) は複数のファイルディスクリプタ(ソケット)を調べ、I/O可能なものを返すシステムコールです。 ソケットに対する読み取りはデフォルトではデータがなければブロック(データが到着するまで待つ)しますが、事前にI/O可能かを確認しておけばブロックすることはありません。
epoll¶
Warning
linuxのシステムコールなので、Mac, BSDには実装されていません。
poll¶
poll = s.poll()
poll.register(fd)
#(fd, event)
p = poll.poll()
select.select¶
select.select(rlist, wlist, xlist[, timeout])
- rlist: 読み込み可能になるまで待つ
- wlist: 書き込み可能になるまで待つ
- xlist: “例外状態 (exceptional condition)” になるまで待つ
戻り値は、rlist,wlist, xlistを要素にしたリストです。
I/Oの多重化
I/Oでブロッキングが発生し、一つのクライアントとしか通信できないということが起こります。
- fork
- threads
- I/Oの多重化
- 非同期I/O
登録されたファイルディスクリプタを一つ一つ見に行く実装になっている 管理できるディスクリプタ数に上限がある。 移植性の高いプログラムが書けます。
poll
pollは殆どselectと同じですが、次のような違いがあります。 管理できるディスクリプタ数が無制限になる。 pollシステムコール自体を実装しているシステムがselectより少ないため、移植性などに優れない。
epoll
select, pollと違って、ディスクリプタの状態がkernel内で管理される いちいちディスクリプタのセットをkernelに送る必要がない kernelが管理しているので、全ループではなく、変わったものに対して通知できる 上記の特徴からO(1)の計算量で計算できるようです。
epollやkqueueを仮想化してプラットフォーム非依存にするためのCライブラリがlibeventらしいですよ。詳しくは知らないけど
select.kqueue¶
kqueue.control(changelist, max_events[, timeout=None])
- changelistは、keventオブジェクトのリストです。 一度代入したら、次回からはNoneを指定しても問題ありません。
- max_eventsは、0以上の整数です。
戻り値は、発生したイベントのリストです。(for文で取り出していきます。) そのイベント内容は
<select.kevent ident=3 filter=-4 flags=0x21 fflags=0x2 data=0x0 udata=0x0>
のようにして、各値に数値が格納されたものとなっています。
select.kevent¶
kevent(fd, filter, flags)
fdは、os.openの値の方が便利です。 os.O_DIRECTORYを使えば、ディレクトリの更新も監視ができます。
filter¶
KQ_FILTER_READ ディスクリプタを受け取り、読み込めるデータが存在する時に戻る |
KQ_FILTER_WRITE ディスクリプタを受け取り、書き込み可能な時に戻る |
KQ_FILTER_AIO AIO リクエスト |
KQ_FILTER_VNODE fflag で監視されたイベントが1つ以上発生したときに戻る |
KQ_FILTER_PROC プロセスID上のイベントを監視する |
KQ_FILTER_NETDEV ネットワークデバイス上のイベントを監視する (Mac OS X では利用不可) |
KQ_FILTER_SIGNAL 監視しているシグナルがプロセスに届いたときに戻る |
KQ_FILTER_TIMER 任意のタイマを設定します |
flags¶
イベントには、ADDを指定しないと発生しない?かもしれない。
KQ_EV_ADD イベントを追加したり修正する |
KQ_EV_DELETE キューからイベントを取り除く |
KQ_EV_ENABLE control()がイベントを返すのを許可する |
KQ_EV_DISABLE イベントを無効にする |
KQ_EV_ONESHOT イベントを最初の発生後無効にする |
KQ_EV_CLEAR イベントを受け取った後状態をリセットする |
KQ_EV_SYSFLAGS 内部イベント |
KQ_EV_FLAG1 内部イベント |
KQ_EV_EOF フィルタ依存のEOF状態 |
KQ_EV_ERROR 戻り値を参照 |
イベント通知¶
イベントの受け取り方
for e in kq.control(None, 4):
if e.fflags & select.KQ_NOTE_XXXX:
print("event XXXX")
KQ_NOTE_LOWAT ソケットバッファの最低基準値 |
KQ_FILTER_VNODE フィルタのフラグ:定数 |
KQ_NOTE_DELETE unlink() が呼ばれた |
KQ_NOTE_WRITE 書き込みが発生した |
KQ_NOTE_EXTEND ファイルのサイズが拡張された |
KQ_NOTE_ATTRIB 属性が変更された |
KQ_NOTE_LINK リンクカウントが変更された |
KQ_NOTE_RENAME ファイル名が変更された |
KQ_NOTE_REVOKE ファイルアクセスが破棄された |
KQ_FILTER_PROC フィルタフラグ:定数 |
KQ_NOTE_EXIT プロセスが終了した |
KQ_NOTE_FORK プロセスが fork() を呼び出した |
KQ_NOTE_EXEC プロセスが新しいプロセスを実行した |
KQ_NOTE_PCTRLMASK 内部フィルタフラグ |
KQ_NOTE_PDATAMASK 内部フィルタフラグ |
KQ_NOTE_TRACK fork() の呼び出しを超えてプロセスを監視する |
KQ_NOTE_CHILD NOTE_TRACK に対して子プロセスに渡される |
KQ_NOTE_TRACKERR 子プロセスにアタッチできなかった |
KQ_FILTER_NETDEV フィルタフラグ (Mac OS X では利用不可):定数 |
KQ_NOTE_LINKUP リンクアップしている |
KQ_NOTE_LINKDOWN リンクダウンしている |
KQ_NOTE_LINKINV リンク状態が不正 |
Note
移植性のある select() を使わずに、現時点では *BSD でしか使えない kqueue/kevent を使うということは、 よほどパフォーマンスにこだわらなければならない場面だと思われます。