最近看了《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之类的文件吧,还没有细研究过,有哪位高人知道的,希望不吝赐教。