Hello, Greg

Thank you for your comments, it is true that ARM had fork(), however this
was not the real fork():

arch/arm/src/common/gnu/fork.S and arm_fork.c are the entry point. They
take a snapshot of the registers of the caller, allocate the child, copy
the used part of the stack, and start the child.

However, in the generic layer, libs/libc/unistd/lib_fork.c: fork() calls
up_fork(). vfork() calls the same up_fork(), and then calls
waitpid(). sched/task/task_fork.c: nxtask_setup_fork() calls
addrenv_join(parent, child). That is the same call that pthread_create()
uses. The child joins the address environment of the parent.
up_create_stack() then gives the child its own stack.

This is not fork(), and also not vfork(). It is
vfork()-with-a-private-stack. Not POSIX.

The failure is silent, programs that calls POSIX fork() compiled and ran,
and the writes of the child went into the variables of the parent. #19562
changed only the generic layer. up_fork() now takes a bool that says which
primitive the caller used, and nxtask_setup_fork() uses it to choose
between addrenv_join() and a new addrenv_fork(). arm_fork.c does the same
work as before and receives the flag.

I agree that the documentation was inconsistent, and I would like to fix
it. Documentation/guides/fork_vfork_migration.rst is new in that patch and
describes the two primitives. If you tell me which page you read, I will
correct it.

Two follow-up patches are open, one for each architecture that restores
POSIX fork(): #19772 for Xtensa, tested on real hardware, and #19773 for
ARM64, a draft because I have only tested it under QEMU. ARM 32-bit is next.

Please tell me if I have misread anything.

Best Regards,
Marco Casaroli


On Mon, Aug 10, 2026 at 4:25 PM Gregory Nutt <[email protected]> wrote:

> There are ARM-specific implementations of fork.  But I don't know a lot
> about these:
>
> arch/arm/src/common/Make.defs:CMN_ASRCS += fork.S
> arch/arm/src/common/gnu/fork.S
> arch/arm/src/common/iar/fork.S
> arch/arm/src/common/arm_fork.c
> Arch/arm/src/common/arm_fork.h
>
> I think some of documentation is incorrect.  There is architecture
> independent description here:
>
> Include/nuttx/sched.h
> libs/libc/unistd/lib_fork.c:
> Sched/task/task_fork.c
>
> ________________________________
> From: Marco C. <[email protected]>
> Sent: Monday, August 10, 2026 5:01 AM
> To: [email protected] <[email protected]>
> Subject: Re: Please test vfork(). PR #19562 divides fork() and vfork().
>
> Hello Nathan,
>
> Architectures with no MMU cannot implement fork(). So they will only have
> vfork().
>
> The PR descriptions guides through what to do if you use fork() today:
>
> to run a program -> use vfork() + exec*() or better yet posix_spawn()
> second flow that share the caller memory -> pthread_create()
> real independent copy of the process (needs MMU) -> wait for the follow up
> PRs
>
> Let me know if you have any questions.
>
> Marco Casaroli
>
>
> On Mon, Aug 10, 2026 at 1:58 PM Nathan Hartman <[email protected]>
> wrote:
>
> > What should architectures that don't have a MMU do?
> >
> > On Mon, Aug 10, 2026 at 7:42 AM Marco C. <[email protected]>
> wrote:
> >
> > > Hello,
> > >
> > > https://github.com/apache/nuttx/pull/19562
> > >
> > > Currently, in NuttX, fork() and vfork() do the same thing. The child
> > shares
> > > .data, .bss and the heap with the parent. The child also runs at the
> same
> > > time as the parent. This is the behaviour of vfork(), not of fork().
> > >
> > > A program that uses POSIX fork() compiles and runs. But the child
> writes
> > > into the variables of the parent. There is no error message.
> > >
> > > This pull request gives each function its own behaviour. It is a
> breaking
> > > change. It removes fork() from all architectures. unistd.h no longer
> > > declares fork(), so a call to fork() now fails to build. Each
> > architecture
> > > can add fork() again later, with the correct behaviour (which requires
> > > MMU). This is step 1 of the plan in issue #19540.
> > >
> > > vfork() keeps its behaviour. These architectures now have vfork(): ARM
> > > 32-bit, ARM64, RISC-V and x86_64 in all build modes; SIM, but not on
> > > Windows; MIPS32. The kernel builds of ARM 32-bit, ARM64 and x86_64 are
> > new.
> > >
> > > Please test vfork() on your board. CI builds this branch, but CI does
> not
> > > run vfork() on hardware. This patch changes the address environments,
> the
> > > stack setup and the register context of each architecture. I tested 24
> > > configurations with QEMU and two on real hardware.
> > >
> > > Do these steps:
> > >
> > > 1. Get this branch and apache/nuttx-apps#3685.
> > > 2. Build your configuration.
> > > 3. Run ostest.
> > >
> > > The vfork() test is the first test. It writes this line:
> > >
> > > vfork_test: Child 5 ran and exited before the parent resumed
> > >
> > > Send the target, the build mode and this line. A report of a failure is
> > as
> > > useful as a report of a pass. Comments on the semantics are also
> welcome.
> > >
> > > Thank you,
> > > Marco Casaroli
> > >
> >
>

Reply via email to