Blame docs/src/guide/processes.rst

Packit b5b901
Processes
Packit b5b901
=========
Packit b5b901
Packit b5b901
libuv offers considerable child process management, abstracting the platform
Packit b5b901
differences and allowing communication with the child process using streams or
Packit b5b901
named pipes.
Packit b5b901
Packit b5b901
A common idiom in Unix is for every process to do one thing and do it well. In
Packit b5b901
such a case, a process often uses multiple child processes to achieve tasks
Packit b5b901
(similar to using pipes in shells). A multi-process model with messages
Packit b5b901
may also be easier to reason about compared to one with threads and shared
Packit b5b901
memory.
Packit b5b901
Packit b5b901
A common refrain against event-based programs is that they cannot take
Packit b5b901
advantage of multiple cores in modern computers. In a multi-threaded program
Packit b5b901
the kernel can perform scheduling and assign different threads to different
Packit b5b901
cores, improving performance. But an event loop has only one thread.  The
Packit b5b901
workaround can be to launch multiple processes instead, with each process
Packit b5b901
running an event loop, and each process getting assigned to a separate CPU
Packit b5b901
core.
Packit b5b901
Packit b5b901
Spawning child processes
Packit b5b901
------------------------
Packit b5b901
Packit b5b901
The simplest case is when you simply want to launch a process and know when it
Packit b5b901
exits. This is achieved using ``uv_spawn``.
Packit b5b901
Packit b5b901
.. rubric:: spawn/main.c
Packit b5b901
.. literalinclude:: ../../code/spawn/main.c
Packit b5b901
    :linenos:
Packit b5b901
    :lines: 6-8,15-
Packit b5b901
    :emphasize-lines: 11,13-17
Packit b5b901
Packit b5b901
.. NOTE::
Packit b5b901
Packit b5b901
    ``options`` is implicitly initialized with zeros since it is a global
Packit b5b901
    variable.  If you change ``options`` to a local variable, remember to
Packit b5b901
    initialize it to null out all unused fields::
Packit b5b901
Packit b5b901
        uv_process_options_t options = {0};
Packit b5b901
Packit b5b901
The ``uv_process_t`` struct only acts as the handle, all options are set via
Packit b5b901
``uv_process_options_t``. To simply launch a process, you need to set only the
Packit b5b901
``file`` and ``args`` fields. ``file`` is the program to execute. Since
Packit Service e08953
``uv_spawn`` uses :man:`execvp(3)` internally, there is no need to supply the full
Packit b5b901
path. Finally as per underlying conventions, **the arguments array has to be
Packit b5b901
one larger than the number of arguments, with the last element being NULL**.
Packit b5b901
Packit b5b901
After the call to ``uv_spawn``, ``uv_process_t.pid`` will contain the process
Packit b5b901
ID of the child process.
Packit b5b901
Packit b5b901
The exit callback will be invoked with the *exit status* and the type of *signal*
Packit b5b901
which caused the exit.
Packit b5b901
Packit b5b901
.. rubric:: spawn/main.c
Packit b5b901
.. literalinclude:: ../../code/spawn/main.c
Packit b5b901
    :linenos:
Packit b5b901
    :lines: 9-12
Packit b5b901
    :emphasize-lines: 3
Packit b5b901
Packit b5b901
It is **required** to close the process watcher after the process exits.
Packit b5b901
Packit b5b901
Changing process parameters
Packit b5b901
---------------------------
Packit b5b901
Packit b5b901
Before the child process is launched you can control the execution environment
Packit b5b901
using fields in ``uv_process_options_t``.
Packit b5b901
Packit b5b901
Change execution directory
Packit b5b901
++++++++++++++++++++++++++
Packit b5b901
Packit b5b901
Set ``uv_process_options_t.cwd`` to the corresponding directory.
Packit b5b901
Packit b5b901
Set environment variables
Packit b5b901
+++++++++++++++++++++++++
Packit b5b901
Packit b5b901
``uv_process_options_t.env`` is a null-terminated array of strings, each of the
Packit b5b901
form ``VAR=VALUE`` used to set up the environment variables for the process. Set
Packit b5b901
this to ``NULL`` to inherit the environment from the parent (this) process.
Packit b5b901
Packit b5b901
Option flags
Packit b5b901
++++++++++++
Packit b5b901
Packit b5b901
Setting ``uv_process_options_t.flags`` to a bitwise OR of the following flags,
Packit b5b901
modifies the child process behaviour:
Packit b5b901
Packit b5b901
* ``UV_PROCESS_SETUID`` - sets the child's execution user ID to ``uv_process_options_t.uid``.
Packit b5b901
* ``UV_PROCESS_SETGID`` - sets the child's execution group ID to ``uv_process_options_t.gid``.
Packit b5b901
Packit b5b901
Changing the UID/GID is only supported on Unix, ``uv_spawn`` will fail on
Packit b5b901
Windows with ``UV_ENOTSUP``.
Packit b5b901
Packit b5b901
* ``UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS`` - No quoting or escaping of
Packit b5b901
  ``uv_process_options_t.args`` is done on Windows. Ignored on Unix.
Packit b5b901
* ``UV_PROCESS_DETACHED`` - Starts the child process in a new session, which
Packit b5b901
  will keep running after the parent process exits. See example below.
Packit b5b901
Packit b5b901
Detaching processes
Packit b5b901
-------------------
Packit b5b901
Packit b5b901
Passing the flag ``UV_PROCESS_DETACHED`` can be used to launch daemons, or
Packit b5b901
child processes which are independent of the parent so that the parent exiting
Packit b5b901
does not affect it.
Packit b5b901
Packit b5b901
.. rubric:: detach/main.c
Packit b5b901
.. literalinclude:: ../../code/detach/main.c
Packit b5b901
    :linenos:
Packit b5b901
    :lines: 9-30
Packit b5b901
    :emphasize-lines: 12,19
Packit b5b901
Packit b5b901
Just remember that the handle is still monitoring the child, so your program
Packit b5b901
won't exit. Use ``uv_unref()`` if you want to be more *fire-and-forget*.
Packit b5b901
Packit b5b901
Sending signals to processes
Packit b5b901
----------------------------
Packit b5b901
Packit b5b901
libuv wraps the standard ``kill(2)`` system call on Unix and implements one
Packit b5b901
with similar semantics on Windows, with *one caveat*: all of ``SIGTERM``,
Packit b5b901
``SIGINT`` and ``SIGKILL``, lead to termination of the process. The signature
Packit b5b901
of ``uv_kill`` is::
Packit b5b901
Packit b5b901
    uv_err_t uv_kill(int pid, int signum);
Packit b5b901
Packit b5b901
For processes started using libuv, you may use ``uv_process_kill`` instead,
Packit b5b901
which accepts the ``uv_process_t`` watcher as the first argument, rather than
Packit b5b901
the pid. In this case, **remember to call** ``uv_close`` on the watcher.
Packit b5b901
Packit b5b901
Signals
Packit b5b901
-------
Packit b5b901
Packit b5b901
libuv provides wrappers around Unix signals with `some Windows support
Packit b5b901
<http://docs.libuv.org/en/v1.x/signal.html#signal>`_ as well.
Packit b5b901
Packit b5b901
Use ``uv_signal_init()`` to initialize
Packit b5b901
a handle and associate it with a loop. To listen for particular signals on
Packit b5b901
that handler, use ``uv_signal_start()`` with the handler function. Each handler
Packit b5b901
can only be associated with one signal number, with subsequent calls to
Packit b5b901
``uv_signal_start()`` overwriting earlier associations. Use ``uv_signal_stop()`` to
Packit b5b901
stop watching. Here is a small example demonstrating the various possibilities:
Packit b5b901
Packit b5b901
.. rubric:: signal/main.c
Packit b5b901
.. literalinclude:: ../../code/signal/main.c
Packit b5b901
    :linenos:
Packit b5b901
    :emphasize-lines: 17-18,27-28
