sigaction的EPC等细节求助

alpha
🔖︎ 10 订阅
👍︎ 4 点赞

目前情况:第四个点17/18(wrong answer),第六个点2/8(sth unexpected happended),遇到两点困惑,求助一下助教和同学:

1. EPC什么情况加4

针对第四个测试点,试过几个版本:

  1. SIGSYS的EPC不加4:过14个点
  2. SIGSYS直接加4:过12个点
  3. SIGSYS只在默认处理函数(忽略)执行后加4:过16个点
  4. 在3. 的基础上,在出现越界后(内核态)、SIGSEGV信号发出前将EPC加4,过17个点
  5. 在4. 的基础上,SIGILL也在出现越界后、SIGILL信号发出前将EPC加4,但还是只能过17个点。

想求助一下,以上各步骤的设计是否合理

2. sig相关函数行为

指导书规定了不能阻塞SIGKILL和改变行为,但没有详细约定以下行为,我参考linux manual page进行以下设计:

  1. sigprocmask不能将SIGKILL位设为1,但是相关尝试仍返回0(成功),只是写入时候去掉SIGKILL对应位上的1:(图片来自man sigprocmask)image.png
  2. sigaction中,signum如果是SIGKILL,直接返回-1:(图片来自man sigaction)image.png
  3. sigaction中,如果某个信号的屏蔽集之中SIGKILL对应的位为1,在写入进程对应的注册信号的信号屏蔽集sigaction.sa_mask时去掉SIGKILL对应位上的1,但仍返回0(成功注册):(图片来自man sigaction)image.png

想请教以下助教,以上设计能否满足正确性的约定?

还有我这种错误情况有没有其他错误的可能,求助过来人🙏。

----6/15 再补充两个问题 ---- image.png

我的疑问是:

  1. 如果一类信号被阻塞,然后来了2个该类信号,那我是响应1次还是2次?助教答疑帖说只存在一个实例,又说被阻塞?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#ifdef __x86_64__
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#define PRINT printf
#else
#include <lib.h>
#define PRINT debugf
#endif
#define VICTIM 2
int gbl = 0;

void handler(int sig) {
PRINT("handler: sig is %d\n", sig);
PRINT("handler: gbl is %d\n", gbl++);
}

int main() {
struct sigaction sa2;
sa2.sa_handler = handler;
sigemptyset(&sa2.sa_mask);
sigaction(VICTIM, &sa2, 0);
sigset_t sb;
sigemptyset(&sb);
sigaddset(&sb, VICTIM);
sigprocmask(0, &sb, 0);
PRINT("Now sig %d is blocked\n", VICTIM);
kill(0, VICTIM);
kill(0, VICTIM);
PRINT("Unblocking sig %d ...\n", VICTIM);
sigprocmask(1, &sb, 0);
PRINT("returned to current flow\n");
return 0;
}

在Linux上确实响应了1次,就是说第二个kill(0, VICTIM)消失了,想问一下助教这个机制和课程组要求的是否一致?

  1. 还有,在从开始响应信号,这个时候取消了相应位的pending,从这时候开始到处理函数返回,同类信号被阻塞,这时候如果又来了一个该类信号,会再将pending置位,等待处理。 image.png 但处理结束后,为什么一定要先回到之前的控制流,不能认为注册函数结束后也是判断是否需要响应信号的时机吗?考虑如下程序:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#ifdef __x86_64__
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#define PRINT printf
#else
#include <lib.h>
#define PRINT debugf
#endif
#define VICTIM 2
int gbl = 0;

void handler(int sig) {
PRINT("handler: sig is %d\n", sig);
PRINT("handler: gbl is %d\n", gbl++);
kill(0, VICTIM);
PRINT("handler: reaching end\n");
}

int main() {
struct sigaction sa2;
sa2.sa_handler = handler;
sigemptyset(&sa2.sa_mask);
sigaction(VICTIM, &sa2, 0);
kill(0, VICTIM);
PRINT("returned to current flow\n");
return 0;
}

linux有如下两种执行情况:

case1: 在handler返回后再次执行handler(无限循环): image.png case2: 不再响应,但是没有回到正常控制流,且异常退出(???): image.png

同样,不知道case1是否符合课程组的要求,想请教一下助教/(ㄒoㄒ)/~~

回复主题帖

__cxc__
👍︎ 3 点赞

我过了test 4,但test6一个都没过,我是这样做的:
1 只对sigsys有epc+4,其他的默认处理只有忽略或exit
2 我为了保证sigkill最高的优先级,进行了如下略微抽象的实现:我的实现中sigkill不能设置sigaction,注册会返回成功但实际上没有注册函数;对sigkill的发送和其他信号一样,但只要在发送某个信号时发现pending里已经有sigkill,就不发送这个新信号;在开始进行信号从小到大的选择前进行检查,只要pending里有sigkill,就将pending设为只有sigkill,并直接选择sigkill作为要处理的信号,直接处理


MC的大虾1472 回复 __cxc__
👍︎ 0 点赞

发现大家都是把SIGKILL和其他的信号一样进行处理,不知道有没有人这样做:
在kill函数当中,如果发现信号是SIGKILL,直接获取对应的env并且destroy?


__cxc__ 回复 MC的大虾1472
👍︎ 0 点赞

这样可能会忽略exit函数中终止进程前的其他处理,不过问题应该不大


MC的大虾1472 回复 __cxc__
👍︎ 0 点赞

我查看了一下助教发布的问题集合

对于默认终止进程的处理函数,规定都使用exit退出进程。

不知道大大是怎么做到在用户注册sig_entry之前就让用户能跳转到exit的位置?(我的sig_entry只有在用户一次调用sigaction之后才会注册上)


__cxc__ 回复 MC的大虾1472
👍︎ 1 点赞

exit是在sig_entry里调用的;sig_entry是在libos的libmain里注册的,位于进入main之前


MC的大虾1472 回复 __cxc__
👍︎ 0 点赞

原来如此,非常感谢

回复主题帖

戴波(助教)
👍︎ 0 点赞
  1. 对于sigkill的设计是正确的。

  2. “Linux只响应了一次”和课程组的要求一致。

  3. case1是正确的。

回复主题帖

alpha
👍︎ 0 点赞

note: SIGSEGV的EPC不用加4,但是也要照常分配物理页面

0%