Hello,

(1) in my opinion and from small system (AVR) standpoint, having the options default off (or at least having the option to turn them off) is very useful.

For a system this small, it is quite unlikely to have the need for most of the process management. More likely than not, most processes will be spawned at init time and then just continue to run until poweroff. Everything related to process groups, controlling terminal, process termination etc. is unlikely to be used because having the ability to do so would - together with the applications - exceed the chip's program memory. RAM fragmentation seems like a risk too (if you terminate and launch processes, you are likely to get to a point where you become unable to allocate memory block for stack)

If it is acceptable, that is a different question - as far as I remember, many similar discussions about breaking POSIX by choice of the user did not go well for the small systems in recent past.

(2) very much yes please for SIGCHLD etc. (point 2 in PR order) for the same reason as above - if possible without affecting code readability/maintainability too severely and without spending too much time. Any way to avoid code size increase for features related to process management is good to have.

As for point 3 (SA_RESTART) - if it only builds with ENABLE_ALL_SIGNALS and does not affect code size with ENABLE_PARTIAL_SIGNALS that is IMO completely fine.

On 2026-11-05 17:56, Marco C. wrote:
Hello,

NuttX does not have POSIX job control today. On master:

- setpgid() fails with EPERM if pgid is not the process ID. setsid()
  returns the process ID and does not start a session. kill(-pgrp)
  signals only the process pgrp.
- /dev/tty does not exist, and a terminal keeps one foreground process
  ID.
- WIFSTOPPED(), WSTOPSIG() and WIFCONTINUED() are always 0.
  waitid(P_PGID) gives ENOSYS.
- No code reads SA_RESTART, so each interrupted blocking call fails with
  EINTR.

#19184 added the terminal ioctls and these stubs for one process per
group. I propose real process groups, and that model stays when the new
options are off. (A POSIX process group is not a NuttX task group: a
task group is one POSIX process.)

What POSIX says (POSIX.1-2024):

- <unistd.h>, _POSIX_JOB_CONTROL: "The implementation supports job
  control. This symbol shall always be set to a value greater than
  zero."
- setpgid(), change history of Issue 6: "The setpgid() function is
  mandatory since _POSIX_JOB_CONTROL is required to be defined in this
  version."
- XBD chapter 10 has /dev/tty in "The following files shall exist on
  conforming systems and shall be both readable and writable".
- sigaction(), SA_RESTART: "the function shall restart and shall not
  fail with [EINTR] unless otherwise specified".

Our posix.rst lists POSIX_JOB_CONTROL in PSE54 only, not in PSE51 to
PSE53. A small system does not need it, so the two large parts have an
option that is off by default.

The work, in PR order:

1. Process groups and sessions: CONFIG_SCHED_PROCESS_GROUPS, default n,
   needs SCHED_HAVE_PARENT. setpgid(), getpgid(), setsid() and getsid()
   become system calls, at the end of the table, so no other number
   moves. kill(0), kill(-pgid) and killpg() signal the whole group.
   POSIX_SPAWN_SETSID works. Ctrl-C and Ctrl-Z go to the foreground
   group. About +820 lines.
2. Stopped and continued children: no option. SIGCHLD with CLD_STOPPED
   and CLD_CONTINUED, WUNTRACED, WCONTINUED, waitid(P_PGID). waitpid()
   uses the wait loop of waitid(). About +770 -580 lines.
3. SA_RESTART: no option, in each CONFIG_ENABLE_ALL_SIGNALS build. About
   +480 lines.
4. Controlling terminals: CONFIG_TTY_CTTY, default n, needs part 1 and
   TTY_SIGINT or TTY_SIGTSTP. /dev/tty, SIGHUP when the session leader
   exits, SIGTTIN and SIGTTOU for a background group, on the serial
   driver, pty and telnet. The TIOCSCTTY(pid) form of NSH and nxinit
   stays. About +1080 lines.
5. nxinit starts a console service in a new session. Small.

With the options off, parts 1 and 4 add one small function,
nxsig_kill_foreground(), and behavior stays the same. The one exception
is killpg(1), which POSIX leaves unspecified: it now gives ENOSYS.

Parts 2 and 3 change behavior for everyone, as POSIX asks:

- NSH already waits with WUNTRACED. With SCHED_HAVE_PARENT it will
return to the prompt when the foreground command stops. Today it keeps
  waiting. This is from the source; I did not run it yet.
- examples/nanoxterm is the only program in the two repositories that
  sets SA_RESTART. Its calls will restart, as it asks.
- Each task gets one more byte, and each pending signal action one more
  bool. arm64 and RISC-V get a check at system call exit. Flat builds
  and x86_64 retry inside read(), write(), the socket calls and
  waitpid(). Other architectures with system calls (Arm, Xtensa, MIPS)
  keep EINTR for now, because the handler runs only after the system
  call returns. They need the same change in their dispatcher.

I did not measure the size yet. Each PR will give it.

Tests so far, on a tree from 2026-10-20, with test programs that I will
add to nuttx-apps:

- qemu-armv8a:knsh: process groups 21/21, terminals 22/22, stopped
  children 8/10 (the other 2 need part 3), SA_RESTART 10/11 (the other
  case is a timeout on a local socket), ostest 0.
- qemu-armv8a:nsh (flat): ostest 0. Most cases need fork() and skip:
  4 process group cases ran, and no terminal case. I will change the
  tests to run these cases without fork().
- With all parts and my other open work on one tree: a POSIX shell on
  the console runs Ctrl-Z, jobs, bg, fg and Ctrl-C.
- Not run: protected builds, and parts 1 and 4 on other architectures.

I will rebase and test again before each PR. Part 2 has conflicts on
master now.

Not done yet, and I plan to do these too:

- Define _POSIX_JOB_CONTROL and answer sysconf(_SC_JOB_CONTROL) when the
  option is on.
- POSIX_SPAWN_SETPGROUP: posix_spawnattr_setpgroup() gives ENOSYS today.
  Builds without fork() need it, so it goes in part 1.
- Orphaned process groups: no stop, and EIO on a read of the terminal.
- SIGTTOU for tcsetattr() and similar calls from a background group.
  Only tcsetpgrp() has it now.
- kill(-1), which gives ENOSYS.
- A blocked SIGCONT does not continue a process, and without
  SCHED_CHILD_STATUS a stop can be lost.

WIFSIGNALED() also stays 0 in this work. A separate change covers it.

Questions:

1. Is this direction acceptable: real process groups and sessions behind
   default-off options, and the current stubs when they are off?
2. Parts 2 and 3 have no option, because they are standard behavior and
   an option would keep the wrong behavior in some builds. Does anyone
   want an option for them after the sizes are known?

Thanks,
Marco Casaroli

Reply via email to