信号相关

相关数据结构

信号量

#define SIGSET_LEN 		1
typedef struct {
	unsigned long __val[SIGSET_LEN];
} __sigset_t;

这里采取了位来表示信号,某一位为1表示一个信号存在.

信号处理器

typedef void (*__sighandler_t)(int);

是一个函数指针,指向一个参数为一个int,没有返回值的函数

信号处理部分

存在一个信号处理器(union),一个mask,表示执行该信号时阻塞这些信号以及一个flag.

struct sigaction {
	union {		// let's make it simple, only sa_handler is supported 
		__sighandler_t sa_handler;
		// void (*sa_sigaction)(int, siginfo_t *, void *);
	} __sigaction_handler;
	__sigset_t sa_mask;	// signals to be blocked during handling 
	int sa_flags;
	// void (*sa_restorer)(void);	// this field is not used on risc-v
};

内核包装下的信号处理部分

每一个信号都有一个信号处理函数.这些东西组成一个链表

typedef struct __ksigaction_t {
	struct __ksigaction_t *next;
	struct sigaction sigact;
	int signum;
} ksigaction_t;

sig_frame

这个是在信号量下的trapframe:

struct sig_frame {
	__sigset_t mask;
	struct trapframe *tf;
	struct sig_frame *next;
};

进程改动

下面介绍对进程相关实现的改动

进程添加了几个成员:

	ksigaction_t *sig_act;
	__sigset_t sig_set;
	__sigset_t sig_pending;
	struct sig_frame *sig_frame;

kill改成了要处理的信号.

sig_act存放信号的处理函数,sig_actsig_pending存放了设置了什么信号和谁给他设置了什么信号.sig_frame用于存放信号处理的栈帧.

修改了初始化的部分,添加了几个初始化的操作:把上面的四个成员初始化以及回收进程的清空数据(相关函数:allocproc和freeproc)

更改kill和exit调用,使之满足信号的要求.

clone的时候满足相关的要求.(flag的CLONE_SET和CLONE_CLEAR)配合set_tid_address系统调用.

内存改动

memlayout.h的改动,在用户进程下面放置了一个信号的处理trampoline,然后通过修改kernel.ld和kernel.lds吧sig_trampoline.S的代码放在了这个位置.

#define USER_TOP (MAXVA)    // virtual address
#define TRAMPOLINE (USER_TOP - PGSIZE)  // virtual address
#define TRAPFRAME (TRAMPOLINE - 2 * PGSIZE) // virtual address
#define SIG_TRAMPOLINE 	(TRAMPOLINE - PGSIZE)

修改进程的页表,让每个进程支持sig_trampoline.

修改了内核编译的脚本,让sig_trampoline存放在合适的位置

    .text : {
        *(.text .text.*)
        . = ALIGN(0x1000);
        _trampoline = .;
        *(trampsec)
        . = ALIGN(0x1000);
        ASSERT(. - _trampoline == 0x1000, "error: trampoline larger than one page");
        _sig_trampoline = .;
        *(sigtrampsec)
        . = ALIGN(0x1000);
        ASSERT(. - _sig_trampoline == 0x1000, "error: sig_trampoline larger than one page");
        PROVIDE(etext = .);

    }

4 相关处理API

int set_sigaction(
	int signum, 
	struct sigaction const *act, 
	struct sigaction *oldact, 
	int len
)

设置sigaction

如果oldact不为NULL,就把当前进程signum的action赋给oldact

如果act不为NULL,就把act赋给当前进程signum的action.

int sigprocmask(
	int how, 
	__sigset_t *set, 
	__sigset_t *oldset, 
	int len
)

how分成三种,一种是加blocj,一种是取消block,一种是直接赋值

如果oldact不为NULL,就把当前进程的sigset赋给oldset.

如果act不为NULL,就把set按照how的方式赋给当前进程.

相关syscall

#define SYS_rt_sigaction    134
#define SYS_rt_sigprocmask  135
#define SYS_rt_sigtimedwait 137
#define SYS_gettid          178
#define SYS_prlimit64       261
#define SYS_rt_sigreturn    139

信号执行的路径

非常类似于中断执行的路径.

  • 进入到sighandle里面,首先找到要处理的信号类型.

  • 然后把p->killed置为下一个要处理的信号.接着跳转到下一步

  • 修改trapframe,让这一次中断结束后直接跳转到信号执行的sig_trampoline.S中

  • 把信号赋给a0寄存器,找到对应的信号处理函数的函数指针.

  • 进入到汇编代码,首先先执行处理函数,然后执行返回的系统调用.

Last updated