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]