式启动了inets中的httpd,下面简单分析一下过程,函数粒度的介绍。
1,下面是application inets的代码目录,虽然ftp、tftp、http_client、http_lib、http_server、inets_app在这目录中并列,其实inets_app扮演顶层控制角色;
只有inets_app是一个application,而其他都是module---application的一部分并且需要application启动和管理。
[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的目录和文件:
[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的启动停止:
inets_app.erl:behaviour是application,其中定义启动函数start/2;
inets_sup.erl:behaviour是supervisor,其中定义start_link/0和回调函数init/1;
inets.erl:是一个模块,实现了几个关于application的启动、停止、服务信息、trace开关控制的函数;
inet.app.src:是application inets的配置文件,重要!
3,简单说说inets.erl,这里没有复杂的东西也不是最重要的:
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.