Zbr's days.
January
Sun Mon Tue Wed Thu Fri Sat
 
23
     
2007
Months
Jan

About :: TODO :: Blog :: RSS :: Old blog :: Projects :: GIT :: Gallery :: Notes

Tue, 23 Jan 2007

How do signals work in Linux?


Likely it is the same in all other Unix systems too, but I only checked Linux kernel.

There are two types of signals - synchronous, which are for example result of error operation, they always happen synchronously after the wrong operation, and asynchronous ones - they can be delivered at any time, for example using kill() call.
No matter what type of signal we received, it was produced exactly the same way.

When signal is generated and it is not blocked, mask of pending signals is updated. Later (when process is scheduled for execution) kernel will examine mask of pending signals and if there are any, it will start to deliver them one-by-one. If handler is set to SIG_DFL or SIG_IGN either process will be killed (actually default action will take place), or signal will be dropped. The most interesting case thus is when there is our own handler.
In that case kernel will eventually call setup_frame() function, which will setup new signal stack (or use existing if SA_ONSTACK flag is set), save current context (copy registers, error value, some thread info and other interesting information), setup return call (function which will be called when signal handler is completed to return back to kernel). Save context procedure includes filling struct sigframe, which contains all info needed to continue to run interrupted task and/or schedule it after some time.

After some GNU asm learning and googling, I've managed to run function on top of its own stack. Code (for x86) is pretty simple:

	asm volatile (
		"mov	%0,%%eax	\n" /* Start address */
		"mov	%1,%%ebx	\n" /*  Arguments */
		"mov	%%esp,%%edx	\n" /* save old sp to edx */
		"mov	%2,%%esp	\n" /* change stack */

		"push	%%edx		\n"
		"push	%%ebx		\n" /* copy arguments to new stack */

		"call	*%%eax		\n" /* call (*func)(arg); */
		"mov	4(%%esp),%%edx	\n"
		"mov	%%edx,%%esp\n" /* restore old stack */
		:
		: "g"(func), "g"(data), "g"(stack+stack_size)
		: "eax", "ebx", "edx", "esp"
		);
It was lurked in PTL threading library, which is the only one which does support preemptive userspace scheduling. Provided function is indeed called on its own stack. But when signal is delivered, its $ebp does not contain that stack, but instead it contains address somewhere in the middle of the new stack, which rised a suspicion, that glibc installs own signal handler, and then calls my, but experiments with x86_64 test machine showed, that kernel indeed jumps directly into my own signal handler. Unfortunately I do not have fast x86 test machine, and do not want to spread power to two arches currently, so I will setup my VIA C3 test machine and will run some signal tests on its modified kernel to detect, where exactly stack pointer for interrupted context is stored. When this issue will be resolved, correct preemptible scheduler for M-on-N threading model implementation will be just a matter of hours.

/devel/threading :: Link / Comments (0)

Please solve this captcha to be allowed to post (need to reload in a minute): 24 - 51

Comments are closed for this story.