Add logic to retain child task exit status if so configured
git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@5553 42af7a65-404d-4744-a932-0658087f49c3
This commit is contained in:
parent
685d2dfebe
commit
3736ae6ba9
@ -4002,7 +4002,7 @@
|
||||
Start of support of LCD1602 alphanumeric LCD. I need a few
|
||||
more parts before I can finish integrating this one.
|
||||
* arch/arm/src/*/chip.h and arch/arm/include/*/chip.h: Move all
|
||||
priority ragnes from the src to the include chip.h header file.
|
||||
priority ranges from the src to the include chip.h header file.
|
||||
* arch/arm/include/armv7-m/irq.h: Add inline functions to enable
|
||||
and disable interrupts via the BASEPRI register.
|
||||
* arch/arm/Kconfig: Add new option CONFIG_ARM7VM_USEBASEI
|
||||
@ -4012,4 +4012,8 @@
|
||||
while CONFIG_ARM7VM_USEBASEPRI=y.
|
||||
* arch/arm/src/stm32/stm32_serial.c: Add support for USART
|
||||
single wire more (Contributed by the PX4 team).
|
||||
* sched/: Implement support for retaining child task status after
|
||||
the child task exists. This is behavior required by POSIX.
|
||||
But in NuttX is only enabled with CONFIG_SCHED_HAVE_PARENT and
|
||||
CONFIG_SCHED_CHILD_STATUS
|
||||
|
||||
|
@ -12,7 +12,7 @@
|
||||
<h1><big><font color="#3c34ec">
|
||||
<i>NuttX RTOS Porting Guide</i>
|
||||
</font></big></h1>
|
||||
<p>Last Updated: January 13, 2013</p>
|
||||
<p>Last Updated: January 23, 2013</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@ -4481,11 +4481,73 @@ build
|
||||
instrumentation is selected. Set to zero to disable.
|
||||
</li>
|
||||
<li>
|
||||
<code>CONFIG_SCHED_HAVE_PARENT</code>: Remember the ID of the parent thread when a new child thread is created.
|
||||
<code>CONFIG_SCHED_HAVE_PARENT</code>: Remember the ID of the parent thread when a new child task is created.
|
||||
This support enables some additional features (such as <code>SIGCHLD</code>) and modifies the behavior of other interfaces.
|
||||
For example, it makes <code>waitpid()</code> more standards complete by restricting the waited-for tasks to the children of the caller.
|
||||
Default: disabled.
|
||||
</li>
|
||||
<li>
|
||||
<code>CONFIG_SCHED_CHILD_STATUS</code>: If this option is selected, then the exit status of the child task will be retained after the child task exits.
|
||||
This option should be selected if you require knowledge of a child process' exit status.
|
||||
Without this setting, <code>wait()</code>, <code>waitpid()</code> or <code>waitid()</code> may fail.
|
||||
For example, if you do:
|
||||
<p><ol>
|
||||
<li>
|
||||
Start child task
|
||||
</li>
|
||||
<li>
|
||||
Wait for exit status (using <code>wait()</code>, <code>waitpid()</code> or <code>waitid()</code>).
|
||||
</li>
|
||||
</ol></p>
|
||||
<p>
|
||||
This can fail because the child task may run to completion before the wait begins.
|
||||
There is a non-standard work-around in this case:
|
||||
The above sequence will work if you disable pre-emption using <code>sched_lock()</code> prior to starting the child task, then re-enable pre-emption with <code>sched_unlock()</code> after the wait completes.
|
||||
This works because the child task is not permitted to run until the wait is in place.
|
||||
</p>
|
||||
<p>
|
||||
The standard solution would be to enable <code>CONFIG_SCHED_CHILD_STATUS</code>.
|
||||
In this case the exit status of the child task is retained after the child exits and the wait will successful obtain the child task's exit status whether it is called before the child task exits or not.
|
||||
</p>
|
||||
<p>
|
||||
<b>Warning</b>:
|
||||
If you enable this feature, then your application must either (1) take responsibility for reaping the child status with <code>wait()</code>, <code>waitpid()</code> or <code>waitid()</code>, or (2) suppress retention of child status.
|
||||
If you do not reap the child status, then you have a memory leak and your system will eventually fail.
|
||||
</p>
|
||||
Retention of child status can be suppressed on the parent using logic like:
|
||||
</p>
|
||||
<ul><pre>
|
||||
struct sigaction sa;
|
||||
|
||||
sa.sa_handler = SIG_IGN;
|
||||
sa.sa_flags = SA_NOCLDWAIT;
|
||||
int ret = sigaction(SIGCHLD, &sa, NULL);
|
||||
</pre></ul>
|
||||
</li>
|
||||
<li>
|
||||
<code>CONFIG_PREALLOC_CHILDSTATUS</code>: To prevent runaway child status allocations and to improve
|
||||
allocation performance, child task exit status structures are pre-allocated when the system boots.
|
||||
This setting determines the number of child status structures that will be pre-allocated.
|
||||
If this setting is not defined or if it is defined to be zero then a value of 2*<code>MAX_TASKS</code> is used.
|
||||
<p>
|
||||
Note that there cannot be more that <code>CONFIG_MAX_TASKS</code> tasks in total.
|
||||
However, the number of child status structures may need to be significantly larger because this number includes the maximum number of tasks that are running PLUS the number of tasks that have exit'ed without having their exit status reaped (via <code>wait()</code>, <code>waitpid()</code> or <code>waitid()</code>).
|
||||
</p>
|
||||
<p>
|
||||
Obviously, if tasks spawn children indefinitely and never have the exit status reaped, then you may have a memory leak!
|
||||
If you enable the <code>SCHED_CHILD_STATUS</code> feature, then your application must take responsibility for either (1) reaping the child status with <code>wait()</code>, <code>waitpid()</code> or <code>waitid()</code> or it must (2) suppress retention of child status. Otherwise, your system will eventually fail.
|
||||
</p>
|
||||
<p>
|
||||
Retention of child status can be suppressed on the parent using logic like:
|
||||
</p>
|
||||
<ul><pre>
|
||||
struct sigaction sa;
|
||||
|
||||
sa.sa_handler = SIG_IGN;
|
||||
sa.sa_flags = SA_NOCLDWAIT;
|
||||
int ret = sigaction(SIGCHLD, &sa, NULL);
|
||||
</pre></ul>
|
||||
</li>
|
||||
<li>
|
||||
<code>CONFIG_SYSTEM_TIME16</code>:
|
||||
The range of system time is, by default, 32-bits.
|
||||
|
@ -13,7 +13,7 @@
|
||||
<h1><big><font color="#3c34ec"><i>NuttX Operating System<p>User's Manual</i></font></big></h1>
|
||||
<p><small>by</small></p>
|
||||
<p>Gregory Nutt<p>
|
||||
<p>Last Updated: January 13, 2013</p>
|
||||
<p>Last Updated: January 23, 2013</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@ -1767,20 +1767,114 @@ priority of the calling task is returned.
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p>Scheduler locking interfaces</p>
|
||||
<p>
|
||||
<b>Task Control Interfaces</b>.
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p>
|
||||
<b>Scheduler locking interfaces</b>.
|
||||
This <i>non-standard</i> interfaces are used to enable and disable pre-emption and to test is pre-emption is currently enabled.
|
||||
</p>
|
||||
<ul>
|
||||
<li><a href="#schedlock">2.3.1 sched_lock</a></li>
|
||||
<li><a href="#schedunlock">2.3.2 sched_unlock</a></li>
|
||||
<li><a href="#schedlockcount">2.3.3 sched_lockcount</a></li>
|
||||
</ul>
|
||||
<p>Task synchronization interfaces</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<b>Task synchronization interfaces</b>.
|
||||
<code>wait()</code>, <code>waitpid()</code> or <code>waitid()</code> may be used to wait for termination of child tasks.
|
||||
</p>
|
||||
<ul>
|
||||
<li><a href="#waitpid">2.3.4 waitpid</a></li>
|
||||
<li><a href="#waitid">2.3.5 waitid</a></li>
|
||||
<li><a href="#wait">2.3.6 wait</a></li>
|
||||
</ul>
|
||||
<p>
|
||||
<code>atexit()</code> and <code>on_exit()</code> may be use to register callback functions that are executed when a task exits.
|
||||
</p>
|
||||
<ul>
|
||||
<li><a href="#atexit">2.3.7 atexit</a></li>
|
||||
<li><a href="#onexit">2.3.8 on_exit</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
<b>Parent and Child Tasks</b>.
|
||||
The task synchronization interfaces historically depend upon parent and child relationships between tasks.
|
||||
But default, NuttX does not use any parent/child knowledge.
|
||||
However, there are three important configuration options that can change that.
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p>
|
||||
<b><code>CONFIG_SCHED_HAVE_PARENT</code></b>.
|
||||
If this setting is defined, then it instructs NuttX to remember the task ID of the parent task when each new child task is created.
|
||||
This support enables some additional features (such as <code>SIGCHLD</code>) and modifies the behavior of other interfaces.
|
||||
For example, it makes <code>waitpid()</code> more standards complete by restricting the waited-for tasks to the children of the caller.
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<b><code>CONFIG_SCHED_CHILD_STATUS</code></b>
|
||||
If this option is selected, then the exit status of the child task will be retained after the child task exits.
|
||||
This option should be selected if you require knowledge of a child process' exit status.
|
||||
Without this setting, <code>wait()</code>, <code>waitpid()</code> or <code>waitid()</code> may fail.
|
||||
For example, if you do:
|
||||
</p>
|
||||
<ol>
|
||||
<li>
|
||||
Start child task
|
||||
</li>
|
||||
<li>
|
||||
Wait for exit status (using <code>wait()</code>, <code>waitpid()</code> or <code>waitid()</code>).
|
||||
</li>
|
||||
</ol>
|
||||
<p>
|
||||
This may fail because the child task may run to completion before the wait begins.
|
||||
There is a non-standard work-around in this case:
|
||||
The above sequence will work if you disable pre-emption using <code>sched_lock()</code> prior to starting the child task, then re-enable pre-emption with <code>sched_unlock()</code> after the wait completes.
|
||||
This works because the child task is not permitted to run until the wait is in place.
|
||||
</p>
|
||||
<p>
|
||||
The standard solution would be to enable <code>CONFIG_SCHED_CHILD_STATUS</code>.
|
||||
In this case the exit status of the child task is retained after the child exits and the wait will successful obtain the child task's exit status whether it is called before the child task exits or not.
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<b><code>CONFIG_PREALLOC_CHILDSTATUS</code></b>.
|
||||
To prevent runaway child status allocations and to improve allocation performance, child task exit status structures are pre-allocated when the system boots.
|
||||
This setting determines the number of child status structures that will be pre-allocated.
|
||||
If this setting is not defined or if it is defined to be zero then a value of 2*<code>MAX_TASKS</code> is used.
|
||||
</p>
|
||||
<p>
|
||||
Note that there cannot be more that <code>CONFIG_MAX_TASKS</code> tasks in total.
|
||||
However, the number of child status structures may need to be significantly larger because this number includes the maximum number of tasks that are running PLUS the number of tasks that have exit'ed without having their exit status reaped (via <code>wait()</code>, <code>waitpid()</code> or <code>waitid()</code>).
|
||||
</p>
|
||||
<p>
|
||||
Obviously, if tasks spawn children indefinitely and never have the exit status reaped, then you may have a memory leak!
|
||||
(See <b>Warning</b> below)
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
<b>Warning</b>:
|
||||
If you enable the <code>CONFIG_SCHED_CHILD_STATUS</code> feature, then your application must either (1) take responsibility for reaping the child status with <code>wait()</code>, <code>waitpid()</code> or <code>waitid()</code>, or (2) suppress retention of child status.
|
||||
If you do not reap the child status, then you have a memory leak and your system will eventually fail.
|
||||
</p>
|
||||
Retention of child status can be suppressed on the parent using logic like:
|
||||
</p>
|
||||
<ul><pre>
|
||||
struct sigaction sa;
|
||||
|
||||
sa.sa_handler = SIG_IGN;
|
||||
sa.sa_flags = SA_NOCLDWAIT;
|
||||
int ret = sigaction(SIGCHLD, &sa, NULL);
|
||||
</pre></ul>
|
||||
|
||||
<H3><a name="schedlock">2.3.1 sched_lock</a></H3>
|
||||
|
||||
@ -4589,10 +4683,14 @@ sigaction().
|
||||
interface of the same name.
|
||||
Differences from the POSIX implementation include:
|
||||
<ul>
|
||||
<li>Special values of sa_handler in the struct sigaction act input
|
||||
not handled (SIG_DFL, SIG_IGN).
|
||||
<li>All sa_flags in struct sigaction of act input are ignored
|
||||
(all treated like SA_SIGINFO).
|
||||
<li>
|
||||
There are no default actions so the special value <code>SIG_DFL</code> is treated like <code>SIG_IGN</code>.
|
||||
</li>
|
||||
<li>
|
||||
All <code>sa_flags</code> in struct sigaction of act input are ignored (all treated like <code>SA_SIGINFO</code>).
|
||||
The one exception is if <code>CONFIG_SCHED_CHILDSTATUS</code> is defined;
|
||||
then <code>SA_NOCLDWAIT</code> is supported but only for <code>SIGCHLD</code>.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<H3><a name="sigprocmask">2.8.7 sigprocmask</a></H3>
|
||||
|
@ -1810,7 +1810,7 @@ config STM32_TIM14_DAC2
|
||||
|
||||
endchoice
|
||||
|
||||
bool STM32_USART
|
||||
config STM32_USART
|
||||
bool
|
||||
|
||||
menu "U[S]ART Configuration"
|
||||
|
@ -334,12 +334,79 @@ defconfig -- This is a configuration file similar to the Linux
|
||||
CONFIG_TASK_NAME_SIZE - Specifies that maximum size of a
|
||||
task name to save in the TCB. Useful if scheduler
|
||||
instrumentation is selected. Set to zero to disable.
|
||||
CONFIG_SCHED_HAVE_PARENT - Remember the ID of the parent thread
|
||||
when a new child thread is created. This support enables some
|
||||
CONFIG_SCHED_HAVE_PARENT - Remember the ID of the parent task
|
||||
when a new child task is created. This support enables some
|
||||
additional features (such as SIGCHLD) and modifies the behavior
|
||||
of other interfaces. For example, it makes waitpid() more
|
||||
standards complete by restricting the waited-for tasks to the
|
||||
children of the caller. Default: disabled.
|
||||
CONFIG_SCHED_CHILD_STATUS
|
||||
If this option is selected, then the exit status of the child task
|
||||
will be retained after the child task exits. This option should be
|
||||
selected if you require knowledge of a child process' exit status.
|
||||
Without this setting, wait(), waitpid() or waitid() may fail. For
|
||||
example, if you do:
|
||||
|
||||
1) Start child task
|
||||
2) Wait for exit status (using wait(), waitpid(), or waitid()).
|
||||
|
||||
This can fail because the child task may run to completion before
|
||||
the wait begins. There is a non-standard work-around in this case:
|
||||
The above sequence will work if you disable pre-emption using
|
||||
sched_lock() prior to starting the child task, then re-enable pre-
|
||||
emption with sched_unlock() after the wait completes. This works
|
||||
because the child task is not permitted to run until the wait is in
|
||||
place.
|
||||
|
||||
The standard solution would be to enable CONFIG_SCHED_CHILD_STATUS. In
|
||||
this case the exit status of the child task is retained after the
|
||||
child exits and the wait will successful obtain the child task's
|
||||
exit status whether it is called before the child task exits or not.
|
||||
|
||||
Warning: If you enable this feature, then your application must
|
||||
either (1) take responsibility for reaping the child status with wait(),
|
||||
waitpid(), or waitid(), or (2) suppress retention of child status.
|
||||
If you do not reap the child status, then you have a memory leak and
|
||||
your system will eventually fail.
|
||||
|
||||
Retention of child status can be suppressed on the parent using logic like:
|
||||
|
||||
struct sigaction sa;
|
||||
|
||||
sa.sa_handler = SIG_IGN;
|
||||
sa.sa_flags = SA_NOCLDWAIT;
|
||||
int ret = sigaction(SIGCHLD, &sa, NULL);
|
||||
|
||||
CONFIG_PREALLOC_CHILDSTATUS
|
||||
To prevent runaway child status allocations and to improve
|
||||
allocation performance, child task exit status structures are pre-
|
||||
allocated when the system boots. This setting determines the number
|
||||
of child status structures that will be pre-allocated. If this
|
||||
setting is not defined or if it is defined to be zero then a value
|
||||
of 2*MAX_TASKS is used.
|
||||
|
||||
Note that there cannot be more that CONFIG_MAX_TASKS tasks in total.
|
||||
However, the number of child status structures may need to be
|
||||
significantly larger because this number includes the maximum number
|
||||
of tasks that are running PLUS the number of tasks that have exit'ed
|
||||
without having their exit status reaped (via wait(), waitid(), or
|
||||
waitpid()).
|
||||
|
||||
Obviously, if tasks spawn children indefinitely and never have the
|
||||
exit status reaped, then you may have a memory leak! If you enable
|
||||
the SCHED_CHILD_STATUS feature, then your application must take
|
||||
responsibility for either (1) reaping the child status with wait(),
|
||||
waitpid(), or waitid() or it must (2) suppress retention of child
|
||||
status. Otherwise, your system will eventually fail.
|
||||
|
||||
Retention of child status can be suppressed on the parent using logic like:
|
||||
|
||||
struct sigaction sa;
|
||||
|
||||
sa.sa_handler = SIG_IGN;
|
||||
sa.sa_flags = SA_NOCLDWAIT;
|
||||
int ret = sigaction(SIGCHLD, &sa, NULL);
|
||||
|
||||
CONFIG_START_YEAR, CONFIG_START_MONTH, CONFIG_START_DAY -
|
||||
Used to initialize the internal time logic.
|
||||
CONFIG_GREGORIAN_TIME - Enables Gregorian time conversions.
|
||||
|
@ -82,10 +82,15 @@ CONFIG_BOARD_LOOPSPERMSEC=100
|
||||
# CONFIG_SIM_M32 is not set
|
||||
# CONFIG_SIM_WALLTIME is not set
|
||||
|
||||
#
|
||||
# External Memory Configuration
|
||||
#
|
||||
|
||||
#
|
||||
# Architecture Options
|
||||
#
|
||||
# CONFIG_ARCH_NOINTC is not set
|
||||
# CONFIG_ARCH_VECNOTIRQ is not set
|
||||
# CONFIG_ARCH_DMA is not set
|
||||
# CONFIG_ARCH_IRQPRIO is not set
|
||||
# CONFIG_CUSTOM_STACK is not set
|
||||
@ -93,6 +98,7 @@ CONFIG_BOARD_LOOPSPERMSEC=100
|
||||
# CONFIG_ARCH_HAVE_VFORK is not set
|
||||
# CONFIG_ARCH_STACKDUMP is not set
|
||||
# CONFIG_ENDIAN_BIG is not set
|
||||
# CONFIG_ARCH_HAVE_RAMFUNCS is not set
|
||||
|
||||
#
|
||||
# Board Settings
|
||||
@ -132,6 +138,7 @@ CONFIG_RR_INTERVAL=0
|
||||
# CONFIG_SCHED_INSTRUMENTATION is not set
|
||||
CONFIG_TASK_NAME_SIZE=32
|
||||
CONFIG_SCHED_HAVE_PARENT=y
|
||||
# CONFIG_SCHED_CHILD_STATUS is not set
|
||||
# CONFIG_JULIAN_TIME is not set
|
||||
CONFIG_START_YEAR=2007
|
||||
CONFIG_START_MONTH=2
|
||||
@ -242,8 +249,8 @@ CONFIG_SERIAL=y
|
||||
#
|
||||
# File system configuration
|
||||
#
|
||||
# CONFIG_FS_FAT is not set
|
||||
# CONFIG_FS_RAMMAP is not set
|
||||
# CONFIG_FS_FAT is not set
|
||||
# CONFIG_FS_NXFFS is not set
|
||||
# CONFIG_FS_ROMFS is not set
|
||||
|
||||
@ -271,6 +278,7 @@ CONFIG_MM_REGIONS=1
|
||||
# CONFIG_BINFMT_EXEPATH is not set
|
||||
# CONFIG_NXFLAT is not set
|
||||
# CONFIG_ELF is not set
|
||||
# CONFIG_BUILTIN is not set
|
||||
# CONFIG_PIC is not set
|
||||
# CONFIG_SYMTAB_ORDEREDBYNAME is not set
|
||||
|
||||
@ -318,7 +326,6 @@ CONFIG_LIB_SENDFILE_BUFSIZE=512
|
||||
#
|
||||
# Built-In Applications
|
||||
#
|
||||
# CONFIG_BUILTIN is not set
|
||||
|
||||
#
|
||||
# Examples
|
||||
|
@ -74,6 +74,7 @@
|
||||
#define TCB_FLAG_NONCANCELABLE (1 << 2) /* Bit 2: Pthread is non-cancelable */
|
||||
#define TCB_FLAG_CANCEL_PENDING (1 << 3) /* Bit 3: Pthread cancel is pending */
|
||||
#define TCB_FLAG_ROUND_ROBIN (1 << 4) /* Bit 4: Round robin sched enabled */
|
||||
#define TCB_FLAG_NOCLDWAIT (1 << 5) /* Bit 5: Do not retain child exit status */
|
||||
|
||||
/* Values for struct child_status_s ch_flags */
|
||||
|
||||
@ -183,7 +184,7 @@ struct child_status_s
|
||||
FAR struct child_status_s *flink;
|
||||
|
||||
uint8_t ch_flags; /* Child status: See CHILD_FLAG_* definitions */
|
||||
pid_y ch_pid; /* Child task ID */
|
||||
pid_t ch_pid; /* Child task ID */
|
||||
int ch_status; /* Child exit status */
|
||||
};
|
||||
#endif
|
||||
|
@ -129,11 +129,13 @@
|
||||
|
||||
/* struct sigaction flag values */
|
||||
|
||||
#define SA_NOCLDSTOP 1 /* Do not generate SIGCHILD when
|
||||
#define SA_NOCLDSTOP (1 << 0) /* Do not generate SIGCHILD when
|
||||
* children stop (ignored) */
|
||||
#define SA_SIGINFO 2 /* Invoke the signal-catching function
|
||||
#define SA_SIGINFO (1 << 1) /* Invoke the signal-catching function
|
||||
* with 3 args instead of 1
|
||||
* (always assumed) */
|
||||
#define SA_NOCLDWAIT (1 << 2) /* If signo=SIGCHLD, exit status of child
|
||||
* processes will be discarded */
|
||||
|
||||
/* These are the possible values of the signfo si_code field */
|
||||
|
||||
|
@ -158,13 +158,13 @@ typedef uint16_t ino_t;
|
||||
* negative PID values are used to represent invalid PIDs.
|
||||
*/
|
||||
|
||||
typedef int pid_t;
|
||||
typedef int16_t pid_t;
|
||||
|
||||
/* id_t is a general identifier that can be used to contain at least a pid_t,
|
||||
* uid_t, or gid_t.
|
||||
*/
|
||||
|
||||
typedef unsigned int id_t;
|
||||
typedef int16_t id_t;
|
||||
|
||||
/* Signed integral type of the result of subtracting two pointers */
|
||||
|
||||
|
@ -42,13 +42,88 @@ config SCHED_HAVE_PARENT
|
||||
bool "Support parent/child task relationships"
|
||||
default n
|
||||
---help---
|
||||
Remember the ID of the parent thread when a new child thread is
|
||||
Remember the ID of the parent task when a new child task is
|
||||
created. This support enables some additional features (such as
|
||||
SIGCHLD) and modifies the behavior of other interfaces. For
|
||||
example, it makes waitpid() more standards complete by restricting
|
||||
the waited-for tasks to the children of the caller. Default:
|
||||
disabled.
|
||||
|
||||
config SCHED_CHILD_STATUS
|
||||
bool "Retain child exit status"
|
||||
default n
|
||||
depends on SCHED_HAVE_PARENT
|
||||
---help---
|
||||
If this option is selected, then the exit status of the child task
|
||||
will be retained after the child task exits. This option should be
|
||||
selected if you require knowledge of a child process' exit status.
|
||||
Without this setting, wait(), waitpid() or waitid() may fail. For
|
||||
example, if you do:
|
||||
|
||||
1) Start child task
|
||||
2) Wait for exit status (using wait(), waitpid(), or waitid()).
|
||||
|
||||
This can fail because the child task may run to completion before
|
||||
the wait begins. There is a non-standard work-around in this case:
|
||||
The above sequence will work if you disable pre-emption using
|
||||
sched_lock() prior to starting the child task, then re-enable pre-
|
||||
emption with sched_unlock() after the wait completes. This works
|
||||
because the child task is not permitted to run until the wait is in
|
||||
place.
|
||||
|
||||
The standard solution would be to enable SCHED_CHILD_STATUS. In
|
||||
this case the exit status of the child task is retained after the
|
||||
child exits and the wait will successful obtain the child task's
|
||||
exit status whether it is called before the child task exits or not.
|
||||
|
||||
Warning: If you enable this feature, then your application must
|
||||
either (1) take responsibility for reaping the child status with wait(),
|
||||
waitpid(), or waitid(), or (2) suppress retention of child status.
|
||||
If you do not reap the child status, then you have a memory leak and
|
||||
your system will eventually fail.
|
||||
|
||||
Retention of child status can be suppressed on the parent using logic like:
|
||||
|
||||
struct sigaction sa;
|
||||
|
||||
sa.sa_handler = SIG_IGN;
|
||||
sa.sa_flags = SA_NOCLDWAIT;
|
||||
int ret = sigaction(SIGCHLD, &sa, NULL);
|
||||
|
||||
config PREALLOC_CHILDSTATUS
|
||||
int "Number of pre-allocated child status"
|
||||
default 0
|
||||
depends on SCHED_CHILD_STATUS
|
||||
---help---
|
||||
To prevent runaway child status allocations and to improve
|
||||
allocation performance, child task exit status structures are pre-
|
||||
allocated when the system boots. This setting determines the number
|
||||
of child status structures that will be pre-allocated. If this
|
||||
setting is not defined or if it is defined to be zero then a value
|
||||
of 2*MAX_TASKS is used.
|
||||
|
||||
Note that there cannot be more that CONFIG_MAX_TASKS tasks in total.
|
||||
However, the number of child status structures may need to be
|
||||
significantly larger because this number includes the maximum number
|
||||
of tasks that are running PLUS the number of tasks that have exit'ed
|
||||
without having their exit status reaped (via wait(), waitid(), or
|
||||
waitpid()).
|
||||
|
||||
Obviously, if tasks spawn children indefinitely and never have the
|
||||
exit status reaped, then you may have a memory leak! If you enable
|
||||
the SCHED_CHILD_STATUS feature, then your application must take
|
||||
responsibility for either (1) reaping the child status with wait(),
|
||||
waitpid(), or waitid() or it must (2) suppress retention of child
|
||||
status. Otherwise, your system will eventually fail.
|
||||
|
||||
Retention of child status can be suppressed on the parent using logic like:
|
||||
|
||||
struct sigaction sa;
|
||||
|
||||
sa.sa_handler = SIG_IGN;
|
||||
sa.sa_flags = SA_NOCLDWAIT;
|
||||
int ret = sigaction(SIGCHLD, &sa, NULL);
|
||||
|
||||
config JULIAN_TIME
|
||||
bool "Enables Julian time conversions"
|
||||
default n
|
||||
@ -88,7 +163,7 @@ config PRIORITY_INHERITANCE
|
||||
Set to enable support for priority inheritance on mutexes and semaphores.
|
||||
|
||||
config SEM_PREALLOCHOLDERS
|
||||
int "Pre-allocated holders"
|
||||
int "Number of pre-allocated holders"
|
||||
default 16
|
||||
depends on PRIORITY_INHERITANCE
|
||||
---help---
|
||||
|
@ -264,7 +264,7 @@ extern const tasklist_t g_tasklisttable[NUM_TASK_STATES];
|
||||
int os_bringup(void);
|
||||
void task_start(void);
|
||||
int task_schedsetup(FAR _TCB *tcb, int priority, start_t start,
|
||||
main_t main);
|
||||
main_t main, uint8_t ttype);
|
||||
int task_argsetup(FAR _TCB *tcb, FAR const char *name, FAR const char *argv[]);
|
||||
void task_exithook(FAR _TCB *tcb, int status);
|
||||
int task_deletecurrent(void);
|
||||
@ -273,8 +273,7 @@ int task_deletecurrent(void);
|
||||
void weak_function task_initialize(void);
|
||||
FAR struct child_status_s *task_allocchild(void);
|
||||
void task_freechild(FAR struct child_status_s *status);
|
||||
FAR struct child_status_s *task_addchild(FAR _TCB *tcb, pid_t pid, int status,
|
||||
uint8_t flags);
|
||||
void task_addchild(FAR _TCB *tcb, FAR struct child_status_s *child);
|
||||
FAR struct child_status_s *task_findchild(FAR _TCB *tcb, pid_t pid);
|
||||
FAR struct child_status_s *task_removechild(FAR _TCB *tcb, pid_t pid);
|
||||
void task_removechildren(FAR _TCB *tcb);
|
||||
|
@ -286,7 +286,7 @@ void os_start(void)
|
||||
|
||||
/* Initialize the processor-specific portion of the TCB */
|
||||
|
||||
g_idletcb.flags = TCB_FLAG_TTYPE_KERNEL;
|
||||
g_idletcb.flags = (TCB_FLAG_TTYPE_KERNEL | TCB_FLAG_NOCLDWAIT);
|
||||
up_initial_state(&g_idletcb);
|
||||
|
||||
/* Initialize the semaphore facility(if in link). This has to be done
|
||||
|
@ -1,7 +1,7 @@
|
||||
/****************************************************************************
|
||||
* sched/pthread_create.c
|
||||
*
|
||||
* Copyright (C) 2007-2009, 2011 Gregory Nutt. All rights reserved.
|
||||
* Copyright (C) 2007-2009, 2011, 2013 Gregory Nutt. All rights reserved.
|
||||
* Author: Gregory Nutt <gnutt@nuttx.org>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -354,15 +354,10 @@ int pthread_create(FAR pthread_t *thread, FAR pthread_attr_t *attr,
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Mark this task as a pthread (this setting will be needed in
|
||||
* task_schedsetup() when up_initial_state() is called.
|
||||
*/
|
||||
|
||||
ptcb->flags |= TCB_FLAG_TTYPE_PTHREAD;
|
||||
|
||||
/* Initialize the task control block */
|
||||
|
||||
ret = task_schedsetup(ptcb, priority, pthread_start, (main_t)start_routine);
|
||||
ret = task_schedsetup(ptcb, priority, pthread_start, (main_t)start_routine,
|
||||
TCB_FLAG_TTYPE_PTHREAD);
|
||||
if (ret != OK)
|
||||
{
|
||||
sched_releasetcb(ptcb);
|
||||
|
@ -159,8 +159,29 @@ int waitid(idtype_t idtype, id_t id, siginfo_t *info, int options)
|
||||
* TCB is actually a child of this task.
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_SCHED_CHILD_STATUS
|
||||
if (rtcb->children == NULL)
|
||||
{
|
||||
/* There are no children */
|
||||
|
||||
err = ECHILD;
|
||||
goto errout_with_errno;
|
||||
}
|
||||
else if (idtype == P_PID)
|
||||
{
|
||||
if (task_findchild(rtcb, (pid_t)id) == NULL)
|
||||
{
|
||||
/* This specific pid is not a child */
|
||||
|
||||
err = ECHILD;
|
||||
goto errout_with_errno;
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (rtcb->nchildren == 0)
|
||||
{
|
||||
/* There are no children */
|
||||
|
||||
err = ECHILD;
|
||||
goto errout_with_errno;
|
||||
}
|
||||
@ -175,11 +196,64 @@ int waitid(idtype_t idtype, id_t id, siginfo_t *info, int options)
|
||||
goto errout_with_errno;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Loop until the child that we are waiting for dies */
|
||||
|
||||
for (;;)
|
||||
{
|
||||
#ifdef CONFIG_SCHED_CHILD_STATUS
|
||||
/* Check if the task has already died. Signals are not queued in
|
||||
* NuttX. So a possibility is that the child has died and we
|
||||
* missed the death of child signal (we got some other signal
|
||||
* instead).
|
||||
*/
|
||||
|
||||
DEBUGASSERT(rtcb->children);
|
||||
if (rtcb->children == NULL)
|
||||
{
|
||||
/* This should not happen. I am just wasting your FLASH. */
|
||||
|
||||
err = ECHILD;
|
||||
goto errout_with_errno;
|
||||
}
|
||||
else if (idtype == P_PID)
|
||||
{
|
||||
FAR struct child_status_s *child;
|
||||
|
||||
/* We are waiting for a specific PID. Get the current status
|
||||
* of the child task.
|
||||
*/
|
||||
|
||||
child = task_findchild(rtcb, (pid_t)id);
|
||||
DEBUGASSERT(child);
|
||||
if (!child)
|
||||
{
|
||||
/* Yikes! The child status entry just disappeared! */
|
||||
|
||||
err = ECHILD;
|
||||
goto errout_with_errno;
|
||||
}
|
||||
|
||||
/* Did the child exit? */
|
||||
|
||||
if ((child->ch_flags & CHILD_FLAG_EXITED) != 0)
|
||||
{
|
||||
/* The child has exited. Return the saved exit status */
|
||||
|
||||
info->si_signo = SIGCHLD;
|
||||
info->si_code = CLD_EXITED;
|
||||
info->si_value.sival_ptr = NULL;
|
||||
info->si_pid = (pid_t)id;
|
||||
info->si_status = child->ch_status;
|
||||
|
||||
/* Discard the child entry and break out of the loop */
|
||||
|
||||
(void)task_removechild(rtcb, (pid_t)id);
|
||||
task_freechild(child);
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* Check if the task has already died. Signals are not queued in
|
||||
* NuttX. So a possibility is that the child has died and we
|
||||
* missed the death of child signal (we got some other signal
|
||||
@ -197,6 +271,7 @@ int waitid(idtype_t idtype, id_t id, siginfo_t *info, int options)
|
||||
err = EINTR;
|
||||
goto errout_with_errno;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Wait for any death-of-child signal */
|
||||
|
||||
|
@ -274,6 +274,9 @@ errout:
|
||||
pid_t waitpid(pid_t pid, int *stat_loc, int options)
|
||||
{
|
||||
FAR _TCB *rtcb = (FAR _TCB *)g_readytorun.head;
|
||||
#ifdef CONFIG_SCHED_CHILD_STATUS
|
||||
FAR struct child_status_s *child;
|
||||
#endif
|
||||
FAR struct siginfo info;
|
||||
sigset_t sigset;
|
||||
int err;
|
||||
@ -300,12 +303,33 @@ pid_t waitpid(pid_t pid, int *stat_loc, int options)
|
||||
|
||||
sched_lock();
|
||||
|
||||
/* Verify that this task actually has children and that the the requeste
|
||||
/* Verify that this task actually has children and that the the request
|
||||
* TCB is actually a child of this task.
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_SCHED_CHILD_STATUS
|
||||
if (rtcb->children == NULL)
|
||||
{
|
||||
/* There are no children */
|
||||
|
||||
err = ECHILD;
|
||||
goto errout_with_errno;
|
||||
}
|
||||
else if (pid != (pid_t)-1)
|
||||
{
|
||||
/* This specific pid is not a child */
|
||||
|
||||
if (task_findchild(rtcb, pid) == NULL)
|
||||
{
|
||||
err = ECHILD;
|
||||
goto errout_with_errno;
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (rtcb->nchildren == 0)
|
||||
{
|
||||
/* There are no children */
|
||||
|
||||
err = ECHILD;
|
||||
goto errout_with_errno;
|
||||
}
|
||||
@ -320,6 +344,7 @@ pid_t waitpid(pid_t pid, int *stat_loc, int options)
|
||||
goto errout_with_errno;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Loop until the child that we are waiting for dies */
|
||||
|
||||
@ -337,7 +362,12 @@ pid_t waitpid(pid_t pid, int *stat_loc, int options)
|
||||
* chilren.
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_SCHED_CHILD_STATUS
|
||||
DEBUGASSERT(rtcb->children);
|
||||
if (rtcb->children == NULL)
|
||||
#else
|
||||
if (rtcb->nchildren == 0)
|
||||
#endif
|
||||
{
|
||||
/* There were one or more children when we started so they
|
||||
* must have exit'ed. There are just no bread crumbs left
|
||||
@ -351,6 +381,35 @@ pid_t waitpid(pid_t pid, int *stat_loc, int options)
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef CONFIG_SCHED_CHILD_STATUS
|
||||
/* We are waiting for a specific PID. Get the current status
|
||||
* of the child task.
|
||||
*/
|
||||
|
||||
child = task_findchild(rtcb, pid);
|
||||
DEBUGASSERT(child);
|
||||
if (!child)
|
||||
{
|
||||
/* Yikes! The child status entry just disappeared! */
|
||||
|
||||
err = ECHILD;
|
||||
goto errout_with_errno;
|
||||
}
|
||||
|
||||
/* Did the child exit? */
|
||||
|
||||
if ((child->ch_flags & CHILD_FLAG_EXITED) != 0)
|
||||
{
|
||||
/* The child has exited. Return the saved exit status */
|
||||
|
||||
*stat_loc = child->ch_status;
|
||||
|
||||
/* Discard the child entry and break out of the loop */
|
||||
|
||||
(void)task_removechild(rtcb, pid);
|
||||
task_freechild(child);
|
||||
}
|
||||
#else
|
||||
/* We are waiting for a specific PID. We can use kill() with
|
||||
* signal number 0 to determine if that task is still alive.
|
||||
*/
|
||||
@ -368,6 +427,7 @@ pid_t waitpid(pid_t pid, int *stat_loc, int options)
|
||||
err = ECHILD;
|
||||
goto errout_with_errno;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Wait for any death-of-child signal */
|
||||
|
@ -1,7 +1,7 @@
|
||||
/****************************************************************************
|
||||
* sched/sig_action.c
|
||||
*
|
||||
* Copyright (C) 2007-2009 Gregory Nutt. All rights reserved.
|
||||
* Copyright (C) 2007-2009, 2013 Gregory Nutt. All rights reserved.
|
||||
* Author: Gregory Nutt <gnutt@nuttx.org>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -43,6 +43,7 @@
|
||||
#include <signal.h>
|
||||
#include <queue.h>
|
||||
#include <sched.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "os_internal.h"
|
||||
#include "sig_internal.h"
|
||||
@ -156,10 +157,11 @@ static FAR sigactq_t *sig_allocateaction(void)
|
||||
* Assumptions:
|
||||
*
|
||||
* POSIX Compatibility:
|
||||
* - Special values of sa_handler in the struct sigaction
|
||||
* act input not handled (SIG_DFL, SIG_IGN).
|
||||
* - All sa_flags in struct sigaction of act input are
|
||||
* ignored (all treated like SA_SIGINFO).
|
||||
* - There are no default actions so the special value SIG_DFL is treated
|
||||
* like SIG_IGN.
|
||||
* - All sa_flags in struct sigaction of act input are ignored (all
|
||||
* treated like SA_SIGINFO). The one exception is if CONFIG_SCHED_CHILDSTATUS
|
||||
* is defined; then SA_NOCLDWAIT is supported but only for SIGCHLD
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
@ -167,17 +169,19 @@ int sigaction(int signo, FAR const struct sigaction *act, FAR struct sigaction *
|
||||
{
|
||||
FAR _TCB *rtcb = (FAR _TCB*)g_readytorun.head;
|
||||
FAR sigactq_t *sigact;
|
||||
int ret = ERROR; /* Assume failure */
|
||||
int ret;
|
||||
|
||||
/* Since sigactions can only be installed from the running thread of
|
||||
* execution, no special precautions should be necessary.
|
||||
*/
|
||||
|
||||
/* Verify the signal */
|
||||
/* Verify the signal number */
|
||||
|
||||
if (GOOD_SIGNO(signo))
|
||||
if (!GOOD_SIGNO(signo))
|
||||
{
|
||||
ret = OK; /* Assume success */
|
||||
set_errno(EINVAL);
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
/* Find the signal in the sigactionq */
|
||||
|
||||
@ -201,22 +205,81 @@ int sigaction(int signo, FAR const struct sigaction *act, FAR struct sigaction *
|
||||
}
|
||||
}
|
||||
|
||||
/* If no sigaction was found, but one is needed, then
|
||||
* allocate one.
|
||||
/* If the argument act is a null pointer, signal handling is unchanged;
|
||||
* thus, the call can be used to enquire about the current handling of
|
||||
* a given signal.
|
||||
*/
|
||||
|
||||
if (!sigact && act && act->sa_u._sa_handler)
|
||||
if (!act)
|
||||
{
|
||||
return OK;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_SCHED_HAVE_PARENT) && defined(CONFIG_SCHED_CHILD_STATUS)
|
||||
|
||||
/* Handle a special case. Retention of child status can be suppressed
|
||||
* if signo == SIGCHLD and sa_flags == SA_NOCLDWAIT.
|
||||
*
|
||||
* POSIX.1 leaves it unspecified whether a SIGCHLD signal is generated
|
||||
* when a child process terminates. In NuttX, a SIGCHLD signal is
|
||||
* generated in this case; but in some other implementations, it may not
|
||||
* be.
|
||||
*/
|
||||
|
||||
if (signo == SIGCHLD && (act->sa_flags & SA_NOCLDWAIT) != 0)
|
||||
{
|
||||
irqstate_t flags;
|
||||
|
||||
/* We do require a critical section to muck with the TCB values that
|
||||
* can be modified by the child thread.
|
||||
*/
|
||||
|
||||
flags = irqsave();
|
||||
|
||||
/* Mark that status should be not be retained */
|
||||
|
||||
rtcb->flags |= TCB_FLAG_NOCLDWAIT;
|
||||
|
||||
/* Free all pending exit status */
|
||||
|
||||
task_removechildren(rtcb);
|
||||
irqrestore(flags);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Handle the case where no sigaction is supplied (SIG_IGN) */
|
||||
|
||||
if (act->sa_u._sa_handler == SIG_IGN)
|
||||
{
|
||||
/* If there is a old sigaction, remove it from sigactionq */
|
||||
|
||||
sq_rem((FAR sq_entry_t*)sigact, &rtcb->sigactionq);
|
||||
|
||||
/* And deallocate it */
|
||||
|
||||
sig_releaseaction(sigact);
|
||||
}
|
||||
|
||||
/* A sigaction has been supplied */
|
||||
|
||||
else
|
||||
{
|
||||
/* Check if a sigaction was found */
|
||||
|
||||
if (!sigact)
|
||||
{
|
||||
/* No sigaction was found, but one is needed. Allocate one. */
|
||||
|
||||
sigact = sig_allocateaction();
|
||||
|
||||
/* An error has occurred if we could not allocate the sigaction */
|
||||
|
||||
if (!sigact)
|
||||
{
|
||||
ret = ERROR;
|
||||
set_errno(ENOMEM);
|
||||
return ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Put the signal number in the queue entry */
|
||||
|
||||
sigact->signo = (uint8_t)signo;
|
||||
@ -225,34 +288,12 @@ int sigaction(int signo, FAR const struct sigaction *act, FAR struct sigaction *
|
||||
|
||||
sq_addlast((FAR sq_entry_t*)sigact, &rtcb->sigactionq);
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the new sigaction if so requested */
|
||||
/* Set the new sigaction */
|
||||
|
||||
if ((sigact) && (act))
|
||||
{
|
||||
/* Check if it is a request to install a new handler */
|
||||
|
||||
if (act->sa_u._sa_handler)
|
||||
{
|
||||
COPY_SIGACTION(&sigact->act, act);
|
||||
}
|
||||
|
||||
/* No.. It is a request to remove the old handler */
|
||||
|
||||
else
|
||||
{
|
||||
/* Remove the old sigaction from sigactionq */
|
||||
|
||||
sq_rem((FAR sq_entry_t*)sigact, &rtcb->sigactionq);
|
||||
|
||||
/* And deallocate it */
|
||||
|
||||
sig_releaseaction(sigact);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -39,24 +39,44 @@
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <sched.h>
|
||||
#include <errno.h>
|
||||
#include <debug.h>
|
||||
|
||||
#include "os_internal.h"
|
||||
|
||||
#if defined(CONFIG_SCHED_HAVE_PARENT) && defined(CONFIG_SCHED_CHILD_STATUS)
|
||||
|
||||
/*****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
*****************************************************************************/
|
||||
/* Note that there cannot be more that CONFIG_MAX_TASKS tasks in total.
|
||||
* However, the number of child status structures may need to be significantly
|
||||
* larger because this number includes the maximum number of tasks that are
|
||||
* running PLUS the number of tasks that have exit'ed without having their
|
||||
* exit status reaped (via wait(), waitid(), or waitpid()).
|
||||
*
|
||||
* Obviously, if tasks spawn children indefinitely and never have the exit
|
||||
* status reaped, then you have a memory leak!
|
||||
*/
|
||||
|
||||
#if !defined(CONFIG_PREALLOC_CHILDSTATUS) || CONFIG_PREALLOC_CHILDSTATUS == 0
|
||||
# undef CONFIG_PREALLOC_CHILDSTATUS
|
||||
# define CONFIG_PREALLOC_CHILDSTATUS (2*CONFIG_MAX_TASKS)
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_DEBUG
|
||||
# undef CONFIG_DEBUG_CHILDSTATUS
|
||||
#endif
|
||||
|
||||
/*****************************************************************************
|
||||
* Private Types
|
||||
*****************************************************************************/
|
||||
/* Globals are maintained in a structure to minimize name collisions. Note
|
||||
* that there cannot be more that CONFIG_MAX_TASKS tasks in total. So using
|
||||
* CONFIG_MAX_TASKS should be sufficient (at least one task, the IDLE thread,
|
||||
* will have no parent).
|
||||
*/
|
||||
/* Globals are maintained in a structure to minimize name collisions. */
|
||||
|
||||
struct child_pool_s
|
||||
{
|
||||
struct child_status_s alloc[CONFIG_MAX_TASKS];
|
||||
struct child_status_s alloc[CONFIG_PREALLOC_CHILDSTATUS];
|
||||
FAR struct child_status_s *freelist;
|
||||
};
|
||||
|
||||
@ -70,6 +90,40 @@ static struct child_pool_s g_child_pool;
|
||||
* Private Functions
|
||||
*****************************************************************************/
|
||||
|
||||
/*****************************************************************************
|
||||
* Name: task_dumpchildren
|
||||
*
|
||||
* Description:
|
||||
* Dump all of the children when the part TCB list is modified.
|
||||
*
|
||||
* Parameters:
|
||||
* tcb - The parent TCB.
|
||||
*
|
||||
* Return Value:
|
||||
* None.
|
||||
*
|
||||
* Assumptions:
|
||||
* Called early in initialization. No special precautions are required.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_DEBUG_CHILDSTATUS
|
||||
static void task_dumpchildren(FAR _TCB *tcb, FAR const char *msg)
|
||||
{
|
||||
FAR struct child_status_s *child;
|
||||
int i;
|
||||
|
||||
dbg("Parent TCB=%p: %s\n", tcb, msg);
|
||||
for (i = 0, child = tcb->children; child; i++, child = child->flink)
|
||||
{
|
||||
dbg(" %d. ch_flags=%02x ch_pid=%d ch_status=%d\n",
|
||||
i, child->ch_flags, child->ch_pid, child->ch_status);
|
||||
}
|
||||
}
|
||||
#else
|
||||
# task_dumpchildren(t,m)
|
||||
#endif
|
||||
|
||||
/*****************************************************************************
|
||||
* Public Functions
|
||||
*****************************************************************************/
|
||||
@ -88,7 +142,7 @@ static struct child_pool_s g_child_pool;
|
||||
* None.
|
||||
*
|
||||
* Assumptions:
|
||||
* Called early in initializatin. No special precautions are required.
|
||||
* Called early in initialization. No special precautions are required.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
@ -102,9 +156,9 @@ void task_initialize(void)
|
||||
|
||||
prev = &g_child_pool.alloc[0];
|
||||
g_child_pool.freelist = prev;
|
||||
for (i = 0; i < CONFIG_MAX_TASKS; i++)
|
||||
for (i = 0; i < CONFIG_PREALLOC_CHILDSTATUS; i++)
|
||||
{
|
||||
curr = &g_child_pool.alloc[i]
|
||||
curr = &g_child_pool.alloc[i];
|
||||
prev->flink = curr;
|
||||
prev = curr;
|
||||
}
|
||||
@ -179,18 +233,14 @@ void task_freechild(FAR struct child_status_s *child)
|
||||
* Name: task_addchild
|
||||
*
|
||||
* Description:
|
||||
* Find a child status structure in the given TCB.
|
||||
* Add a child status structure in the given TCB.
|
||||
*
|
||||
* Parameters:
|
||||
* tcb - The TCB of the parent task to containing the child status.
|
||||
* pid - The ID of the child to create
|
||||
* status - Child exit status (should be zero)
|
||||
* flags - Child flags (see CHILD_FLAGS_* defininitions)
|
||||
* child - The structure to be added
|
||||
*
|
||||
* Return Value:
|
||||
* On success, a non-NULL pointer to a child status structure. NULL is
|
||||
* returned if (1) there are no free status structures, or (2) an entry
|
||||
* with this PID already exists.
|
||||
* N
|
||||
*
|
||||
* Assumptions:
|
||||
* Called during task creation processing in a safe context. No special
|
||||
@ -198,37 +248,14 @@ void task_freechild(FAR struct child_status_s *child)
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
FAR struct child_status_s *task_addchild(FAR _TCB *tcb, pid_t pid, int status,
|
||||
uint8_t flags)
|
||||
void task_addchild(FAR _TCB *tcb, FAR struct child_status_s *child)
|
||||
{
|
||||
FAR struct child_status_s *child;
|
||||
|
||||
/* Make sure that there is not already a structure for this PID */
|
||||
|
||||
child = task_findchild(tcb, pid);
|
||||
if (child)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Allocate a new status structure */
|
||||
|
||||
child = task_allocchild(void);
|
||||
if (child)
|
||||
{
|
||||
/* Initialize the structure */
|
||||
|
||||
child->ch_flags = flags;
|
||||
child->ch_pid = pid;
|
||||
child->ch_status = status;
|
||||
|
||||
/* Add the entry into the TCB list of children */
|
||||
|
||||
status->flink = tcb->children;
|
||||
tcb->childen = status;
|
||||
}
|
||||
child->flink = tcb->children;
|
||||
tcb->children = child;
|
||||
|
||||
return child;
|
||||
task_dumpchildren(tcb, "task_addchild");
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
@ -325,6 +352,7 @@ FAR struct child_status_s *task_removechild(FAR _TCB *tcb, pid_t pid)
|
||||
}
|
||||
|
||||
curr->flink = NULL;
|
||||
task_dumpchildren(tcb, "task_removechild");
|
||||
}
|
||||
|
||||
return curr;
|
||||
@ -360,6 +388,9 @@ void task_removechildren(FAR _TCB *tcb)
|
||||
next = curr->flink;
|
||||
task_freechild(curr);
|
||||
}
|
||||
|
||||
tcb->children = NULL;
|
||||
task_dumpchildren(tcb, "task_removechildren");
|
||||
}
|
||||
|
||||
#endif /* CONFIG_SCHED_HAVE_PARENT && CONFIG_SCHED_CHILD_STATUS */
|
||||
|
@ -1,7 +1,7 @@
|
||||
/****************************************************************************
|
||||
* sched/task_create.c
|
||||
*
|
||||
* Copyright (C) 2007-2010 Gregory Nutt. All rights reserved.
|
||||
* Copyright (C) 2007-2010, 2013 Gregory Nutt. All rights reserved.
|
||||
* Author: Gregory Nutt <gnutt@nuttx.org>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -81,7 +81,7 @@
|
||||
*
|
||||
* Input Parameters:
|
||||
* name - Name of the new task
|
||||
* type - Type of the new task
|
||||
* ttype - Type of the new task
|
||||
* priority - Priority of the new task
|
||||
* stack_size - size (in bytes) of the stack needed
|
||||
* entry - Entry point of a new task
|
||||
@ -99,10 +99,10 @@
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef CONFIG_CUSTOM_STACK
|
||||
static int thread_create(const char *name, uint8_t type, int priority,
|
||||
static int thread_create(const char *name, uint8_t ttype, int priority,
|
||||
int stack_size, main_t entry, const char **argv)
|
||||
#else
|
||||
static int thread_create(const char *name, uint8_t type, int priority,
|
||||
static int thread_create(const char *name, uint8_t ttype, int priority,
|
||||
main_t entry, const char **argv)
|
||||
#endif
|
||||
{
|
||||
@ -142,15 +142,9 @@ static int thread_create(const char *name, uint8_t type, int priority,
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Mark the type of this thread (this setting will be needed in
|
||||
* task_schedsetup() when up_initial_state() is called.
|
||||
*/
|
||||
|
||||
tcb->flags |= type;
|
||||
|
||||
/* Initialize the task control block */
|
||||
|
||||
ret = task_schedsetup(tcb, priority, task_start, entry);
|
||||
ret = task_schedsetup(tcb, priority, task_start, entry, ttype);
|
||||
if (ret != OK)
|
||||
{
|
||||
goto errout_with_tcb;
|
||||
|
@ -1,7 +1,7 @@
|
||||
/****************************************************************************
|
||||
* sched/task_exithook.c
|
||||
*
|
||||
* Copyright (C) 2011-2012 Gregory Nutt. All rights reserved.
|
||||
* Copyright (C) 2011-2013 Gregory Nutt. All rights reserved.
|
||||
* Author: Gregory Nutt <gnutt@nuttx.org>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -202,6 +202,12 @@ static inline void task_sigchild(FAR _TCB *tcb, int status)
|
||||
FAR _TCB *ptcb;
|
||||
siginfo_t info;
|
||||
|
||||
/* Only exiting tasks should generate SIGCHLD. pthreads use other
|
||||
* mechansims.
|
||||
*/
|
||||
|
||||
if ((tcb->flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_TASK)
|
||||
{
|
||||
/* Keep things stationary through the following */
|
||||
|
||||
sched_lock();
|
||||
@ -217,22 +223,50 @@ static inline void task_sigchild(FAR _TCB *tcb, int status)
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SCHED_CHILD_STATUS
|
||||
/* Check if the parent task has suppressed retention of child exit
|
||||
* status information. Only 'tasks' report exit status, not pthreads.
|
||||
* pthreads have a different mechanism.
|
||||
*/
|
||||
|
||||
if ((ptcb->flags & TCB_FLAG_NOCLDWAIT) == 0)
|
||||
{
|
||||
FAR struct child_status_s *child;
|
||||
|
||||
/* No.. Find the exit status entry for this task in the parent TCB */
|
||||
|
||||
child = task_findchild(ptcb, getpid());
|
||||
DEBUGASSERT(child);
|
||||
if (child)
|
||||
{
|
||||
/* Mark that the child has exit'ed */
|
||||
|
||||
child->ch_flags |= CHILD_FLAG_EXITED;
|
||||
|
||||
/* Save the exit status */
|
||||
|
||||
child->ch_status = status;
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* Decrement the number of children from this parent */
|
||||
|
||||
DEBUGASSERT(ptcb->nchildren > 0);
|
||||
ptcb->nchildren--;
|
||||
#endif
|
||||
|
||||
/* Set the parent to an impossible PID. We do this because under certain
|
||||
* conditions, task_exithook() can be called multiple times. If this
|
||||
* function is called again, sched_gettcb() will fail on the invalid
|
||||
* parent PID above, nchildren will be decremented once and all will be
|
||||
* well.
|
||||
/* Set the parent to an impossible PID. We do this because under
|
||||
* certain conditions, task_exithook() can be called multiple times.
|
||||
* If this function is called again, sched_gettcb() will fail on the
|
||||
* invalid parent PID above, nchildren will be decremented once and
|
||||
* all will be well.
|
||||
*/
|
||||
|
||||
tcb->parent = INVALID_PROCESS_ID;
|
||||
|
||||
/* Create the siginfo structure. We don't actually know the cause. That
|
||||
* is a bug. Let's just say that the child task just exit-ted for now.
|
||||
/* Create the siginfo structure. We don't actually know the cause.
|
||||
* That is a bug. Let's just say that the child task just exit-ted
|
||||
* for now.
|
||||
*/
|
||||
|
||||
info.si_signo = SIGCHLD;
|
||||
@ -241,13 +275,14 @@ static inline void task_sigchild(FAR _TCB *tcb, int status)
|
||||
info.si_pid = tcb->pid;
|
||||
info.si_status = status;
|
||||
|
||||
/* Send the signal. We need to use this internal interface so that we can
|
||||
* provide the correct si_code value with the signal.
|
||||
/* Send the signal. We need to use this internal interface so that we
|
||||
* can provide the correct si_code value with the signal.
|
||||
*/
|
||||
|
||||
(void)sig_received(ptcb, &info);
|
||||
sched_unlock();
|
||||
}
|
||||
}
|
||||
#else
|
||||
# define task_sigchild(tcb,status)
|
||||
#endif
|
||||
@ -344,6 +379,12 @@ void task_exithook(FAR _TCB *tcb, int status)
|
||||
(void)lib_flushall(tcb->streams);
|
||||
#endif
|
||||
|
||||
/* Discard any un-reaped child status (no zombies here!) */
|
||||
|
||||
#if defined(CONFIG_SCHED_HAVE_PARENT) && defined(CONFIG_SCHED_CHILD_STATUS)
|
||||
task_removechildren(tcb);
|
||||
#endif
|
||||
|
||||
/* Free all file-related resources now. This gets called again
|
||||
* just be be certain when the TCB is delallocated. However, we
|
||||
* really need to close files as soon as possible while we still
|
||||
|
@ -141,7 +141,8 @@ int task_init(FAR _TCB *tcb, const char *name, int priority,
|
||||
|
||||
/* Initialize the task control block */
|
||||
|
||||
ret = task_schedsetup(tcb, priority, task_start, entry);
|
||||
ret = task_schedsetup(tcb, priority, task_start, entry,
|
||||
TCB_FLAG_TTYPE_TASK);
|
||||
if (ret == OK)
|
||||
{
|
||||
/* Setup to pass parameters to the new task */
|
||||
|
@ -71,6 +71,9 @@
|
||||
|
||||
int task_reparent(pid_t ppid, pid_t chpid)
|
||||
{
|
||||
#ifdef CONFIG_SCHED_CHILD_STATUS
|
||||
FAR struct child_status_s *child;
|
||||
#endif
|
||||
_TCB *ptcb;
|
||||
_TCB *chtcb;
|
||||
_TCB *otcb;
|
||||
@ -127,12 +130,30 @@ int task_reparent(pid_t ppid, pid_t chpid)
|
||||
|
||||
/* Then reparent the child */
|
||||
|
||||
chtcb->parent = ppid; /* The task specified by ppid is the new parent */
|
||||
|
||||
#ifdef CONFIG_SCHED_CHILD_STATUS
|
||||
/* Remove the child status entry from old parent TCB */
|
||||
|
||||
child = task_removechild(otcb, chpid);
|
||||
if (child)
|
||||
{
|
||||
/* Add the child status entry to the new parent TCB */
|
||||
|
||||
task_addchild(ptcb, child);
|
||||
ret = OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = -ENOENT;
|
||||
}
|
||||
#else
|
||||
DEBUGASSERT(otcb->nchildren > 0);
|
||||
|
||||
chtcb->parent = ppid; /* The task specified by ppid is the new parent */
|
||||
otcb->nchildren--; /* The orignal parent now has one few children */
|
||||
ptcb->nchildren++; /* The new parent has one additional child */
|
||||
ret = OK;
|
||||
#endif
|
||||
|
||||
errout_with_ints:
|
||||
irqrestore(flags);
|
||||
|
@ -154,6 +154,7 @@ static int task_assignpid(FAR _TCB *tcb)
|
||||
*
|
||||
* Parameters:
|
||||
* tcb - The TCB of the new, child task.
|
||||
* ttype - Type of the new thread: task, pthread, or kernel thread
|
||||
*
|
||||
* Returned Value:
|
||||
* None
|
||||
@ -165,13 +166,57 @@ static int task_assignpid(FAR _TCB *tcb)
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_SCHED_HAVE_PARENT
|
||||
static inline void task_saveparent(FAR _TCB *tcb)
|
||||
static inline void task_saveparent(FAR _TCB *tcb, uint8_t ttype)
|
||||
{
|
||||
FAR _TCB *rtcb = (FAR _TCB*)g_readytorun.head;
|
||||
|
||||
DEBUGASSERT(rtcb->nchildren < UINT16_MAX);
|
||||
/* Save the parent task's ID in the child task's TCB. I am not sure if
|
||||
* this makes sense for the case of pthreads or not, but I don't think it
|
||||
* is harmful in any event.
|
||||
*/
|
||||
|
||||
tcb->parent = rtcb->pid;
|
||||
|
||||
/* Exit status only needs to be retained for the case of tasks, however */
|
||||
|
||||
if (ttype == TCB_FLAG_TTYPE_TASK)
|
||||
{
|
||||
#ifdef CONFIG_SCHED_CHILD_STATUS
|
||||
FAR struct child_status_s *child;
|
||||
|
||||
/* Make sure that there is not already a structure for this PID in the
|
||||
* parent TCB. There should not be.
|
||||
*/
|
||||
|
||||
child = task_findchild(rtcb, tcb->pid);
|
||||
DEBUGASSERT(!child);
|
||||
if (!child)
|
||||
{
|
||||
/* Allocate a new status structure */
|
||||
|
||||
child = task_allocchild();
|
||||
}
|
||||
|
||||
/* Did we successfully find/allocate the child status structure? */
|
||||
|
||||
DEBUGASSERT(child);
|
||||
if (child)
|
||||
{
|
||||
/* Yes.. Initialize the structure */
|
||||
|
||||
child->ch_flags = ttype;
|
||||
child->ch_pid = tcb->pid;
|
||||
child->ch_status = 0;
|
||||
|
||||
/* Add the entry into the TCB list of children */
|
||||
|
||||
task_addchild(rtcb, child);
|
||||
}
|
||||
#else
|
||||
DEBUGASSERT(rtcb->nchildren < UINT16_MAX);
|
||||
rtcb->nchildren++;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#else
|
||||
# define task_saveparent(tcb)
|
||||
@ -235,7 +280,7 @@ static inline void task_dupdspace(FAR _TCB *tcb)
|
||||
* priority - Priority of the new task
|
||||
* entry - Entry point of a new task
|
||||
* main - Application start point of the new task
|
||||
* type - Type of the new thread: task, pthread, or kernel thread
|
||||
* ttype - Type of the new thread: task, pthread, or kernel thread
|
||||
*
|
||||
* Return Value:
|
||||
* OK on success; ERROR on failure.
|
||||
@ -245,7 +290,8 @@ static inline void task_dupdspace(FAR _TCB *tcb)
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int task_schedsetup(FAR _TCB *tcb, int priority, start_t start, main_t main)
|
||||
int task_schedsetup(FAR _TCB *tcb, int priority, start_t start, main_t main,
|
||||
uint8_t ttype)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@ -264,9 +310,17 @@ int task_schedsetup(FAR _TCB *tcb, int priority, start_t start, main_t main)
|
||||
tcb->start = start;
|
||||
tcb->entry.main = main;
|
||||
|
||||
/* Save the thrad type. This setting will be needed in
|
||||
* up_initial_state() is called.
|
||||
*/
|
||||
|
||||
ttype &= TCB_FLAG_TTYPE_MASK;
|
||||
tcb->flags &= ~TCB_FLAG_TTYPE_MASK;
|
||||
tcb->flags |= ttype;
|
||||
|
||||
/* Save the task ID of the parent task in the TCB */
|
||||
|
||||
task_saveparent(tcb);
|
||||
task_saveparent(tcb, ttype);
|
||||
|
||||
/* exec(), pthread_create(), task_create(), and vfork() all
|
||||
* inherit the signal mask of the parent thread.
|
||||
|
@ -136,12 +136,6 @@ FAR _TCB *task_vforksetup(start_t retaddr)
|
||||
|
||||
(void)env_dup(child);
|
||||
|
||||
/* Mark the type of this thread (this setting will be needed in
|
||||
* task_schedsetup() when up_initial_state() is called.
|
||||
*/
|
||||
|
||||
child->flags |= TCB_FLAG_TTYPE_TASK;
|
||||
|
||||
/* Get the priority of the parent task */
|
||||
|
||||
#ifdef CONFIG_PRIORITY_INHERITANCE
|
||||
@ -153,7 +147,8 @@ FAR _TCB *task_vforksetup(start_t retaddr)
|
||||
/* Initialize the task control block. This calls up_initial_state() */
|
||||
|
||||
svdbg("Child priority=%d start=%p\n", priority, retaddr);
|
||||
ret = task_schedsetup(child, priority, retaddr, parent->entry.main);
|
||||
ret = task_schedsetup(child, priority, retaddr, parent->entry.main,
|
||||
TCB_FLAG_TTYPE_TASK);
|
||||
if (ret != OK)
|
||||
{
|
||||
goto errout_with_tcb;
|
||||
|
Loading…
Reference in New Issue
Block a user