10e552da7SchristosProcesses 20e552da7Schristos========= 30e552da7Schristos 40e552da7Schristoslibuv offers considerable child process management, abstracting the platform 50e552da7Schristosdifferences and allowing communication with the child process using streams or 60e552da7Schristosnamed pipes. 70e552da7Schristos 80e552da7SchristosA common idiom in Unix is for every process to do one thing and do it well. In 90e552da7Schristossuch a case, a process often uses multiple child processes to achieve tasks 100e552da7Schristos(similar to using pipes in shells). A multi-process model with messages 110e552da7Schristosmay also be easier to reason about compared to one with threads and shared 120e552da7Schristosmemory. 130e552da7Schristos 140e552da7SchristosA common refrain against event-based programs is that they cannot take 150e552da7Schristosadvantage of multiple cores in modern computers. In a multi-threaded program 160e552da7Schristosthe kernel can perform scheduling and assign different threads to different 170e552da7Schristoscores, improving performance. But an event loop has only one thread. The 180e552da7Schristosworkaround can be to launch multiple processes instead, with each process 190e552da7Schristosrunning an event loop, and each process getting assigned to a separate CPU 200e552da7Schristoscore. 210e552da7Schristos 220e552da7SchristosSpawning child processes 230e552da7Schristos------------------------ 240e552da7Schristos 250e552da7SchristosThe simplest case is when you simply want to launch a process and know when it 260e552da7Schristosexits. This is achieved using ``uv_spawn``. 270e552da7Schristos 280e552da7Schristos.. rubric:: spawn/main.c 290e552da7Schristos.. literalinclude:: ../../code/spawn/main.c 30*5f2f4271Schristos :language: c 310e552da7Schristos :linenos: 320e552da7Schristos :lines: 6-8,15- 330e552da7Schristos :emphasize-lines: 11,13-17 340e552da7Schristos 350e552da7Schristos.. NOTE:: 360e552da7Schristos 370e552da7Schristos ``options`` is implicitly initialized with zeros since it is a global 380e552da7Schristos variable. If you change ``options`` to a local variable, remember to 390e552da7Schristos initialize it to null out all unused fields:: 400e552da7Schristos 410e552da7Schristos uv_process_options_t options = {0}; 420e552da7Schristos 430e552da7SchristosThe ``uv_process_t`` struct only acts as the handle, all options are set via 440e552da7Schristos``uv_process_options_t``. To simply launch a process, you need to set only the 450e552da7Schristos``file`` and ``args`` fields. ``file`` is the program to execute. Since 460e552da7Schristos``uv_spawn`` uses :man:`execvp(3)` internally, there is no need to supply the full 470e552da7Schristospath. Finally as per underlying conventions, **the arguments array has to be 480e552da7Schristosone larger than the number of arguments, with the last element being NULL**. 490e552da7Schristos 500e552da7SchristosAfter the call to ``uv_spawn``, ``uv_process_t.pid`` will contain the process 510e552da7SchristosID of the child process. 520e552da7Schristos 530e552da7SchristosThe exit callback will be invoked with the *exit status* and the type of *signal* 540e552da7Schristoswhich caused the exit. 550e552da7Schristos 560e552da7Schristos.. rubric:: spawn/main.c 570e552da7Schristos.. literalinclude:: ../../code/spawn/main.c 58*5f2f4271Schristos :language: c 590e552da7Schristos :linenos: 600e552da7Schristos :lines: 9-12 610e552da7Schristos :emphasize-lines: 3 620e552da7Schristos 630e552da7SchristosIt is **required** to close the process watcher after the process exits. 640e552da7Schristos 650e552da7SchristosChanging process parameters 660e552da7Schristos--------------------------- 670e552da7Schristos 680e552da7SchristosBefore the child process is launched you can control the execution environment 690e552da7Schristosusing fields in ``uv_process_options_t``. 700e552da7Schristos 710e552da7SchristosChange execution directory 720e552da7Schristos++++++++++++++++++++++++++ 730e552da7Schristos 740e552da7SchristosSet ``uv_process_options_t.cwd`` to the corresponding directory. 750e552da7Schristos 760e552da7SchristosSet environment variables 770e552da7Schristos+++++++++++++++++++++++++ 780e552da7Schristos 790e552da7Schristos``uv_process_options_t.env`` is a null-terminated array of strings, each of the 800e552da7Schristosform ``VAR=VALUE`` used to set up the environment variables for the process. Set 810e552da7Schristosthis to ``NULL`` to inherit the environment from the parent (this) process. 820e552da7Schristos 830e552da7SchristosOption flags 840e552da7Schristos++++++++++++ 850e552da7Schristos 860e552da7SchristosSetting ``uv_process_options_t.flags`` to a bitwise OR of the following flags, 870e552da7Schristosmodifies the child process behaviour: 880e552da7Schristos 890e552da7Schristos* ``UV_PROCESS_SETUID`` - sets the child's execution user ID to ``uv_process_options_t.uid``. 900e552da7Schristos* ``UV_PROCESS_SETGID`` - sets the child's execution group ID to ``uv_process_options_t.gid``. 910e552da7Schristos 920e552da7SchristosChanging the UID/GID is only supported on Unix, ``uv_spawn`` will fail on 930e552da7SchristosWindows with ``UV_ENOTSUP``. 940e552da7Schristos 950e552da7Schristos* ``UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS`` - No quoting or escaping of 960e552da7Schristos ``uv_process_options_t.args`` is done on Windows. Ignored on Unix. 970e552da7Schristos* ``UV_PROCESS_DETACHED`` - Starts the child process in a new session, which 980e552da7Schristos will keep running after the parent process exits. See example below. 990e552da7Schristos 1000e552da7SchristosDetaching processes 1010e552da7Schristos------------------- 1020e552da7Schristos 1030e552da7SchristosPassing the flag ``UV_PROCESS_DETACHED`` can be used to launch daemons, or 1040e552da7Schristoschild processes which are independent of the parent so that the parent exiting 1050e552da7Schristosdoes not affect it. 1060e552da7Schristos 1070e552da7Schristos.. rubric:: detach/main.c 1080e552da7Schristos.. literalinclude:: ../../code/detach/main.c 109*5f2f4271Schristos :language: c 1100e552da7Schristos :linenos: 1110e552da7Schristos :lines: 9-30 1120e552da7Schristos :emphasize-lines: 12,19 1130e552da7Schristos 1140e552da7SchristosJust remember that the handle is still monitoring the child, so your program 1150e552da7Schristoswon't exit. Use ``uv_unref()`` if you want to be more *fire-and-forget*. 1160e552da7Schristos 1170e552da7SchristosSending signals to processes 1180e552da7Schristos---------------------------- 1190e552da7Schristos 1200e552da7Schristoslibuv wraps the standard ``kill(2)`` system call on Unix and implements one 1210e552da7Schristoswith similar semantics on Windows, with *one caveat*: all of ``SIGTERM``, 1220e552da7Schristos``SIGINT`` and ``SIGKILL``, lead to termination of the process. The signature 1230e552da7Schristosof ``uv_kill`` is:: 1240e552da7Schristos 1250e552da7Schristos uv_err_t uv_kill(int pid, int signum); 1260e552da7Schristos 1270e552da7SchristosFor processes started using libuv, you may use ``uv_process_kill`` instead, 1280e552da7Schristoswhich accepts the ``uv_process_t`` watcher as the first argument, rather than 1290e552da7Schristosthe pid. In this case, **remember to call** ``uv_close`` on the watcher. 1300e552da7Schristos 1310e552da7SchristosSignals 1320e552da7Schristos------- 1330e552da7Schristos 1340e552da7Schristoslibuv provides wrappers around Unix signals with `some Windows support 1350e552da7Schristos<http://docs.libuv.org/en/v1.x/signal.html#signal>`_ as well. 1360e552da7Schristos 1370e552da7SchristosUse ``uv_signal_init()`` to initialize 1380e552da7Schristosa handle and associate it with a loop. To listen for particular signals on 1390e552da7Schristosthat handler, use ``uv_signal_start()`` with the handler function. Each handler 1400e552da7Schristoscan only be associated with one signal number, with subsequent calls to 1410e552da7Schristos``uv_signal_start()`` overwriting earlier associations. Use ``uv_signal_stop()`` to 1420e552da7Schristosstop watching. Here is a small example demonstrating the various possibilities: 1430e552da7Schristos 1440e552da7Schristos.. rubric:: signal/main.c 1450e552da7Schristos.. literalinclude:: ../../code/signal/main.c 146*5f2f4271Schristos :language: c 1470e552da7Schristos :linenos: 1480e552da7Schristos :emphasize-lines: 17-18,27-28 1490e552da7Schristos 1500e552da7Schristos.. NOTE:: 1510e552da7Schristos 1520e552da7Schristos ``uv_run(loop, UV_RUN_NOWAIT)`` is similar to ``uv_run(loop, UV_RUN_ONCE)`` 1530e552da7Schristos in that it will process only one event. UV_RUN_ONCE blocks if there are no 1540e552da7Schristos pending events, while UV_RUN_NOWAIT will return immediately. We use NOWAIT 1550e552da7Schristos so that one of the loops isn't starved because the other one has no pending 1560e552da7Schristos activity. 1570e552da7Schristos 1580e552da7SchristosSend ``SIGUSR1`` to the process, and you'll find the handler being invoked 1590e552da7Schristos4 times, one for each ``uv_signal_t``. The handler just stops each handle, 1600e552da7Schristosso that the program exits. This sort of dispatch to all handlers is very 1610e552da7Schristosuseful. A server using multiple event loops could ensure that all data was 1620e552da7Schristossafely saved before termination, simply by every loop adding a watcher for 1630e552da7Schristos``SIGINT``. 1640e552da7Schristos 1650e552da7SchristosChild Process I/O 1660e552da7Schristos----------------- 1670e552da7Schristos 1680e552da7SchristosA normal, newly spawned process has its own set of file descriptors, with 0, 1690e552da7Schristos1 and 2 being ``stdin``, ``stdout`` and ``stderr`` respectively. Sometimes you 1700e552da7Schristosmay want to share file descriptors with the child. For example, perhaps your 1710e552da7Schristosapplications launches a sub-command and you want any errors to go in the log 1720e552da7Schristosfile, but ignore ``stdout``. For this you'd like to have ``stderr`` of the 1730e552da7Schristoschild be the same as the stderr of the parent. In this case, libuv supports 1740e552da7Schristos*inheriting* file descriptors. In this sample, we invoke the test program, 1750e552da7Schristoswhich is: 1760e552da7Schristos 1770e552da7Schristos.. rubric:: proc-streams/test.c 1780e552da7Schristos.. literalinclude:: ../../code/proc-streams/test.c 179*5f2f4271Schristos :language: c 1800e552da7Schristos 1810e552da7SchristosThe actual program ``proc-streams`` runs this while sharing only ``stderr``. 1820e552da7SchristosThe file descriptors of the child process are set using the ``stdio`` field in 1830e552da7Schristos``uv_process_options_t``. First set the ``stdio_count`` field to the number of 1840e552da7Schristosfile descriptors being set. ``uv_process_options_t.stdio`` is an array of 1850e552da7Schristos``uv_stdio_container_t``, which is: 1860e552da7Schristos 1870e552da7Schristos.. code-block:: c 1880e552da7Schristos 1890e552da7Schristos typedef struct uv_stdio_container_s { 1900e552da7Schristos uv_stdio_flags flags; 1910e552da7Schristos 1920e552da7Schristos union { 1930e552da7Schristos uv_stream_t* stream; 1940e552da7Schristos int fd; 1950e552da7Schristos } data; 1960e552da7Schristos } uv_stdio_container_t; 1970e552da7Schristos 1980e552da7Schristoswhere flags can have several values. Use ``UV_IGNORE`` if it isn't going to be 1990e552da7Schristosused. If the first three ``stdio`` fields are marked as ``UV_IGNORE`` they'll 2000e552da7Schristosredirect to ``/dev/null``. 2010e552da7Schristos 2020e552da7SchristosSince we want to pass on an existing descriptor, we'll use ``UV_INHERIT_FD``. 2030e552da7SchristosThen we set the ``fd`` to ``stderr``. 2040e552da7Schristos 2050e552da7Schristos.. rubric:: proc-streams/main.c 2060e552da7Schristos.. literalinclude:: ../../code/proc-streams/main.c 207*5f2f4271Schristos :language: c 2080e552da7Schristos :linenos: 2090e552da7Schristos :lines: 15-17,27- 2100e552da7Schristos :emphasize-lines: 6,10,11,12 2110e552da7Schristos 2120e552da7SchristosIf you run ``proc-stream`` you'll see that only the line "This is stderr" will 2130e552da7Schristosbe displayed. Try marking ``stdout`` as being inherited and see the output. 2140e552da7Schristos 2150e552da7SchristosIt is dead simple to apply this redirection to streams. By setting ``flags`` 2160e552da7Schristosto ``UV_INHERIT_STREAM`` and setting ``data.stream`` to the stream in the 2170e552da7Schristosparent process, the child process can treat that stream as standard I/O. This 2180e552da7Schristoscan be used to implement something like CGI_. 2190e552da7Schristos 2200e552da7Schristos.. _CGI: https://en.wikipedia.org/wiki/Common_Gateway_Interface 2210e552da7Schristos 2220e552da7SchristosA sample CGI script/executable is: 2230e552da7Schristos 2240e552da7Schristos.. rubric:: cgi/tick.c 2250e552da7Schristos.. literalinclude:: ../../code/cgi/tick.c 226*5f2f4271Schristos :language: c 2270e552da7Schristos 2280e552da7SchristosThe CGI server combines the concepts from this chapter and :doc:`networking` so 2290e552da7Schristosthat every client is sent ten ticks after which that connection is closed. 2300e552da7Schristos 2310e552da7Schristos.. rubric:: cgi/main.c 2320e552da7Schristos.. literalinclude:: ../../code/cgi/main.c 233*5f2f4271Schristos :language: c 2340e552da7Schristos :linenos: 2350e552da7Schristos :lines: 49-63 2360e552da7Schristos :emphasize-lines: 10 2370e552da7Schristos 2380e552da7SchristosHere we simply accept the TCP connection and pass on the socket (*stream*) to 2390e552da7Schristos``invoke_cgi_script``. 2400e552da7Schristos 2410e552da7Schristos.. rubric:: cgi/main.c 2420e552da7Schristos.. literalinclude:: ../../code/cgi/main.c 243*5f2f4271Schristos :language: c 2440e552da7Schristos :linenos: 2450e552da7Schristos :lines: 16, 25-45 2460e552da7Schristos :emphasize-lines: 8-9,18,20 2470e552da7Schristos 2480e552da7SchristosThe ``stdout`` of the CGI script is set to the socket so that whatever our tick 2490e552da7Schristosscript prints, gets sent to the client. By using processes, we can offload the 2500e552da7Schristosread/write buffering to the operating system, so in terms of convenience this 2510e552da7Schristosis great. Just be warned that creating processes is a costly task. 2520e552da7Schristos 2530e552da7Schristos.. _pipes: 2540e552da7Schristos 2550e552da7SchristosParent-child IPC 2560e552da7Schristos---------------- 2570e552da7Schristos 2580e552da7SchristosA parent and child can have one or two way communication over a pipe created by 2590e552da7Schristossettings ``uv_stdio_container_t.flags`` to a bit-wise combination of 2600e552da7Schristos``UV_CREATE_PIPE`` and ``UV_READABLE_PIPE`` or ``UV_WRITABLE_PIPE``. The 2610e552da7Schristosread/write flag is from the perspective of the child process. In this case, 2620e552da7Schristosthe ``uv_stream_t* stream`` field must be set to point to an initialized, 2630e552da7Schristosunopened ``uv_pipe_t`` instance. 2640e552da7Schristos 2650e552da7SchristosNew stdio Pipes 2660e552da7Schristos+++++++++++++++ 2670e552da7Schristos 2680e552da7SchristosThe ``uv_pipe_t`` structure represents more than just `pipe(7)`_ (or ``|``), 2690e552da7Schristosbut supports any streaming file-like objects. On Windows, the only object of 2700e552da7Schristosthat description is the `Named Pipe`_. On Unix, this could be any of `Unix 2710e552da7SchristosDomain Socket`_, or derived from `mkfifo(1)`_, or it could actually be a 2720e552da7Schristos`pipe(7)`_. When ``uv_spawn`` initializes a ``uv_pipe_t`` due to the 2730e552da7Schristos`UV_CREATE_PIPE` flag, it opts for creating a `socketpair(2)`_. 2740e552da7Schristos 2750e552da7SchristosThis is intended for the purpose of allowing multiple libuv processes to 2760e552da7Schristoscommunicate with IPC. This is discussed below. 2770e552da7Schristos 278*5f2f4271Schristos.. _pipe(7): https://man7.org/linux/man-pages/man7/pipe.7.html 279*5f2f4271Schristos.. _mkfifo(1): https://man7.org/linux/man-pages/man1/mkfifo.1.html 280*5f2f4271Schristos.. _socketpair(2): https://man7.org/linux/man-pages/man2/socketpair.2.html 281*5f2f4271Schristos.. _Unix Domain Socket: https://man7.org/linux/man-pages/man7/unix.7.html 2820e552da7Schristos.. _Named Pipe: https://docs.microsoft.com/en-us/windows/win32/ipc/named-pipes 2830e552da7Schristos 2840e552da7Schristos 2850e552da7SchristosArbitrary process IPC 2860e552da7Schristos+++++++++++++++++++++ 2870e552da7Schristos 2880e552da7SchristosSince domain sockets [#]_ can have a well known name and a location in the 2890e552da7Schristosfile-system they can be used for IPC between unrelated processes. The D-BUS_ 2900e552da7Schristossystem used by open source desktop environments uses domain sockets for event 2910e552da7Schristosnotification. Various applications can then react when a contact comes online 2920e552da7Schristosor new hardware is detected. The MySQL server also runs a domain socket on 2930e552da7Schristoswhich clients can interact with it. 2940e552da7Schristos 2950e552da7Schristos.. _D-BUS: https://www.freedesktop.org/wiki/Software/dbus 2960e552da7Schristos 2970e552da7SchristosWhen using domain sockets, a client-server pattern is usually followed with the 2980e552da7Schristoscreator/owner of the socket acting as the server. After the initial setup, 2990e552da7Schristosmessaging is no different from TCP, so we'll re-use the echo server example. 3000e552da7Schristos 3010e552da7Schristos.. rubric:: pipe-echo-server/main.c 3020e552da7Schristos.. literalinclude:: ../../code/pipe-echo-server/main.c 303*5f2f4271Schristos :language: c 3040e552da7Schristos :linenos: 3050e552da7Schristos :lines: 70- 3060e552da7Schristos :emphasize-lines: 5,10,14 3070e552da7Schristos 3080e552da7SchristosWe name the socket ``echo.sock`` which means it will be created in the local 3090e552da7Schristosdirectory. This socket now behaves no different from TCP sockets as far as 3100e552da7Schristosthe stream API is concerned. You can test this server using `socat`_:: 3110e552da7Schristos 3120e552da7Schristos $ socat - /path/to/socket 3130e552da7Schristos 3140e552da7SchristosA client which wants to connect to a domain socket will use:: 3150e552da7Schristos 3160e552da7Schristos void uv_pipe_connect(uv_connect_t *req, uv_pipe_t *handle, const char *name, uv_connect_cb cb); 3170e552da7Schristos 3180e552da7Schristoswhere ``name`` will be ``echo.sock`` or similar. On Unix systems, ``name`` must 3190e552da7Schristospoint to a valid file (e.g. ``/tmp/echo.sock``). On Windows, ``name`` follows a 3200e552da7Schristos``\\?\pipe\echo.sock`` format. 3210e552da7Schristos 3220e552da7Schristos.. _socat: http://www.dest-unreach.org/socat/ 3230e552da7Schristos 3240e552da7SchristosSending file descriptors over pipes 3250e552da7Schristos+++++++++++++++++++++++++++++++++++ 3260e552da7Schristos 3270e552da7SchristosThe cool thing about domain sockets is that file descriptors can be exchanged 3280e552da7Schristosbetween processes by sending them over a domain socket. This allows processes 3290e552da7Schristosto hand off their I/O to other processes. Applications include load-balancing 3300e552da7Schristosservers, worker processes and other ways to make optimum use of CPU. libuv only 3310e552da7Schristossupports sending **TCP sockets or other pipes** over pipes for now. 3320e552da7Schristos 3330e552da7SchristosTo demonstrate, we will look at a echo server implementation that hands of 3340e552da7Schristosclients to worker processes in a round-robin fashion. This program is a bit 3350e552da7Schristosinvolved, and while only snippets are included in the book, it is recommended 3360e552da7Schristosto read the full code to really understand it. 3370e552da7Schristos 3380e552da7SchristosThe worker process is quite simple, since the file-descriptor is handed over to 3390e552da7Schristosit by the master. 3400e552da7Schristos 3410e552da7Schristos.. rubric:: multi-echo-server/worker.c 3420e552da7Schristos.. literalinclude:: ../../code/multi-echo-server/worker.c 343*5f2f4271Schristos :language: c 3440e552da7Schristos :linenos: 3450e552da7Schristos :lines: 7-9,81- 3460e552da7Schristos :emphasize-lines: 6-8 3470e552da7Schristos 3480e552da7Schristos``queue`` is the pipe connected to the master process on the other end, along 3490e552da7Schristoswhich new file descriptors get sent. It is important to set the ``ipc`` 3500e552da7Schristosargument of ``uv_pipe_init`` to 1 to indicate this pipe will be used for 3510e552da7Schristosinter-process communication! Since the master will write the file handle to the 3520e552da7Schristosstandard input of the worker, we connect the pipe to ``stdin`` using 3530e552da7Schristos``uv_pipe_open``. 3540e552da7Schristos 3550e552da7Schristos.. rubric:: multi-echo-server/worker.c 3560e552da7Schristos.. literalinclude:: ../../code/multi-echo-server/worker.c 357*5f2f4271Schristos :language: c 3580e552da7Schristos :linenos: 3590e552da7Schristos :lines: 51-79 3600e552da7Schristos :emphasize-lines: 10,15,20 3610e552da7Schristos 3620e552da7SchristosFirst we call ``uv_pipe_pending_count()`` to ensure that a handle is available 3630e552da7Schristosto read out. If your program could deal with different types of handles, 3640e552da7Schristos``uv_pipe_pending_type()`` can be used to determine the type. 3650e552da7SchristosAlthough ``accept`` seems odd in this code, it actually makes sense. What 3660e552da7Schristos``accept`` traditionally does is get a file descriptor (the client) from 3670e552da7Schristosanother file descriptor (The listening socket). Which is exactly what we do 3680e552da7Schristoshere. Fetch the file descriptor (``client``) from ``queue``. From this point 3690e552da7Schristosthe worker does standard echo server stuff. 3700e552da7Schristos 3710e552da7SchristosTurning now to the master, let's take a look at how the workers are launched to 3720e552da7Schristosallow load balancing. 3730e552da7Schristos 3740e552da7Schristos.. rubric:: multi-echo-server/main.c 3750e552da7Schristos.. literalinclude:: ../../code/multi-echo-server/main.c 376*5f2f4271Schristos :language: c 3770e552da7Schristos :linenos: 3780e552da7Schristos :lines: 9-13 3790e552da7Schristos 3800e552da7SchristosThe ``child_worker`` structure wraps the process, and the pipe between the 3810e552da7Schristosmaster and the individual process. 3820e552da7Schristos 3830e552da7Schristos.. rubric:: multi-echo-server/main.c 3840e552da7Schristos.. literalinclude:: ../../code/multi-echo-server/main.c 385*5f2f4271Schristos :language: c 3860e552da7Schristos :linenos: 3870e552da7Schristos :lines: 51,61-95 3880e552da7Schristos :emphasize-lines: 17,20-21 3890e552da7Schristos 3900e552da7SchristosIn setting up the workers, we use the nifty libuv function ``uv_cpu_info`` to 3910e552da7Schristosget the number of CPUs so we can launch an equal number of workers. Again it is 3920e552da7Schristosimportant to initialize the pipe acting as the IPC channel with the third 3930e552da7Schristosargument as 1. We then indicate that the child process' ``stdin`` is to be 3940e552da7Schristosa readable pipe (from the point of view of the child). Everything is 3950e552da7Schristosstraightforward till here. The workers are launched and waiting for file 3960e552da7Schristosdescriptors to be written to their standard input. 3970e552da7Schristos 3980e552da7SchristosIt is in ``on_new_connection`` (the TCP infrastructure is initialized in 3990e552da7Schristos``main()``), that we accept the client socket and pass it along to the next 4000e552da7Schristosworker in the round-robin. 4010e552da7Schristos 4020e552da7Schristos.. rubric:: multi-echo-server/main.c 4030e552da7Schristos.. literalinclude:: ../../code/multi-echo-server/main.c 404*5f2f4271Schristos :language: c 4050e552da7Schristos :linenos: 4060e552da7Schristos :lines: 31-49 4070e552da7Schristos :emphasize-lines: 9,12-13 4080e552da7Schristos 4090e552da7SchristosThe ``uv_write2`` call handles all the abstraction and it is simply a matter of 4100e552da7Schristospassing in the handle (``client``) as the right argument. With this our 4110e552da7Schristosmulti-process echo server is operational. 4120e552da7Schristos 4130e552da7SchristosThanks to Kyle for `pointing out`_ that ``uv_write2()`` requires a non-empty 4140e552da7Schristosbuffer even when sending handles. 4150e552da7Schristos 4160e552da7Schristos.. _pointing out: https://github.com/nikhilm/uvbook/issues/56 4170e552da7Schristos 4180e552da7Schristos---- 4190e552da7Schristos 4200e552da7Schristos.. [#] In this section domain sockets stands in for named pipes on Windows as 4210e552da7Schristos well. 422