Hatena::Grouperlang

檜山正幸のErlang未確認情報 RSSフィード

2009-03-18 (水)

コマンドライン引数 (3)

| 09:28

http://erlang.g.hatena.ne.jp/m-hiyama/20090314/1237010526 :

erlコマンドのコマンドライン引数を取ろうと思ったら、どうもうまくいかない。

事情は後で書く、たぶん。

で事情を書いたが、あまり解明されてない。マニュアルを読みながら、実験をしてみるのが確実。

マニュアルは読んでも信用しないが吉。

実験用コード

実験のために次のコードをコンパイルして、beamファイルをコードパスがとおっている場所に置く(カレントディレクトリでいい)。start/0をexportしているのは、-s オプションで簡単に起動できるから。

%% -*- coding: utf-8 -*-

-module(argtest).

-export([start/0]).

print_flag_arguments() ->
  List = init:get_arguments(),
  lists:foreach(
    fun ({N, V}) -> io:format("~p --> ~p~n", [N, V]) end,
    List).

print_plain_arguments() ->
  List = init:get_plain_arguments(),
  io:format("(plain) --> ~p~n", [List]).

start() ->
  print_flag_arguments(),
  io:fwrite("~n"),
  print_plain_arguments().

OSコマンドラインから、

OS-Shell> erl -emu_args -noshell -s argtest -s init stop

としてargtestを実行できる。ただし、-s ini stop の後に安易に引数を書くとクラッシュする(-sオプションの仕様)。クラッシュはしなようにして、色んな引数を試してみればよい。

initはpreloaded

| 12:16

kernalに含まれるモジュールはこれだけある。

  1. application
  2. code
  3. disk_log
  4. dist_ac
  5. erl_boot_server
  6. erl_ddll
  7. error_logger
  8. file
  9. global
  10. global_group
  11. heart
  12. inet
  13. net_kernel
  14. os
  15. pg2
  16. rpc
  17. seq_trace
  18. user

このなかでinitはほんとに特殊で、code:which(init) とすると、preloadedとなる。ERTS起動後のローダーによって読み込まれるのではない。どうやら、ERTSの実行バイナリ内に埋め込まれているようだ。Erlangソースinit.erlをリコンパイルして配備し直してもサッパリ変更が反映されないので不思議だったのだが、ランタイムバイナリごと作り直さないとダメってことか。

2009-03-17 (火)

コマンドライン引数 (2)

| 14:33

実際的な対処としては、プレーン引数という概念を一切使わなければそれで済む。これ以上詮索するのは虚しい話なんだが、気になるのでまた少し調べた。推測が多いのでアテにならないけど。

どうも、マニュアル(または仕様)の著者と、CパートのプログラマErlangパートのプログラマで意識がだいぶズレてるようだ。

http://www.erlang.org/doc/man/erl.html :

Plain arguments are not interpreted in any way. They are also stored by the init process and can be retrieved by calling init:get_plain_arguments/0. Plain arguments can occur before the first flag, or after a -- flag. Additionally, the flag -extra causes everything that follows to become plain arguments.

これによれば、プレーン引数

  1. 最初のフラグの前
  2. --フラグの直後
  3. -extraフラグに後続する全ての引数

のハズ。僕は、"after a -- flag"を誤読していて、-extraと同じく、--が出現したらそこから先はフラグの認識をしなくなるかと思っていた。Cパートの引数処理を見ると、どうも僕と同じ解釈(誤解)をしているっぽい。

    while (i < argc) {
        if (argv[i][0] != '-') {
            erts_usage(); // 終了してしまう
        }
        if (strcmp(argv[i], "--") == 0) { /* end of emulator options */
            i++;
            break;
        }
        switch (argv[i][1]) {
        // 
        // 省略
        //
        default:
            erts_fprintf(stderr, "%s unknown flag %s\n", argv[0], argv[i]);
            erts_usage();
        }
        i++;
    }

"--"はオプションフラグ)解析ループをbreakで終了させる。

'+'で始まるオプションエミュレータフラグ)の解析部分と、CパートからErlangパートへのコマンドライン受け渡しがイマイチ分からないのだが、init.erlを見てみた。Erlangで書かれているから解読しやすい。

[追記]erts/etc/common/erlexec.c がホントの起動コマンドのようだ。これのmain()から execv(emu, Eargsp) からエミュレータのmain()を呼んでいる。このとき、引数の配列を編成し直してからエミュレータに渡している。よって、erlexec内での引数処理も見ないとダメだね。[/追記]

Erlangパートinit.erlの作者は、明らかに"--"を違った意味で解釈している。"--"をend_argsというシンボルで表しているが、argsはなんと「フラグ引数」を意味している。つまり、-foo bar baz -zot ... というコマンドラインがあると、bar baz はフラグ-fooの引数パラメータとか呼んで区別すべき!!)と呼んでいる。そのフラグパラメータの終了を"--"でマークするという解釈(これは間違いない)。

したがって、-foo bar -- baz -zot なら、-fooのパラメータはbarだけとなり、bazはプレーン引数となる。-foo -- bar baz -zot なら、-fooのパラメータはナシ、bar bazはプレーン引数となる。また、--がどのフラグよりも前に出現してしまうとプレーン引数扱いとなる。フラグのスコープ内に出現したときだけ、"--"がend_of_flag_paramsとなるわけ。

「最初のフラグの前はプレーン引数」という記述もErlnagパートだけなら正しい。が、Cパートはそうなってないようだ。

