代码:
/* ctsw.c : context switcher
*/
#include <kernel.h>
static void *kstack;
extern int set_evec(int, long);
/* contextswitch - saves kernel context, switches to proc */
enum proc_req contextswitch(struct proc_ctrl_blk *proc) {
enum proc_req call;
kprintf("switching to %d\n", getpid(proc));
asm volatile("pushf\n" // save kernel flags
"pusha\n" // save kernel regs
"movl %%esp, %0\n" // save kernel %esp
"movl %1, %%esp\n" // load proc %esp
"popa\n" // load proc regs (from proc stack)
"iret" // switch to proc
: "=g" (kstack)
: "g" (proc->esp)
);
_entry_point:
asm volatile("pusha\n" // save proc regs
"movl %%esp, %0\n" // save proc %esp
"movl %2, %%esp\n" // restore kernel %esp
"movl %%eax, %1\n" // grabs syscall from process
"popa\n" // restore kernel regs (from kstack)
"popf" // restore kernel flags
: "=g" (proc->esp), "=g" (call)
: "g" (kstack)
);
kprintf("back to the kernel!\n");
return call;
}
void contextinit() {
set_evec(49, (long)&&_entry_point);
}
这是一个小型,合作,非抢占式内核的上下文切换器。 contextswitch()
使用要加载的进程的堆栈指针调用dispatcher()
。加载%esp和其他通用寄存器后,将调用iret
并开始运行用户进程。
我需要设置一个中断,以便在contextswitch()
之后返回iret
中的点,这样我就可以恢复内核上下文并将系统调用的值返回给dispatcher()
。
如何从函数外部访问_entry_point
的内存地址?
答案 0 :(得分:4)
切换函数的实现:使它看起来像这样:
然后你可以设置中断从一开始就运行该功能。需要有一个“当前用户进程”的全局指针 - 在进程之间切换,由“调用内核例程”运行的内核代码只是将该变量更改为指向不同的进程。
对于从内核到用户模式的初始切换,您将需要一个特殊情况,用于启动后运行的初始进程。在那之后,上面的函数应该能够处理它。
答案 1 :(得分:3)
在与GCC玩了一会儿之后,我得到了答案。
下降到装配静音GCC警告未使用的标签。
所以,
_entry_point:
替换为
asm volatile("_entry_point:");
和
void contextinit() {
set_evec_(49, &&_entry_point);
}
替换为
void contextinit() {
long x;
asm("movl $_entry_point, %%eax\n"
"movl %%eax, %0": "=g" (x) : : "%eax");
set_evec(49, x);
}
答案 2 :(得分:1)
除了使用内联汇编访问_entry_point之外,您还可以将其定义为函数,如:
asm volatile("_entry_point:");
void contextinit() {
extern void _entry_point();
set_evec(49, (long)&_entry_point);
}