x
x
xv6-sifive文档
Search…
⌃K

信号相关

相关数据结构
信号量
#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寄存器,找到对应的信号处理函数的函数指针.
  • 进入到汇编代码,首先先执行处理函数,然后执行返回的系统调用.