Packit b5b901
Packit b5b901
.. NOTE::
Packit b5b901
Packit b5b901
    ``uv_run(loop, UV_RUN_NOWAIT)`` is similar to ``uv_run(loop, UV_RUN_ONCE)``
Packit b5b901
    in that it will process only one event. UV_RUN_ONCE blocks if there are no
Packit b5b901
    pending events, while UV_RUN_NOWAIT will return immediately. We use NOWAIT
Packit b5b901
    so that one of the loops isn't starved because the other one has no pending
Packit b5b901
    activity.
Packit b5b901
Packit b5b901
Send ``SIGUSR1`` to the process, and you'll find the handler being invoked
Packit b5b901
4 times, one for each ``uv_signal_t``. The handler just stops each handle,
Packit b5b901
so that the program exits. This sort of dispatch to all handlers is very
Packit b5b901
useful. A server using multiple event loops could ensure that all data was
Packit b5b901
safely saved before termination, simply by every loop adding a watcher for
Packit b5b901
``SIGINT``.
Packit b5b901
Packit b5b901
Child Process I/O
Packit b5b901
-----------------
Packit b5b901
Packit b5b901
A normal, newly spawned process has its own set of file descriptors, with 0,
Packit b5b901
1 and 2 being ``stdin``, ``stdout`` and ``stderr`` respectively. Sometimes you
Packit b5b901
may want to share file descriptors with the child. For example, perhaps your
Packit b5b901
applications launches a sub-command and you want any errors to go in the log
Packit b5b901
file, but ignore ``stdout``. For this you'd like to have ``stderr`` of the
Packit b5b901
child be the same as the stderr of the parent. In this case, libuv supports
Packit b5b901
*inheriting* file descriptors. In this sample, we invoke the test program,
Packit b5b901
which is:
Packit b5b901
Packit b5b901
.. rubric:: proc-streams/test.c
Packit b5b901
.. literalinclude:: ../../code/proc-streams/test.c
Packit b5b901
Packit b5b901
The actual program ``proc-streams`` runs this while sharing only ``stderr``.
Packit b5b901
The file descriptors of the child process are set using the ``stdio`` field in
Packit b5b901
``uv_process_options_t``. First set the ``stdio_count`` field to the number of
Packit b5b901
file descriptors being set. ``uv_process_options_t.stdio`` is an array of
Packit b5b901
``uv_stdio_container_t``, which is:
Packit b5b901
Packit Service e08953
.. code-block:: c
Packit Service e08953
Packit Service e08953
    typedef struct uv_stdio_container_s {
Packit Service e08953
        uv_stdio_flags flags;
Packit Service e08953
Packit Service e08953
        union {
Packit Service e08953
            uv_stream_t* stream;
Packit Service e08953
            int fd;
Packit Service e08953
        } data;
Packit Service e08953
    } uv_stdio_container_t;
Packit b5b901
Packit b5b901
where flags can have several values. Use ``UV_IGNORE`` if it isn't going to be
Packit b5b901
used. If the first three ``stdio`` fields are marked as ``UV_IGNORE`` they'll
Packit b5b901
redirect to ``/dev/null``.
Packit b5b901
Packit b5b901
Since we want to pass on an existing descriptor, we'll use ``UV_INHERIT_FD``.
Packit b5b901
Then we set the ``fd`` to ``stderr``.
Packit b5b901
Packit b5b901
.. rubric:: proc-streams/main.c
Packit b5b901
.. literalinclude:: ../../code/proc-streams/main.c
Packit b5b901
    :linenos:
Packit b5b901
    :lines: 15-17,27-
Packit b5b901
    :emphasize-lines: 6,10,11,12
