Re: Problem with ucontext_t struct in signal handler
Glynn Clements <[email protected]>
| Newsgroups | org.kernel.vger.linux-c-programming |
|---|---|
| Message-ID | <[email protected]> |
XComp wrote:
> > In any case, I don't think that you can rely upon the old_context
> > being usable once you leave the signal handler. Even if you copy the
> > contents, the uc_mcontext field may not be meaningful outside of the
> > handler.
>
> So you think that solving the problem in this way will lead me in a
> dead end?
> But I am confused because of the fact that it works sometimes.
> Shouldn't it work the whole time or at no time only?
Intermittent faults are common with multi-threaded programming.
If a register is getting trashed, that will matter if you actually
need the register's value, but not if you're about to store a new
value there.
Looking at the disassembly:
movl $0, i
.L2:
cmpl $99999999, i
jg .L3
incl i
jmp .L2
.L3:
with absolute addresses:
8048606: movl $0, i
804860d:
8048610: cmpl $99999999, i
8048617:
804861a: jg 0x8048624
804861c: incl i
8048622: jmp 0x8048610
8048624: ...
my guess is that the flags aren't being saved, so when the signal
occurs between the "cmpl" and "jg" instructions, the sign flag is
being cleared, triggering the jump prematurely.
Adding the following:
printf("\nold_context.uc_mcontext.gregs[REG_EFL] = %08x\n",
((ucontext_t*) old_context)->uc_mcontext.gregs[REG_EFL]);
printf("\nold_context.uc_mcontext.gregs[REG_IPL] = %08x\n",
((ucontext_t*) old_context)->uc_mcontext.gregs[REG_EIP]);
shows the following:
[Signal Handler] SIGPROF was raised at 21883912...
old_context.uc_mcontext.gregs[REG_EFL] = 00000203
old_context.uc_mcontext.gregs[REG_IPL] = 08048622
[Signal Handler] SIGPROF was raised at 43508833...
old_context.uc_mcontext.gregs[REG_EFL] = 00000203
old_context.uc_mcontext.gregs[REG_IPL] = 08048610
[Signal Handler] SIGPROF was raised at 65140032...
old_context.uc_mcontext.gregs[REG_EFL] = 00000297
old_context.uc_mcontext.gregs[REG_IPL] = 0804861a
[Thread Function] Error: 1st counting didn't finished (65140032)...
More precisely, in the cases where it fails, EIP is always 0x0804861a
(the "jg" instruction), and EFL always has the sign flag (0x80) set.
In the cases where it succeeds:
[Signal Handler] SIGPROF was raised at 22107659...
old_context.uc_mcontext.gregs[REG_EFL] = 00000297
old_context.uc_mcontext.gregs[REG_IPL] = 0804861c
[Signal Handler] SIGPROF was raised at 43727791...
old_context.uc_mcontext.gregs[REG_EFL] = 00000207
old_context.uc_mcontext.gregs[REG_IPL] = 08048610
[Signal Handler] SIGPROF was raised at 65357434...
old_context.uc_mcontext.gregs[REG_EFL] = 00000203
old_context.uc_mcontext.gregs[REG_IPL] = 08048610
[Signal Handler] SIGPROF was raised at 86987791...
old_context.uc_mcontext.gregs[REG_EFL] = 00000283
old_context.uc_mcontext.gregs[REG_IPL] = 0804861c
[Thread Function] 1st counting worked fine...
the above scenario never occurs.
It appears that you can't even use old_context to jump out of the
handler. In fact, the setcontext(2) manual page says:
If the context was obtained by a call to a signal handler, then old
standard text says that "program execution continues with the program
instruction following the instruction interrupted by the signal". How-
ever, this sentence was removed in SUSv2, and the present verdict is
"the result is unspecified".
I suspect that by the time that the kernel passes control to
user-space, the flags which libc saves are no longer correct. If
that's the case, the only solution is to return from the signal
handler back into the kernel, and allow the kernel to restore the
registers.
Note that the original code doesn't have this problem. The
swapcontext() which jumps out of the signal handler into user-space
jumps to a context which has just been created by makecontext() to
invoke scheduler().
And scheduler() never resumes a saved user-space context. The context
which it passes to setcontext() is either the original context created
by makecontext(), or it is the signal-space context which was saved
from within the signal handler by the swapcontext(). In the latter
case, the task resumes within the signal handler, from which it
returns normally.
So the code never attempts to resume a saved user-space context from
within the signal handler. From the perspective of the thread1() or
thread2() function, it gets a signal, enters the signal handler, then
(after all of the other tasks have had a turn) leaves the signal
handler.
--
Glynn Clements <[email protected]>