Why Are Several Signal Numbers Architecture-Dependent on Linux

Why signals are platform-dependent in Linux?

The same is true of a lot of things that shouldn't be platform-dependent: ioctl numbers, syscall numbers, fcntl numbers, stat and sysv ipc structures, mmap flags, etc. etc. etc. The reason is historical mistakes.

Early on, Linux modeled each platform's definitions on the predominant existing proprietary unix used there. For some, there was an attempt to actually be able to run binaries from the proprietary unix, and in some rare cases, for a while, it kind of worked. For the rest of was just a colossal waste of effort and technical debt. Even where it did work, these old systems are long gone.

Modern Linux has somewhat remedied this, using unified numbering for all new syscalls, unified asm-generic definitions for new types, etc. But all the legacy baggage still exists.

Command KILL can send all signals?

Yes, it can.

You can use it like this:

kill [options] <pid> [...]

Example:

kill -USR1 6127

This will send the USR1 signal to process with pid 6127

Or else with signals numbers:

kill -9 6127

All this is detailed on kill manual and you can see it typing man kill on your terminal, the output will be something like this:

NAME
kill - send a signal to a process

SYNOPSIS
kill [options] [...]

DESCRIPTION
The default signal for kill is TERM. Use -l or -L to list available signals. Particularly useful signals include HUP, INT,
KILL, STOP, CONT, and 0. Alternate signals may be specified in three
ways:
-9, -SIGKILL or -KILL. Negative PID values may be used to choose whole process groups; see the PGID column in ps command output.
A PID of -1 is special; it indicates all processes except the kill
process itself and init.

You can type kill -l to see a short list of signals or you can take a look at the signal(7) manual with man 7 signal to see a complete list of signals with descriptions:

Standard signals
Linux supports the standard signals listed below. Several signal numbers are architecture-dependent, as indicated in the "Value"
column. (Where three values are given, the first one is usually valid
for alpha and sparc, the middle one for x86, arm, and most other architectures, and the last one for mips. (Values for parisc
are not shown; see the Linux kernel source for signal numbering on
that
architecture.) A - denotes that a signal is absent on the corresponding architecture.)

   First the signals described in the original POSIX.1-1990 standard.

Signal Value Action Comment
──────────────────────────────────────────────────────────────────────
SIGHUP 1 Term Hangup detected on controlling terminal
or death of controlling process
SIGINT 2 Term Interrupt from keyboard
SIGQUIT 3 Core Quit from keyboard
SIGILL 4 Core Illegal Instruction
SIGABRT 6 Core Abort signal from abort(3)
SIGFPE 8 Core Floating point exception
SIGKILL 9 Term Kill signal
SIGSEGV 11 Core Invalid memory reference
SIGPIPE 13 Term Broken pipe: write to pipe with no
readers
SIGALRM 14 Term Timer signal from alarm(2)
SIGTERM 15 Term Termination signal
SIGUSR1 30,10,16 Term User-defined signal 1
SIGUSR2 31,12,17 Term User-defined signal 2
SIGCHLD 20,17,18 Ign Child stopped or terminated
SIGCONT 19,18,25 Cont Continue if stopped
SIGSTOP 17,19,23 Stop Stop process
SIGTSTP 18,20,24 Stop Stop typed at terminal
SIGTTIN 21,21,26 Stop Terminal input for background process
SIGTTOU 22,22,27 Stop Terminal output for background process

The signals SIGKILL and SIGSTOP cannot be caught, blocked, or ignored.

And a lot of more info about signals. ;)

How to use SIGUSR1 alternate values

The manual says

Several signal numbers are architecture dependent, as indicated in the "Value" column. (Where three values are given, the first one is usually valid for alpha and sparc, the middle one for i386, ppc and sh, and the last one for mips. A - denotes that a signal is absent on the corresponding architecture.)

The SIGUSR1 will have exactly one value on whatever machine you're running on ... there are no alternate values; just use the defined constant SIGUSR1. Using 16 will give you the wrong signal ... look further in the table and you will see SIGSTKFLT.

I need one more custom signal

What's wrong with SIGUSR2?

Precisely Why Does uname -m Report the Wrong Architecture When Run By Sun Grid Engine (SGE)?

uname -m is used to report the personality(2) of the 'virtual machine' running the code. Because the kernel can run code with different personalities (say, 32-bit code on a 64-bit machine, complete with 'only' a 32-bit address space, or short inodes, different signal numbers, or similar constraints), programs might use the output of uname -m to determine how they should run -- i.e., which kernel interfaces they will get at runtime.

