Последовательность переключения контекста
- Вызов прерывания (например: прерывание по таймеру (trap.c 109))
- Вызов планировщика (yield (proc.c 390))
- Запустить переключатель контекста (swtch (proc.c 387))
До того, как произойдет прерывание переключения контекста
Vector.S (сгенерировать из Perl-скрипта)
vector 32: pushl $0 //error code pushl $32 //vector number of timer interrupt jmp alltraps
trapasm.S
Ref: ссылки на некоторые регистры
Регистр сегмента сохраняет физический адрес смещения сегмента
# trap frame .globl alltraps alltraps: pushl %ds #data segment register pushl %es #extra segment register pushl %fs #general segment register pushl %gs #general segment register pushal #all local registers
Настроить сегменты данных
# Set up data segments. movw $(SEG_KDATA<<3), %ax movw %ax, %ds movw %ax, %es # Call trap(tf), where tf=%esp by calling convention pushl %esp call trap
Затем в trap.c 111 вызывается yield () для вызова планировщика.
Кроме того, после вызова планировщика система запускает переключение контекста, указанное ранее в swtch (struct context ** old, struct context * new)
Переключение контекста начинается!
Переключение контекста записывает в сборку и ссылки компоновщиком!
// defs.h 129 void swtch(struct context** old, struct context* new);
swtch.S
swtch: movl 4(%esp) %eax # address of **old !!little endian movl 8(%esp) %edx # address of *new #save all callee registers pushl %ebp pushl %ebx pushl %esi pushl %edi # change esp pointer movl %esp, (%eax) # (%eax) means *old (points to address of old) movl %edx, %esp # load new callee-save registers # do not pop the EIP since this is the return address popl %edi popl %esi popl %ebx popl %ebp ret
Напоминание: соглашение о вызовах в стеке
- локальные переменные | EBP (указатель кадра) | обратный адрес | параметры
Структура данных контекста (proc.h 28)
// reverse order from pushl registers struct context { uint edi; uint esi; uint ebx; uint ebp; uint eip; }
Напоминание: контекст всегда находится на вершине некоторого стека
Инициализация процессора
main.c
// main.c 39 mpmain(); // finish this processor's set up // Common CPU setup code static void mpmain(void) { cprintf("cpu%d: starting %d\n", cpuid(), cpuid()); idtinit(); // load idt register xchg(&(mycpu()->started), 1); // tell startothers() we're up scheduler(); // start running processes }
Что делает xchg (volatile uint * addr, uint newval)?
xchg произвел обмен значениями с хранилищем значений в addr, которое используется для реализации блокировки.
Образец из курса MIT:
xchg% eax, адрес
- освободить другие действия ЦП для доступа к адресу addr
- tmp: = * адрес
- * адрес: =% eax
- % eax: = tmp
- Разморозить деятельность других ЦП по адресу адреса
Вернемся к нашей теме, сосредоточимся на функции scheduler ():
Что он делает, так это после инициализации всех требований, которые нам нужны для XV6, он запускает одну процедуру из текущего контекста (планировщик процессора)
proc.c
// proc.c 326 void scheduler(void) { ... // inside loop of the ptable acquire(&ptable.lock); for(p = ptable.proc; p < &ptable.proc[NPROC]; p++) { if(p->state != RUNNABLE) continue; // switch to chosen process c->proc = p; switchuvm(p); p->state = RUNNING; // do context switch // c->scheduler points to current context & restore the context of p swtch(&(c->scheduler), p->context ); switchkvm(); c->proc = 0; } }
Затем мы знаем, что контекст переключается на первый запускаемый процесс.
Однако как XV6 подготавливает контекст для процесса?
Как создается контекст?
Что ж, когда первый пользовательский процесс создается в userinit () или выполняет процесс fork (), они оба вызывают allocproc, чтобы найти НЕИСПОЛЬЗУЕМЫЙ процесс в ptable.
статическая структура proc * allocproc (void) (proc.c 75)
static struct proc* allocproc(void) { // after finding an UNUSED process found: p->state = EMBRYO; p->pid = nextpid++; release(&ptable.lock); // Allocate kernel stack. if((p->kstack = kalloc()) == 0){ p->state = UNUSED; return 0; } // stack pointer sp = p->kstack + KSTACKSIZE; //Leave room for trap frame sp -= sizeof *p->tf; p->tf = (struct trapframe*)sp; // Set up new context to start executing at forkret, // which returns to trapret. sp -= 4; *(uint*)sp = (uint)trapret; sp -= sizeof *p->context; p->context = (struct context*)sp; memset(p->context, 0, sizeof *p->context); // that's why it is not required to push eip in swtch.S p->context->eip = (uint) forkret; return p; }
Узнайте больше о переключении контекста
Обсудите возможность переключения с текущего процесса на планировщик, когда происходит прерывание по времени!
недействительный график (недействителен) (proc.c 371)
swtch(&p->context, mycpu()->scheduler);
# move esp value to what %eax points to movl %esp, (%eax) # (%eax) means *old (points to address of old) movl %edx, %esp
Пусть указатель стека указывает на контекст, который мы хотим переключить. Вот адрес расписания процессора.
movl %edx, %esp
Теперь мы должны знать весь процесс переключения контекста!
Последовательность переключения контекста
- Прервать текущий процесс. Затем переключитесь на ›планировщик процессора.
- Планировщик находит RUNNABLE proc в таблице ptable и переключает контекст с планировщика на другой RUNNABLE процесс
Процесс - ›Планировщик -› Процесс
Планировщик - это мост между двумя процессами
Следующее - как вернуться в пользовательское пространство?
После переключения контекста
Место назначения: вернуться на уровень пользователя
Стратегия: Calling Convention! Используйте EIP, чтобы получить обратный адрес
switch.S - ›sched (proc.c) -› yield (proc.c) - ›trap (trap.c) -› trapret (trapasm.S, но настраивается в allocproc ()) - ›iret ( trapasm.S)
Напоминание: почему XV6 должен помещать ESP в стек?
Ответ: как параметр для void trap (struct trapframe * tf)
Резюме
В этом посте кратко описан процесс переключения контекста в небольшой системе XV6.
Это легко понять. Подумайте о том, что у каждого процесса есть собственное личное пространство для хранения данных, включая регистры и стеки. Затем переключение контекста - это механизм для подготовки ресурсов для ЦП, когда мы хотим завершить работу из разных процессов.
Кроме того, планировщик является мостом, который помогает процессам в этом.