📄 serdev.sgml
字号:
<para>If <acronym>SNMP</acronym> support was enabled, another child will be created.</para> </listitem> </itemizedlist> <para> The following initialization will be performed in dont fork mode. (look into function <function moreinfo="none">main_loop</function> in file <filename moreinfo="none">main.c</filename>. </para> <itemizedlist> <listitem> <para>Another child will be forked for the timer subsystem.</para> </listitem> <listitem> <para> Initialize the <acronym>FIFO</acronym> server if enabled, this will fork another child. For more info about the <acronym>FIFO</acronym> server, see section The <acronym>FIFO</acronym> server. </para> </listitem> <listitem> <para> Call <function moreinfo="none">init_child(0).</function> The function performs per-child specific initialization of all loaded modules. A module can be initialized though <function moreinfo="none">mod_init</function> function. The function is called <emphasis>BEFORE</emphasis> the server forks and thus is common for all children. </para> <para> If there is anything, that needs to be initialized in every child separately (for example if each child needs to open its own file descriptor), it cannot be done in <function>mod_init</function>. To make such initialization possible, a module can export another initialization function called <function moreinfo="none">init_child</function>. The function will be called in all children <emphasis>AFTER</emphasis> fork of the server. </para> <para> And since we are in <quote>dont fork</quote> mode and there will no children processing requests (remember the main process will be processing all requests), the <function moreinfo="none">init_child</function> wouldn't be called. </para> <para> That would be bad, because <function moreinfo="none">child_init</function> might do some initialization that must be done otherwise modules might not work properly. </para> <para> To make sure that module initialization is complete we will call <function moreinfo="none">init_child</function> here for the main process even if we are not going to fork. </para> </listitem> </itemizedlist> <para> That's it. Everything has been initialized properly and as the last step we will call <function moreinfo="none">udp_rcv_loop</function> which is the main loop function. The function will be described later. </para> </section> <!-- dont-fork-set --> <section id="dont-fork-not-set"> <title><varname>dont_fork</varname> is not set (zero)</title> <para> <varname>dont_fork</varname> is not set. That means that the server will fork children and the children will be processing incoming requests. How many children will be created depends on the configuration (<varname>children</varname> variable). The main process will be sleeping and handling signals only. </para> <para> The main process will then initialize the <acronym>FIFO</acronym> server. The <acronym>FIFO</acronym> server needs another child to handle communication over <acronym>FIFO</acronym> and thus another child will be created. The <acronym>FIFO</acronym> server will be described in more detail later. </para> <para> Then the main process will perform another fork for the timer attendant. The child will take care of timer lists and execute specified function when a timer hits. </para> <para> The main process is now completely initialized, it will sleep in <function moreinfo="none">pause</function> function until a signal comes and call <function moreinfo="none">handle_sigs</function> when such condition occurs. </para> <para> The following initialization will be performed by each child separately: </para> <para> Each child executes <function moreinfo="none">init_child</function> function. The function will sequentially call <function moreinfo="none">child_init</function> functions of all loaded modules. </para> <para> Because the function is called in each child separately, it can initialize per-child specific data. For example if a module needs to communicate with database, it must open a database connection. If the connection would be opened in <function moreinfo="none">mod_init</function> function, all the children would share the same connection and locking would be necessary to avoid conflicts. On the other hand if the connection was opened in <function moreinfo="none">child_init</function> function, each child will have its own connection and concurrency conflicts will be handled by the database server. </para> <para> And last, but not least, each child executes <function moreinfo="none">udp_rcv_loop</function> function which contains the main loop logic. </para> </section> <!-- dont-fork-not-set --> </section> <!-- forking --> </chapter> <!-- server-startup --> <chapter id="main-loop"> <title>Main Loop</title> <para> Upon startup, all children execute <function moreinfo="none">recvfrom</function> function. The process will enter the kernel mode. When there is no data to be processed at the moment, the kernel will put the process on list of processes waiting for data and the process will be put asleep. </para> <para> When data to be processed was received, the first process on the list will be removed from the list and woken up. After the process finished processing of the data, it will call <function moreinfo="none">recvfrom</function> again and will be put by the kernel at the end of the list. </para> <para> When next data arrives, the first process on the list will be removed, processes the data and will be put on the end of the list again. And so on... </para> <para> The main loop logic can be found in function <function moreinfo="none">udp_rcv_loop</function> in file <filename moreinfo="none">udp_server.c</filename>. </para> <para> The message is received using <function moreinfo="none">recvfrom</function> function. The received data is stored in buffer and zero terminated. </para> <para> If configured so, basic sanity checks over the received message will be performed. </para> <para> The message is then processed by <function moreinfo="none">receive_msg</function> function and <function moreinfo="none">recvfrom</function> is called again. </para> <section id="recv-message"> <title><function moreinfo="none">receive_msg</function> Function</title> <para> The function can be found in <filename moreinfo="none">receive.c</filename> file. </para> <itemizedlist> <listitem> <para> In the server, a request or response is represented by <structname>sip_msg</structname> structure. The structure is allocated in this function. The original message is stored in <structfield>buf</structfield> attribute of the structure and is zero terminated. Then, another copy of the received message will be created and the copy will be stored in <structfield>orig</structfield> field. The original copy will be not modified during the server operation. All changes will be made to the copy in <structfield>buf</structfield> field. The second copy of the message will be removed in the future. </para> </listitem> <listitem> <para> The message will be parsed (function <function moreinfo="none">parse_msg</function>). We don't need the whole message header to be parsed at this stage. Only the first line and first Via header need to be parsed. The server needs to know if the message is request or response - hence the first line. The server also needs the first Via to be able to add its own Via - hence the first Via. Nothing else will be parsed at the moment - this saves time. (Message parser as well as <structname>sip_msg</structname> structure will be described later). </para> </listitem> <listitem> <para> A module may register callbacks. Each callback have associated an event, that will trigger the callback. One such callback is <emphasis>pre-script</emphasis> callback. Such callback will be called immediately before the routing part of the config file will be executed. If there are such callbacks registered, they will be executed now. </para> </listitem> <listitem> <para> As the next step we will determine type of the message. If the message being processed is a REQUEST then basic sanity checks will be performed (make sure that there is the first Via and parsing was successful) and the message will be passed to routing engine. The routing engine is one of the most complicated parts of the server and will be in detail described in chapter <link linkend="routing-engine">The Routing Engine</link>. </para> </listitem> <listitem> <para> If the message is a RESPONSE, it will be simply forwarded to its destination. </para> </listitem> <listitem> <para> After all, <emphasis>post-script</emphasis> callbacks will be executed if any and the structure representing the message will be released. </para> </listitem> <listitem> <para> Processing of the message is done now and the process is ready for another <acronym>SIP</acronym> message. </para> </listitem> </itemizedlist> </section> <!-- recv-message --> </chapter> <!-- main-loop --> <chapter id="server-shutdown"> <title>The Server Shutdown</title> <para> The server shutdown can be triggered by sending a signal to the server. The server will behave differently upon receiving various types of signals, here is a brief summary: </para> <itemizedlist> <listitem> <para> <emphasis>SIGINT, SIGPIPE, SIGTERM, SIGCHLD</emphasis> will terminate the server. </para> </listitem> <listitem> <para> <emphasis>SIGUSR1</emphasis> will print statistics and let the server continue. </para> </listitem> <listitem> <para> <emphasis>SIGHUP, SIGUSR2</emphasis> will be ignored. </para> </listitem> </itemizedlist> <para> There is only one common signal handler for all signals - function <function moreinfo="none">sig_usr</function> in file <filename moreinfo="none">main.c</filename>. </para> <para> In normal mode of operation (<varname>dont_fork</varname> variable is not set), the main server is not processing any requests, it calls <function moreinfo="none">pause</function> function and will be waiting for signals only. What happens when a signal arrives is shown in the previous paragraph. </para> <para> When in normal mode (<varname>dont_fork</varname> is not set), the signal handler of the main process will only store number of the signal received. All the processing logic will be executed by the main process outside the signal handler (function <function moreinfo="none">handle_sigs</function>) The function will be called immediately after the signal handler finish. The main process usually does some cleanup and running such things outside the signal handler is much more safe than doing it from the handler itself. Children only print statistics and exit or ignore the signal completely, that is quite safe and can be done directly from the signal handler of children. </para> <para> When <varname>dont_fork</varname> is set, all the cleanup will be done directly from the signal handler, because there is only one process - the main process. This is not so safe as the previous case, but this mode should be used for debugging only and such shortcoming doesn't harm in that case. </para> <para> Upon receipt of SIGINT, SIGPIPE or SIGTERM <function moreinfo="none">destroy_modules</function> will be called. Each module may register so-called <function moreinfo="none">destroy</function> function if it needs to do some cleanup when the server is terminating (flush of cache to disk for example). <function moreinfo="none">destroy_modules</function> will call destroy function of all loaded modules. </para> <para> If you need to terminate the server and all of its children, the best way how to do it is to send SIGTERM to the main process, the main process will in turn send the same signal to its children. </para> <para> The main process and its children are in the same process group. Therefore the main process can kill all its children simply by sending a signal to pid 0, sending to pid 0 will send the signal to all processes in the same process group as the sending process. This is how the main process will terminate all children when it is going to shut down. </para> <para> If one child exited during normal operation, the whole server will be shut down. This is better than let the server continue - a dead child might hold a lock and that could block the whole server, such situation cannot be avoided easily. Instead of that it is better to shutdown the whole server and let it restart. </para> </chapter> <!-- server-shutdown --> <chapter id="internal-data-struct"> <title>Internal Data Structures</title> <para> There are some data structures that are important and widely used in the server. We will describe them in detail in this section. </para> <note> <para> There are many more structures and types defined across the server and modules. We will describe only the most important and common data structure here. The rest will be described in other sections if needed. </para> </note> <section id="str"> <title>Type <type>str</type></title> <para> One of our main goals was to make <acronym>SER</acronym> really fast. There are many functions across the server that need to work with strings. Usually these functions need to know string length. We wanted to avoid using of <function moreinfo="none">strlen</function> because the function is relatively slow. It must scan the whole string and find the first occurrence of zero character. To avoid this, we created <type>str</type> type. The type has 2 fields, field <structfield>s</structfield> is pointer to the beginning of the string and field <structfield>len</structfield> is length of the string. We then calculate length of the string only once and later reuse saved value. </para> <important> <para> <type>str</type> structure is quite important because it is widely used in <acronym>SER</acronym> (most functions accept <type>str</type> instead of <type>char*</type>). </para> </important> <para><emphasis><type>str</type> Type Declaration</emphasis></para> <programlisting format="linespecific">struct _str{ char* s; int len;};typedef struct _str str; </programlisting> <para> The declaration can be found in header file <filename moreinfo="none">str.h</filename>. </para> <warning> <para>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -