このブログの更新は Twitterアカウント @m_hiyama で通知されます。
Follow @m_hiyama

メールでのご連絡は hiyama{at}chimaira{dot}org まで。

はじめてのメールはスパムと判定されることがあります。最初は、信頼されているドメインから差し障りのない文面を送っていただけると、スパムと判定されにくいと思います。

参照用 記事

Erlang実験室:.erlangの実行は、誰がいつどうやるの?

盆休みの前に、駆け込みポスト。

「.erlang」という名前のファイルにErlangの式を書いておくと、ERTSの起動時にそれを評価(実行)してくれます。経験上次のことは知っていました。

  1. カレントディレクトリに.erlangがあればそれが評価される。
  2. カレントディレクトリに.erlangがないときは、ホームディレクトリの.erlangが評価される。
  3. Eshellの起動より前に.erlangが実行される。
  4. コマンドラインの-eval, -run, -sの指定より前に.erlangが実行される。

実行の順序は、

  1. .erlangの内容
  2. コマンドラインの-eval, -run, -sの指定
  3. Eshell(対話モードのとき)

です。

もう少し正確に.erlangの実行手順を知りたいと思い調べたら、けっこう手間がかかってしまったよー。

ERTS本体は.erlangとは無関係

".erlang" という文字列がどっかにハードコードされているはずだと思い、まず、/otp_src_R12B-3/erts/ の下にある *.c, *.h ファイル内を検索しましたが、当該文字列は見つかりませんでした。

念のため、実行ファイル(バイナリ)にstringsしてみても ".erlang" は出てきません。

意外なところで見つかった".erlang"

となると、Erlangで書かれたコード内に ".erlang" があるのでしょう。otp_src_R12B-3/lib/ に移って検索を続けます。おそらくkernelだろうと思って、otp_src_R12B-3/lib/kernel/src/ 内を探しましたが、ここにもないんですよ、文字列 ".erlang" が。

うーん?? じゃ、stdlibか? と otp_src_R12B-3/lib/stdlib/src/ を探したら、おー、ありました。.erlangを読んでいるのは stdlib/src/c.erl のerlangrc関数でした。


%% Utilities to use from shell.

...(省略)...

%% erlangrc(Home)
%% Try to run a ".erlang" file, first in the current directory
%% else in home directory.

erlangrc() ->
case init:get_argument(home) of
{ok,[[Home]]} ->
erlangrc([Home]);
_ ->
f_p_e(["."], ".erlang")
end.

erlangrc([Home]) ->
f_p_e([".",Home], ".erlang").

erlangrcの実体はf_p_eですが、f_p_eはfile:path_evalを呼んで処理しています。これで、.erlangの検索と実行の方法はハッキリしました。

が、c:erlangrcはいつどこで実行されるのでしょう?c:erlangrcの呼び側を探したのですが、呼び出しが全然見つからなかったんですよ。

Erlangソースじゃなかったのね

otp_src_R12B-3/lib/sasl/src/systools_make.erlの次の記述からハッと気が付きました。


script_end() ->
[{apply, {c, erlangrc, []}},
{progress, started}].

Erlangソースじゃなくてブートスクリプトかも。

それで、erl5.6.3/releases/R12B/start.script を読んでみたら、オシリのほうが次のようでした。


{progress,applications_loaded},
{apply,{application,start_boot,[kernel,permanent]}},
{apply,{application,start_boot,[stdlib,permanent]}},
{apply,{c,erlangrc,[]}},
{progress,started}]}.

initプロセスによるブート手順の最後で{apply,{c,erlangrc,[]}}というスクリプト行が実行されるので、c:erlangrc() が呼ばれるわけです。

initの動作

initは、ERTS内で最初に起動されるシステムプロセス(カーネルプロセス)で、ブート手順をブートスクリプトに従って実行します。このシステム初期化を行う関数はinit:boot/1 です。boot/1からboot/3が呼ばれ、boot/3からdo_boot、do_bootからeval_scriptとstart_emが呼ばれます。eval_scriptがブートスクリプトの実行、start_emでコマンドラインから指定された式や関数を実行します。


boot(BootArgs) ->
register(init, self()),
process_flag(trap_exit, true),
{Start0,Flags,Args} = parse_boot_args(BootArgs),
Start = map(fun prepare_run_args/1, Start0),
Flags0 = flags_to_atoms_again(Flags),
boot(Start,Flags0,Args).

...(省略)...

boot(Start,Flags,Args) ->
BootPid = do_boot(Flags,Start),
State = #state{flags = Flags,
args = Args,
start = Start,
bootpid = BootPid},
boot_loop(BootPid,State).

...(省略)...

%%% -------------------------------------------------
%%% The boot process fetches a boot script and loads
%%% all modules specified and starts spec. processes.
%%% Processes specified with -s or -run are finally started.
%%% -------------------------------------------------

do_boot(Flags,Start) ->
...(省略)...
eval_script(BootList,Init,PathFls,{Root,BootVars},Path,
{true,Embedded,ParallelLoad},Deb),

%% To help identifying Purify windows that pop up,
%% print the node name into the Purify log.
(catch erlang:system_info({purify, "Node: " ++ atom_to_list(node())})),

start_em(Start).

...(省略)...

%% Do start all processes specified at command line using -s!
%% Use apply here instead of spawn to ensure syncronicity for
%% those servers that wish to have it so.
%% Disadvantage: anything started with -s that does not
%% eventually spawn will hang the startup routine.

%% We also handle -eval here. The argument is an arbitrary
%% expression that should be parsed and evaluated.

start_em([S|Tail]) ->
...(省略)...

フーッ、これでやっと納得がいったよ。