Learning 1 documentation

select

«  re   ::   Contents   ::   subprocess  »

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 を使うということは、 よほどパフォーマンスにこだわらなければならない場面だと思われます。

«  re   ::   Contents   ::   subprocess  »

inserted by FC2 system