A process can terminate in one of the following basic ways:
In some operating systems, if a parent process dies, then all of its child processes die too. This isn't the case in QNX Neutrino.
When a process terminates—no matter why—all of its resources are cleaned up:
A process can terminate itself by having any thread in the process call exit(). Returning from main() (i.e., in the main thread) also terminates the process, because the code that's returned to calls exit(). This isn't true of threads other than the main thread; returning normally from one of them causes pthread_exit() to be called, which terminates only that thread.
The value passed to exit() or returned from main() is called the exit status.
When a process dies by calling exit(), "normal exit processing" happens. This includes:
A process can be terminated by a signal for a number of reasons. Ultimately, all of these reasons will result in a signal's being set on the process. A signal is something that can interrupt the flow of your threads at any time. The default action for most signals is to terminate the process.
Here are some of the reasons that a process might be terminated by a signal:
kill( pid, SIGTERM );
When a process dies due to a signal that isn't handled or masked, "normal exit processing" doesn't happen, so this is often called abnormal termination of a process.
To get the kernel to display some diagnostics whenever a process terminates abnormally, configure procnto with multiple -v options. If the process has fd 2 open, then the diagnostics are displayed using (stderr); otherwise; you can specify where the diagnostics get displayed by using the -D option to your startup. For example, the -D as used in this buildfile excerpt will cause the output to go to a serial port:
[virtual=x86,bios +compress] .bootstrap = { startup-bios -D 8250..115200 procnto -vvvv }
You can also have the current state of a terminated process written to a file so that you can later bring up the debugger and examine just what happened. This type of examination is called postmortem debugging. This happens only if the process is terminated due to one of these signals:
Signal | Description |
---|---|
SIGABRT | Program-called abort function |
SIGBUS | Parity error |
SIGEMT | EMT instruction (emulation trap)
Note that SIGEMT and SIGDEADLK (mutex deadlock; see SyncMutexEvent()) refer to the same signal. |
SIGFPE | Floating-point error or division by zero |
SIGILL | Illegal instruction executed
One possible cause for this signal is trying to perform an operation that requires I/O privileges. To request these privileges:
|
SIGQUIT | Quit |
SIGSEGV | Segmentation violation |
SIGSYS | Bad argument to a system call |
SIGTRAP | Trace trap (not reset when caught) |
SIGXCPU | Exceeded the CPU limit |
The process that dumps the state to a file when the process terminates is called dumper, which must be running when the abnormal termination occurs. This is extremely useful, because embedded systems may run unassisted for days or even years before a crash occurs, making it impossible to reproduce the actual circumstances leading up to the crash.
A process must have one or more threads. If the number of threads in a process goes to 0, the process is terminated.
When a thread calls pthread_exit(), that thread is terminated. If any thread but the main thread returns from its thread function, then the wrapping code calls pthread_exit(). If the last thread in a process calls pthread_exit(), then the process is terminated; in this case, "normal exit processing" doesn't happen.