Packit b5b901
Packit b5b901
If you run ``proc-stream`` you'll see that only the line "This is stderr" will
Packit b5b901
be displayed. Try marking ``stdout`` as being inherited and see the output.
Packit b5b901
Packit b5b901
It is dead simple to apply this redirection to streams.  By setting ``flags``
Packit b5b901
to ``UV_INHERIT_STREAM`` and setting ``data.stream`` to the stream in the
Packit b5b901
parent process, the child process can treat that stream as standard I/O. This
Packit b5b901
can be used to implement something like CGI_.
Packit b5b901
Packit Service e08953
.. _CGI: https://en.wikipedia.org/wiki/Common_Gateway_Interface
Packit b5b901
Packit b5b901
A sample CGI script/executable is:
Packit b5b901
Packit b5b901
.. rubric:: cgi/tick.c
Packit b5b901
.. literalinclude:: ../../code/cgi/tick.c
Packit b5b901
Packit b5b901
The CGI server combines the concepts from this chapter and :doc:`networking` so
Packit b5b901
that every client is sent ten ticks after which that connection is closed.
Packit b5b901
Packit b5b901
.. rubric:: cgi/main.c
Packit b5b901
.. literalinclude:: ../../code/cgi/main.c
Packit b5b901
    :linenos:
Packit b5b901
    :lines: 49-63
Packit b5b901
    :emphasize-lines: 10
Packit b5b901
Packit b5b901
Here we simply accept the TCP connection and pass on the socket (*stream*) to
Packit b5b901
``invoke_cgi_script``.
Packit b5b901
Packit b5b901
.. rubric:: cgi/main.c
Packit b5b901
.. literalinclude:: ../../code/cgi/main.c
Packit b5b901
    :linenos:
Packit b5b901
    :lines: 16, 25-45
Packit b5b901
    :emphasize-lines: 8-9,18,20
