jingfei195887 commented on issue #20414:
URL: https://github.com/apache/nuttx/issues/20414#issuecomment-6083306753

   I got the same result on ARM stm32f103-minimum:nsh with a 34% system image 
increase. Thank you @acassis  for your analysis breakdown. I share similar 
conclusions and have investigated the "directories opened through the file 
layer (~2.1 KB)" item.
   
   ## About the **"directories opened through the file layer (~2.1 KB)"** item
   
   here is the mechanism behind the growth, and a concrete proposal to let 
size-constrained configs opt back out. Measured on the final linked image, 
`stm32f103-minimum:nsh`, both sides built with the same arm-none-eabi-gcc 
10.3.1.
   
   ### The architectural change
   
   **2016 (7.19)** — directories were a self-contained leaf subsystem under 
`fs/dirent/`. `opendir()` was roughly:
   
   ```c
   dir   = kumm_zalloc(sizeof(struct fs_dirent_s));
   inode = inode_search(&path, ...);
   /* populate dir from inode, return */
   ```
   
   No file descriptor, no `struct file`, no VFS open/read/close path. 
`readdir/closedir/seekdir/rewinddir` were standalone functions operating 
directly on `fs_dirent_s`.
   
   **Current** — a directory is a first-class file descriptor. `opendir()` now 
lives in libc:
   
   ```c
   dir     = lib_malloc(sizeof(*dir));   /* DIR is basically { int fd } */
   fd      = open(path, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
   dir->fd = fd;
   ```
   
   `readdir()` → `read(fd)` → `file_readv` → `dir_read()`; `closedir()` → 
`close(fd)`. A new `fs/vfs/fs_dir.c` bolts directories onto the file layer 
through a synthetic `g_dir_inode` + `g_dir_fileops`, with 
`dir_open/close/read/seek/ioctl` and `dir_allocate`.
   
   ### Where the ~2 KB actually goes (the non-obvious part)
   
   The visible directory symbols actually **shrank**: 
`opendir`+`readdir`+`closedir` went **530 B → 170 B**. So the growth is *not* 
in the dir calls themselves. It is in the machinery the dir path now **forces** 
to be linked:
   
   - `fs_dir.c` glue itself: ~790 B `.text` + 40 B RAM (`g_dir_inode`)
   - the generic `open()` / fd-allocation path, the `struct file` lifecycle, 
the vectored `file_readv` read path, and `fd_path` construction 
(`inode_getpath` + asprintf + a PATH_MAX scratch buffer)
   
   In a configuration that also opens regular files, most of this machinery is 
shared and effectively free. In a directory-centric minimal image it is paid 
for by directories — which is why this shows up as a standalone item.
   
   ### Why it is deliberate, not a regression
   
   Making directories real descriptors is exactly what provides `dirfd()`, 
`fdopendir()`, `O_DIRECTORY`, `fchdir()`, and `/proc/self/fd` consistency. A 
wholesale revert to the 2016 leaf implementation would drop those POSIX 
behaviours, so the bulk of this item is the cost of a conformance feature, not 
fat to trim.
   
   
   ### Proposal: an opt-out CONFIG for a lightweight DIR implementation
   
   Since the bulk of the ~2.1 KB is the *descriptor* machinery rather than the 
directory logic, the natural lever is to let size-constrained builds opt back 
out of it with a Kconfig — e.g. `CONFIG_FS_DIRENT_LITE` (name TBD):
   
   - **When disabled (default):** current behaviour — directories are 
first-class fds, full POSIX conformance.
   - **When enabled:** `opendir()` resolves the inode and allocates a 
`fs_dirent_s` directly (the 2016 leaf model), and 
`readdir/closedir/seekdir/rewinddir` operate on that struct instead of going 
through `open()`/`read(fd)`/`close(fd)`. No synthetic `g_dir_inode`, no forced 
`file_readv`/fd-lifecycle pull-in, no `fd_path` construction.
   
   What this reclaims and what it costs:
   
   - **Reclaims:** most of the `fs_dir.c` glue plus the dir-only portion of the 
forced file-layer path — i.e. the bulk of this item, for configs that only need 
`opendir/readdir/closedir`.
   - **Costs:** the descriptor-based POSIX behaviours that motivated the 
current design — `dirfd()`, `fdopendir()`, `O_DIRECTORY` returning a usable dir 
fd, `fchdir()` on a dir fd, and dir fds appearing in `/proc/self/fd`. These 
would compile out under the lite path.
   
   Mountpoint filesystems are unaffected either way: both the legacy and 
current paths dispatch directory I/O through 
`inode->u.i_mops->{opendir,readdir,...}`, so no per-filesystem changes are 
needed — the opt-out is confined to the VFS dir layer.
   
   The open question for maintainers is whether carrying two dir 
implementations behind a CONFIG is acceptable maintenance surface for the size 
win. If it is, I can prototype `CONFIG_FS_DIRENT_LITE`, measure the exact 
linked-image delta on `stm32f103-minimum:nsh`, and run 
`opendir/readdir/dup(dirfd)` on `sim:nsh` to confirm the conformance boundary 
behaves as documented.
   
   **Bottom line:** the ~2.1 KB is overwhelmingly the price of 
directories-as-descriptors (a POSIX feature), not waste. It is not meaningfully 
reclaimable by local cleanups; the only lever that recovers most of it is an 
explicit opt-out CONFIG that trades the dir-fd POSIX behaviours for size — 
which is why it belongs behind a Kconfig rather than as an unconditional change.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to