So it is important that uname -m reflect the personality, not the full extent of the hardware.

Perhaps you can insert a call to setarch(8) or personality(2) into your software before the fork(2) exec() of your uname -m command and subsequent helper programs.

Extract Program Counter (Instruction Pointer) in signal handler (ppc64)

I'm not aware of this being documented anywhere. However, setup_sigcontext for ppc64 fills in the gp_regs array from a struct pt_regs that forms part of the task state. Therefore, that struct can be taken as a guide for which registers are which. There is also a set of PT_Rxxx defines immediately below the definition of that struct, which confirms bits of the mapping that are not immediately obvious from the struct (e.g. general purpose register 1 is indeed in gp_regs[1]).

How does SIGSTOP work in Linux kernel?

It's been a while since I touched the kernel, but I'll try to give as much detail as possible. I had to look up some of this stuff in various other places, so some details might be a little messy, but I think this gives a good idea of what happens under the hood.

When a signal is raised, the TIF_SIGPENDING flag is set in the process descriptor structure. Before returning to user mode, the kernel tests this flag with test_thread_flag(TIF_SIGPENDING), which will return true (because a signal is pending).

The exact details of where this happens seem to be architecture dependent, but you can see an example for um:

void interrupt_end(void)
{
struct pt_regs *regs = ¤t->thread.regs;

if (need_resched())
schedule();
if (test_thread_flag(TIF_SIGPENDING))
do_signal(regs);
if (test_and_clear_thread_flag(TIF_NOTIFY_RESUME))
tracehook_notify_resume(regs);
}

Anyway, it ends up calling arch_do_signal(), which is also architecture dependent and is defined in the corresponding signal.c file (see the example for x86):

void arch_do_signal(struct pt_regs *regs)
{
struct ksignal ksig;

if (get_signal(&ksig)) {
/* Whee! Actually deliver the signal. */
handle_signal(&ksig, regs);
return;
}

/* Did we come from a system call? */
if (syscall_get_nr(current, regs) >= 0) {
/* Restart the system call - no handlers present */
switch (syscall_get_error(current, regs)) {
case -ERESTARTNOHAND:
case -ERESTARTSYS:
case -ERESTARTNOINTR:
regs->ax = regs->orig_ax;
regs->ip -= 2;
break;

case -ERESTART_RESTARTBLOCK:
regs->ax = get_nr_restart_syscall(regs);
regs->ip -= 2;
break;
}
}

/*
* If there's no signal to deliver, we just put the saved sigmask
* back.
*/
restore_saved_sigmask();
}

As you can see, arch_do_signal() calls get_signal(), which is also in signal.c.

The bulk of the work happens inside get_signal(), it's a huge function, but eventually it seems to process the special case of SIGSTOP here:

    if (sig_kernel_stop(signr)) {
/*
* The default action is to stop all threads in
* the thread group. The job control signals
* do nothing in an orphaned pgrp, but SIGSTOP
* always works. Note that siglock needs to be
* dropped during the call to is_orphaned_pgrp()
* because of lock ordering with tasklist_lock.
* This allows an intervening SIGCONT to be posted.
* We need to check for that and bail out if necessary.
*/
if (signr != SIGSTOP) {
spin_unlock_irq(&sighand->siglock);

/* signals can be posted during this window */

if (is_current_pgrp_orphaned())
goto relock;

spin_lock_irq(&sighand->siglock);
}

if (likely(do_signal_stop(ksig->info.si_signo))) {
/* It released the siglock. */
goto relock;
}

/*
* We didn't actually stop, due to a race
* with SIGCONT or something like that.
*/
continue;
}

See the full function here.

do_signal_stop() does the necessary processing to handle SIGSTOP, you can also find it in signal.c. It sets the task state to TASK_STOPPED with set_special_state(TASK_STOPPED), a macro that is defined in include/sched.h that updates the current process descriptor status. (see the relevant line in signal.c). Further down, it calls freezable_schedule() which in turn calls schedule(). schedule() calls __schedule() (also in the same file) in a loop until an eligible task is found. __schedule() attempts to find the next task to schedule (next in the code), and the current task is prev. The state of prev is checked, and because it was changed to TASK_STOPPED, deactivate_task() is called, which moves the task from the run queue to the sleep queue:

    } else {
...

deactivate_task(rq, prev, DEQUEUE_SLEEP | DEQUEUE_NOCLOCK);

...

}

deactivate_task() (also in the same file) removes the process from the runqueue by decrementing the on_rq field of the task_struct to 0 and calling dequeue_task(), which moves the process to the new (waiting) queue.

