名前付きパイプのFIFOに関する備忘録
コマンドで作成
$ mkfifo ./hoge.fifo
パイプの簡単な動作確認
書き込み側$ echo "hoge" > {fifoファイル}
読み取り側
$ cat {fifoファイル}
どちらが先でもOKで、両サイドでファイルをオープンした段階で処理が始まります(ブロックする)
リモートポートフォワードで使う
任意のクライアントAがグローバルIPが割り振られていないホストBに対してSSHにログインしたい場合は、グローバルIPが割り当てられているホストC経由でポートフォワードすることにより、SSHログインすることが出来ます。具体的には、グローバルIPが割り当てられているホストCで以下を実行して
$ mkfifo myfifo
$ nc -l -p {接続用のポート} < myfifo \
| nc -l {Cのホスト名} {B-C間で接続する用のポート番号} > myfifo
ホストBで以下を実行
$ mkfifo myfifo
$ nc {Cのホスト名} {B-C間で接続する用のポート番号} < myfifo \
| nc localhost {BのSSHのポート} > myfifo
これで任意のクライアントAで以下を実行すると接続できます。
$ ssh {Bのユーザ名}@{Cのホスト名} -p {接続用のポート}
netcatはTCP通信したり、TCPサーバを立てれるコマンドになります。netcatの標準入力は接続先に渡され、接続先からのデータは標準出力に渡されます。
FIFOを使ったリモートフォワードの仕組みとしてはだいたいこんな感じ(説明がかなり冗長…)
- ホストBとホストCが接続される
- ホストAからホストCの接続用ポート対してSSHを実行(データを入力)
- 2の標準出力はホストCのB-C間接続しているnetcatの標準入力として渡される
- ホストBのB-C間接続しているnetcatの入力として渡される
- 4の標準出力はホストBのlocal sshしているnetcatの標準入力として渡される
- ホストBのlocal SSHのレスポンスが標準出力としてFIFOに渡される
- ホストBのFIFOに渡されたデータはB-C間接続しているnetcatの標準入力として渡される
- ホストCのB-C間接続しているnetcatにデータが渡され、ホストCのFIFOに標準出力として渡される
- ホストCのFIFOに渡されたデータはA-C間接続しているnetcatの標準入力として渡される
- ホストAにSSHのレスポンスが返る
selectで試してみる
多重I/Oシステムコールのselectで試してみました#!/usr/bin/env python
# -*- coding: utf-8 -*-
import select
PATH = '/path/to/file/'
FIFO_SIZE = 3
select_list = []
for num in range(FIFO_SIZE):
f = open(PATH + 'hoge{0}.fifo'.format(num+1), 'r') # ここでブロックする
select_list.append(f)
while True:
rready, wready, xready = select.select(select_list, [], [])
for fd in rready:
line = fd.read()
if len(line) == 0:
continue
print(line)
ここで注意しないといけないのが、デフォルトではFIFOのオープン時にもう一つの読み書きの口がオープンされるまでは処理がブロックされるということ。上記スクリプトを起動したときに、他のプロセスが対象FIFOに対して書き込みオープンしなければ、ブロック状態が解除されず処理が進みません。さらに上記例だと、複数のFIFOをオープンしているのでhoge1.fifo→hoge2.fifo→hoge3.fifoの順に書き込みオープンしないと先の処理にすすめません。(オープン後は正常に読取り処理が行われます)
以下のようにノンブロックでファイルをオープンすればブロックを回避できます。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import select
import os
PATH = '/path/to/file/'
FIFO_SIZE = 3
select_list = []
for num in range(FIFO_SIZE):
f = os.open(PATH + 'hoge{0}.fifo'.format(num+1), os.O_RDONLY | os.O_NONBLOCK)
select_list.append(f)
while True:
rready, wready, xready = select.select(select_list, [], [])
for fd in rready:
line = os.read(fd, 1024)
if len(line) == 0:
continue
print(line)
Pythonの場合はopen関数の代わりにos.open関数を使ってos.O_NONBLOCKのフラグを立ててオープンすることになります。