layout: post
title: "Signals"
subtitle: "Another rewrite"
tags: [osdev]

Time for another live rewrite - or at least a restructuring.

###Signals in newlib

Newlib has a kind of signals built in without any help from the kernel.
Those signals can't be sent between processes, though, so I'd like to
implement my own.

I'll just look at `signal` and `kill` and ignore things like
`sigaction` for now.

First of all, in order to compile newlib with kernel supported signals,
you need the line

    :::make
    newlib_cflags="${newlib_cflags} -DSIGNAL_PROVIDED"

in your entry in `newlib/configure.host` as described in [the osdev
wiki](http://wiki.osdev.org/OS_Specific_Toolchain#Signal_handling).

Then you need the syscalls:

    :::c
    sig_t signal(int signum, sig_t handler);
    int kill(int pid, int sig);

###Raising signals

The [posix
specification](http://pubs.opengroup.org/onlinepubs/9699919799/)
contains a lot of rules about how signals should behave in a number of
situations. I probably got a lot of them wrong, but I'll try to fix them
as I go along later.

The way I chose to implement signals was through a list for each
process. When a signal is sent using `kill` or similar, a `signal_t`

    :::c
    typedef struct
    {
        uint32_t sig;
        uint32_t sender;
        list_head_t queue;
    } signal_t;

is created and added to the processes list:

    :::c
    int signal_process(int pid, int signum)
    {
        process_t *p = get_process(pid);
        ...
        signal_t *signal = calloc(1, sizeof(signal_t));
        signal->sig = signum;
        signal->sender = current->proc->pid;
        init_list(signal->queue);
        append_to_list(p->signal_queue, signal->queue);

        if(p == current->proc)
        {
            handle_signals(current);
        }
        return 0;
    }

If the currently running thread is in the process being signalled, we
handle the signals immediately. Otherwise it can wait for a bit.

`signal_process` is called by the `kill` syscall.

###The signal syscall

The `signal` syscall lets the process select how to handle a certain
signal. Each process also contains a table of `sig_t` and the `signal`
syscall calls the following function:

    :::c
    sig_t switch_handler(int signum, sig_t handler)
    {
        ...
        sig_t old = current->proc->signal_handler[signum];
        current->proc->signal_handler[signum] = handler;
        return old;
    }

The cut out part of this function contains code to make sure the handler
of signal 9 (`SIGKILL`) is never changed from `SIG_DFL`.

###Handling signals

Signals should be handled as soon as possible, though there's no reason
to let the receiving process skip the line in the scheduler. So as soon
as the process continues running should be soon enough.

I chose to hook into the my interrupt handler:

    :::c
    registers_t *idt_handler(registers_t *r)
    {
        ...
        if(int_handlers[r->int_no)
        {
            ...
            registers_t *ret = int_handlers[r->int_no](r);

            if((ret->cs & 0x3) == 0x3)
            {
                ret = (registers_t *)handle_signals((thread_t *)ret);
                ...
            }
            ...
            return ret
        }
        ...
    }

So, what does `handle_signals` actually do?

It start by finding the signal queue of the process to which the passed
thread belongs and steps through it one entry at a time. If a signal is
queued and not blocked (actual blocking is not implemented yet) it
checks what the handler for that signal number is. The handler may be
`SIG_IGN`, `SIG_DFL` or a pointer to a function in userspace.

If the handler is set to `SIG_DFL` the function looks up the correct
default signal handler in a table and calls it. It may be one of the
following:

    :::c
    void sighandler_ignore(int num)
    {
        (void)num;
    }
    void sighandler_terminate(int num)
    {
        fprintf(stderr,, "Process %x terminated by signal %x\n", \
            current->proc->pid, num);
        _exit(num);
    }
    void sighandler_coredump(int num)
    { /* Same as above */ }
    void sighandler_stop(int num)
    { /* Not implemented yet */ }
    void sighandler_continue(int num)
    { /* Not implemented yet */ }

If the handler is a function a new thread is created to handle it:

    :::c
        ...
        sig_t handler = th->proc->signal_handler[signal->sig];
        thread_t *h = new_thread((void (*)(void))handler, 1);

        append_to_list(th->proc->threads, h->process_threads);
        h->proc = th->proc;
        uint32_t *stack = ((uint32_t *)th->r.useresp;
        *--stack = signal->sig;
        *--stack = SIGNAL_RETURN_ADDRESS;
        h->r.useresp = h->r.ebp = (uint32_t)stack;
        remove_from_list(signal->queue);
        free(signal);

        scheduler_remove(h);
        scheduler_sleep(th, &h->waiting);
        scheduler_cheat(h);
        schedule();
    }

This creates a new thread and pushes the signal number (as an argument)
and a return address to the threads stack. It then places the new thread
at the beginning of the schedulers queue and switches to it.

`SIGNAL_RETURN_ADDRESS` is chosen to cause a page fault when the signal
handler returns. The page fault handler catches this and recognizes the
address so that the thread can be safely destroyed.

Now the page fault handler can also be setup to send a `SIGSEGV` signal
when a userspace thread page faults.

###Code

Git commit
[1ec132c](https://github.com/thomasloven/os5/tree/1ec132ce60ba627e522019d456e0cec993193153)