事実として:

  1. "--"は、直後の引数を強制的にプレーン引数とみなすエスケープのようには働かない。
  2. 原則的に、"--"はフラグパラメータ列を終了させるマーカー(データではない)。
  3. だが、出現位置により "--"自体がプレーン引数データとみなされる。
  4. また、出現位置により "--"がCパートのオプション解析を終了させるマーカー。
  5. -extraは、後続の全ての引数を強制的にプレーン引数にする効果を持つ。

なんか悲惨。

2009-03-16 (月)

コマンドライン引数の処理手順の目安

| 08:53

コマンドライン引数の挙動が変なので調べようとした。実際にはチャンと調べてないが、調べる順番だけは記しておく。

[追記]

(0) /erts/etc/common/erlexec.c

起動用のコマンドはエミュレータと別にある。

[/追記]

(1) /erts/emulator/sys/<OS名>/erl_main.c

/erts/emulator/sys/<OS名>/に下にあるerl_main.cを見る。ここにCのエントリーポイントがある。

int
main(int argc, char **argv)
{
    erl_start(argc, argv);
    return 0;
}

要するに、erl_start()に丸投げ。

(2) /erts/emulator/beam/erl_init.c

erl_init.c の erl_start(int argc, char **argv) を見る。すると、erl_start内で early_init(int *argc, char **argv) が呼ばれている。

(3) /erts/emulator/beam/erl_init.c

関数early_init()は、同じerl_init.c内にある。

static void
early_init(int *argc, char **argv)
{
/* ... */
}

(4) /erts/emulator/beam/erl_init.c

early_init()内で、

  1. erl_alloc.cの erts_alloc_init(argc, argv) が呼ばれる。
  2. sys/<OS名>/sys.cの erl_sys_args(argc, argv) が呼ばれる。

(5) /erts/emulator/beam/erl_alloc.c

erl_alloc.cのerts_alloc_init(int *argc, char **argv) から、同じファイル内の static void handle_args(int *argc, char **argv, init_t *init) が呼ばれる。

(6) /erts/emulator/sys/<OS名>/sys.c

sys.cのerl_sys_args(argc, argv) は大したことはしてないようだ。

(7) /erts/emulator/beam/erl_init.c

erl_start()に制御が戻ると、erl_start()内でも引数処理を行う。erl_start()の最後のほうで、

    boot_argc = argc - i;  /* Number of arguments to init */
    boot_argv = &argv[i];

    erl_first_process_otp("otp_ring0", NULL, 0, boot_argc, boot_argv);

erl_first_process_otp()からinitプロセスに残余の引数が渡る。ここから先はErlangパートの処理になる。

(8) lib/kernel/src/init.erl

kernelのinit.erlを読むとよい。Erlangで書かれたparse_boot_args(Args) 関数引数処理。

見るファイル:

  1. /erts/emulator/beam/erl_init.c
  2. /erts/emulator/sys/<OS名>/erl_main.c
  3. /erts/emulator/beam/erl_alloc.c
  4. /erts/emulator/sys/<OS名>/sys.c
  5. /lib/kernel/src/init.erl

ZulZul2012/12/25 22:46You are so awesome for helping me solve this myrsety.

ntcmludliintcmludlii2012/12/27 21:12cbBQgd , [url=http://avrysmvevqkf.com/]avrysmvevqkf[/url], [link=http://wxvfrxagnjtl.com/]wxvfrxagnjtl[/link], http://gpndvmacrriz.com/

kkpzbzykkpzbzy2012/12/28 04:26O4Y3qr <a href="http://vqbiadhaqhlr.com/">vqbiadhaqhlr</a>

itjozdohgaitjozdohga2012/12/29 05:17UPRdfv , [url=http://chdttolnllbf.com/]chdttolnllbf[/url], [link=http://scoaptdkhphc.com/]scoaptdkhphc[/link], http://qbwfrzhkgbwt.com/

2009-03-14 (土)

コマンドライン引数

| 15:02

erlコマンドのコマンドライン引数を取ろうと思ったら、どうもうまくいかない。先に結論を書いてしまえば:

  • プレーン引数(plain arguments)は使わないほうが吉!

事情は後で書く、たぶん。

XamdiXamdi2013/08/09 06:03I can't hear anyithng over the sound of how awesome this article is.

CristobalCristobal2013/08/14 02:39Now that's <a href="http://zmtcfl.com">sublet!</a> Great to hear from you.

2009-01-30 (金)

分散アプリケーションの運用

| 16:22

分散アプリケーションの、failover、takeoverとは、それぞれ後継ぎ、お引っ越しってことだ。

後継ぎ:

お引っ越し:


(中断、後で続き書くだろう)

[追記]うーーー、中断したままだ。いったん中止。続きがあるなら別エントリーで。基本文書は:

[/追記]

2008-12-24 (水)

rexサーバー

| 13:09

どんなERTSでもrex(remote execの略だと思う)サーバーはいるようだ。rexは //kernel/rpc.erl内で定義されている。

-module(rpc).

%% General rpc, broadcast,multicall, promise and parallel evaluator
%% facility

%% This code used to reside in net.erl, but has now been moved to
%% a searate module.

-define(NAME, rex).

-behaviour(gen_server).

[追記]searateはseparateね、メモ編でも言ったけど。[/追記]

ノードをまたいだRPC機構を使いたいとき、とりあえずrexの利用を考えてみるとよさそうだ。