關於io_request

關於io_request

相關module

涉及到kernel/io, kernel/user, kernel/application_master, stdlib/slave.

Erlang中的標準io接口

stdlib/io module, 爲erlang中的標準的io接口模塊,其io操作基於process實現, 如對文件, 標準輸入輸出等操作,都可以使用 此module進行處理(對於文件,file:open/2, 默認是使用io模塊進行操作,除非指定raw選項).

 

使用io module具有一些好處:我們可以使用統一的接口對實現io操作; 其他的process也可以 訪問被打開的io device; 在分佈式環境中,可以進行基於Process Message機制的交互. 至於缺點,毫無疑問,就是可能導致性能下降, 比如對於file的操作,使用raw模式會更快.

關於group leader

一組process,可以組成一個group, 請注意這裏的group和kernel中的 pg2, global, global_groupe等沒有任何關係.

group leader可以理解成process的一個屬性.我們可以通過erlang:process_info/1,可以查看process的group_leader 信息. group_leader對應的value爲某個process pid.

 

每當我們spawn一個新的process時, 其都會繼承parent的group_leader屬性.

 

估計很多人會有疑問,group_leader有何作用呢?

 

process的io module相關的操作, 會轉交給group_leader進行處理. 所以group_leader類似一個router, 其接受io module的 io_request請求,然後轉發給相應的module進行處理.

 

對於io這方面的操作,很多語言可能是在運行時的內部進行實現支持. erlang在運行時之上又分離出group_leader這一層, 目的就是在分佈式系統中, 我們可以通過重新設置process的group_leader,從而 達到重定向其io流的方向. 這個概念似乎和*unix中的"|", 重定向有點類似.

 

我們可以使用group_leader,實現分佈式系統中, 節點io的重定向, 這樣便於我們調試和記錄一些信息.

 

erlang emulator啓動時,init作爲第一個process(通常編號爲<0.0.0>), 是所有process的group_leader.

 

kenerl lib中user模塊, 負責處理具體的io_request, 其可以在erlang shell輸出數據, 可以從erlang shell讀取數據,因此對於我們 本文所講述的io_request, user是最頂層的group_leader.

application與group_leader

application創建的時候, 其默認的group_leader爲user process, 在application對應的application master中,其設置group leader 爲self, 但是app master同時保存原有的group leader即user.

 

application中所有的process的group leader都指向其的application master. 任意一個process的io request信息都會發送給app master.

 

注意,前面我們提過, app同時保存了舊的group leader, 在app master的loop中, 我們看到處理代碼如下:

IoReq when element(1, IoReq) =:= io_request ->
  State#state.gleader ! IoReq,
  init_loop(Pid, Tag, State, Type);
....

其將io request消息,簡單的發送給了old group leader, 也就是user. 這裏這麼做,我猜測可能是爲了以後的某些功能.

使用io模塊

我們經常調用io:format/1,2,3, 用來顯示一些調試及提示信息, 那這個命令執行過程是怎麼樣的呢?

 

如果使用io:fromat/1,2, 沒有指定io device, 那麼default_output即是調用此函數process的group_leader(). 如果此process屬於某個application,那麼依據前面的知識,我們可以知道,消息的路線如下:

 

io:fromat/1,2 -> app master (group_leader) -> user

 

我們也可以使用io:format/3, 通過指定io device爲user, 指定目標device. 那麼消息的路線如下:

 

io:format(user, Msg, D) -> user

slave節點的io request

stdlib中的slave module,可以用來啓動slave節點, slave module說明中有下面的描述:

 

All TTY output produced at the slave will be sent back to the master node. File I/O is done via the master.

 

這裏是如何實現的呢?

 

比如我們的master節點通過ssh啓動slave節點, 其參數中,有一個 -master 選項,其設置爲master node()(具體參看stdlib/slave.erl),  當遠程的slave節點的erlang emulator啓動時, user_sup module判斷 -master 如果設置, 則不會創建一個新的user process, 其獲取master的user pid, 然後註冊成本地user name指向master的user pid.

 

具體代碼如下(user_sup.erl):

 

case get_user() of
   ...
   {master, Master} ->
       Pid = start_slave(Master),
       {ok, Pid, Pid};
   ...

start_slave(Master) ->
   case rpc:call(Master, erlang, whereis, [user]) of
       User when is_pid(User) ->
           spawn(?MODULE, relay, [User]);
   _ ->
       ...
   end.

relay(Pid) ->
   register(user, self()),
   relay1(Pid).

relay1(Pid) ->
   receive
       X ->
           Pid ! X,
   relay1(Pid)
   end

在relay1的loop中,任何io request及其他消息,只是簡單的交給遠程的master進行處理.

一個簡單的例子

東拉西扯,說了很多東西,可能比較模糊. 讓我們舉一個簡單的例子, 希望可以讓你的思路更加清晰.

 

我們在本地,啓動兩個node foo, bar, 將Node bar的group leader設置爲foo的user, 看看效果:

 

foo:

erl -sname foo
(foo@cheng)1>

 

bar:

erl -sname bar
(bar@cheng)1> net_kernel:connect_node('foo@cheng').
true
(bar@cheng)2> [Foo] = nodes().
['foo@cheng']
(bar@cheng)3> User = rpc:call(Foo, erlang, whereis, [user]).
<5268.28.0>
(bar@cheng)4> group_leader(User, self()).

 

此時,在foo的shell發生了變化: foo:

 

(foo@cheng)1> true

 

這個就是bar節點上調用group_leader/2的返回值:true.

 

好了, 現在bar的erlang shell 所在process的group leader已經設置爲bar的user process. 讓我們調用io module輸出一些信息:

 

bar:

(bar@cheng)5> io:format("hello, i'm ~p~n", [self()]).

 

foo顯示信息:

(foo@cheng)1> hello, i'm <5230.35.0>
(foo@cheng)1> ok

 

其中<5230.35.0>爲bar中shell process的pid. 下面的ok,是在bar中調用io:format/2後的輸出. 我們在bar中的標準輸出,現在已經傳輸到foo中.

 

那麼有沒有什麼方法,可以讓輸出顯示在bar中呢? 當然可以!

 

bar:

(bar@cheng)6> io:format(user, "please show in the bar~n", []).
please show in the bar
ok

 

我們通過指定io device爲local user, 來使io request顯示在本地.

小提示

可以通過設置group leader來實現io request重定向, 在記錄日誌,調試系統方面具有一定的作用.

 

一點問題:io reqeust採用request-reponse的方式進行交互, 發送的消息隨後會等待應答, 比如調用 io:format返回的ok, 即是應答.

發佈了10 篇原創文章 · 獲贊 0 · 訪問量 2855
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章