Then, schedule() checks the number of runnable processes and selects the next task to enter the CPU according to the scheduling policies in effect (I think this is a little bit out of scope by now).

At the end of the day, SIGSTOP moves a process from the runnable queue to a waiting queue until that process receives SIGCONT.

What is the interface for ARM system calls and where is it defined in the Linux kernel?

In ARM world, you do a software interrupt (mechanism to signal the kernel) by supervisor call / svc (previously called SWI).

ARM assembly (UAL) syntax looks like this:

SVC{<c>}{<q>} {#}<imm>

(In Linux you need to pass #0)

You should cheat from other projects like bionic or uClibc.

CentOS rpm package depends on lib soname which it builds using cmake

When RPM builds packages, it automatically scans all the installed files and creates a list of things (eg, executables, libraries) that are provided by the package, as well as dependencies needed (eg, shared libraries, system features).

The idea is that tools (like rpmbuild) can identify at least some of the program dependencies automatically, so they should automatically add them as the package dependencies.

Libraries are a bit funny, because they are treated as both dependencies and a feature provided by the RPM package.

AFAIK, a so-version enables this feature specifically because that signals to the RPM's dependency finder that this is a well-formed library that others might want to consume.

There's a couple of things you can do:

  • Assuming the package actually includes a libABC.so.1 and libABC.so.1()(64bit) is listed in the provides of the package, just let it be? It makes it easier for other programs to eventually depend on this library.

  • If you don't like this behaviour at all, you can just disable this entire feature. Use AutoReq: no.

kernel stack and user space stack

  1. What's the difference between kernel stack and user stack ?

In short, nothing - apart from using a different location in memory (and hence a different value for the stack pointer register), and usually different memory access protections. I.e. when executing in user mode, kernel memory (part of which is the kernel stack) will not be accessible even if mapped. Vice versa, without explicitly being requested by the kernel code (in Linux, through functions like copy_from_user()), user memory (including the user stack) is not usually directly accessible.


  1. Why is [ a separate ] kernel stack used ?

Separation of privileges and security. For one, user space programs can make their stack (pointer) anything they want, and there is usually no architectural requirement to even have a valid one. The kernel therefore cannot trust the user space stack pointer to be valid nor usable, and therefore will require one set under its own control. Different CPU architectures implement this in different ways; x86 CPUs automatically switch stack pointers when privilege mode switches occur, and the values to be used for different privilege levels are configurable - by privileged code (i.e. only the kernel).


  1. If a local variable is declared in an ISR, where will it be stored ?

On the kernel stack. The kernel (Linux kernel, that is) does not hook ISRs directly to the x86 architecture's interrupt gates but instead delegates the interrupt dispatch to a common kernel interrupt entry/exit mechanism which saves pre-interrupt register state before calling the registered handler(s). The CPU itself when dispatching an interrupt might execute a privilege and/or stack switch, and this is used/set up by the kernel so that the common interrupt entry code can already rely on a kernel stack being present.

That said, interrupts that occur while executing kernel code will simply (continue to) use the kernel stack in place at that point. This can, if interrupt handlers have deeply nested call paths, lead to stack overflows (if a deep kernel call path is interrupted and the handler causes another deep path; in Linux, filesystem / software RAID code being interrupted by network code with iptables active is known to trigger such in untuned older kernels ... solution is to increase kernel stack sizes for such workloads).


  1. Does each process have its own kernel stack ?

Not just each process - each thread has its own kernel stack (and, in fact, its own user stack as well). Remember the only difference between processes and threads (to Linux) is the fact that multiple threads can share an address space (forming a process).


  1. How does the process coordinate between both these stacks ?

Not at all - it doesn't need to. Scheduling (how / when different threads are being run, how their state is saved and restored) is the operating system's task and processes don't need to concern themselves with this. As threads are created (and each process must have at least one thread), the kernel creates kernel stacks for them, while user space stacks are either explicitly created/provided by whichever mechanism is used to create a thread (functions like makecontext() or pthread_create() allow the caller to specify a memory region to be used for the "child" thread's stack), or inherited (by on-access memory cloning, usually called "copy on write" / COW, when creating a new process).

That said, the process can influence scheduling of its threads and/or influence the context (state, amongst that is the thread's stack pointer). There are multiple ways for this: UNIX signals, setcontext(), pthread_yield() / pthread_cancel(), ... - but this is digressing a bit from the original question.



Related Topics



Leave a reply



Submit