记一个十分诡异的bug:结构体的内容会莫名其妙的变化。

本文节选自我的实验报告。
北航的操作系统课程会在学期末布置若干个"挑战性任务",任务要求是在本学期完成的系统内核中增添新的功能。我选择的挑战性任务是实现进程间通信的信号机制,下文记录了我在传输信号结构体时遇到的一个bug。解决这个bug的过程加深了我对系统编程的理解,特此记录。
s
是指向struct signal
结构体的指针。

明明没有修改s->signum
的值,为什么输出结果会发生变化呢?难道是sigx
自减时修改了它的值?还是说printk
修改了它的值?这都不可能啊。
单看这一段代码,我实在是摸不着头脑。
抓耳挠腮了很久,我终于找到了问题所在,在新建信号结构体时,我是在一个函数内部新建了一个struct signal
结构体,再将链表里的指针指向这个结构体。代码如下:
int sys_sendsig(u_int envid, int sig) {
...
struct signal s = {.signum = sig};
TAILQ_INSERT_HEAD(&e->sig_pending, &s, sig_link);
...
}
我在用java写oo作业时,经常会写出这样的业务逻辑,并且不会出现bug。这种写法很直观,很容易理解,就是新建一个东西再放到链表里嘛。
但是在系统内核里就不能这么写,这么写就会出bug。
问题就出在"在一个函数内部新建结构体",这只是在栈上申请了一个局部变量,当这段空间释放后被重新使用时,这段空间原有的内容就会被覆盖。
为了解决这个问题,我联想到了Env
结构体的组织方式:先使用全局变量申请固定的空间,这样结构体的内容就不会被更改了。每次需要申请信号结构体时,只需要取出一份来用就可以了。代码如下:
// lab4-challenge
struct signal sigs[SIG_BUFFER] __attribute__((aligned(BY2PG)));
解决这个bug的经历,是我这次挑战性任务收获最大的部分。