Packit b5b901
Packit b5b901
The ``stdout`` of the CGI script is set to the socket so that whatever our tick
Packit b5b901
script prints, gets sent to the client. By using processes, we can offload the
Packit b5b901
read/write buffering to the operating system, so in terms of convenience this
Packit b5b901
is great. Just be warned that creating processes is a costly task.
Packit b5b901
Packit b5b901
.. _pipes:
Packit b5b901
Packit b5b901
Parent-child IPC
Packit Service e08953
----------------
Packit b5b901
Packit b5b901
A parent and child can have one or two way communication over a pipe created by
Packit b5b901
settings ``uv_stdio_container_t.flags`` to a bit-wise combination of
Packit b5b901
``UV_CREATE_PIPE`` and ``UV_READABLE_PIPE`` or ``UV_WRITABLE_PIPE``. The
Packit Service e08953
read/write flag is from the perspective of the child process.  In this case,
Packit Service e08953
the ``uv_stream_t* stream`` field must be set to point to an initialized,
Packit Service e08953
unopened ``uv_pipe_t`` instance.
Packit Service e08953
Packit Service e08953
New stdio Pipes
Packit Service e08953
+++++++++++++++
Packit Service e08953
Packit Service e08953
The ``uv_pipe_t`` structure represents more than just `pipe(7)`_ (or ``|``),
Packit Service e08953
but supports any streaming file-like objects. On Windows, the only object of
Packit Service e08953
that description is the `Named Pipe`_.  On Unix, this could be any of `Unix
Packit Service e08953
Domain Socket`_, or derived from `mkfifo(1)`_, or it could actually be a
Packit Service e08953
`pipe(7)`_.  When ``uv_spawn`` initializes a ``uv_pipe_t`` due to the
Packit Service e08953
`UV_CREATE_PIPE` flag, it opts for creating a `socketpair(2)`_.
Packit Service e08953
Packit Service e08953
This is intended for the purpose of allowing multiple libuv processes to
Packit Service e08953
communicate with IPC. This is discussed below.
Packit Service e08953
Packit Service e08953
.. _pipe(7): http://man7.org/linux/man-pages/man7/pipe.7.html
Packit Service e08953
.. _mkfifo(1): http://man7.org/linux/man-pages/man1/mkfifo.1.html
Packit Service e08953
.. _socketpair(2): http://man7.org/linux/man-pages/man2/socketpair.2.html
Packit Service e08953
.. _Unix Domain Socket: http://man7.org/linux/man-pages/man7/unix.7.html
Packit Service e08953
.. _Named Pipe: https://docs.microsoft.com/en-us/windows/win32/ipc/named-pipes
Packit Service e08953
Packit b5b901
Packit b5b901
Arbitrary process IPC
Packit b5b901
+++++++++++++++++++++
Packit b5b901
Packit b5b901
Since domain sockets [#]_ can have a well known name and a location in the
Packit b5b901
file-system they can be used for IPC between unrelated processes. The D-BUS_
Packit b5b901
system used by open source desktop environments uses domain sockets for event
Packit b5b901
notification. Various applications can then react when a contact comes online
Packit b5b901
or new hardware is detected. The MySQL server also runs a domain socket on
Packit b5b901
which clients can interact with it.
Packit b5b901
Packit Service e08953
.. _D-BUS: https://www.freedesktop.org/wiki/Software/dbus
Packit b5b901
Packit b5b901
When using domain sockets, a client-server pattern is usually followed with the
Packit b5b901
creator/owner of the socket acting as the server. After the initial setup,
Packit b5b901
messaging is no different from TCP, so we'll re-use the echo server example.
Packit b5b901
Packit b5b901
.. rubric:: pipe-echo-server/main.c
Packit b5b901
.. literalinclude:: ../../code/pipe-echo-server/main.c
Packit b5b901
    :linenos:
Packit b5b901
    :lines: 70-
Packit b5b901
    :emphasize-lines: 5,10,14
Packit b5b901
Packit b5b901
We name the socket ``echo.sock`` which means it will be created in the local
Packit b5b901
directory. This socket now behaves no different from TCP sockets as far as
Packit b5b901
the stream API is concerned. You can test this server using `socat`_::
Packit b5b901
Packit b5b901
    $ socat - /path/to/socket
Packit b5b901
Packit b5b901
A client which wants to connect to a domain socket will use::
Packit b5b901
Packit b5b901
    void uv_pipe_connect(uv_connect_t *req, uv_pipe_t *handle, const char *name, uv_connect_cb cb);
Packit b5b901
Packit b5b901
where ``name`` will be ``echo.sock`` or similar. On Unix systems, ``name`` must
Packit b5b901
point to a valid file (e.g. ``/tmp/echo.sock``). On Windows, ``name`` follows a
Packit b5b901
``\\?\pipe\echo.sock`` format.
Packit b5b901
Packit b5b901
.. _socat: http://www.dest-unreach.org/socat/
Packit b5b901
Packit b5b901
Sending file descriptors over pipes
Packit b5b901
+++++++++++++++++++++++++++++++++++
Packit b5b901
Packit b5b901
The cool thing about domain sockets is that file descriptors can be exchanged
Packit b5b901
between processes by sending them over a domain socket. This allows processes
Packit b5b901
to hand off their I/O to other processes. Applications include load-balancing
Packit b5b901
servers, worker processes and other ways to make optimum use of CPU. libuv only
Packit b5b901
supports sending **TCP sockets or other pipes** over pipes for now.
Packit b5b901
Packit b5b901
To demonstrate, we will look at a echo server implementation that hands of
Packit b5b901
clients to worker processes in a round-robin fashion. This program is a bit
Packit b5b901
involved, and while only snippets are included in the book, it is recommended
Packit b5b901
to read the full code to really understand it.
Packit b5b901
Packit b5b901
The worker process is quite simple, since the file-descriptor is handed over to
Packit b5b901
it by the master.
Packit b5b901
Packit b5b901
.. rubric:: multi-echo-server/worker.c
Packit b5b901
.. literalinclude:: ../../code/multi-echo-server/worker.c
Packit b5b901
    :linenos:
Packit b5b901
    :lines: 7-9,81-
Packit b5b901
    :emphasize-lines: 6-8
Packit b5b901
Packit b5b901
``queue`` is the pipe connected to the master process on the other end, along
Packit b5b901
which new file descriptors get sent. It is important to set the ``ipc``
Packit b5b901
argument of ``uv_pipe_init`` to 1 to indicate this pipe will be used for
Packit b5b901
inter-process communication! Since the master will write the file handle to the
Packit b5b901
standard input of the worker, we connect the pipe to ``stdin`` using
Packit b5b901
``uv_pipe_open``.
Packit b5b901
Packit b5b901
.. rubric:: multi-echo-server/worker.c
Packit b5b901
.. literalinclude:: ../../code/multi-echo-server/worker.c
Packit b5b901
    :linenos:
Packit b5b901
    :lines: 51-79
Packit b5b901
    :emphasize-lines: 10,15,20
Packit b5b901
Packit b5b901
First we call ``uv_pipe_pending_count()`` to ensure that a handle is available
Packit b5b901
to read out. If your program could deal with different types of handles,
Packit b5b901
``uv_pipe_pending_type()`` can be used to determine the type.
Packit b5b901
Although ``accept`` seems odd in this code, it actually makes sense. What
Packit b5b901
``accept`` traditionally does is get a file descriptor (the client) from
Packit b5b901
another file descriptor (The listening socket). Which is exactly what we do
Packit b5b901
here. Fetch the file descriptor (``client``) from ``queue``. From this point
Packit b5b901
the worker does standard echo server stuff.
Packit b5b901
Packit b5b901
Turning now to the master, let's take a look at how the workers are launched to
Packit b5b901
allow load balancing.
Packit b5b901
Packit b5b901
.. rubric:: multi-echo-server/main.c
Packit b5b901
.. literalinclude:: ../../code/multi-echo-server/main.c
Packit b5b901
    :linenos:
Packit b5b901
    :lines: 9-13
Packit b5b901
Packit b5b901
The ``child_worker`` structure wraps the process, and the pipe between the
Packit b5b901
master and the individual process.
Packit b5b901
Packit b5b901
.. rubric:: multi-echo-server/main.c
Packit b5b901
.. literalinclude:: ../../code/multi-echo-server/main.c
Packit b5b901
    :linenos:
Packit b5b901
    :lines: 51,61-95
Packit b5b901
    :emphasize-lines: 17,20-21
Packit b5b901
Packit b5b901
In setting up the workers, we use the nifty libuv function ``uv_cpu_info`` to
Packit b5b901
get the number of CPUs so we can launch an equal number of workers. Again it is
Packit b5b901
important to initialize the pipe acting as the IPC channel with the third
Packit b5b901
argument as 1. We then indicate that the child process' ``stdin`` is to be
Packit b5b901
a readable pipe (from the point of view of the child). Everything is
Packit b5b901
straightforward till here. The workers are launched and waiting for file
Packit b5b901
descriptors to be written to their standard input.
Packit b5b901
Packit b5b901
It is in ``on_new_connection`` (the TCP infrastructure is initialized in
Packit b5b901
``main()``), that we accept the client socket and pass it along to the next
Packit b5b901
worker in the round-robin.
Packit b5b901
Packit b5b901
.. rubric:: multi-echo-server/main.c
Packit b5b901
.. literalinclude:: ../../code/multi-echo-server/main.c
Packit b5b901
    :linenos:
Packit b5b901
    :lines: 31-49
Packit b5b901
    :emphasize-lines: 9,12-13
Packit b5b901
Packit b5b901
The ``uv_write2`` call handles all the abstraction and it is simply a matter of
Packit b5b901
passing in the handle (``client``) as the right argument. With this our
Packit b5b901
multi-process echo server is operational.
Packit b5b901
Packit b5b901
Thanks to Kyle for `pointing out`_ that ``uv_write2()`` requires a non-empty
Packit b5b901
buffer even when sending handles.
Packit b5b901
Packit b5b901
.. _pointing out: https://github.com/nikhilm/uvbook/issues/56
Packit b5b901
Packit b5b901
----
Packit b5b901
Packit b5b901
.. [#] In this section domain sockets stands in for named pipes on Windows as
Packit b5b901
    well.