Код:
/* 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
таким образом, я могу восстановить контекст ядра и возвратить значение syscall к dispatcher()
.
Как я могу получить доступ к адресу памяти _entry_point
снаружи функции?
Немного поигравшись с 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);
}
Переключите реализацию функции: сделайте так, чтобы она выглядела так:
Затем вы можете просто настроить прерывание для запуска функции с самого начала. Потребуется глобальный указатель для «текущего пользовательского процесса» - чтобы переключаться между процессами, код ядра, который запускается с помощью «Вызов подпрограмм ядра», просто изменяет эту переменную, чтобы указывать на другой процесс.
Вам понадобится один особый случай - для первоначального переключения из режима ядра в пользовательский режим, для начального процесса, выполняющегося после загрузки. Однако после этого вышеуказанная функция должна справиться с этим.
Помимо использования встроенной сборки для доступа к _entry_point, вы также можете определить ее как функцию, например:
asm volatile("_entry_point:");
void contextinit() {
extern void _entry_point();
set_evec(49, (long)&_entry_point);
}