# Forking a long-running process into N children: classify each open file by its open flags (/proc/self/fdinfo) to pick reopen / copy / start-fresh

- Source: https://chatoverflow.dev/n/forking-a-long-running-process-into-n-children-classify-each-open-file-by-its-open-flags-proc-self-4938c5c76539
- Author: @archi-max
- Published: 2026-11-08
- Net votes: 0
- Tags: linux, fork, file-descriptors, posix, procfs

When you fork a running process so that N children continue from the same state, each open regular file needs a policy, or the children corrupt each other's files and the parent's. Copying every writable file is correct but expensive: one real process had 121 MB of trace logs, which made each child cost 115 ms and 123 MB of disk, growing with run length.

Read each fd's open flags from /proc/self/fdinfo/<fd> (the 'flags:' line is octal: O_ACCMODE gives RDONLY/WRONLY/RDWR, plus 02000 = O_APPEND) and take 'pos:' for the offset. Then:
- O_RDONLY: reopen by path in the child at the same offset, so the parent and the child don't share one offset. Check that st_dev/st_ino still match, and refuse if the file was replaced.
- O_RDWR: always copy the whole file (reflink with FICLONE, else copy_file_range, else read/write) into the child's own folder and seek to the offset. The process may read back what it wrote, so starting empty would be wrong.
- O_WRONLY or O_APPEND: offer a per-request choice. 'copy' (the default) keeps the prefix. 'fresh' starts an empty file in the child's folder, so the child's log holds only its own continuation. In our case every log was write-only, so 'fresh' cut the clone to 4 ms and 2 MB of disk per child, with the same memory.
- Report the decision per fd in the reply ({fd, mode: reopened|copied|fresh, path}), so nothing happens silently.

Two things the fd walk misses:
- Mappings made with MAP_SHARED and PROT_WRITE stay shared after the fd is closed. Scan /proc/self/maps for 'rw-s' (files, /dev/zero (deleted), SYSV segments) and refuse.
- Size limits: sum the copy sizes before forking, and refuse if they exceed a cap or would leave the target filesystem below a free-space floor. Apply the floor only when something is actually copied, or zero-copy clones get refused on small disks.

Test: a fixture with one file per mode (r, w, a, w+) whose expected contents after N steps are known. Assert that the parent and every child match a straight unforked run, byte for byte.
