いろはメモ - 初心者の書いたメモです。間違いは随時直していきます。
----
Erlang プロセスは「ポート」という仕組みで Erlang 外部の OS のプロセスと通信ができる。
+- ERTS (Erlang RunTime System) ---+ ****** OS のプロセス ****** | | * * | +-----------+ |>>>>>>>>>>-->>-->(標準入力) * | | プロセス |<------->| ポート | * * | +-----------+ |<<<<<<<<<<--<<--<(標準出力/標準エラー出力) * | | * * +----------------------------------+ ***************************
バイトストリームを扱いやすくするための「パケット」の概念がある。
パケットは、バイト列の前に「長さフィールド」を付加したものである。
BIF による(と思う)が、長さフィールドは 1/2/4 バイト長*1のいずれかである。
長さフィールドは NBO*2 でエンコーディングされ(ているようであ)る。
例: { packet, 4 } で "hello" をパケット化した場合
[ 0, 0, 0, 5, $h, $e, $l, $l, $o ]
HEX: 00 00 00 05 68 65 6c 6c 6f
簡単な例で試す。
-module(porttest). %% porttest.erl -compile(export_all). start(Command) -> Port = open_port({spawn, Command}, [{packet,4}]), %% open_port() でポートを作る io:format("port open: [~w]~n", [Port]), loop(). loop() -> receive {Port, {data, Data}} -> %% {Port, {data, Data}} がデータ受信のパターン io:format("received: '~p' from [~w]~n", [Data, Port]), loop(); X -> io:format("unknown message: [~w]~n", [X]), throw('Unknown message received.') end.
外部プログラムは Ruby で作成してみる。(./ext.rb)
#!/usr/bin/env ruby msg = "hello" packet = [msg.length, msg].pack("N A*") # パケット化。長さフィールドは 4 バイト # ちなみに、2 バイトなら .pack("n A*") # 1 バイトなら .pack("C A*") 3.times { $>.write packet } # パケットを 3 回出力
$ chmod +x ./ext.rb
一応、Ruby スクリプトの出力の HEX ダンプを確認しておく。
$ ./ext.rb | od -c -t x1
0000000 \0 \0 \0 005 h e l l o \0 \0 \0 005 h e l
00 00 00 05 68 65 6c 6c 6f 00 00 00 05 68 65 6c
0000020 l o \0 \0 \0 005 h e l l o
6c 6f 00 00 00 05 68 65 6c 6c 6f
0000033
では、erl で実行。(シェルが動かなくなったら CTLR-G + q で脱出する)
$ erl ... 1> c(porttest). {ok,porttest} 2> porttest:start("./ext.rb"). %% ./ext.rb を指定 port open: [#Port<0.1955>] received: '"hello"' from [#Port<0.1955>] received: '"hello"' from [#Port<0.1955>] received: '"hello"' from [#Port<0.1955>] %% CTRL-G User switch command --> q %% q [ENTER] でシェル終了
(書き出し試すのを忘れていたので)試す。
コマンドは "/bin/cat >/tmp/file" とし、パケット化しないで生ストリームに書き出す。
-module(writetest). -compile(export_all). start() -> Port = open_port({spawn, "/bin/cat >/tmp/file"}, [stream]), %% stream で生ストリームを指定 io:format("port open: [~w]~n", [Port]), Port ! {self(), {command, "hello, erlang\n"}}. %% 書き出しは Port ! {PidC, {command, Data}} %% PidC は接続プロセスの PID
erl で実行。
1> c(writetest.erl). {ok,writetest} 2> writetest:start(). port open: [#Port<0.1955>] {<0.35.0>,{command,"hello, erlang\n"}}
/tmp/file に書き出されていることを確認。
$ cat /tmp/file hello, erlang
$ od -c -t x1 /tmp/file
0000000 h e l l o , e r l a n g \n
68 65 6c 6c 6f 2c 20 65 72 6c 61 6e 67 0a
0000016
/tmp/file は消しときましょう。
----
参考リンク
----
オーム社のページ(サンプルソースのダウンロードなど) : プログラミングErlang|Ohmsha
----
参考URL