Commit d12200d1 authored by 3KBz's avatar 3KBz
Browse files

update docs

No related merge requests found
Showing with 126 additions and 0 deletions
+126 -0
doc/final/img/signal.png

114 KB

# 决赛:信号机制
信号用于实现进程间的通信,他有很多种来源,如外部信号,有终端Ctrl-C产生的SIGINT信号,定时器到期产生的SIGALRM信号等;还有显示请求信号,如`kill`函数向进程发送信号,在GalaxyOS中,信号的发送主要通过`kill`系统调用实现
## 信号实现原理
**信号相关数据结构**
在进程PCB中,定义了如下几个与信号相关的字段
```c
struct proc {
...
struct sigaction sigaction[NSIG];
__sigset_t sig_pending;
__sigset_t sig_set;
struct sig_frame *sig_frame;
...
};
```
成员 `sigpending` 表示进程是否有信号需要处理。成员 `sigaction` 表示信号相应的处理方法
信号的关键数据结构如下
```c
typedef struct {
unsigned long __val[SIGSET_LEN];
} __sigset_t;
struct sigaction
{
union {
__sighandler_t sa_handler;
// void (*sa_sigaction)(int, siginfo_t *, void *);
} __sigaction_handler;
__sigset_t sa_mask;
int sa_flags;
// void (*sa_restorer)(void);
};
```
**发送信号**
信号的发送通过 `kill()` 系统调用,它可以发送一个信号给指定的进程,其原型如下
```c
int kill(pid_t pid, int sig);
```
调用`kill()`系统调用后,会陷入内核,调用`sys_kill()`,通过参数pid指定接收信号的进程,参数sig则是发送的信号,进而将信号发送给指定进程,其调用链大致过程如下,但是我们对于其处理还不够完善,日后还需要进行完善
> kill()
>
> | User Space
>
> ------------------------------------------------------------------------------------------------------------------------------------
>
> | Kernel Space
> sys_kill()
> |---> find_task_by_pid()
> |---> send_signal()
> |---> signal_wake_up()
**内核触发信号处理函数**
上面介绍了怎么发生一个信号给指定的进程,为了尽快让信号得到处理,我们把信号处理过程放置在进程从内核态返回到用户态前,如下代码所示
```asm
# Trampoline for signal handling
#include "../include/sysnum.h"
.section sigtrampsec
.globl sig_trampoline
sig_trampoline:
.globl sig_handler
# void sig_handler(int signum, __sig_handler_t handler);
sig_handler:
jalr a1
li a7, SYS_rt_sigreturn
ecall
# Terminate the proc directly, and this is the default handler
# for any signal that's not set by user proc
.globl default_sigaction
# void term_handler(int signum);
default_sigaction:
# exit(-1);
li a0, -1
li a7, SYS_exit
ecall
```
其处理过程大致如下图
![signal](./img/signal.png)
**设置信号处理程序**
信号处理程序主要实现在`sigaction`中,用户可以通过系统调用`rt_sigaction`来设置一个信号处理程序,其实现逻辑比较简单,进程管理结构中有个 `sigaction` 的字段,它是一个 `struct sigaction` 结构的数组,每个元素保存着对应信号的处理程序,所以,我们只需要根据传入参数`signum`获取对应的信号处理程序,然后将其设置为新的信号处理程序即可
```c
int sigaction(int signum, struct sigaction *act, struct sigaction *oldact)
{
// printf("sigaction start\n");
struct proc *current = myproc();
if (signum < 1 || signum > NSIG)
return -1;
if(oldact)
*oldact = current->sigaction[signum];
if(act)
current->sigaction[signum] = *act;
// printf("sigaction ok\n");
return 0;
}
```
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment