-
Notifications
You must be signed in to change notification settings - Fork 7
Signals
Signals are the lowest level inter-process communication API offered by the Serena kernel. They can not carry any payload and there is only a limited set of them available. Currently 32 distinct signals are supported by the kernel.
Note that signals in Serena OS are quite different from other systems like Linux, FreeBSD, etc and they are much closer in their behavior to how signals work in systems like AmigaDOS and TRIPOS.
First, contrary to POSIX, Serena OS makes a clear distinction between signals as an IPC mechanism and CPU exceptions which are handle3d by a separate API. POSIX lumps those two things together into a single solution which makes working with signals harder than necessary and negatively impacts their performance.
Secondly, signals in Serena OS do not interrupt system calls and system calls remain undisturbed by the arrival of a signal. You wait for signals with the sigwait() or sigtimedwait() functions or the higher-level dispatch queue signal monitor API.
Finally, Serena OS gives you explicit control over how signals that are sent to a process should be routed to a particular virtual processor or virtual processor group.
Many of the signal APIs expect you to pass a signal scope parameter. A signal scope defines the set of processes or virtual processors that should receive and react to a signal. Currently the following scopes are defined:
- Virtual processor group
- Virtual processor
- Process
- Process children
- Process group
- Process session
There are fundamentally two different kinds of signalling operations: A process X signalling a process Y and a virtual processor A (or the kernel) signalling a virtual processor B. The first kind of signalling is also known as "external signalling" whereas the second kind of signalling is known as "internal signalling".
In the scenario where a signal is sent 'internally' between two different virtual processors living inside the same process or between the kernel and a virtual processor, things are simple and straight forward because the signal is from the start targeting a specific virtual processor or virtual processor group. Because of that signal delivery is very efficient and security rules do not come into play.
In the other case though, where a signal is generate by process X and sent to process Y, the question arises to which virtual processor or virtual processor group inside of process Y the signal should be delivered for consumption.
The answer to that question in Serena OS is that you explicitly specify the route for such a signal: you tell the system to which virtual processor or virtual processor group a signal should be routed that was sent to the process. You do this with the help of the sigroute() function. This way your application has explicit control over how process-level signals should be distributed inside the application. This also makes it easy to route a process-level signal to a serial or concurrent dispatch queue which will then invoke a signal monitor when a signal is sent to the application.
Every signal is associated with a default route that the signal takes if no specific route is set up. The following table lists the supported signals and their default routes:
| Signal | Routable | Default Action | Description |
|---|---|---|---|
| SIGKILL | No | Terminate process | Force terminates a process |
| SIGVPRQ | No | Relinquish VCPU | Force relinquish a VCPU (kernel only) |
| SIGVPDS | No | Suspend VCPU | Safe-suspend a VCPU (kernel only) |
| SIGABRT | Yes | Terminate process | Triggered by the abort() function |
| SIGSTOP | No | Stop process | Force-suspends a process |
| SIGTSTP | Yes | Stop process | TTY: stop process |
| SIGCONT | No | Continue process | TTY: continue process |
| SIGXCPU | Yes | Terminate process | Process exceeded its CPU time limit |
| SIGHUP | Yes | TBD | TBD |
| SIGQUIT | Yes | Terminate process | TTY: terminate process |
| SIGINT | Yes | None | TTY: interrupt process |
| SIGALRM | Yes | TBD | TBD |
| SIGCHILD | Yes | None | Status of child process changed |
| SIGWINCH | Yes | None | TTY: terminal screen size changed |
| SIGTTIN | Yes | Stop process | TTY: background process tried to read from stdin |
| SIGTTOUT | Yes | Stop process | TTY: background process tried to write to stdout |
The remaining signals are user signals that may be used for any purpose. User signals do not have any default action associated with them.
You send a signal to a specific virtual processor or virtual processor group inside your process or another process, process group or session with the sigsend() function.
You receive a signal by invoking either the sigwait() or sigtimedwait() function. These functions block the caller until a signal has arrived. They immediately return if a signal is already pending when they are invoked. The difference between sigwait() and sigtimedwait() is that the former will wait indefinitely until a signal arrives while the later allows you to specify a timeout value. The function will return with ETIMEDOUT if the timeout is reached and no signal has arrived.
These functions return at most one signal at a time. If more than one signal is pending then they pick the highest priority signal and return it. You invoke the function again to receive the next lower priority signal.
You use the sigpending() function to find out which signals are currently pending on a virtual processor. It returns a bit set of all pending signals. Note that this function does not clear the pending signals. Use the signal waiting functions to actually clear/consume the signal.