ОС2/Пројекат 2022
Пројекат 2022. године је први пројекат из Оперативних система 1 и 2 у ком се користи xv6 оперативни систем на RISC-V архитектури. У овом водичу се налазе корисни савети при изради пројекта.
Додавање интерфејса распоређивача
Интерфејс дефинисан поставком за распоређивач је исти онај из школског језгра:
struct proc *get(); // Дохватање спремног процеса из реда чекања
void put(struct proc *); // Убацивање спремног процеса у ред чекања
xv6 у себи садржи Round-robin распоређивач директно имплементиран у функцији scheduler()
. Неопходно је заменити овај алгоритам са траженим интерфејсом. Места од значаја су она где процес мења стање.
get()
уscheduler()
након укључивања прекида - прелаз из RUNNABLE у RUNNING.put()
уyield()
- кад процес предаје контекст неком другом процесу.put()
уuserinit()
- кад се креира први кориснички процес.put()
уfork()
- кад се форкује нов процес.put()
уwakeup()
- прелаз из SLEEPING у RUNNABLE.put()
уkill()
- кад процес треба да се заврши треба да га пробудимо.
Имплементирање преотимања по временском квантуму
Концепт временског квантума (timeslice) из овог пројекта је исти као онај из школског језгра на ОС1. xv6 мења контекст (преотима) процесе на сваки тајмерски прекид. Ово се може лако променити да подржава преотимање након истека квантума или непостојање преотимања (nonpreemptive scheduling). Места од значаја:
- Позив
yield()
уusertrap()
се треба догодити само ако је истекао квантум, или никад ако нема преотимања. Овде би се одузимала тренутна вредност све док квантум не истекне. - Исто и за
kerneltrap()
, који је само случај тајмерског прекида у кернел режиму. - Праћење временског квантума неког процеса се може имплементирати на разне начине, али најчешћи је памтити га у
struct proc
, где би онда требало постављати његову вредност уuserinit()
иfork()
.
Додавање новог системског позива
- Дефинишете потпис системског позива са корисничке стране у
user.h
- Додати његов назив из претходног фајла у Perl скрипту
usys.pl
за генерисање кода за позив (не мењатиusys.S
јер је то аутоматски генерисан assembly фајл - брише се при сваком превођењу). - Дефинисати број системског позива (22 је следећи у низу) у
syscall.h
- Имплементирати системски позив у фајлу
sysproc.c
. Потпис мора битиuint64 sys_poziv(void)
- Дохватање целобројних аргумената, показивача и стрингова је омогућено уз помоћне функције
argint()
,argaddr()
иargstr()
респективно у фајлуsyscall.c
. - Први аргумент помоћних функција је индекс аргумента у потпису функције са корисничке стране, а сама вредност се прослеђује преко показивача који је други аргумент.
- Повратна вредност 0 из помоћних функција означава успех.
argraw(int n)
директно враћа вредности регистараa0-a5
који се номинално користе за пренос аргумената при позиву процедуре.
- Дохватање целобројних аргумената, показивача и стрингова је омогућено уз помоћне функције
- Додати назив функције из прошле тачке у низ показивача функције
syscalls
уsyscall.c
. Индекс функције у низу мора да се слаже из индекса уsyscall.h
.
Окружење
Makefile
Дебаговање корисничких програма
Да би дебаговали корисничке програме, морате у конфигурацији за дебаговање у окружењу променити бинарни фајл који ће gdb-multiarch да дебагује (подразумевано сам кернел).
- Мени Run → Edit Configurations → Remote Debug → Debug (или ваш назив конфигурације) → Symbol file промените на бинарни фајл корисничког програма (налазе се у директоријуму
/user/
и сви почињу са доњом цртом). - Поставите breakpoint, покрените ОС са
qemu-gdb
и у њему покрените изабрани програм.
Промена учестаности тајмерског прекида
У сврху тестирања на одбрани је понекад тражено да се повећа учестаност прекида ради прецизнијих мерења перформанси (помоћу системског позива uptime()
) и видљивијег ефекта распоређивања. Интервал тајмера се може променити у фајлу start.c
у функцији timerinit()
.
Тумачење scause и sepc
Прескочене су резервисане вредности.
scause вредност | Значење | Потенцијални проблем |
---|---|---|
0 | Адреса у PC није поравната | Кернел је променио контекст у невалидан процес (вероватно је распоређивач вратио невалидан процес). |
1 | Грешка у приступу инструкцији | |
2 | Илегална инструкција | Уколико користите floating-point типове, они нису подржани на овој имплементацији RISC-V. |
3 | Breakpoint | Не би требало да се појави као разлог за панику. |
4 | Адреса са које се чита није поравната | Лош показивач. |
5 | Грешка у приступу при читању | |
6 |
| |
7 | Грешка у приступу при писању/атомичној инструкцији. | |
8 | ecall из корисничког режима | Не би требало да се појави као разлог за панику. |
9 | ecall из супервизорског (кернел) режима | Не би требало да се дешава уопште у xv6. |
c | Странична грешка при учитавању инструкције | Кернел је променио контекст у невалидан процес (вероватно је распоређивач вратио невалидан процес). |
d | Странична грешка при читању |
|
f | Странична грешка при писању/атомичној инструкцији |
|
Вредност sepc регистра означава вредност PC у тренутку кад је настала грешка. Може се тачно пронаћи место у коду претраживњем ове адресе у фајлу kernel.asm
који садржи и C код кернела и генерисани RISC-V асембли са тачном адресом инструкција.