Re: EVFILT_SIGNAL - observe "standard" signals before they are delivered?
Re: EVFILT_SIGNAL - observe "standard" signals before they are delivered?
- Subject: Re: EVFILT_SIGNAL - observe "standard" signals before they are delivered?
- From: Terry Lambert <email@hidden>
- Date: Thu, 6 Sep 2007 16:19:36 -0700
On Sep 5, 2007, at 12:00 PM, Jim Correia wrote:
The man page says:
EVFILT_SIGNAL Takes the signal number to monitor as the
identifier and
returns when the given signal is delivered to the
process.
This coexists with the signal() and sigaction()
facili-
ties, and has a lower precedence. The filter
will record
all attempts to deliver a signal to a process,
even if the
signal has been marked as SIG_IGN. Event
notification
happens after normal signal delivery processing.
data
returns the number of times the signal has
occurred since
the last call to kevent(). This filter
automatically sets
the EV_CLEAR flag internally.
Am I interpreting that correctly to mean that a signal whose default
behavior is process termination (SIGSEGV, for example) will not be
observable by the kevent mechanism?
Short answer:
No, it just means that you don't get to see the signal before the
process does and prevent its delivery; you are only an observer. It
also means that there is no guarantee of ordering, so you're not
allowed to make any assumptions about it.
-
Long answer:
Signals are very complicated on MacOS X due to interaction with the
Mach AST model (True64 UNIX has the same issues, as it is also Mach-
based, and uses the AST mechanism to deliver signals).
A signal starts as a Mach exception, which can be intercepted and
averted, if you hold the exception port for the process and know what
you are doing. For example, gdb only partially knows what it's doing;
if you don't turn off "start-with-subshell", which is on by default,
for example, gdb wil hold the exception port on the subshell rather
than the process being debugged, and you will not be able to override
signals, since you will only see them in gdb via ptrace, rather than
as a Mach exception.
After it goes to the task exception port because it hasn't been
intercepted at the Mach exception level, it becomes an AST_BSD, and
then is only handled when a thread stops being uninterruptible. It
becomes interruptible in Mach, which throws the AST at that point.
This means there can be considerable latency, if an operation is
uninterruptible, and it can also mean that the operation that was in
progress at the time of the exception being thrown may in fact have
completed successfully, rather than being interrupted (so, for
example, you seldom see a disk read getting interrupted by a signal
and returning EINTR).
After this, if the operation was interruptible (e.g. a tsleep/msleep
operation on a tty wait queue or whatever), the operation is
interrupted, and goes to return an EINTR up to the user/kernel
boundary (an error on the BSD system call).
When we run up to the user/kernel boundary, we return to an address
other than the address requested, with a bunch of state on the stack
(this boils down to a "siginfo" structure with potentially multiple
ucontext, mcontext, and other state structures dealing with process
state prior to the running of a signal handler). The address we
return to is several bytes off the actual system call stub return
address, and it jumps to the signal handler for the signal in question
(based, in part, on some contents of the siginfo structure which must
be treated as opaque by user space application).
The handler runs in user space, then calls a system call that says
"put my state back to what it was before the handler fired", and is
passed the context information we passed to user space in the first
place ("sigreturn").
At this point, the thread state is restored, and we return the EINTR
to the system call caller.
-
The thrown AST is caught in kern_sig.c, which is what throws the KNOTE
that causes the kevent to get delivered to you. Unless it gets here,
you don't get the event.
-
So you won't see the signal if you intentionally intercept it (mostly
by Mach means) prior to the thrown AST being turned into a "real"
signal, which in turn will get delivered when it's delivered, which
can be before or after your event gets delivered, depending on the
code path on which the exception happened.
You can ALSO not see a signal if the signal would have been EINTR, and
the operation was not an explicit signal send, AND the underlying
operation completed successfully because all the work it had to do
happened before the process (Mach task, actually) became
interruptible. Our UNIX certification has the same permanent
interpretation on this that True64 UNIX has, and that a couple of
vendors have had on their signal implementations in the past.
-- Terry
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Darwin-dev mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden