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