Update markdown and tabs and stuff
This commit is contained in:
@@ -17,15 +17,17 @@ I'll just look at `signal` and `kill` and ignore things like
|
||||
First of all, in order to compile newlib with kernel supported signals,
|
||||
you need the line
|
||||
|
||||
newlib_cflags="${newlib_cflags} -DSIGNAL_PROVIDED"
|
||||
:::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:
|
||||
|
||||
sig_t signal(int signum, sig_t handler);
|
||||
int kill(int pid, int sig);
|
||||
:::c
|
||||
sig_t signal(int signum, sig_t handler);
|
||||
int kill(int pid, int sig);
|
||||
|
||||
###Raising signals
|
||||
|
||||
@@ -38,31 +40,33 @@ 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`
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t sig;
|
||||
uint32_t sender;
|
||||
list_head_t queue;
|
||||
} 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:
|
||||
|
||||
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);
|
||||
:::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(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.
|
||||
@@ -75,13 +79,14 @@ 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:
|
||||
|
||||
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;
|
||||
}
|
||||
:::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`.
|
||||
@@ -94,24 +99,25 @@ as the process continues running should be soon enough.
|
||||
|
||||
I chose to hook into the my interrupt handler:
|
||||
|
||||
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
|
||||
}
|
||||
...
|
||||
}
|
||||
:::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?
|
||||
|
||||
@@ -125,43 +131,45 @@ 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:
|
||||
|
||||
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 */ }
|
||||
:::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:
|
||||
|
||||
...
|
||||
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();
|
||||
}
|
||||
:::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
|
||||
|
||||
Reference in New Issue
Block a user