posix signals support, 1st pass

git-svn-id: file:///srv/svn/repos/haiku/trunk/current@1623 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
lillo 2002-10-23 17:31:10 +00:00
parent 54d6a27c67
commit f510e6ce60
24 changed files with 789 additions and 196 deletions

View File

@ -60,6 +60,9 @@ status_t sys_get_next_thread_info(team_id team, int32 *cookie, thread_info *info
status_t sys_get_team_info(team_id id, team_info *info);
status_t sys_get_next_team_info(int32 *cookie, team_info *info);
int sys_kill(pid_t pid, int sig);
int sys_sigaction(int sig, const struct sigaction *act, struct sigaction *oact);
region_id sys_vm_create_anonymous_region(const char *name, void **address, int addr_type,
addr size, int wiring, int lock);
region_id sys_vm_clone_region(const char *name, void **address, int addr_type,

View File

@ -21,6 +21,7 @@ extern "C" {
#include <cbuf.h>
#include <vm.h>
#include <smp.h>
#include <signal.h>
#include <arch/thread_struct.h>
extern spinlock_t thread_spinlock;
@ -101,7 +102,11 @@ struct thread {
int state;
int next_state;
union cpu_ent *cpu;
int pending_signals;
sigset_t sig_pending;
sigset_t sig_block_mask;
struct sigaction sig_action[32];
bool in_kernel;
sem_id sem_blocking;
int sem_count;

View File

@ -178,6 +178,10 @@ enum {
#define EOPNOTSUPP (B_POSIX_ERROR_BASE + 43)
#define ENOTSOCK (B_POSIX_ERROR_BASE + 44)
#define ERESTARTSYS (B_POSIX_ERROR_BASE + 45)
#define ERESTARTNOINTR (B_POSIX_ERROR_BASE + 46)
#define ERESTARTNOHAND (B_POSIX_ERROR_BASE + 47)
#define ENOMEM B_NO_MEMORY
#define EACCES B_PERMISSION_DENIED
#define EINTR B_INTERRUPTED

166
headers/posix/signal.h Normal file
View File

@ -0,0 +1,166 @@
#ifndef _SIGNAL_H_
#define _SIGNAL_H_
/*
** Distributed under the terms of the OpenBeOS License.
*/
#include <sys/types.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef int sig_atomic_t;
typedef void (*__signal_func_ptr)(int);
__signal_func_ptr signal(int signal, __signal_func_ptr signal_func);
int raise(int signal);
#ifdef __cplusplus
}
#endif
#define SIG_DFL ((__signal_func_ptr) 0)
#define SIG_IGN ((__signal_func_ptr) 1)
#define SIG_ERR ((__signal_func_ptr)-1)
/*
The numbering of signals for BeOS attempts to maintain
some consistency with UN*X conventions so that things
like "kill -9" do what you expect.
*/
#define SIGHUP 1 /* hangup -- tty is gone! */
#define SIGINT 2 /* interrupt */
#define SIGQUIT 3 /* `quit' special character typed in tty */
#define SIGILL 4 /* illegal instruction */
#define SIGCHLD 5 /* child process exited */
#define SIGABRT 6 /* abort() called, dont' catch */
#define SIGPIPE 7 /* write to a pipe w/no readers */
#define SIGFPE 8 /* floating point exception */
#define SIGKILL 9 /* kill a team (not catchable) */
#define SIGSTOP 10 /* suspend a thread (not catchable) */
#define SIGSEGV 11 /* segmentation violation (read: invalid pointer) */
#define SIGCONT 12 /* continue execution if suspended */
#define SIGTSTP 13 /* `stop' special character typed in tty */
#define SIGALRM 14 /* an alarm has gone off (see alarm()) */
#define SIGTERM 15 /* termination requested */
#define SIGTTIN 16 /* read of tty from bg process */
#define SIGTTOU 17 /* write to tty from bg process */
#define SIGUSR1 18 /* app defined signal 1 */
#define SIGUSR2 19 /* app defined signal 2 */
#define SIGWINCH 20 /* tty window size changed */
#define SIGKILLTHR 21 /* be specific: kill just the thread, not team */
#define SIGTRAP 22
#define SIGBUS SIGSEGV /* for old style code */
/*
Signal numbers 23-32 are currently free but may be used in future
releases. Use them at your own peril (if you do use them, at least
be smart and use them backwards from signal 32).
*/
#define __signal_max 22
#define NSIG (__signal_max+1)
typedef long sigset_t;
/*
The Posix interface for signal handling functions isn't as useful
as it could be. The standard indicates that only a single argument
(the signal number) is passed to the signal handler. It is useful
to have more information and the BeOS provides two extra arguments.
However, to remain compatible with Posix and ANSI C, we declare the
sa_handler field of the sigaction struct as type __signal_func_ptr.
That means you'll need to cast any function you assign to the
sa_handler field. NOTE: C++ member functions can not be signal
handlers (because they expect a "this" pointer as the first
argument).
The 3 arguments that the BeOS provides to signal handlers are as
follows:
- The first argument is the signal number (as an integer).
- The next argument is whatever value is put in the sa_userdata field
of the sigaction struct.
- The last argument is a pointer to a vregs struct (defined
below). The vregs struct contains the contents of the volatile
registers at the time the signal was delivered to your thread.
You can change the fields of the structure. After your signal
handler completes, the OS uses this struct to reload the
registers for your thread (privileged registers are not loaded
of course). The vregs struct is of course terribly machine
dependent and is guaranteed to change, potentially even between
different models of the PowerPC family. If you use it, you
should expect to have to re-work your code when new processors
come out. Nonetheless the ability to change the registers does
open some interesting programming possibilities.
*/
struct sigaction {
__signal_func_ptr sa_handler;
sigset_t sa_mask;
int sa_flags;
void *sa_userdata; /* will be passed to the signal handler */
};
typedef struct stack_t {
void *ss_sp;
size_t ss_size;
int ss_flags;
} stack_t;
#define SA_NOCLDSTOP 0x01 /* for sa_flags */
#define SA_ONESHOT 0x02
#define SA_NOMASK 0x04
#define SA_NODEFER SA_NOMASK
#define SA_RESTART 0x08
#define SA_STACK 0x10
int sigaction(int sig, const struct sigaction *act, struct sigaction *oact);
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int signo);
int sigdelset(sigset_t *set, int signo);
int sigismember(const sigset_t *set, int signo);
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
extern const char * const sys_siglist[NSIG];
const char *strsignal(int sig);
const void set_signal_stack(void *ptr, size_t size);
int sigaltstack(const stack_t *ss, stack_t *oss); /* XXXdbg */
extern inline int
sigismember(const sigset_t *set, int sig)
{
sigset_t mask = (((sigset_t) 1) << (( sig ) - 1)) ;
return (*set & mask) ? 1 : 0 ;
}
extern inline int
sigaddset(sigset_t *set, int sig)
{
sigset_t mask = (((sigset_t) 1) << (( sig ) - 1)) ;
return ((*set |= mask), 0) ;
}
extern inline int
sigdelset(sigset_t *set, int sig)
{
sigset_t mask = (((sigset_t) 1) << (( sig ) - 1)) ;
return ((*set &= ~mask), 0) ;
}
#define SIG_BLOCK 1 /* defines for the how arg of sigprocmask() */
#define SIG_UNBLOCK 2
#define SIG_SETMASK 3
int sigpending(sigset_t *set);
int sigsuspend(const sigset_t *mask);
int kill(pid_t pid, int sig);
int send_signal(pid_t tid, uint sig);
#endif /* _SIGNAL_H_ */

View File

@ -18,6 +18,10 @@ void arch_thread_switch_kstack_and_call(struct thread *t, addr new_kstack, void
//struct thread *arch_thread_get_current_thread(void);
//void arch_thread_set_current_thread(struct thread *t);
void arch_setup_signal_frame(struct thread *t, struct sigaction *sa, int sig, int sig_mask);
int64 arch_restore_signal_frame(void);
void arch_check_syscall_restart(struct thread *t);
// for any inline overrides
#include <arch_thread.h>

View File

@ -95,6 +95,8 @@ struct iframe {
unsigned int edx;
unsigned int ecx;
unsigned int eax;
unsigned int orig_eax;
unsigned int orig_edx;
unsigned int vector;
unsigned int error_code;
unsigned int eip;
@ -112,6 +114,10 @@ void i386_enter_uspace(addr entry, void *args, addr ustack_top);
void i386_set_kstack(addr kstack);
void i386_switch_stack_and_call(addr stack, void (*func)(void *), void *arg);
void i386_swap_pgdir(addr new_pgdir);
void i386_fsave(void *fpu_state);
void i386_fxsave(void *fpu_state);
void i386_frstor(void *fpu_state);
void i386_fxrstor(void *fpu_state);
void i386_fsave_swap(void *old_fpu_state, void *new_fpu_state);
void i386_fxsave_swap(void *old_fpu_state, void *new_fpu_state);

View File

@ -15,6 +15,9 @@ extern "C" {
void i386_push_iframe(struct thread *t, struct iframe *frame);
void i386_pop_iframe(struct thread *t);
void i386_return_from_signal();
void i386_end_return_from_signal();
static
inline struct thread *

View File

@ -18,6 +18,7 @@ struct arch_thread {
struct farcall interrupt_stack;
// used to track interrupts on this thread
struct iframe *current_iframe;
struct iframe *iframes[IFRAME_TRACE_DEPTH];
int iframe_ptr;

View File

@ -11,6 +11,7 @@ extern "C" {
#include <thread_types.h>
#include <arch/thread.h>
#include <signal.h>
// Uncomment the line below to compile the single-queue scheduler
//#define NEW_SCHEDULER
@ -18,6 +19,10 @@ extern "C" {
void resched(void);
void start_scheduler(void);
#define BLOCKABLE_SIGS (~((1L << (SIGKILL - 1)) | (1L << (SIGSTOP - 1))))
void handle_signals(struct thread *t, int state);
void thread_enqueue(struct thread *t, struct thread_queue *q);
struct thread *thread_lookat_queue(struct thread_queue *q);
struct thread *thread_dequeue(struct thread_queue *q);
@ -45,9 +50,7 @@ int thread_kill_thread_nowait(thread_id id);
#define thread_get_current_thread arch_thread_get_current_thread
struct thread *thread_get_thread_struct(thread_id id);
#ifdef NEW_SCHEDULER
struct thread *thread_get_thread_struct_locked(thread_id id);
#endif /* NEW_SCHEDULER */
static thread_id thread_get_current_thread_id(void);
static inline thread_id
@ -98,6 +101,8 @@ int user_setrlimit(int resource, const struct rlimit * rlp);
int user_setenv(const char *name, const char *value, int overwrite);
int user_getenv(const char *name, char **value);
int user_sigaction(int sig, const struct sigaction *act, struct sigaction *oact);
#if 1
// XXX remove later
int thread_test(void);

View File

@ -113,6 +113,7 @@ KernelLd kernel
<$(SOURCE_GRIST)!core>queue.o
<$(SOURCE_GRIST)!core>scheduler.o
<$(SOURCE_GRIST)!core>sem.o
<$(SOURCE_GRIST)!core>signal.o
<$(SOURCE_GRIST)!core>smp.o
<$(SOURCE_GRIST)!core>syscalls.o
<$(SOURCE_GRIST)!core>sysctl.o
@ -159,6 +160,7 @@ KernelLd kernel.so
<$(SOURCE_GRIST)!core>queue.o
<$(SOURCE_GRIST)!core>scheduler.o
<$(SOURCE_GRIST)!core>sem.o
<$(SOURCE_GRIST)!core>signal.o
<$(SOURCE_GRIST)!core>smp.o
<$(SOURCE_GRIST)!core>syscalls.o
<$(SOURCE_GRIST)!core>sysctl.o
@ -576,6 +578,18 @@ KernelLd fibo :
bin/fibo
;
KernelLd sig_test :
libglue2.o
<$(SOURCE_GRIST)!apps>sig_test.o
libroot.so
:
$(SUBDIR)/ldscripts/$(OBOS_ARCH)/app.ld
:
:
:
bin/sig_test
;
KernelLd fortune :
libglue2.o
<$(SOURCE_GRIST)!apps!fortune>main.o

View File

@ -1,6 +1,6 @@
SubDir OBOS_TOP src kernel apps ;
KernelObjects false_main.c fibo_main.c init.c true_main.c ;
KernelObjects false_main.c fibo_main.c init.c true_main.c sig_test.c ;
SubInclude OBOS_TOP src kernel apps cpuinfo ;
SubInclude OBOS_TOP src kernel apps echo ;

View File

@ -6,6 +6,7 @@
#include <syscalls.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
@ -21,6 +22,7 @@ struct command cmds[] = {
{"cat", &cmd_cat},
{"cd", &cmd_cd},
{"pwd", &cmd_pwd},
{"kill", &cmd_kill},
{"help", &cmd_help},
{NULL, NULL}
};
@ -185,6 +187,20 @@ int cmd_stat(int argc, char *argv[])
return 0;
}
int cmd_kill(int argc, char *argv[])
{
int rc;
if (argc < 3) {
printf("not enough arguments to kill\n");
return 0;
}
rc = sys_kill(atoi(argv[2]), atoi(argv[1]));
if (rc)
printf("kill failed\n");
return 0;
}
int cmd_help(int argc, char *argv[])
{
printf("command list:\n\n");
@ -198,6 +214,7 @@ int cmd_help(int argc, char *argv[])
printf("cat <file> : dumps the file to stdout\n");
printf("mount <path> <device> <fsname> : tries to mount <device> at <path>\n");
printf("unmount <path> : tries to unmount at <path>\n");
printf("kill <sig> <tid> : sends signal <sig> to thread <tid>\n");
return 0;
}

View File

@ -8,6 +8,7 @@ int cmd_mkdir(int argc, char *argv[]);
int cmd_cat(int argc, char *argv[]);
int cmd_cd(int argc, char *argv[]);
int cmd_pwd(int argc, char *argv[]);
int cmd_kill(int argc, char *argv[]);
int cmd_help(int argc, char *argv[]);
int cmd_create_proc(int argc,char *argv[]);
typedef int(cmd_handler_proc)(int argc,char *argv[]);

View File

@ -0,0 +1,80 @@
/*
** Small test for signals handling
*/
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <syscalls.h>
void sig_handler(int);
void install_handler(int);
int32 thread(void *);
void
sig_handler(int sig)
{
int i;
printf("sig_test (sig_handler): Received signal #%d...\n", sig);
printf("Counting for fun: ");
for (i=0; i<10; i++)
printf("%d ", i);
printf("\n");
}
void
install_handler(int sig)
{
struct sigaction newa;
struct sigaction olda;
memset(&newa, 0, sizeof(newa));
newa.sa_handler = sig_handler;
newa.sa_flags = SA_NOMASK | SA_RESTART;
if (sys_sigaction(sig, &newa, &olda) < 0) {
printf("Failed installing handler for sig #%d!\n", sig);
}
}
int32
thread(void *data)
{
printf("Started snoozing thread...\n");
while (1) {
if (snooze(1000000000L) == B_INTERRUPTED)
printf("sig_test (thread): snooze was interrupted!\n");
}
return 0;
}
int
main(int argc, char **argv)
{
int i;
printf("Installing signal handlers...\n");
for (i=1; i<=NSIG; i++)
install_handler(i);
printf("Spawning some threads...\n");
for (i=0; i<5; i++)
resume_thread(spawn_thread(thread, "sig_test aux thread", B_NORMAL_PRIORITY, NULL));
printf("Done. Entering sleep mode...\n");
while (1) {
if (sys_snooze(1000000000L) == B_INTERRUPTED)
// this never gets called as the syscall is always restarted
printf("sig_test (main): snooze was interrupted!\n");
}
return 0;
}

View File

@ -22,7 +22,8 @@ KernelObjects
<$(SOURCE_GRIST)>port.c
<$(SOURCE_GRIST)>queue.c
<$(SOURCE_GRIST)>scheduler.c
<$(SOURCE_GRIST)>sem.c
<$(SOURCE_GRIST)>sem.c
<$(SOURCE_GRIST)>signal.c
<$(SOURCE_GRIST)>smp.c
<$(SOURCE_GRIST)>syscalls.c
<$(SOURCE_GRIST)>sysctl.c

View File

@ -165,8 +165,10 @@ i386_handle_trap(struct iframe frame)
int ret = B_HANDLED_INTERRUPT;
struct thread *thread = thread_get_current_thread();
if (thread)
if (thread) {
i386_push_iframe(thread, &frame);
thread->arch_info.current_iframe = &frame;
}
// if(frame.vector != 0x20)
// dprintf("i386_handle_trap: vector 0x%x, ip 0x%x, cpu %d\n", frame.vector, frame.eip, smp_get_current_cpu());
@ -249,7 +251,7 @@ i386_handle_trap(struct iframe frame)
}
break;
}
if (ret == B_INVOKE_SCHEDULER) {
int state = disable_interrupts();
GRAB_THREAD_LOCK();
@ -261,6 +263,7 @@ i386_handle_trap(struct iframe frame)
if (frame.cs == USER_CODE_SEG || frame.vector == 99) {
thread_atkernel_exit();
}
// dprintf("0x%x cpu %d!\n", thread_get_current_thread_id(), smp_get_current_cpu());
if (thread)

View File

@ -15,6 +15,8 @@
.align 8; \
name: \
pushl $vector; \
pushl $-1; \
pushl $-1; \
jmp int_bottom
#define TRAP(name, vector) \
@ -23,6 +25,8 @@ name: \
name: \
pushl $0; \
pushl $vector; \
pushl %edx; \
pushl %eax; \
jmp int_bottom
TRAP(trap0, 0)
@ -92,7 +96,7 @@ int_bottom:
pop %es
pop %ds
popa
addl $8,%esp
addl $16,%esp
iret
// custom stack -> copy registers to kernel stack and switch there
@ -102,22 +106,22 @@ int_bottom:
addl _interrupt_stack_offset,%edx
lss (%edx),%esp
movl %ebx,%ds
subl $84,%esp
subl $92,%esp
movl %esp,%edi
movl $19,%ecx
movl $21,%ecx
rep movsl
movl %eax,%ds
subl $76,%esi
subl $84,%esi
movl %esi,(%edi) // save custom stack address
movl %ebx,4(%edi)
call i386_handle_trap
lss 76(%esp),%esp // reload custom stack address
lss 84(%esp),%esp // reload custom stack address
pop %gs
pop %fs
pop %es
pop %ds
popa
addl $8,%esp
addl $16,%esp
iret
_interrupt_stack_offset:
@ -151,3 +155,13 @@ FUNCTION(i386_stack_switch):
pushl %ecx
popf
jmp *%edx
FUNCTION(i386_return_from_signal):
popl %eax // flush the only signal handler parameter
movl $103, %eax // This syscall will restore the cpu context to the
movl $0, %ecx // one existing before calling the signal handler
lea 4(%esp), %edx
int $99
ret
FUNCTION(i386_end_return_from_signal):

View File

@ -10,8 +10,11 @@
#include <memheap.h>
#include <thread.h>
#include <arch/thread.h>
#include <arch_cpu.h>
#include <int.h>
#include <string.h>
#include <Errors.h>
#include <signal.h>
// from arch_interrupts.S
@ -194,3 +197,115 @@ arch_thread_enter_uspace(addr entry, void *args, addr ustack_top)
i386_enter_uspace(entry, args, ustack_top - 4);
}
void
arch_setup_signal_frame(struct thread *t, struct sigaction *sa, int sig, int sig_mask)
{
struct iframe *frame = t->arch_info.current_iframe;
uint32 *stack = (uint32 *)frame->user_esp;
uint32 *code;
uint32 *fpu_state;
if (frame->orig_eax >= 0) {
// we're coming from a syscall
switch (frame->eax) {
case ERESTARTNOHAND:
frame->eax = EINTR;
break;
case EINTR:
case ERESTARTSYS:
if (!(sa->sa_flags & SA_RESTART)) {
frame->eax = EINTR;
break;
}
/* fallthrough */
case ERESTARTNOINTR:
dprintf("### restarting syscall %d after signal %d\n", frame->orig_eax, sig);
frame->eax = frame->orig_eax;
frame->edx = frame->orig_edx;
frame->eip -= 2;
break;
}
}
stack -= 192;
code = stack + 25;
fpu_state = stack + 64;
stack[0] = (uint32)code; // return address when sa_handler done
stack[1] = sig; // only argument to sa_handler
stack[2] = frame->gs;
stack[3] = frame->fs;
stack[4] = frame->es;
stack[5] = frame->ds;
stack[6] = frame->edi;
stack[7] = frame->esi;
stack[8] = frame->ebp;
stack[9] = frame->esp;
stack[10] = frame->ebx;
stack[11] = frame->edx;
stack[12] = frame->ecx;
stack[13] = frame->eax;
stack[18] = frame->eip;
stack[19] = frame->cs;
stack[20] = frame->flags;
stack[21] = frame->user_esp;
stack[22] = frame->user_ss;
stack[23] = sig_mask;
stack[24] = (uint32)fpu_state;
i386_fsave(fpu_state);
memcpy(code, i386_return_from_signal, (i386_end_return_from_signal - i386_return_from_signal));
frame->user_esp = (uint32)stack;
frame->eip = (uint32)sa->sa_handler;
}
int64
arch_restore_signal_frame(void)
{
struct thread *t = thread_get_current_thread();
struct iframe *frame;
uint32 fpu_state;
dprintf("### arch_restore_signal_frame: entry\n");
frame = t->arch_info.current_iframe;
t->sig_block_mask = *((sigset_t *)((void *)frame->user_esp + sizeof(struct iframe))) & BLOCKABLE_SIGS;
fpu_state = *((uint32 *)((void *)frame->user_esp + sizeof(struct iframe) + sizeof(uint32)));
memcpy((void *)frame, (void *)frame->user_esp, sizeof(struct iframe));
i386_frstor((void *)fpu_state);
dprintf("### arch_restore_signal_frame: exit\n");
frame->orig_eax = -1; /* disable syscall checks */
return (int64)frame->eax | ((int64)frame->edx << 32);
}
void
arch_check_syscall_restart(struct thread *t)
{
struct iframe *frame = t->arch_info.current_iframe;
dprintf("### arch_check_syscall_restart: entry\n");
if (frame->orig_eax >= 0) {
dprintf("### arch_check_syscall_restart: coming from a syscall\n");
if ((frame->eax == ERESTARTNOHAND) ||
(frame->eax == EINTR) ||
(frame->eax == ERESTARTSYS) ||
(frame->eax == ERESTARTNOINTR)) {
dprintf("### arch_check_syscall_restart: syscall restart needed\n");
frame->eax = frame->orig_eax;
frame->edx = frame->orig_edx;
frame->eip -= 2;
}
}
dprintf("### arch_check_syscall_restart: exit\n");
}

View File

@ -101,6 +101,30 @@ FUNCTION(arch_cpu_global_TLB_invalidate):
movl %eax,%cr3
ret
/* void i386_fsave(void *fpu_state); */
FUNCTION(i386_fsave):
movl 4(%esp), %eax
fsave (%eax)
ret
/* void i386_fxsave(void *fpu_state); */
FUNCTION(i386_fxsave):
movl 4(%esp), %eax
fxsave (%eax)
ret
/* void i386_frstor(void *fpu_state); */
FUNCTION(i386_frstor):
movl 4(%esp), %eax
frstor (%eax)
ret
/* void i386_fxrstor(void *fpu_state); */
FUNCTION(i386_fxrstor):
movl 4(%esp), %eax
fxrstor (%eax)
ret
/* void i386_fsave_swap(void *old_fpu_state, void *new_fpu_state); */
FUNCTION(i386_fsave_swap):
movl 4(%esp),%eax

View File

@ -401,9 +401,9 @@ acquire_sem_etc(sem_id id, int32 count, uint32 flags, bigtime_t timeout)
TRACE_BLOCK(("acquire_sem_etc(id = %ld): block name = %s, thread = %p, name = %s\n", id, gSems[slot].name, t, t->name));
// do a quick check to see if the thread has any pending kill signals
// do a quick check to see if the thread has any pending signals
// this should catch most of the cases where the thread had a signal
if ((flags & B_CAN_INTERRUPT) && (t->pending_signals & SIG_KILL)) {
if ((flags & B_CAN_INTERRUPT) && t->sig_pending) {
gSems[slot].count += count;
status = B_INTERRUPTED;
goto err;
@ -436,9 +436,9 @@ acquire_sem_etc(sem_id id, int32 count, uint32 flags, bigtime_t timeout)
RELEASE_SEM_LOCK(gSems[slot]);
GRAB_THREAD_LOCK();
// check again to see if a kill signal is pending.
// check again to see if a signal is pending.
// it may have been delivered while setting up the sem, though it's pretty unlikely
if ((flags & B_CAN_INTERRUPT) && (t->pending_signals & SIG_KILL)) {
if ((flags & B_CAN_INTERRUPT) && t->sig_pending) {
struct thread_queue wakeup_queue;
// ok, so a tiny race happened where a signal was delivered to this thread while
// it was setting up the sem. We can only be sure a signal wasn't delivered
@ -768,6 +768,8 @@ sem_interrupt_thread(struct thread *t)
if ((t->sem_flags & B_CAN_INTERRUPT) == 0)
return ERR_SEM_NOT_INTERRUPTABLE;
t->next_state = B_THREAD_READY;
slot = t->sem_blocking % MAX_SEMS;
GRAB_SEM_LOCK(gSems[slot]);

249
src/kernel/core/signal.c Normal file
View File

@ -0,0 +1,249 @@
/* POSIX signals handling routines
**
** Copyright 2002, Angelo Mottola, a.mottola@libero.it. All rights reserved.
** Distributed under the terms of the OpenBeOS License.
*/
#include <kernel.h>
#include <debug.h>
#include <thread.h>
#include <arch/thread.h>
#include <int.h>
#include <sem.h>
#include <string.h>
#include <signal.h>
#include <syscalls.h>
const char * const sys_siglist[NSIG] = {
"NONE", "HUP", "INT", "QUIT", "ILL", "CHLD", "ABRT", "PIPE",
"FPE", "KILL", "STOP", "SEGV", "CONT", "TSTP", "ALRM", "TERM",
"TTIN", "TTOU", "USR1", "USR2", "WINCH", "KILLTHR", "TRAP"
};
// Expects interrupts off and thread lock held.
void
handle_signals(struct thread *t, int state)
{
uint32 sig_mask = t->sig_pending & (~t->sig_block_mask);
int i, sig;
struct sigaction *handler;
if (sig_mask) {
for (i = 0; i < NSIG; i++) {
if (sig_mask & 0x1) {
sig = i + 1;
handler = &t->sig_action[i];
sig_mask >>= 1;
t->sig_pending &= ~(1L << i);
dprintf("Thread 0x%lx received signal %s\n", t->id, sys_siglist[sig]);
if (handler->sa_handler == SIG_IGN) {
// signal is to be ignored
// XXX apply zombie cleaning on SIGCHLD
continue;
}
if (handler->sa_handler == SIG_DFL) {
// default signal behaviour
switch (sig) {
case SIGCHLD:
case SIGWINCH:
case SIGTSTP:
case SIGTTIN:
case SIGTTOU:
case SIGCONT:
continue;
case SIGSTOP:
t->next_state = B_THREAD_SUSPENDED;
continue;
case SIGQUIT:
case SIGILL:
case SIGTRAP:
case SIGABRT:
case SIGFPE:
case SIGSEGV:
dprintf("Shutting down thread 0x%lx due to signal #%d\n", t->id, sig);
case SIGKILL:
case SIGKILLTHR:
default:
RELEASE_THREAD_LOCK();
restore_interrupts(state);
thread_exit(sig);
}
}
// User defined signal handler
dprintf("### Setting up custom signal handler frame...\n");
arch_setup_signal_frame(t, handler, sig, t->sig_block_mask);
if (handler->sa_flags & SA_ONESHOT)
handler->sa_handler = SIG_DFL;
if (!(handler->sa_flags & SA_NOMASK))
t->sig_block_mask |= (handler->sa_mask | (1L << sig)) & BLOCKABLE_SIGS;
return;
} else
sig_mask >>= 1;
}
arch_check_syscall_restart(t);
}
}
int
send_signal_etc(pid_t tid, uint sig, uint32 flags)
{
int state;
struct thread *t, *main_t;
if ((sig < 1) || (sig > 32))
return B_BAD_VALUE;
state = disable_interrupts();
GRAB_THREAD_LOCK();
t = thread_get_thread_struct_locked(tid);
if (!t) {
RELEASE_THREAD_LOCK();
restore_interrupts(state);
return B_BAD_THREAD_ID;
}
// XXX check permission
// Signals to kernel threads will only wake them up
if (t->team == team_get_kernel_team()) {
if (t->state == B_THREAD_SUSPENDED) {
t->state = t->next_state = B_THREAD_READY;
thread_enqueue_run_q(t);
}
}
else {
t->sig_pending |= (1L << (sig - 1));
switch (sig) {
case SIGKILL:
// Forward KILLTHR to the main thread of the team
main_t = t->team->main_thread;
main_t->sig_pending |= (1L << (SIGKILLTHR - 1));
// Wake up main thread
if (main_t->state == B_THREAD_SUSPENDED) {
main_t->state = main_t->next_state = B_THREAD_READY;
thread_enqueue_run_q(main_t);
} else if (main_t->state == B_THREAD_WAITING)
sem_interrupt_thread(main_t);
// Fallthrough
case SIGKILLTHR:
// Wake up suspended threads and interrupt waiting ones
if (t->state == B_THREAD_SUSPENDED) {
t->state = t->next_state = B_THREAD_READY;
thread_enqueue_run_q(t);
} else if (t->state == B_THREAD_WAITING)
sem_interrupt_thread(t);
break;
case SIGCONT:
// Wake up thread if it was suspended
if ((t->state == B_THREAD_READY) ||
(t->state == B_THREAD_SUSPENDED)) {
t->state = t->next_state = B_THREAD_READY;
thread_enqueue_run_q(t);
}
break;
default:
if (t->sig_pending & ((~t->sig_block_mask) | (1L << (SIGCHLD - 1)))) {
// Interrupt thread if it was waiting
if (t->state == B_THREAD_WAITING)
sem_interrupt_thread(t);
}
break;
}
}
if (!(flags & B_DO_NOT_RESCHEDULE))
resched();
RELEASE_THREAD_LOCK();
restore_interrupts(state);
return B_OK;
}
int
has_signals_pending(struct thread *t)
{
if (!t)
t = thread_get_current_thread();
return (t->sig_pending & ~t->sig_block_mask);
}
int
sys_kill(pid_t tid, int sig)
{
return send_signal_etc(tid, sig, 0);
}
int
user_sigaction(int sig, const struct sigaction *act, struct sigaction *oact)
{
struct sigaction kact;
struct sigaction koact;
int rc;
if ((!act) || (!oact))
return EINVAL;
rc = user_memcpy(&kact, act, sizeof(struct sigaction));
if (rc < 0)
return rc;
rc = user_memcpy(&koact, oact, sizeof(struct sigaction));
if (rc < 0)
return rc;
rc = sys_sigaction(sig, &kact, &koact);
if (rc < 0)
return rc;
rc = user_memcpy(oact, &koact, sizeof(struct sigaction));
return rc;
}
int
sys_sigaction(int sig, const struct sigaction *act, struct sigaction *oact)
{
struct thread *t;
int state;
if ((sig < 1) || (sig > 32))
return EINVAL;
if ((sig == SIGKILL) || (sig == SIGKILLTHR) || (sig == SIGSTOP))
return EINVAL;
state = disable_interrupts();
GRAB_THREAD_LOCK();
t = thread_get_current_thread();
memcpy(oact, &t->sig_action[sig - 1], sizeof(struct sigaction));
memcpy(&t->sig_action[sig - 1], act, sizeof(struct sigaction));
if (act->sa_handler == SIG_IGN)
t->sig_pending &= ~(1L << (sig - 1));
else if (act->sa_handler == SIG_DFL) {
if ((sig == SIGCONT) || (sig == SIGCHLD) || (sig == SIGWINCH))
t->sig_pending &= ~(1L << (sig - 1));
}
else
dprintf("### custom signal handler set\n");
RELEASE_THREAD_LOCK();
restore_interrupts(state);
return 0;
}

View File

@ -6,6 +6,7 @@
#include <kernel.h>
#include <ksyscalls.h>
#include <syscalls.h>
#include <int.h>
#include <arch/int.h>
#include <debug.h>
@ -385,6 +386,15 @@ int syscall_dispatcher(unsigned long call_num, void *arg_buffer, uint64 *call_re
case SYSCALL_GET_NEXT_TEAM_INFO:
*call_ret = user_get_next_team_info((int32 *)arg0, (team_info *)arg1);
break;
case SYSCALL_RETURN_FROM_SIGNAL:
*call_ret = arch_restore_signal_frame();
break;
case SYSCALL_KILL:
*call_ret = sys_kill((pid_t)arg0, (int)arg1);
break;
case SYSCALL_SIGACTION:
*call_ret = user_sigaction((int)arg0, (const struct sigaction *)arg1, (struct sigaction *)arg2);
break;
default:
*call_ret = -1;
}

View File

@ -1,4 +1,4 @@
/* Threading and team information */
/* Threading routines */
/*
** Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
@ -75,11 +75,8 @@ static struct thread_queue run_q[(B_MAX_PRIORITY / 2) + 1] = { { NULL, NULL }, }
struct thread_queue dead_q;
static void thread_entry(void);
#ifndef NEW_SCHEDULER
static struct thread *thread_get_thread_struct_locked(thread_id id);
#endif /* not NEW_SCHEDULER */
static void thread_kthread_exit(void);
static void deliver_signal(struct thread *t, int signal);
//static void deliver_signal(struct thread *t, int signal);
// insert a thread onto the tail of a queue
@ -266,11 +263,13 @@ create_thread_struct(const char *name)
t->q_next = NULL;
t->priority = -1;
t->args = NULL;
t->pending_signals = SIG_NONE;
t->sig_pending = 0;
t->sig_block_mask = 0;
memset(t->sig_action, 0, 32 * sizeof(struct sigaction));
t->in_kernel = true;
t->user_time = 0;
t->kernel_time = 0;
t->last_time = 0;
t->last_time = 0;
sprintf(temp, "thread_0x%lx_retcode_sem", t->id);
t->return_code_sem = create_sem(0, temp);
@ -326,7 +325,9 @@ _create_user_thread_kentry(void)
t = thread_get_current_thread();
// a signal may have been delivered here
thread_atkernel_exit();
// thread_atkernel_exit();
t->last_time = system_time();
t->in_kernel = false;
// jump to the entry point in user space
arch_thread_enter_uspace((addr)t->entry, t->args, t->user_stack_base + STACK_SIZE);
@ -343,7 +344,9 @@ _create_kernel_thread_kentry(void)
struct thread *t;
t = thread_get_current_thread();
t->last_time = system_time();
// call the entry function with the appropriate args
func = (void *)t->entry;
@ -466,70 +469,22 @@ thread_create_kernel_thread_etc(const char *name, int (*func)(void *), void *arg
int
thread_suspend_thread(thread_id id)
{
int state;
struct thread *t;
int retval;
bool global_resched = false;
state = disable_interrupts();
GRAB_THREAD_LOCK();
t = thread_get_current_thread();
if (t->id != id) {
t = thread_get_thread_struct_locked(id);
}
if (t != NULL) {
if (t->team == team_get_kernel_team()) {
// no way
retval = EPERM;
} else if (t->in_kernel == true) {
t->pending_signals |= SIG_SUSPEND;
retval = B_NO_ERROR;
} else {
t->next_state = B_THREAD_SUSPENDED;
global_resched = true;
retval = B_NO_ERROR;
}
} else
retval = ERR_INVALID_HANDLE;
RELEASE_THREAD_LOCK();
restore_interrupts(state);
if (global_resched)
int rv;
rv = send_signal_etc(id, SIGSTOP, B_DO_NOT_RESCHEDULE);
if (rv == B_OK)
smp_send_broadcast_ici(SMP_MSG_RESCHEDULE, 0, 0, 0, NULL, SMP_MSG_FLAG_SYNC);
return retval;
return rv;
}
int
thread_resume_thread(thread_id id)
{
int state;
struct thread *t;
int retval;
state = disable_interrupts();
GRAB_THREAD_LOCK();
t = thread_get_thread_struct_locked(id);
if (t != NULL && t->state == B_THREAD_SUSPENDED) {
t->state = B_THREAD_READY;
t->next_state = B_THREAD_READY;
thread_enqueue_run_q(t);
retval = B_NO_ERROR;
} else
retval = ERR_INVALID_HANDLE;
RELEASE_THREAD_LOCK();
restore_interrupts(state);
return retval;
return send_signal_etc(id, SIGCONT, B_DO_NOT_RESCHEDULE);
}
#ifndef NEW_SCHEDULER
status_t
thread_set_priority(thread_id id, int32 priority)
@ -614,7 +569,7 @@ _dump_thread_info(struct thread *t)
dprintf("(%d)\n", t->cpu->info.cpu_num);
else
dprintf("\n");
dprintf("pending_signals: 0x%x\n", t->pending_signals);
dprintf("sig_pending: 0x%lx\n", t->sig_pending);
dprintf("in_kernel: %d\n", t->in_kernel);
dprintf("sem_blocking:0x%lx\n", t->sem_blocking);
dprintf("sem_count: 0x%x\n", t->sem_count);
@ -1072,13 +1027,7 @@ thread_exit(int retcode)
}
RELEASE_TEAM_LOCK();
restore_interrupts(state);
#if 0
// Now wait for all of the threads to die
// XXX block on a semaphore
while((volatile int)p->num_threads > 0) {
snooze(10000); // 10 ms
}
#endif
// wait until all threads in team are dead.
acquire_sem_etc(p->death_sem, p->num_threads, 0, 0);
delete_sem(p->death_sem);
}
@ -1125,55 +1074,25 @@ thread_exit(int retcode)
}
static int
_thread_kill_thread(thread_id id, bool wait_on)
{
int state;
struct thread *t;
int rc;
// dprintf("_thread_kill_thread: id %d, wait_on %d\n", id, wait_on);
state = disable_interrupts();
GRAB_THREAD_LOCK();
t = thread_get_thread_struct_locked(id);
if (t != NULL) {
if (t->team == team_get_kernel_team()) {
// can't touch this
rc = EPERM;
} else {
deliver_signal(t, SIG_KILL);
rc = B_NO_ERROR;
if (t->id == thread_get_current_thread()->id)
wait_on = false; // can't wait on ourself
}
} else
rc = ERR_INVALID_HANDLE;
RELEASE_THREAD_LOCK();
restore_interrupts(state);
if (rc < 0)
return rc;
if (wait_on)
thread_wait_on_thread(id, NULL);
return rc;
}
int
thread_kill_thread(thread_id id)
{
return _thread_kill_thread(id, true);
int rv;
rv = send_signal_etc(id, SIGKILLTHR, B_DO_NOT_RESCHEDULE);
if (rv < 0)
return rv;
if (id != thread_get_current_thread()->id)
thread_wait_on_thread(id, NULL);
return rv;
}
int
thread_kill_thread_nowait(thread_id id)
{
return _thread_kill_thread(id, false);
return send_signal_etc(id, SIGKILLTHR, B_DO_NOT_RESCHEDULE);
}
@ -1192,6 +1111,10 @@ thread_wait_on_thread(thread_id id, int *retcode)
struct thread *t;
int rc;
rc = send_signal_etc(id, SIGCONT, 0);
if (rc != B_OK)
return rc;
state = disable_interrupts();
GRAB_THREAD_LOCK();
@ -1199,7 +1122,7 @@ thread_wait_on_thread(thread_id id, int *retcode)
if (t != NULL) {
sem = t->return_code_sem;
} else {
sem = ERR_INVALID_HANDLE;
sem = B_BAD_THREAD_ID;
}
RELEASE_THREAD_LOCK();
@ -1244,11 +1167,9 @@ thread_get_thread_struct(thread_id id)
return t;
}
#ifndef NEW_SCHEDULER
static struct thread *thread_get_thread_struct_locked(thread_id id)
#else /* NEW_SCHEDULER */
struct thread *thread_get_thread_struct_locked(thread_id id)
#endif /* NEW_SCHEDULER */
struct thread *
thread_get_thread_struct_locked(thread_id id)
{
struct thread_key key;
@ -1258,61 +1179,6 @@ struct thread *thread_get_thread_struct_locked(thread_id id)
}
// sets the pending signal flag on a thread and possibly does some work to wake it up, etc.
// expects the thread lock to be held
static void
deliver_signal(struct thread *t, int signal)
{
// dprintf("deliver_signal: thread %p (%d), signal %d\n", t, t->id, signal);
switch (signal) {
case SIG_KILL:
t->pending_signals |= SIG_KILL;
switch (t->state) {
case B_THREAD_SUSPENDED:
t->state = B_THREAD_READY;
t->next_state = B_THREAD_READY;
thread_enqueue_run_q(t);
break;
case B_THREAD_WAITING:
sem_interrupt_thread(t);
break;
default:
;
}
break;
default:
t->pending_signals |= signal;
}
}
// expects the thread lock to be held
static void
_check_for_thread_sigs(struct thread *t, int state)
{
if (t->pending_signals == SIG_NONE)
return;
if (t->pending_signals & SIG_KILL) {
t->pending_signals &= ~SIG_KILL;
RELEASE_THREAD_LOCK();
restore_interrupts(state);
thread_exit(0);
// never gets to here
}
if (t->pending_signals & SIG_SUSPEND) {
t->pending_signals &= ~SIG_SUSPEND;
t->next_state = B_THREAD_SUSPENDED;
// XXX will probably want to delay this
resched();
}
}
// called in the int handler code when a thread enters the kernel for any reason
void
@ -1333,13 +1199,8 @@ thread_atkernel_entry(void)
t->user_time += now - t->last_time;
t->last_time = now;
GRAB_THREAD_LOCK();
t->in_kernel = true;
_check_for_thread_sigs(t, state);
RELEASE_THREAD_LOCK();
restore_interrupts(state);
}
@ -1360,7 +1221,7 @@ thread_atkernel_exit(void)
state = disable_interrupts();
GRAB_THREAD_LOCK();
_check_for_thread_sigs(t, state);
handle_signals(t, state);
t->in_kernel = false;

View File

@ -150,3 +150,8 @@ SYSCALL6(sys_sysctl, 71)
SYSCALL3(sys_setenv, 79)
SYSCALL2(sys_getenv, 80)
/* Signal handling calls */
SYSCALL0(sys_return_from_signal, 103)
SYSCALL2(sys_kill, 104)
SYSCALL3(sys_sigaction, 105)