| 姓名 | 学号 |
|---|---|
| 蔡骐骏 | 12210214 |
| 戚起宁 | 12212228 |
| 于斯瑶 | 12211655 |
本模块依赖以下核心结构体:
struct ksignal:表示一个进程的信号上下文,包含以下成员:struct sigactionstruct ucontext:用于保存用户上下文,使得信号处理函数返回后可以恢复原先状态。
siginit(struct proc *p):初始化进程的信号处理结构。siginit_fork(...):子进程继承父进程的信号处理行为(不继承sigpending)。siginit_exec(...):exec 调用后保留sigmask和sigpending,重置除SIG_IGN外的处理器为默认。
do_signal():内核在返回用户态之前调用该函数,以处理一个或多个未阻塞的、挂起的信号。
sys_sigaction(...):提供用户注册和查询信号处理函数的系统调用接口。
- 所有信号初始时设置为默认处理(
SIG_DFL)。 - 所有信号掩码清空。
- 子进程通过
siginit_fork复制父进程的sigaction和sigmask,但清空sigpending,防止重复处理。 exec调用保留sigmask和sigpending,但重置所有sigaction为默认行为(除SIG_IGN)。
处理流程:
- 跳过被阻塞或未挂起的信号。
- 对
SIGKILL、SIGSTOP,直接终止进程,不允许被忽略或捕获。 - 对于可自定义处理的信号:
- 将
siginfo_t和ucontext写入用户栈。 - 修改
trapframe的epc,sp,a0~a2,ra以跳转执行信号处理函数。 - 临时修改
sigmask阻止信号重入。
- 将
-
校验信号合法性。
-
SIGKILL和SIGSTOP不允许设置为非默认处理,否则返回-EINVAL。 -
支持保存旧的
sigaction至oldact。 -
从用户空间复制
act至内核并保存。
在sys_sigkill()中,我们在checkpoint 1的基础上添加了siginfo.
-
siginfo.signo为信号的signo -
siginfo.si_pid为发送信号的用户进程pid -
siginfo其他变量为0
//sys_sigkill()
p->signal.siginfos[signo].si_signo = signo;
p->signal.siginfos[signo].si_code = 0; // 默认为0
p->signal.siginfos[signo].si_pid = curr_proc()->pid;
p->signal.siginfos[signo].si_status = 0;
p->signal.siginfos[signo].addr = 0;由于原代码中不涉及内核发送的信号,我们将SIGUSR2作为自定义的内核发出信号,并在系统调用中添加了KTEST_SEND_SIGUSR2作为用户进程主动请求
uint64 ktest_syscall(uint64 args[6]) {
uint64 which = args[0];
switch (which) {
// ...
case KTEST_SEND_SIGUSR2:
return sys_sigkill(args[1], SIGUSR2, 0);
}
return 0;
}在发送时将pid置为-1
//sys_sigkill()
//特殊处理: 内核发送的信号
if(signo == SIGUSR2) {
p->signal.siginfos[signo].si_pid = -1;
sigaddset(&p->signal.sigpending, signo);
release(&p->lock);
return 0;
}在进程退出或者被杀死时,将会发送 SIGCHLD到它的父进程。
sys_sigkill(parent->pid, SIGCHLD, code);在sigkill中,如果信号类型为SIGCHLD,将p->signal.siginfos[signo].si_code 设为code,即进程退出码。同时如果父进程状态为SLEEPING,将父进程唤醒。
if (signo == SIGCHLD)
{
p->signal.siginfos[signo].si_code = code;
sigaddset(&p->signal.sigpending, signo);
if (p->state == SLEEPING) {
p->state = RUNNABLE;
add_task(p);
}
release(&p->lock);
return 0;
}在do_signal中,如果sa->sa_sigaction为SIG_DFL,即默认处理,则忽略信号。
if (signo == SIGCHLD && sa->sa_sigaction == SIG_DFL)
{
continue;
}如果不是,则进入用户注册的handler中进行处理。
增加两种信号:SIGSTOP 和 SIGCONT。当前者被 delivered 到某个进程时,使其进入无限期暂停运行的状
态,直到 SIGCONT 被 delivered 到该进程。
//do_signal()
if (signo == SIGSTOP) {
sigdelset(&p->signal.sigpending, signo);
p->need_stop = 1;
continue;
}
if (signo == SIGCONT) {
sigdelset(&p->signal.sigpending, signo);
// 如果进程已经被SIGSTOP停止,恢复它
if (p->state == STOPPED) {
p->state = RUNNABLE;
}
// 清除need_stop标志,防止进程在返回用户态时被停止
p->need_stop = 0;
// 清除所有等待的SIGSTOP信号
if (sigismember(&p->signal.sigpending, SIGSTOP)) {
sigdelset(&p->signal.sigpending, SIGSTOP);
}
continue;
}//sys_sigkill()
// 特殊处理SIGSTOP信号
if (signo == SIGSTOP) {
// SIGSTOP不能被阻塞或忽略
// 不直接设置进程状态为STOPPED,而是设置pending信号
// 让进程在do_signal中处理SIGSTOP信号时自行停止
sigaddset(&p->signal.sigpending, signo);
// 如果进程正在睡眠,唤醒它让它处理信号
if (p->state == SLEEPING) {
p->state = RUNNABLE;
add_task(p);
}
debugf("proc %d received SIGSTOP via sys_sigkill", p->pid);
release(&p->lock);
return 0;
}
// 特殊处理SIGCONT信号
if (signo == SIGCONT) {
sigaddset(&p->signal.sigpending, signo);
// 清除need_stop标志,防止进程在返回用户态时被停止
p->need_stop = 0;
// 如果进程已经被SIGSTOP停止,恢复它
if (p->state == STOPPED) {
p->state = RUNNABLE;
add_task(p);
}
// 清除所有等待的SIGSTOP信号
if (sigismember(&p->signal.sigpending, SIGSTOP)) {
sigdelset(&p->signal.sigpending, SIGSTOP);
}
release(&p->lock); // 释放在findByPid中获取的锁
return 0;
}


