Mercurial
comparison third_party/libuv/docs/src/guide/filesystem.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 Filesystem | |
| 2 ========== | |
| 3 | |
| 4 Simple filesystem read/write is achieved using the ``uv_fs_*`` functions and the | |
| 5 ``uv_fs_t`` struct. | |
| 6 | |
| 7 .. note:: | |
| 8 | |
| 9 The libuv filesystem operations are different from :doc:`socket operations | |
| 10 <networking>`. Socket operations use the non-blocking operations provided | |
| 11 by the operating system. Filesystem operations use blocking functions | |
| 12 internally, but invoke these functions in a `thread pool`_ and notify | |
| 13 watchers registered with the event loop when application interaction is | |
| 14 required. | |
| 15 | |
| 16 .. _thread pool: https://docs.libuv.org/en/v1.x/threadpool.html#thread-pool-work-scheduling | |
| 17 | |
| 18 All filesystem functions have two forms - *synchronous* and *asynchronous*. | |
| 19 | |
| 20 The *synchronous* forms automatically get called (and **block**) if the | |
| 21 callback is null. The return value of functions is a :ref:`libuv error code | |
| 22 <libuv-error-handling>`. This is usually only useful for synchronous calls. | |
| 23 The *asynchronous* form is called when a callback is passed and the return | |
| 24 value is 0. | |
| 25 | |
| 26 Reading/Writing files | |
| 27 --------------------- | |
| 28 | |
| 29 A file descriptor is obtained using | |
| 30 | |
| 31 .. code-block:: c | |
| 32 | |
| 33 int uv_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, int mode, uv_fs_cb cb) | |
| 34 | |
| 35 ``flags`` and ``mode`` are standard | |
| 36 `Unix flags <https://man7.org/linux/man-pages/man2/open.2.html>`_. | |
| 37 libuv takes care of converting to the appropriate Windows flags. | |
| 38 | |
| 39 File descriptors are closed using | |
| 40 | |
| 41 .. code-block:: c | |
| 42 | |
| 43 int uv_fs_close(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) | |
| 44 | |
| 45 | |
| 46 Filesystem operation callbacks have the signature: | |
| 47 | |
| 48 .. code-block:: c | |
| 49 | |
| 50 void callback(uv_fs_t* req); | |
| 51 | |
| 52 Let's see a simple implementation of ``cat``. We start with registering | |
| 53 a callback for when the file is opened: | |
| 54 | |
| 55 .. rubric:: uvcat/main.c - opening a file | |
| 56 .. literalinclude:: ../../code/uvcat/main.c | |
| 57 :language: c | |
| 58 :linenos: | |
| 59 :lines: 41-53 | |
| 60 :emphasize-lines: 4, 6-7 | |
| 61 | |
| 62 The ``result`` field of a ``uv_fs_t`` is the file descriptor in case of the | |
| 63 ``uv_fs_open`` callback. If the file is successfully opened, we start reading it. | |
| 64 | |
| 65 .. rubric:: uvcat/main.c - read callback | |
| 66 .. literalinclude:: ../../code/uvcat/main.c | |
| 67 :language: c | |
| 68 :linenos: | |
| 69 :lines: 26-39 | |
| 70 :emphasize-lines: 2,8,12 | |
| 71 | |
| 72 In the case of a read call, you should pass an *initialized* buffer which will | |
| 73 be filled with data before the read callback is triggered. The ``uv_fs_*`` | |
| 74 operations map almost directly to certain POSIX functions, so EOF is indicated | |
| 75 in this case by ``result`` being 0. In the case of streams or pipes, the | |
| 76 ``UV_EOF`` constant would have been passed as a status instead. | |
| 77 | |
| 78 Here you see a common pattern when writing asynchronous programs. The | |
| 79 ``uv_fs_close()`` call is performed synchronously. *Usually tasks which are | |
| 80 one-off, or are done as part of the startup or shutdown stage are performed | |
| 81 synchronously, since we are interested in fast I/O when the program is going | |
| 82 about its primary task and dealing with multiple I/O sources*. For solo tasks | |
| 83 the performance difference usually is negligible and may lead to simpler code. | |
| 84 | |
| 85 Filesystem writing is similarly simple using ``uv_fs_write()``. *Your callback | |
| 86 will be triggered after the write is complete*. In our case the callback | |
| 87 simply drives the next read. Thus read and write proceed in lockstep via | |
| 88 callbacks. | |
| 89 | |
| 90 .. rubric:: uvcat/main.c - write callback | |
| 91 .. literalinclude:: ../../code/uvcat/main.c | |
| 92 :language: c | |
| 93 :linenos: | |
| 94 :lines: 17-24 | |
| 95 :emphasize-lines: 6 | |
| 96 | |
| 97 .. warning:: | |
| 98 | |
| 99 Due to the way filesystems and disk drives are configured for performance, | |
| 100 a write that 'succeeds' may not be committed to disk yet. | |
| 101 | |
| 102 We set the dominos rolling in ``main()``: | |
| 103 | |
| 104 .. rubric:: uvcat/main.c | |
| 105 .. literalinclude:: ../../code/uvcat/main.c | |
| 106 :language: c | |
| 107 :linenos: | |
| 108 :lines: 55- | |
| 109 :emphasize-lines: 2 | |
| 110 | |
| 111 .. warning:: | |
| 112 | |
| 113 The ``uv_fs_req_cleanup()`` function must always be called on filesystem | |
| 114 requests to free internal memory allocations in libuv. | |
| 115 | |
| 116 Filesystem operations | |
| 117 --------------------- | |
| 118 | |
| 119 All the standard filesystem operations like ``unlink``, ``rmdir``, ``stat`` are | |
| 120 supported asynchronously and have intuitive argument order. They follow the | |
| 121 same patterns as the read/write/open calls, returning the result in the | |
| 122 ``uv_fs_t.result`` field. The full list: | |
| 123 | |
| 124 .. rubric:: Filesystem operations | |
| 125 .. code-block:: c | |
| 126 | |
| 127 int uv_fs_close(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb); | |
| 128 int uv_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, int mode, uv_fs_cb cb); | |
| 129 int uv_fs_read(uv_loop_t* loop, uv_fs_t* req, uv_file file, const uv_buf_t bufs[], unsigned int nbufs, int64_t offset, uv_fs_cb cb); | |
| 130 int uv_fs_unlink(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb); | |
| 131 int uv_fs_write(uv_loop_t* loop, uv_fs_t* req, uv_file file, const uv_buf_t bufs[], unsigned int nbufs, int64_t offset, uv_fs_cb cb); | |
| 132 int uv_fs_copyfile(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, int flags, uv_fs_cb cb); | |
| 133 int uv_fs_mkdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, uv_fs_cb cb); | |
| 134 int uv_fs_mkdtemp(uv_loop_t* loop, uv_fs_t* req, const char* tpl, uv_fs_cb cb); | |
| 135 int uv_fs_mkstemp(uv_loop_t* loop, uv_fs_t* req, const char* tpl, uv_fs_cb cb); | |
| 136 int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb); | |
| 137 int uv_fs_scandir(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, uv_fs_cb cb); | |
| 138 int uv_fs_scandir_next(uv_fs_t* req, uv_dirent_t* ent); | |
| 139 int uv_fs_opendir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb); | |
| 140 int uv_fs_readdir(uv_loop_t* loop, uv_fs_t* req, uv_dir_t* dir, uv_fs_cb cb); | |
| 141 int uv_fs_closedir(uv_loop_t* loop, uv_fs_t* req, uv_dir_t* dir, uv_fs_cb cb); | |
| 142 int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb); | |
| 143 int uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb); | |
| 144 int uv_fs_rename(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, uv_fs_cb cb); | |
| 145 int uv_fs_fsync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb); | |
| 146 int uv_fs_fdatasync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb); | |
| 147 int uv_fs_ftruncate(uv_loop_t* loop, uv_fs_t* req, uv_file file, int64_t offset, uv_fs_cb cb); | |
| 148 int uv_fs_sendfile(uv_loop_t* loop, uv_fs_t* req, uv_file out_fd, uv_file in_fd, int64_t in_offset, size_t length, uv_fs_cb cb); | |
| 149 int uv_fs_access(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, uv_fs_cb cb); | |
| 150 int uv_fs_chmod(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, uv_fs_cb cb); | |
| 151 int uv_fs_utime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, double mtime, uv_fs_cb cb); | |
| 152 int uv_fs_futime(uv_loop_t* loop, uv_fs_t* req, uv_file file, double atime, double mtime, uv_fs_cb cb); | |
| 153 int uv_fs_lutime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, double mtime, uv_fs_cb cb); | |
| 154 int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb); | |
| 155 int uv_fs_link(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, uv_fs_cb cb); | |
| 156 int uv_fs_symlink(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, int flags, uv_fs_cb cb); | |
| 157 int uv_fs_readlink(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb); | |
| 158 int uv_fs_realpath(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb); | |
| 159 int uv_fs_fchmod(uv_loop_t* loop, uv_fs_t* req, uv_file file, int mode, uv_fs_cb cb); | |
| 160 int uv_fs_chown(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, uv_gid_t gid, uv_fs_cb cb); | |
| 161 int uv_fs_fchown(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_uid_t uid, uv_gid_t gid, uv_fs_cb cb); | |
| 162 int uv_fs_lchown(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, uv_gid_t gid, uv_fs_cb cb); | |
| 163 int uv_fs_statfs(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb); | |
| 164 | |
| 165 | |
| 166 .. _buffers-and-streams: | |
| 167 | |
| 168 Buffers and Streams | |
| 169 ------------------- | |
| 170 | |
| 171 The basic I/O handle in libuv is the stream (``uv_stream_t``). TCP sockets, UDP | |
| 172 sockets, and pipes for file I/O and IPC are all treated as stream subclasses. | |
| 173 | |
| 174 Streams are initialized using custom functions for each subclass, then operated | |
| 175 upon using | |
| 176 | |
| 177 .. code-block:: c | |
| 178 | |
| 179 int uv_read_start(uv_stream_t*, uv_alloc_cb alloc_cb, uv_read_cb read_cb); | |
| 180 int uv_read_stop(uv_stream_t*); | |
| 181 int uv_write(uv_write_t* req, uv_stream_t* handle, | |
| 182 const uv_buf_t bufs[], unsigned int nbufs, uv_write_cb cb); | |
| 183 | |
| 184 The stream based functions are simpler to use than the filesystem ones and | |
| 185 libuv will automatically keep reading from a stream when ``uv_read_start()`` is | |
| 186 called once, until ``uv_read_stop()`` is called. | |
| 187 | |
| 188 The discrete unit of data is the buffer -- ``uv_buf_t``. This is simply | |
| 189 a collection of a pointer to bytes (``uv_buf_t.base``) and the length | |
| 190 (``uv_buf_t.len``). The ``uv_buf_t`` is lightweight and passed around by value. | |
| 191 What does require management is the actual bytes, which have to be allocated | |
| 192 and freed by the application. | |
| 193 | |
| 194 .. ERROR:: | |
| 195 | |
| 196 **THIS PROGRAM DOES NOT ALWAYS WORK, NEED SOMETHING BETTER** | |
| 197 | |
| 198 To demonstrate streams we will need to use ``uv_pipe_t``. This allows streaming | |
| 199 local files [#]_. Here is a simple tee utility using libuv. Doing all operations | |
| 200 asynchronously shows the power of evented I/O. The two writes won't block each | |
| 201 other, but we have to be careful to copy over the buffer data to ensure we don't | |
| 202 free a buffer until it has been written. | |
| 203 | |
| 204 The program is to be executed as:: | |
| 205 | |
| 206 ./uvtee <output_file> | |
| 207 | |
| 208 We start off opening pipes on the files we require. libuv pipes to a file are | |
| 209 opened as bidirectional by default. | |
| 210 | |
| 211 .. rubric:: uvtee/main.c - read on pipes | |
| 212 .. literalinclude:: ../../code/uvtee/main.c | |
| 213 :language: c | |
| 214 :linenos: | |
| 215 :lines: 62-80 | |
| 216 :emphasize-lines: 4,5,15 | |
| 217 | |
| 218 The third argument of ``uv_pipe_init()`` should be set to 1 for IPC using named | |
| 219 pipes. This is covered in :doc:`processes`. The ``uv_pipe_open()`` call | |
| 220 associates the pipe with the file descriptor, in this case ``0`` (standard | |
| 221 input). | |
| 222 | |
| 223 We start monitoring ``stdin``. The ``alloc_buffer`` callback is invoked as new | |
| 224 buffers are required to hold incoming data. ``read_stdin`` will be called with | |
| 225 these buffers. | |
| 226 | |
| 227 .. rubric:: uvtee/main.c - reading buffers | |
| 228 .. literalinclude:: ../../code/uvtee/main.c | |
| 229 :language: c | |
| 230 :linenos: | |
| 231 :lines: 19-22,44-60 | |
| 232 | |
| 233 The standard ``malloc`` is sufficient here, but you can use any memory allocation | |
| 234 scheme. For example, node.js uses its own slab allocator which associates | |
| 235 buffers with V8 objects. | |
| 236 | |
| 237 The read callback ``nread`` parameter is less than 0 on any error. This error | |
| 238 might be EOF, in which case we close all the streams, using the generic close | |
| 239 function ``uv_close()`` which deals with the handle based on its internal type. | |
| 240 Otherwise ``nread`` is a non-negative number and we can attempt to write that | |
| 241 many bytes to the output streams. Finally remember that buffer allocation and | |
| 242 deallocation is application responsibility, so we free the data. | |
| 243 | |
| 244 The allocation callback may return a buffer with length zero if it fails to | |
| 245 allocate memory. In this case, the read callback is invoked with error | |
| 246 UV_ENOBUFS. libuv will continue to attempt to read the stream though, so you | |
| 247 must explicitly call ``uv_close()`` if you want to stop when allocation fails. | |
| 248 | |
| 249 The read callback may be called with ``nread = 0``, indicating that at this | |
| 250 point there is nothing to be read. Most applications will just ignore this. | |
| 251 | |
| 252 .. rubric:: uvtee/main.c - Write to pipe | |
| 253 .. literalinclude:: ../../code/uvtee/main.c | |
| 254 :language: c | |
| 255 :linenos: | |
| 256 :lines: 9-13,23-42 | |
| 257 | |
| 258 ``write_data()`` makes a copy of the buffer obtained from read. This buffer | |
| 259 does not get passed through to the write callback trigged on write completion. To | |
| 260 get around this we wrap a write request and a buffer in ``write_req_t`` and | |
| 261 unwrap it in the callbacks. We make a copy so we can free the two buffers from | |
| 262 the two calls to ``write_data`` independently of each other. While acceptable | |
| 263 for a demo program like this, you'll probably want smarter memory management, | |
| 264 like reference counted buffers or a pool of buffers in any major application. | |
| 265 | |
| 266 .. WARNING:: | |
| 267 | |
| 268 If your program is meant to be used with other programs it may knowingly or | |
| 269 unknowingly be writing to a pipe. This makes it susceptible to `aborting on | |
| 270 receiving a SIGPIPE`_. It is a good idea to insert:: | |
| 271 | |
| 272 signal(SIGPIPE, SIG_IGN) | |
| 273 | |
| 274 in the initialization stages of your application. | |
| 275 | |
| 276 .. _aborting on receiving a SIGPIPE: http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#The_special_problem_of_SIGPIPE | |
| 277 | |
| 278 File change events | |
| 279 ------------------ | |
| 280 | |
| 281 All modern operating systems provide APIs to put watches on individual files or | |
| 282 directories and be informed when the files are modified. libuv wraps common | |
| 283 file change notification libraries [#fsnotify]_. This is one of the more | |
| 284 inconsistent parts of libuv. File change notification systems are themselves | |
| 285 extremely varied across platforms so getting everything working everywhere is | |
| 286 difficult. To demonstrate, I'm going to build a simple utility which runs | |
| 287 a command whenever any of the watched files change:: | |
| 288 | |
| 289 ./onchange <command> <file1> [file2] ... | |
| 290 | |
| 291 .. note:: | |
| 292 | |
| 293 Currently this example only works on OSX and Windows. | |
| 294 Refer to the `notes of uv_fs_event_start`_ function. | |
| 295 | |
| 296 .. _notes of uv_fs_event_start: https://docs.libuv.org/en/v1.x/fs_event.html#c.uv_fs_event_start | |
| 297 | |
| 298 The file change notification is started using ``uv_fs_event_init()``: | |
| 299 | |
| 300 .. rubric:: onchange/main.c - The setup | |
| 301 .. literalinclude:: ../../code/onchange/main.c | |
| 302 :language: c | |
| 303 :linenos: | |
| 304 :lines: 26- | |
| 305 :emphasize-lines: 15 | |
| 306 | |
| 307 The third argument is the actual file or directory to monitor. The last | |
| 308 argument, ``flags``, can be: | |
| 309 | |
| 310 .. code-block:: c | |
| 311 | |
| 312 /* | |
| 313 * Flags to be passed to uv_fs_event_start(). | |
| 314 */ | |
| 315 enum uv_fs_event_flags { | |
| 316 UV_FS_EVENT_WATCH_ENTRY = 1, | |
| 317 UV_FS_EVENT_STAT = 2, | |
| 318 UV_FS_EVENT_RECURSIVE = 4 | |
| 319 }; | |
| 320 | |
| 321 ``UV_FS_EVENT_WATCH_ENTRY`` and ``UV_FS_EVENT_STAT`` don't do anything (yet). | |
| 322 ``UV_FS_EVENT_RECURSIVE`` will start watching subdirectories as well on | |
| 323 supported platforms. | |
| 324 | |
| 325 The callback will receive the following arguments: | |
| 326 | |
| 327 #. ``uv_fs_event_t *handle`` - The handle. The ``path`` field of the handle | |
| 328 is the file on which the watch was set. | |
| 329 #. ``const char *filename`` - If a directory is being monitored, this is the | |
| 330 file which was changed. Only non-``null`` on Linux and Windows. May be ``null`` | |
| 331 even on those platforms. | |
| 332 #. ``int events`` - one of ``UV_RENAME`` or ``UV_CHANGE``, or a bitwise OR of | |
| 333 both. | |
| 334 #. ``int status`` - If ``status < 0``, there is an :ref:`libuv error<libuv-error-handling>`. | |
| 335 | |
| 336 In our example we simply print the arguments and run the command using | |
| 337 ``system()``. | |
| 338 | |
| 339 .. rubric:: onchange/main.c - file change notification callback | |
| 340 .. literalinclude:: ../../code/onchange/main.c | |
| 341 :language: c | |
| 342 :linenos: | |
| 343 :lines: 9-24 | |
| 344 | |
| 345 ---- | |
| 346 | |
| 347 .. [#fsnotify] inotify on Linux, FSEvents on Darwin, kqueue on BSDs, | |
| 348 ReadDirectoryChangesW on Windows, event ports on Solaris, unsupported on Cygwin | |
| 349 .. [#] see :ref:`pipes` |