xv6中thrd_stop处理程序参数被覆盖的问题解决咨询
问题
我正在编写thrd_stop函数,功能类似xv6第四章实验中的alarm:调用thrd_stop后启动定时器,到达间隔时调用处理程序。与实验不同的是,我需要为处理程序传递参数。
已为proc结构体添加以下属性:
int thrdstop_ticks; int thrdstop_interval; uint64 threstop_handler; uint64 thrdstop_handler_args; struct trapframe thrdstop_context[MAX_THRD_NUM]; int thrdstop_context_used[MAX_THRD_NUM]; int thrdstop_context_id; int timeFlag;
以下是thrd_stop相关代码:
uint64 sys_thrdstop(void) { int delay; uint64 context_id_ptr; uint64 handler, handler_arg; if (argint(0, &delay) < 0) return -1; if (argaddr(1, &context_id_ptr) < 0) return -1; if (argaddr(2, &handler) < 0) return -1; if (argaddr(3, &handler_arg) < 0) return -1; struct proc *proc = myproc(); proc->thrdstop_interval = delay; proc->threstop_handler = handler; proc->thrdstop_handler_args = handler_arg; int kernelPtr; copyin(proc->pagetable, (char *)&kernelPtr, context_id_ptr, sizeof(kernelPtr)); if (kernelPtr == -1) { for (int i = 0; i < MAX_THRD_NUM; i++) { if (!proc->thrdstop_context_used[i]) { kernelPtr = i; copyout(proc->pagetable, context_id_ptr, (char *)&kernelPtr, sizeof(kernelPtr)); proc->thrdstop_context_used[i] = 1; proc->thrdstop_context_id = i; break; } } if (kernelPtr == -1) return -1; } proc->thrdstop_context_id = kernelPtr; return 0; } uint64 sys_thrdresume(void) { int context_id; if (argint(0, &context_id) < 0) return -1; struct proc *proc = myproc(); if (proc->thrdstop_context_used[context_id] == 0) return -1; proc->thrdstop_context_used[context_id] = 0; proc->thrdstop_interval = 0; proc->thrdstop_context_id = -1; // printf("resume\n"); memmove(proc->trapframe, &proc->thrdstop_context[context_id], sizeof(proc->thrdstop_context[context_id])); proc->timeFlag = 0; return 0; }
在usertrap和kerneltrap中做了如下处理:
if (which_dev == 2 && myproc() != 0 && myproc()->state == RUNNING) { if (p->thrdstop_interval == 0 && p->threstop_handler == 0) goto yield; if (p->thrdstop_ticks == p->thrdstop_interval) { if(!p->timeFlag) { // intr_off(); memmove(&p->thrdstop_context[p->thrdstop_context_id],p->trapframe,sizeof(p->thrdstop_context[p->thrdstop_context_id])); // printf("%d",p->trapframe->a0); // printf("kernel %d\n",p->thrdstop_context_id); p->trapframe->a0=p->thrdstop_handler_args; p->trapframe->epc = p->threstop_handler; p->thrdstop_ticks = 0; p->timeFlag=1; } } else { p->thrdstop_ticks += 1; } yield: yield(); }
目前的问题是:将a0设置为处理程序参数后,该参数有时会被其他系统调用(如其他定时器中断)覆盖,导致处理程序无法使用正确参数。请问如何在返回用户空间前正确传递参数,避免被覆盖?
解决方案
核心问题分析
当前在定时器中断触发时直接修改trapframe的a0寄存器,但后续如果有其他系统调用或中断发生,会覆盖trapframe中的寄存器值,导致参数丢失。必须确保只有在即将返回用户空间执行处理程序的最后一刻,才设置参数寄存器,避免中间操作覆盖。
具体修改步骤
新增调用标记位
在proc结构体中添加一个标记位,用来记录是否需要调用定时器处理程序:// 加入proc结构体 int need_call_handler;调整定时器中断处理逻辑
当计时到达间隔时,不再直接修改trapframe,而是保存上下文并设置标记位,同时关中断避免并发修改:if (which_dev == 2 && myproc() != 0 && myproc()->state == RUNNING) { struct proc *p = myproc(); if (p->thrdstop_interval == 0 && p->threstop_handler == 0) goto yield; if (p->thrdstop_ticks == p->thrdstop_interval) { if(!p->timeFlag) { intr_off(); // 关中断保护上下文操作 memmove(&p->thrdstop_context[p->thrdstop_context_id], p->trapframe, sizeof(p->thrdstop_context[p->thrdstop_context_id])); p->need_call_handler = 1; // 标记需要调用处理程序 p->thrdstop_ticks = 0; p->timeFlag = 1; intr_on(); } } else { p->thrdstop_ticks += 1; } yield: yield(); }在返回用户空间前设置参数
在usertrap函数的末尾,即将执行userret()之前检查标记位,此时设置a0和epc,确保后续无其他操作覆盖:// 在usertrap中,userret()调用前添加 struct proc *p = myproc(); if (p->need_call_handler) { p->trapframe->a0 = p->thrdstop_handler_args; p->trapframe->epc = p->threstop_handler; p->need_call_handler = 0; // 重置标记 } userret();修复恢复逻辑的残留状态
在sys_thrdresume中重置标记位,避免残留状态导致错误调用:uint64 sys_thrdresume(void) { int context_id; if (argint(0, &context_id) < 0) return -1; struct proc *proc = myproc(); if (proc->thrdstop_context_used[context_id] == 0) return -1; proc->thrdstop_context_used[context_id] = 0; proc->thrdstop_interval = 0; proc->thrdstop_context_id = -1; proc->need_call_handler = 0; // 重置调用标记 memmove(proc->trapframe, &proc->thrdstop_context[context_id], sizeof(proc->thrdstop_context[context_id])); proc->timeFlag = 0; return 0; }
关键注意事项
- 中断保护:保存上下文和设置标记位时必须关中断,防止其他中断打断导致状态不一致。
- 标记位原子性:确保
need_call_handler的设置与检查是原子操作,避免并发冲突。 - 延迟参数设置:仅在返回用户空间前修改寄存器,确保后续无系统调用或中断覆盖参数。
内容的提问来源于stack exchange,提问作者Adi_Hsiao_0410
相关产品推荐
相关产品推荐

