• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1Processes
2=========
3
4libuv offers considerable child process management, abstracting the platform
5differences and allowing communication with the child process using streams or
6named pipes.
7
8A common idiom in Unix is for every process to do one thing and do it well. In
9such a case, a process often uses multiple child processes to achieve tasks
10(similar to using pipes in shells). A multi-process model with messages
11may also be easier to reason about compared to one with threads and shared
12memory.
13
14A common refrain against event-based programs is that they cannot take
15advantage of multiple cores in modern computers. In a multi-threaded program
16the kernel can perform scheduling and assign different threads to different
17cores, improving performance. But an event loop has only one thread.  The
18workaround can be to launch multiple processes instead, with each process
19running an event loop, and each process getting assigned to a separate CPU
20core.
21
22Spawning child processes
23------------------------
24
25The simplest case is when you simply want to launch a process and know when it
26exits. This is achieved using ``uv_spawn``.
27
28.. rubric:: spawn/main.c
29.. literalinclude:: ../../code/spawn/main.c
30    :language: c
31    :linenos:
32    :lines: 6-8,15-
33    :emphasize-lines: 11,13-17
34
35.. NOTE::
36
37    ``options`` is implicitly initialized with zeros since it is a global
38    variable.  If you change ``options`` to a local variable, remember to
39    initialize it to null out all unused fields::
40
41        uv_process_options_t options = {0};
42
43The ``uv_process_t`` struct only acts as the handle, all options are set via
44``uv_process_options_t``. To simply launch a process, you need to set only the
45``file`` and ``args`` fields. ``file`` is the program to execute. Since
46``uv_spawn`` uses :man:`execvp(3)` internally, there is no need to supply the full
47path. Finally as per underlying conventions, **the arguments array has to be
48one larger than the number of arguments, with the last element being NULL**.
49
50After the call to ``uv_spawn``, ``uv_process_t.pid`` will contain the process
51ID of the child process.
52
53The exit callback will be invoked with the *exit status* and the type of *signal*
54which caused the exit.
55
56.. rubric:: spawn/main.c
57.. literalinclude:: ../../code/spawn/main.c
58    :language: c
59    :linenos:
60    :lines: 9-12
61    :emphasize-lines: 3
62
63It is **required** to close the process watcher after the process exits.
64
65Changing process parameters
66---------------------------
67
68Before the child process is launched you can control the execution environment
69using fields in ``uv_process_options_t``.
70
71Change execution directory
72++++++++++++++++++++++++++
73
74Set ``uv_process_options_t.cwd`` to the corresponding directory.
75
76Set environment variables
77+++++++++++++++++++++++++
78
79``uv_process_options_t.env`` is a null-terminated array of strings, each of the
80form ``VAR=VALUE`` used to set up the environment variables for the process. Set
81this to ``NULL`` to inherit the environment from the parent (this) process.
82
83Option flags
84++++++++++++
85
86Setting ``uv_process_options_t.flags`` to a bitwise OR of the following flags,
87modifies the child process behaviour:
88
89* ``UV_PROCESS_SETUID`` - sets the child's execution user ID to ``uv_process_options_t.uid``.
90* ``UV_PROCESS_SETGID`` - sets the child's execution group ID to ``uv_process_options_t.gid``.
91
92Changing the UID/GID is only supported on Unix, ``uv_spawn`` will fail on
93Windows with ``UV_ENOTSUP``.
94
95* ``UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS`` - No quoting or escaping of
96  ``uv_process_options_t.args`` is done on Windows. Ignored on Unix.
97* ``UV_PROCESS_DETACHED`` - Starts the child process in a new session, which
98  will keep running after the parent process exits. See example below.
99
100Detaching processes
101-------------------
102
103Passing the flag ``UV_PROCESS_DETACHED`` can be used to launch daemons, or
104child processes which are independent of the parent so that the parent exiting
105does not affect it.
106
107.. rubric:: detach/main.c
108.. literalinclude:: ../../code/detach/main.c
109    :language: c
110    :linenos:
111    :lines: 9-30
112    :emphasize-lines: 12,19
113
114Just remember that the handle is still monitoring the child, so your program
115won't exit. Use ``uv_unref()`` if you want to be more *fire-and-forget*.
116
117Sending signals to processes
118----------------------------
119
120libuv wraps the standard ``kill(2)`` system call on Unix and implements one
121with similar semantics on Windows, with *one caveat*: all of ``SIGTERM``,
122``SIGINT`` and ``SIGKILL``, lead to termination of the process. The signature
123of ``uv_kill`` is::
124
125    uv_err_t uv_kill(int pid, int signum);
126
127For processes started using libuv, you may use ``uv_process_kill`` instead,
128which accepts the ``uv_process_t`` watcher as the first argument, rather than
129the pid. In this case, **remember to call** ``uv_close`` on the watcher.
130
131Signals
132-------
133
134libuv provides wrappers around Unix signals with `some Windows support
135<http://docs.libuv.org/en/v1.x/signal.html#signal>`_ as well.
136
137Use ``uv_signal_init()`` to initialize
138a handle and associate it with a loop. To listen for particular signals on
139that handler, use ``uv_signal_start()`` with the handler function. Each handler
140can only be associated with one signal number, with subsequent calls to
141``uv_signal_start()`` overwriting earlier associations. Use ``uv_signal_stop()`` to
142stop watching. Here is a small example demonstrating the various possibilities:
143
144.. rubric:: signal/main.c
145.. literalinclude:: ../../code/signal/main.c
146    :language: c
147    :linenos:
148    :emphasize-lines: 17-18,27-28
149
150.. NOTE::
151
152    ``uv_run(loop, UV_RUN_NOWAIT)`` is similar to ``uv_run(loop, UV_RUN_ONCE)``
153    in that it will process only one event. UV_RUN_ONCE blocks if there are no
154    pending events, while UV_RUN_NOWAIT will return immediately. We use NOWAIT
155    so that one of the loops isn't starved because the other one has no pending
156    activity.
157
158Send ``SIGUSR1`` to the process, and you'll find the handler being invoked
1594 times, one for each ``uv_signal_t``. The handler just stops each handle,
160so that the program exits. This sort of dispatch to all handlers is very
161useful. A server using multiple event loops could ensure that all data was
162safely saved before termination, simply by every loop adding a watcher for
163``SIGINT``.
164
165Child Process I/O
166-----------------
167
168A normal, newly spawned process has its own set of file descriptors, with 0,
1691 and 2 being ``stdin``, ``stdout`` and ``stderr`` respectively. Sometimes you
170may want to share file descriptors with the child. For example, perhaps your
171applications launches a sub-command and you want any errors to go in the log
172file, but ignore ``stdout``. For this you'd like to have ``stderr`` of the
173child be the same as the stderr of the parent. In this case, libuv supports
174*inheriting* file descriptors. In this sample, we invoke the test program,
175which is:
176
177.. rubric:: proc-streams/test.c
178.. literalinclude:: ../../code/proc-streams/test.c
179    :language: c
180
181The actual program ``proc-streams`` runs this while sharing only ``stderr``.
182The file descriptors of the child process are set using the ``stdio`` field in
183``uv_process_options_t``. First set the ``stdio_count`` field to the number of
184file descriptors being set. ``uv_process_options_t.stdio`` is an array of
185``uv_stdio_container_t``, which is:
186
187.. code-block:: c
188
189    typedef struct uv_stdio_container_s {
190        uv_stdio_flags flags;
191
192        union {
193            uv_stream_t* stream;
194            int fd;
195        } data;
196    } uv_stdio_container_t;
197
198where flags can have several values. Use ``UV_IGNORE`` if it isn't going to be
199used. If the first three ``stdio`` fields are marked as ``UV_IGNORE`` they'll
200redirect to ``/dev/null``.
201
202Since we want to pass on an existing descriptor, we'll use ``UV_INHERIT_FD``.
203Then we set the ``fd`` to ``stderr``.
204
205.. rubric:: proc-streams/main.c
206.. literalinclude:: ../../code/proc-streams/main.c
207    :language: c
208    :linenos:
209    :lines: 15-17,27-
210    :emphasize-lines: 6,10,11,12
211
212If you run ``proc-stream`` you'll see that only the line "This is stderr" will
213be displayed. Try marking ``stdout`` as being inherited and see the output.
214
215It is dead simple to apply this redirection to streams.  By setting ``flags``
216to ``UV_INHERIT_STREAM`` and setting ``data.stream`` to the stream in the
217parent process, the child process can treat that stream as standard I/O. This
218can be used to implement something like CGI_.
219
220.. _CGI: https://en.wikipedia.org/wiki/Common_Gateway_Interface
221
222A sample CGI script/executable is:
223
224.. rubric:: cgi/tick.c
225.. literalinclude:: ../../code/cgi/tick.c
226    :language: c
227
228The CGI server combines the concepts from this chapter and :doc:`networking` so
229that every client is sent ten ticks after which that connection is closed.
230
231.. rubric:: cgi/main.c
232.. literalinclude:: ../../code/cgi/main.c
233    :language: c
234    :linenos:
235    :lines: 49-63
236    :emphasize-lines: 10
237
238Here we simply accept the TCP connection and pass on the socket (*stream*) to
239``invoke_cgi_script``.
240
241.. rubric:: cgi/main.c
242.. literalinclude:: ../../code/cgi/main.c
243    :language: c
244    :linenos:
245    :lines: 16, 25-45
246    :emphasize-lines: 8-9,18,20
247
248The ``stdout`` of the CGI script is set to the socket so that whatever our tick
249script prints, gets sent to the client. By using processes, we can offload the
250read/write buffering to the operating system, so in terms of convenience this
251is great. Just be warned that creating processes is a costly task.
252
253.. _pipes:
254
255Parent-child IPC
256----------------
257
258A parent and child can have one or two way communication over a pipe created by
259settings ``uv_stdio_container_t.flags`` to a bit-wise combination of
260``UV_CREATE_PIPE`` and ``UV_READABLE_PIPE`` or ``UV_WRITABLE_PIPE``. The
261read/write flag is from the perspective of the child process.  In this case,
262the ``uv_stream_t* stream`` field must be set to point to an initialized,
263unopened ``uv_pipe_t`` instance.
264
265New stdio Pipes
266+++++++++++++++
267
268The ``uv_pipe_t`` structure represents more than just `pipe(7)`_ (or ``|``),
269but supports any streaming file-like objects. On Windows, the only object of
270that description is the `Named Pipe`_.  On Unix, this could be any of `Unix
271Domain Socket`_, or derived from `mkfifo(1)`_, or it could actually be a
272`pipe(7)`_.  When ``uv_spawn`` initializes a ``uv_pipe_t`` due to the
273`UV_CREATE_PIPE` flag, it opts for creating a `socketpair(2)`_.
274
275This is intended for the purpose of allowing multiple libuv processes to
276communicate with IPC. This is discussed below.
277
278.. _pipe(7): https://man7.org/linux/man-pages/man7/pipe.7.html
279.. _mkfifo(1): https://man7.org/linux/man-pages/man1/mkfifo.1.html
280.. _socketpair(2): https://man7.org/linux/man-pages/man2/socketpair.2.html
281.. _Unix Domain Socket: https://man7.org/linux/man-pages/man7/unix.7.html
282.. _Named Pipe: https://docs.microsoft.com/en-us/windows/win32/ipc/named-pipes
283
284
285Arbitrary process IPC
286+++++++++++++++++++++
287
288Since domain sockets [#]_ can have a well known name and a location in the
289file-system they can be used for IPC between unrelated processes. The D-BUS_
290system used by open source desktop environments uses domain sockets for event
291notification. Various applications can then react when a contact comes online
292or new hardware is detected. The MySQL server also runs a domain socket on
293which clients can interact with it.
294
295.. _D-BUS: https://www.freedesktop.org/wiki/Software/dbus
296
297When using domain sockets, a client-server pattern is usually followed with the
298creator/owner of the socket acting as the server. After the initial setup,
299messaging is no different from TCP, so we'll re-use the echo server example.
300
301.. rubric:: pipe-echo-server/main.c
302.. literalinclude:: ../../code/pipe-echo-server/main.c
303    :language: c
304    :linenos:
305    :lines: 70-
306    :emphasize-lines: 5,10,14
307
308We name the socket ``echo.sock`` which means it will be created in the local
309directory. This socket now behaves no different from TCP sockets as far as
310the stream API is concerned. You can test this server using `socat`_::
311
312    $ socat - /path/to/socket
313
314A client which wants to connect to a domain socket will use::
315
316    void uv_pipe_connect(uv_connect_t *req, uv_pipe_t *handle, const char *name, uv_connect_cb cb);
317
318where ``name`` will be ``echo.sock`` or similar. On Unix systems, ``name`` must
319point to a valid file (e.g. ``/tmp/echo.sock``). On Windows, ``name`` follows a
320``\\?\pipe\echo.sock`` format.
321
322.. _socat: http://www.dest-unreach.org/socat/
323
324Sending file descriptors over pipes
325+++++++++++++++++++++++++++++++++++
326
327The cool thing about domain sockets is that file descriptors can be exchanged
328between processes by sending them over a domain socket. This allows processes
329to hand off their I/O to other processes. Applications include load-balancing
330servers, worker processes and other ways to make optimum use of CPU. libuv only
331supports sending **TCP sockets or other pipes** over pipes for now.
332
333To demonstrate, we will look at a echo server implementation that hands of
334clients to worker processes in a round-robin fashion. This program is a bit
335involved, and while only snippets are included in the book, it is recommended
336to read the full code to really understand it.
337
338The worker process is quite simple, since the file-descriptor is handed over to
339it by the master.
340
341.. rubric:: multi-echo-server/worker.c
342.. literalinclude:: ../../code/multi-echo-server/worker.c
343    :language: c
344    :linenos:
345    :lines: 7-9,81-
346    :emphasize-lines: 6-8
347
348``queue`` is the pipe connected to the master process on the other end, along
349which new file descriptors get sent. It is important to set the ``ipc``
350argument of ``uv_pipe_init`` to 1 to indicate this pipe will be used for
351inter-process communication! Since the master will write the file handle to the
352standard input of the worker, we connect the pipe to ``stdin`` using
353``uv_pipe_open``.
354
355.. rubric:: multi-echo-server/worker.c
356.. literalinclude:: ../../code/multi-echo-server/worker.c
357    :language: c
358    :linenos:
359    :lines: 51-79
360    :emphasize-lines: 10,15,20
361
362First we call ``uv_pipe_pending_count()`` to ensure that a handle is available
363to read out. If your program could deal with different types of handles,
364``uv_pipe_pending_type()`` can be used to determine the type.
365Although ``accept`` seems odd in this code, it actually makes sense. What
366``accept`` traditionally does is get a file descriptor (the client) from
367another file descriptor (The listening socket). Which is exactly what we do
368here. Fetch the file descriptor (``client``) from ``queue``. From this point
369the worker does standard echo server stuff.
370
371Turning now to the master, let's take a look at how the workers are launched to
372allow load balancing.
373
374.. rubric:: multi-echo-server/main.c
375.. literalinclude:: ../../code/multi-echo-server/main.c
376    :language: c
377    :linenos:
378    :lines: 9-13
379
380The ``child_worker`` structure wraps the process, and the pipe between the
381master and the individual process.
382
383.. rubric:: multi-echo-server/main.c
384.. literalinclude:: ../../code/multi-echo-server/main.c
385    :language: c
386    :linenos:
387    :lines: 51,61-95
388    :emphasize-lines: 17,20-21
389
390In setting up the workers, we use the nifty libuv function ``uv_cpu_info`` to
391get the number of CPUs so we can launch an equal number of workers. Again it is
392important to initialize the pipe acting as the IPC channel with the third
393argument as 1. We then indicate that the child process' ``stdin`` is to be
394a readable pipe (from the point of view of the child). Everything is
395straightforward till here. The workers are launched and waiting for file
396descriptors to be written to their standard input.
397
398It is in ``on_new_connection`` (the TCP infrastructure is initialized in
399``main()``), that we accept the client socket and pass it along to the next
400worker in the round-robin.
401
402.. rubric:: multi-echo-server/main.c
403.. literalinclude:: ../../code/multi-echo-server/main.c
404    :language: c
405    :linenos:
406    :lines: 31-49
407    :emphasize-lines: 9,12-13
408
409The ``uv_write2`` call handles all the abstraction and it is simply a matter of
410passing in the handle (``client``) as the right argument. With this our
411multi-process echo server is operational.
412
413Thanks to Kyle for `pointing out`_ that ``uv_write2()`` requires a non-empty
414buffer even when sending handles.
415
416.. _pointing out: https://github.com/nikhilm/uvbook/issues/56
417
418----
419
420.. [#] In this section domain sockets stands in for named pipes on Windows as
421    well.
422