Erlang socket 工作原理

原文

由於對併發的原生支持,erlang在很多時候被用來構建網絡服務器(socket server, http server...)的底層,處理大量的併發連接。理解erlang在底層如何處理socket,以及如何與自己的併發機制結合,對於使用erlang來構建網絡服務至關重要。

erlang的對socket的處理被分爲兩個部分。

Port Driver的中的處理

基本的io模型

在《Erlang Port Driver工作原理》中,已經對port driver的運行機制做了初步的介紹。在erlang中,一個socket被表示爲一個port。socket的port driver的實現全部在OTP/erts/emulator/drivers/common/inet_drv.c中。Port driver遵循非阻塞的原則,不會在處理socket io時阻塞scheduler線程。所有需要阻塞的操作,比如accept,connect,send等等,都是通過driver_select向系統的事件分派器註冊可讀寫事件,等待系統回調後進行處理。

系統的事件分派器在linux上使用epoll實現。多個scheduler線程併發在這個分派器上監聽系統的io事件,這就是整個erlang虛擬機的io處理模型。這與使用C++的ACE Reactor模式基本上是一致的。

所以理論上,在不考慮scheduler線程對於runqueue中process運行的消耗,erlang虛擬機處理io的效率應該與多線程驅動的ACE Reactor(epoll)模型基本一致。

系統調用和數據返回

socket是port,所以當然應該使用port相關的bif與port進行交互。

使用socket發送數據是通過調用port_command或者使用"!"操作符實現的。如果不能立即發送,消息會緩存到driver queue中,等待系統的可寫事件。在driver中有控制driver queue的高低水位(watermark),當累積的發送數據大於高水位時,driver會被設置成busy狀態,直到driver queue中的數據低於地水位才解除。如果向一個busy狀態的socket發送數據,將會造成port_command的阻塞,從而掛起發送process,直到busy狀態解除後退出。

所有其他的命令(open, shutdown, listen, accept, connect...)都作爲socket port的控制命令,通過port_control發送給driver。所有的port_control命令都被實現爲異步模式,會立即返回。比如connect命令會立即返回,而不會等待connect成功。connect的結果是通過process本身的消息隊列返回的。需要注意的是,結果會返回給命令的調用者process,也就是caller,而不是port的owner process,這不同於接收數據都是給owner process。也就是說我們在任何process中調用port_control來對socket發出命令,都可以在本process的消息隊列中得到結果。

Erlang OTP中的封裝

這裏的otp指的是全部用erlang本身實現的otp庫。

在otp中,socket的功能是通過prim_inet.erl模塊向外提供訪問的。prim_inet在本身原始的port命令上進行封裝,將與socket port的消息交互封裝成了函數(connect, accept...)。而prim_inet一個更重要的工作,就是將port層次基於消息的異步交互機制,通過在發送消息後立即receive結果,掛起當前process,變成了阻塞的同步交互機制。比如prim_inet中的connect函數,就是通過調用port_control向driver發送INET_REQ_CONNECT消息後,立即receive結果並將結果返回來實現一個阻塞的connect語義。所以,erlang中對socket的阻塞操作都是在process層面實現的,這樣的阻塞可以被scheduler調度。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章