式啓動了inets中的httpd,下面簡單分析一下過程,函數粒度的介紹。app
1,下面是application inets的代碼目錄,雖然ftp、tftp、http_client、http_lib、http_server、inets_app在這目錄中並列,其實inets_app扮演頂層控制角色;函數
只有inets_app是一個application,而其餘都是module---application的一部分而且須要application啓動和管理。this
[root@james src]# pwd/root/otp_src_17.5/lib/inets/src [root@james src]# l total 32 drwxrwxr-x 2 2004 uucp 4096 Mar 31 20:32 ftp drwxrwxr-x 2 2004 uucp 4096 Mar 31 20:32 http_client drwxrwxr-x 2 2004 uucp 4096 Mar 31 20:32 http_lib drwxrwxr-x 2 2004 uucp 4096 Mar 31 20:32 http_server drwxrwxr-x 2 2004 uucp 4096 Mar 31 20:32 inets_app-rw-rw-r-- 1 2004 uucp 1083 Mar 31 20:32 Makefile-rw-rw-r-- 1 2004 uucp 126 Mar 31 20:32 subdirs.mk drwxrwxr-x 2 2004 uucp 4096 Mar 31 20:32 tftp [root@james src]#
2,下面是inets_app的目錄和文件:spa
[root@james inets_app]# l total 76 -rw-rw-r-- 1 2004 uucp 838 Mar 31 20:32 inets_app.erl -rw-rw-r-- 1 2004 uucp 3216 Mar 31 20:32 inets.app.src -rw-rw-r-- 1 2004 uucp 812 Mar 31 20:32 inets.appup.src -rw-rw-r-- 1 2004 uucp 126 Mar 31 20:32 inets.config -rw-rw-r-- 1 2004 uucp 15651 Mar 31 20:32 inets.erl -rw-rw-r-- 1 2004 uucp 1591 Mar 31 20:32 inets_internal.hrl -rw-rw-r-- 1 2004 uucp 1215 Mar 31 20:32 inets.mk -rw-rw-r-- 1 2004 uucp 11503 Mar 31 20:32 inets_regexp.erl -rw-rw-r-- 1 2004 uucp 2217 Mar 31 20:32 inets_service.erl -rw-rw-r-- 1 2004 uucp 3870 Mar 31 20:32 inets_sup.erl -rw-rw-r-- 1 2004 uucp 11277 Mar 31 20:32 inets_trace.erl -rw-rw-r-- 1 2004 uucp 3256 Mar 31 20:32 Makefile [root@james inets_app]#
如下幾個文件比較重要,控制着整個application的啓動中止:debug
inets_app.erl:behaviour是application,其中定義啓動函數start/2;設計
inets_sup.erl:behaviour是supervisor,其中定義start_link/0和回調函數init/1;code
inets.erl:是一個模塊,實現了幾個關於application的啓動、中止、服務信息、trace開關控制的函數;regexp
inet.app.src:是application inets的配置文件,重要!orm
3,簡單說說inets.erl,這裏沒有複雜的東西也不是最重要的:server
41 %%-------------------------------------------------------------------- 42 %% Function: start([, Type]) -> ok 43 %% 44 %% Type = permanent | transient | temporary 45 %% 46 %% Description: Starts the inets application. Default type 47 %% is temporary. see application(3) 48 %%-------------------------------------------------------------------- 49 start() -> 50 application:start(inets). 51 52 start(Type) -> 53 application:start(inets, Type).
啓動函數start,本質調用了inets_app.erl中的代碼;
91 %%-------------------------------------------------------------------- 92 %% Function: stop() -> ok 93 %% 94 %% Description: Stops the inets application. 95 %%-------------------------------------------------------------------- 96 stop() -> 97 application:stop(inets). 98 ...... 108 stop(stand_alone, Pid) -> 109 true = exit(Pid, shutdown), 110 ok; 111 112 stop(Service, Pid) -> 113 Module = service_module(Service), 114 call_service(Module, stop_service, Pid).
中止函數stop,也是調用了inets_app.erl中的函數,或者使用內嵌函數exit shutdown進程。(這裏略過)
3+,這裏解釋一下:調用application:start(inets)和application:stop(inets),會發生什麼?這個得遵從inets.app.src配置文件的指示!先看配置文件:
{application,inets, [{description, "INETS CXC 138 49"}, {vsn, "%VSN%"}, {modules,[ inets, inets_sup, inets_app, inets_service, inets_regexp, inets_trace, %% FTP ftp, ...... %% HTTP server: httpd, httpd_acceptor, httpd_acceptor_sup, httpd_cgi, httpd_connection_sup, httpd_conf, httpd_custom, ...... tftp_logger, tftp_sup ]}, {registered,[inets_sup, httpc_manager]}, %% If the "new" ssl is used then 'crypto' must be started before inets. {applications,[kernel,stdlib]}, {mod,{inets_app,[]}}, {runtime_dependencies, ["stdlib-2.0","ssl-5.3.4","runtime_tools-1.8.14", "mnesia-4.12","kernel-3.0","erts-6.0"]}]}.
配置文件中的mod,指示了下一棒交給誰------inet_app.erl!
實際上inets_app.erl裏真的實現了一個回調函數start/2,繼續把事情作下去。
4,接下來講inets_app.erl的實現:
26 start(_Type, _State) -> 27 inets_sup:start_link(). 28 29 stop(_State) -> 30 ok.
實現很簡單,就是調用inets_sup.erl中的函數。
5, 下面說inets_sup.erl中的函數start_link函數和回調函數init:
34 %%%========================================================================= 35 %%% External functions 36 %%%========================================================================= 37 start_link() -> 38 supervisor:start_link({local, ?MODULE}, ?MODULE, []). 39 40 %%%========================================================================= 41 %%% Supervisor callback 42 %%%========================================================================= 43 init([]) -> 44 SupFlags = {one_for_one, 10, 3600}, 45 Children = children(), 46 {ok, {SupFlags, Children}}. 47
inets_sup.erl的behaviour天然是supervisor,經過supervisor調用start_link時,init做爲回調函數(supervisor的設計就是這樣)。
SupFlags,是supervisor behaviour的配置參數,在這裏就是一個Tuple結構 ------ {one_for_one, 10, 3600},此處略過(OTP相關文檔中有介紹supervisor)。
5+,下面詳細看children函數,這裏信息量最豐富!
51 get_services() -> 52 case (catch application:get_env(inets, services)) of 53 {ok, Services} -> 54 Services; 55 _ -> 56 [] 57 end. 58 59 children() -> 60 Services = get_services(), 61 HttpdServices = [Service || Service <- Services, is_httpd(Service)], 62 HttpcServices = [Service || Service <- Services, is_httpc(Service)], 63 TftpdServices = [Service || Service <- Services, is_tftpd(Service)], 64 [ftp_child_spec(), httpc_child_spec(HttpcServices), 65 httpd_child_spec(HttpdServices), tftpd_child_spec(TftpdServices)]. 66
作個實驗,這裏頗有收穫:
原來啓動erlang虛擬機時-config inets_httpd18080.config已經被解析(這個解析過程我還沒分析,有時間在搞一篇吧),而且從這裏獲取到。
這樣看來,children函數中Services是一個List結構,而HttpdServices也是一個List結構------列表元素是Tuple結構。
若是httpd服務只有一個配置文件,那麼HttpdServices形如[{httpd, "root/inetsConf/18080.conf"}];若是還有關於https的配置,那麼就像這樣:[{httpd, "/root/inetsConf/18080.conf"}, {httpd, "/root/inetsConf/https_18443.conf"}]~
下面看函數httpd_child_spec。
5++,再看httpd_child_spec函數的代碼:
87 httpd_child_spec(HttpdServices) -> 88 Name = httpd_sup, 89 StartFunc = {httpd_sup, start_link, [HttpdServices]}, 90 Restart = permanent, 91 Shutdown = infinity, 92 Modules = [httpd_sup], 93 Type = supervisor, 94 {Name, StartFunc, Restart, Shutdown, Type, Modules}. 95
其中StartFunc是supervisor啓動worker時須要的Tuple結構,這裏HttpdServices做爲StartFunc的一個部分。
能夠解釋一下,httpd_sup是模塊,start_link是這個模塊的函數,而[HttpdServices]做爲這個函數的參數;
很明顯,這裏並不解析"/root/inetsConf/18080.conf"配置文件,僅做爲參數傳下去,進入了具體的module(好比http_server)~
6,簡單列一下http_server中httpd_sup.erl實現解析參數的代碼------使用proplist把文件讀了再解析再存儲以便後面步驟檢索判斷,看起來很明白,我就不廢話解釋了:(從回調函數init/1開始)
89 %%%========================================================================= 90 %%% Supervisor callback 91 %%%========================================================================= 92 init([HttpdServices]) -> 93 ?hdrd("starting", [{httpd_service, HttpdServices}]), 94 RestartStrategy = one_for_one, 95 MaxR = 10, 96 MaxT = 3600, 97 Children = child_specs(HttpdServices, []), 98 {ok, {{RestartStrategy, MaxR, MaxT}, Children}}. 99...... 118 child_specs([], Acc) -> 119 Acc; 120 child_specs([{httpd, HttpdService} | Rest], Acc) -> 121 ?hdrd("child specs", [{httpd, HttpdService}]), 122 NewHttpdService = (catch mk_tuple_list(HttpdService)), 123 ?hdrd("child specs", [{new_httpd, NewHttpdService}]), 124 case catch child_spec(NewHttpdService) of 125 {error, Reason} -> 126 ?hdri("failed generating child spec", [{reason, Reason}]), 127 error_msg("Failed to start service: ~n~p ~n due to: ~p~n", 128 [HttpdService, Reason]), 129 child_specs(Rest, Acc); 130 Spec -> 131 ?hdrt("child spec", [{child_spec, Spec}]), 132 child_specs(Rest, [Spec | Acc]) 133 end. 134 135 child_spec(HttpdService) -> 136 {ok, Config} = httpd_config(HttpdService), 137 ?hdrt("child spec", [{config, Config}]), 138 Debug = proplists:get_value(debug, Config, []), 139 AcceptTimeout = proplists:get_value(accept_timeout, Config, 15000), 140 httpd_util:valid_options(Debug, AcceptTimeout, Config), 141 httpd_child_spec(Config, AcceptTimeout, Debug). 142 143 httpd_config([Value| _] = Config) when is_tuple(Value) -> 144 case proplists:get_value(file, Config) of 145 undefined -> 146 case proplists:get_value(proplist_file, Config) of 147 undefined -> 148 httpd_conf:validate_properties(Config); 149 File -> 150 try file:consult(File) of 151 {ok, [PropList]} -> 152 httpd_conf:validate_properties(PropList) 153 catch 154 exit:_ -> 155 throw({error, 156 {could_not_consult_proplist_file, File}}) 157 end 158 end; 159 File -> 160 {ok, File} 161 end. 162 163 httpd_child_spec([Value| _] = Config, AcceptTimeout, Debug) 164 when is_tuple(Value) -> 165 ?hdrt("httpd_child_spec - entry", [{accept_timeout, AcceptTimeout}, 166 {debug, Debug}]), 167 Address = proplists:get_value(bind_address, Config, any), 168 Port = proplists:get_value(port, Config, 80), 169 httpd_child_spec(Config, AcceptTimeout, Debug, Address, Port); 170 171 %% In this case the AcceptTimeout and Debug will only have default values... 172 httpd_child_spec(ConfigFile, AcceptTimeoutDef, DebugDef) -> 173 ?hdrt("httpd_child_spec - entry", [{config_file, ConfigFile}, 174 {accept_timeout_def, AcceptTimeoutDef }, 175 {debug_def, DebugDef}]), 176 case httpd_conf:load(ConfigFile) of 177 {ok, ConfigList} -> 178 ?hdrt("httpd_child_spec - loaded", [{config_list, ConfigList}]), 179 case (catch httpd_conf:validate_properties(ConfigList)) of 180 {ok, Config} -> 181 ?hdrt("httpd_child_spec - validated", [{config, Config}] ), 182 Address = proplists:get_value(bind_address, Config, any) , 183 Port = proplists:get_value(port, Config, 80), 184 AcceptTimeout = 185 proplists:get_value(accept_timeout, Config, 186 AcceptTimeoutDef), 187 Debug = 188 proplists:get_value(debug, Config, DebugDef), 189 httpd_child_spec([{file, ConfigFile} | Config], 190 AcceptTimeout, Debug, Address, Port); 191 Error -> 192 Error 193 end; 194 Error -> 195 Error 196 end. 197 198 httpd_child_spec(Config, AcceptTimeout, Debug, Addr, Port) -> 199 Fd = proplists:get_value(fd, Config, undefined), 200 case Port == 0 orelse Fd =/= undefined of 201 true -> 202 httpd_child_spec_listen(Config, AcceptTimeout, Debug, Addr, Port); 203 false -> 204 httpd_child_spec_nolisten(Config, AcceptTimeout, Debug, Addr, Port) 205 end.