最近看了《erlang程式設計》,好書,翻譯的也不錯,兩天時間,基本讀完(我的習慣總是先快速讀完一本書再回過頭,一邊練習一邊翻閱),正試着學習編寫erlang的例子。Joe在網上(http://erlang.org/examples/examples-2.0.html)給了一個簡單系統(sos)的例子,可惜我太笨,怎麼都沒跑成功,又缺少專研的韌勁去弄清楚為什麼,是以幹脆決定自己寫一個簡單的例子。
這個簡單的例子的基本思想就是首先,啟動一個IO程序負責io的讀寫工作(展現一切皆程序的思想,并且資料也應該是屬于程序的,io就是io程序的私有資料),再啟動一個shell負責讀寫和執行使用者的指令(不過,目前的實作隻是從标準輸入讀入輸入并顯示到标準輸出,呵呵,剛起步,以後會慢慢完善)。
los的主檔案是los.erl,其子產品聲明如下:
-module(los).
-export([
boot/0,
make_scripts/0,
read/0,
write/1 ]).
呵呵,比較簡單,boot/0函數啟動los,read/0、write/1從标準輸入輸出讀寫資料;至于make_scripts/0,目前還沒用到,從joe的sos拷貝過來的,等研究清楚了啟動腳本再考慮使用吧。
啟動los也很簡單,首先啟動erl shell,運作los:boot()就完成啟動:
1> los:boot().
los booted!
los_sh started!
los>
1.boot/0的工作就是啟動io服務程序,然後啟動shell等待使用者輸入:
boot() ->
start_io(),
write("los booted!~n"),
los_sh:run(),
write("los goodbye!~n").
2. start_io例程調用make_global例程來建立服務,其實作如下:
start_io() ->
make_global(io, fun io_loop/0).
3. make_global例程實作如下:
make_global(Name, Fun) ->
case whereis(Name) of
undefined ->
Self = self(),
Pid = spawn(fun() ->
make_global(Self,Name,Fun)
end),
receive
{Pid, ack} ->
Pid
end;
Pid ->
Pid
end.
make_global首先判斷Name是否已經注冊,沒有注冊,則調用spawn函數建立一個程序,并傳回Pid,如果已經注冊,直接傳回Pid。
真正注冊程序的例程是make_global/3,其代碼如下:
make_global(Pid, Name, Fun) ->
case register(Name, self()) of
{'EXIT', _} ->
Pid ! {self(), ack};
_ ->
Pid ! {self(), ack},
Fun()
end.
4. boot有兩個write調用,是los提供的系統級函數。write例程的實作如下:
write(Str) ->
{write, Reply} = rpc(io, {write, Str}),
case Reply of
succeed -> succeed;
_ -> failue
end.
write例程就是調用rpc向io程序發送寫的消息和要寫的資料,io會将寫的狀态傳回給目前程序,rpc就是向io程序發送消息并傳回消息。
5. rpc例程的實作如下:
rpc(Name, Q) ->
Name ! {rpc, self(), Q},
receive
{Name, Reply} ->
Reply;
{Name, exit, Why} ->
exit(Why)
end.
6. los還提供了read函數:
read() ->
{read, Data} = rpc(io, read),
Data.
read函數将讀到的資料傳遞給調用者。
呵呵,los還是很簡單的,需要注意的大概就是read和write的消息格式。
下面是los_sh子產品,很簡單:
-module(los_sh).
-export([run/0]).
run() ->
los:write("los_sh started!~n"),
loop().
loop() ->
Data = los:read(),
case lists:member({Data}, [{"q/n"},{"Q/n"},{"quit/n"},{"Quit/n"}]) of
true -> los:write("los_sh quit!~n");
false -> los:write(Data),
loop()
end.
shell就是簡單的調用read和write讀取和顯示使用者的輸入,這并不是真正意義上的shell,下面正試着編寫一些簡單的指令,能夠在los中運作,對系統做一些檔案的操作。
一個比較有意思的問題是,如果将子產品los_sh的名稱改為shell,并編譯生成shell.beam,那麼啟動erl會出現問題,有一個很長的錯誤清單(我并沒有記錄錯誤日志,不過很容易實驗)。
我猜想erlang的shell會尋找類似shell.beam之類的檔案吧,還沒有細研究過,有哪位高人知道的,希望不吝賜教。