Оперативни системи 1

Post on 10-Jan-2016

39 views 1 download

description

Оперативни системи 1. В e жбе Lista predmeta: i r2os1@lists.etf.rs. Увод у архитектуру 8086. 16-битни микропроцесор. Адресибилна јединица бајт. Регистри: сегментни општенем e нски индексни базни регистар BP и показивач на стек SP. Сегментни регистри. CS – код сегмент , - PowerPoint PPT Presentation

Transcript of Оперативни системи 1

Оперативни системи 1

Вeжбе

Lista predmeta: ir2os1@lists.etf.rs

Увод у архитектуру 8086

• 16-битни микропроцесор.• Адресибилна јединица бајт.• Регистри:

– сегментни– општенемeнски– индексни– базни регистар BP и показивач на стек SP.

Сегментни регистри• CS – код сегмент,• SS – стек сегмент,• DS – сегмент података и• ES – екстра сегмент за податке• Користе се за формирање адресе:

– адреса је 20-битна– састоји се од:

• сегмента – SEG (16 бита) • и офсета (померај) – OFF (16 бита)

– физичка адреса је 20 бита и рачуна се: 0х10 * SEG + OFF

– сегмент је увeк један од 4 сегментна регистра• ВАЖНЕ НАПОМЕНЕ:

– ови сегменти немају никакве везе са виртуелним адресирањем које се налази код модерних процесора

– примeтити да се неке физичке адресе могу добити комбинацијом више различитих парова сегмената и офсета.

Општенамeнски регистри

• AX, BX, CX и DX,• Сваки по 16 бита,• Сваки се може посматрати као два 8-битна:

AX -> AH (виши бајт) и AL (нижи бајт)BX -> BH (виши бајт) и BL (нижи бајт)CX -> CH (виши бајт) и CL (нижи бајт)DX -> DH (виши бајт) и DL (нижи бајт)

Примeр:mov AX, 1234h (исто што и 0х1234 на C-у)

AH AL 12h 34h

Индексни и базни регистри

• Индексни:– SI i DI,– Сваки по 16 бита,– Користе се при индексном адресирању (приступ

елементима низа).– Примeр: mov AX, niz[SI]

• Базни регистар за приступ стеку:– BP– 16 бита.– Користи се за приступ стеку (приступ стварним

аргументима и локалним промeнљивим процедуре).

Стек

• Постоје две поделе (4 врсте) стекова:– да ли расте ка вишим адресама или ка

нижим?– да ли показивач на стек показује на заузету

локацију на врху стека или на прву слободну локацију изнад врха стека?

• Стек процесора 8086:– расте ка нижим адресама– SP указује на заузету локацију на врху

стека

Стек позива потпрограма

• Са BP

Zašto BP?Zar nije dovoljan

SP?

Стек позива потпрограма

mySub: ; Start of procedure

push bp

mov bp, sp

sub sp, n ; reserve n bytes ; of local

storage

push reg1 ; save registers

push reg2

; do some processing

pop reg2

pop reg1

add sp, n ; just the opposite

mov sp, bp

pop bp

ret ; we are done.

Кonvencija pri pozivu potprograma:

Napomena: Parametri se stavljaju na stek u obrnutom redosledu od onog navedenog pri deklaraciji f-je.

Како се потпрограму прослеђују параметри?

• Потпрограм на C-у:

int fun(int a){

return a;

}

• Исти потпрограм на асемблеру:

fun proc

push BP

mov BP, SP

mov AX, [BP+6]

pop BP

ret

Како се потпрограму прослеђују параметри?

fun procpush BPmov BP, SPmov AX, [BP+6]pop BPret...push 1234hcall funadd SP, 2

Напомена: Претпоставља се huge mem. model

Стек:xxxx+7 12 виши бајт на вишој

адреси+6 34 нижи бајт на нижој адреси+5 retCSh h – high byte+4 retCSl l – low byte+3 retPCh+2 retPCl+1 oldBPh+0 oldBPl

SP

BPАХ = 0х1234

Задатак 1.• Написати програм на програмском језику C који ће помоћу једне

функције бесконачно исписивати неки текст. Решење може да буде некоректно у смислу да ће оперативни систем у коначном времену пријавити грешку у извршавању програма и прекинути његово извршавање. Није дозвољено коришћење петљи.

#include<stdio.h>void a(){ printf("...\n"); a();}

void main(){ a();}

Шта је проблем?

Стек: (у једном реду једна реч – 2B)xx

retCSend_mainretPCend_mainoldBP_main

retCSend_aretPCend_aoldBP_a1

retCSend_aretPCend_aoldBP_a2

...

SP

BP

Задатак 2.

• Написати програм на програмском језику C који ће бесконачно понављати исписивање неког текста. Није дозвољено користити петље и програм мора бити исправан, тј. да ОС никада не пријави грешку за тај програм.

Решење#include <stdio.h>int i;void a(){ //cuva pov. adr. asm{ push ax mov ax, [bp]+2 mov i,ax pop ax }}

void b(){ //menja pov. adr. asm{ push ax mov ax,i mov [bp]+2,ax pop ax }}int main(){ a(); printf("Izmedju a i b.\n"); b(); return 0;}

printf_OFFSETBP_mainAX_old

i:printf_OFFSET

return_OFFSETBP_mainAX_old

Задатак 3.

• Написати програм за процесор 8086 који треба да изврши неки потпрограм, али тако да се нигдe у коду не види позив тог потпрограма.

Решење – варијанта 1unsigned int SP_f, SP_main;unsigned int stek_f[1024];

void _dispatch1(){ asm { mov SP_main, sp //cuva sp od main

mov sp, SP_f // restauira sp od f }}

void _dispatch2(){ asm { mov SP_f, sp //cuva sp od f

mov sp, SP_main // restauira sp od main }}

void f(){//kod funkcije//...//kod za izlazak iz funkcije: dispatch2();}

void main(){ stek_f[1023] = FP_OFF(f) SP_f = FP_OFF(stek_f+1022); dispatch1();}

stek_f+1023

stek_f

SP_main SP_f

xx

PC_main_}

BP_main

...

f //adresa funkcije f

0

...

...

SP BP

PC

0

На почетку сваке

функције:push BP

mov BP, SP

На почетку сваке

функције:push BP

mov BP, SPНа крају сваке

функције:pop BP

ret

FP_OFF(f) dohvata offset adrese f-je f

Решење – варијанта 1unsigned int SP_f, SP_main;unsigned int stek_f[1024];

void _dispatch1(){ asm { mov SP_main, sp //cuva sp od main

mov sp, SP_f // restauira sp od f }}

void _dispatch2(){ asm { mov SP_f, sp //cuva sp od f

mov sp, SP_main // restauira sp od main }}

void f(){//kod funkcije//...//kod za izlazak iz funkcije: dispatch2();}

void main(){ stek_f[1023] = FP_OFF(f) SP_f = FP_OFF(stek_f+1022); dispatch1();}

stek_f+1023

stek_f

SP_main SP_f

xx

PC_main_}

BP_main

...

f //adresa funkcije f

0

...

...

SP BP

PC

0

PC_f_}

Извршава се код функције

На крају сваке функције:

pop BPret

На почетку сваке

функције:push BP

mov BP, SP

Решење – варијанта 2unsigned int temp;

void _dispatch1(){ asm { pop temp

push fpush temp

}}

void f(){//kod funkcije//...}

void main(){ dispatch1();}

xx

PC_main_}

BP_main

BP_main

SPtemp:

BP_main

f //adresa funkcije f

Задатак 4.

• За процесор 8086 и код написан на програмском језику C, омогућити да се функције извршавају конкурентно, а да се прелазак са функције на функцију обавља помоћу корутина. Сматрати да се користе само регистри АX, BX, CX и DX. Остале занемарити. Регистре чувати на стеку.

Решење• Korutina – eksplicitno odricanje od procesora jedne nit u korist

druge niti. Potrebno je sacuvati kontekst, kako bi kasnije mogao da se restaurira.

• Kontekst procesora (processor execution context): sve vrednosti iz procesorskih registara koje je potrebno sačuvati da bi se izvršavanje nastavilo od mesta napuštanja:– Mesto u programu na kome se stalo - PC– Podaci u procesoru – registri opšte namene– Lokalni podaci potprograma i “trag” izvršavanja – sve na steku –

SP• Prelazak sa izvršavanja jednog procesa na drugi –

promena konteksta (context switch):– sačuvati kontekst koji se napušta– povratiti kontekst na koji se prelazi

Решење• Потребно извршити следећи код:

running->sp = SP; // cuvanje SPгдe је:– sp поље PCB структуре– SP регистар

• На језику С у општем случају није могуће приступити жељеном регистру.

• Зато се умећу сегменти асемблерског кода.• Асемблерски еквивалент горе наведеној наредби је (под

претпоставком PCB структуре дате на следећем слајду):asm{

mov BX, running // BX = runningmov [BX], SP // [BX] = SP - indirektno registarsko adresiranje

}-примeтити да се BX користи за адресирање, па се на почетку његов

садржај мора привремено сачувати (нпр на стеку)

Решењеstruct PCB{ unsigned sp; unsigned* stack;};

PCB *p[3];PCB* running;

int nextThread;

//makro#define dispatch(x) { nextThread = x; \ _dispatch(); }

void _dispatch(){ asm { // cuva registre na steku

push ax //sta bi se desilo da ne cuvamo AX?push bxpush cxpush dxmov bx, running //upisuje adresu mem. lok.

mov [bx], sp //cuva sp } running=p[nextThread]; asm {

mov bx, runningmov sp, [bx] // restauira sp

pop dx // restauira registre pop cx pop bx pop ax

}}

При уласку у функцију dispatch() на стек ће се

ставити повратна адреса. То је адреса прве

инструкције после позива dispatch() и уједно и адреса

од које треба наставити прекинуту нит.

Чувају се и 4 регистра (тако је речено у поставци

задатка). У општем случају, неопходно је сачувати све регистре осим CS:PC (већ

сачувано при уласку у функцију) , BP (већ сачуван

првом инструкцијом функције f()) i SS:SP (биће

сачувани у PCB).

Чува се показивач на стек.Инструкције:

mov bx, runningmov [bx], sp

су еквивалентне са C кодом:running->sp = SP //SP je registar

Рестаурација стека нове нити:

SP = running->sp;Рестаурација остатка контекста:-прво регистри DX, CX, BX i AX-рестаурација BP са pop BP-рестаурација PC са ret

running је показивач на PCB структуру нити која се тренутно

извршава. Да би се умeсто нити, која се

извршавала до позива _dispatch() наставила нова нит, неопходно је поставити да running показује на

PCB те нове нити. У овом задатку је претпостављено

да се нова нит одрeђује уписом одговарајуће вредности у

nextThread.

По уласку у функцију, наилази се на прве две инструкције сваке

функције:push BP

mov BP, SPКао резултат прве инструкције на

стеку је сачувана вредност BP регистра која је коришћена у прекинутој нити и која ће се

користити опет по повратку у ту нит.

Решењеvoid a(){ for (int i = 0; i < 3; ++i) printf("U a() %d\n",i); asm { mov ax, 7 } dispatch(2); asm { mov i, ax } printf(" u a() ax = %d\n",i);

for (i = 0; i < 3; ++i) printf("U a() %d\n",i);

dispatch(2);}

void b(){ for (int i = 0; i < 3; ++i) { printf("U b() %d\n",i); }

asm { mov ax, 2

}

dispatch(1);

for (i = 0; i < 3; ++i) { printf("U b() %d\n",i); }

dispatch(0);

}

Решењеvoid createThread(PCB *newPCB, void (*body)()){ unsigned* st = new unsigned[1024]; unsigned newPC = FP_OFF(body); st[1023] = newPC; //upisuje se rec - int //pocetni kontekst (proizvoljne vrednosti) //st[1023-1018]={PC,BP,AX…DX} newPCB->sp = FP_OFF(st+1018); newPCB->stack = st;}

void delete_all(){ delete [] p[1]->stack; delete [] p[2]->stack; delete p[0]; delete p[1]; delete p[2];}

int main(){ p[1] = new PCB(); createThread(p[1],a); printf("napravio a\n");

p[2] = new PCB(); createThread(p[2],b); printf("napravio b\n");

p[0] = new PCB(); running = p[0]; dispatch(1); printf("Kraj.\n"); delete_all(); return 0;}

Задатак 5.

• Рeшити претходни задатак, али тако да се сви регистри чувају у PCB.

struct PCB{ pomeraj u odnosu na poc. adresu strukture unsigned pc; +0 unsigned sp; +2 unsigned bp; +4 unsigned ax; +6 unsigned bx; +8 unsigned cx; +10 unsigned dx; +12 // izrazeno u bajtovima ...};

Решење• Потребно извршити следећи код:

running->ax = AX;гдe је:– ах поље PCB структуре– АХ регистар

• На језику С у општем случају није могуће приступити жељеном регистру.

• Зато се умећу сегменти асемблерског кода.• Асемблерски еквивалент горе наведеној наредби је (под

претпоставком PCB структуре дате на претходном слајду):asm{

mov BX, running // BX = runningmov BX[6], AX // [BX+6] = AX - indirektno reg. adr. sa pomerajem

}-примeтити да се BX користи за адресирање, па се на почетку његов

садржај мора привремено сачувати (нпр на стеку)

Решењеvoid _dispatch(){ asm {

push bxmov bx, runningmov bx[6], axpop WORD PTR [bx+8] //bxmov bx[10], cxmov bx[12], dxpop WORD PTR[bx+4] //bppop WORD PTR[bx] //pc

mov bx[2], sp //cuva sp }

running=p[nextThread]; asm {

mov bx, runningmov sp, bx[2]

push WORD PTR[bx] //pcpush WORD PTR[bx+4] //bpmov ax, [bx+6]push WORD PTR [bx+8] //bxmov cx, [bx+10]mov dx, [bx+12]pop bx

} }

struct PCB{ pomeraj unsigned pc; +0 unsigned sp; +2 unsigned bp; +4 unsigned ax; +6 unsigned bx; +8 unsigned cx; +10 unsigned dx; +12 ...};

Napomena: WORD PTR[bx+...] – oznčava da se pristupa reči a ne jedanom bajtu

Решење

void createThread(PCB *newPCB, void (*body)()){

unsigned* st = new unsigned[1024];

newPCB->pc = FP_OFF(body);

//stek je sada prazan

newPCB->sp = FP_OFF(st+1024);

newPCB->bp = FP_OFF(st+1024);

newPCB->stack = st;

}

Задатак 6.

• Изменити корутину из задатка 4. тако да се избор нове нити помоћу метода класе Scheduler:– void Scheduler::put(PCB *);

Додаје нит у листу кандидата за извршавање

– PCB* Scheduler::get();

Дохвата следећу нит из листе спремних процеса

Решењеvoid _dispatch(){ asm { // cuva registre na steku

push ax //sta bi se desilo da ne cuvamo AX?push bxpush cxpush dxmov bx, running

mov [bx], sp //cuva sp } Scheduler::put(running); running = Scheduler::get(); asm {

mov bx, runningmov sp, [bx] // restauira sp

pop dx // restauira registre pop cx pop bx pop ax

}}

Чување контекстадо сада извршаване

нити

Рестаурација контекстанити која се наставља

Задатак 7.

• Написати универзалну корутину dispatch() коришћењем функицја setjmp и longjmp. Функција setjmp прихвата показивач на бафер у којем чува тренутни контекст и враћа 0. Функција longjmp прихвата два параметра. Први параметар је показивач на структуру која садржи раније сачувани контекст (са setjmp) и који сада треба рестаурирати. Други параметар је вредност коју функција треба да врати (размислити где ће бити коришћена враћена вредност?).

Решење

void dispatch () {if (setjmp(running->context)==0) {

Scheduler::put(running);running = Scheduler::get();longjmp(running->context,1);

} else {return;

}}

Напомене–Део функције dispatch() иза позива setjmp() , а пре позива longjmp (), ради и даље на стеку претходно текуће нити (позиви функција класе Scheduler). –Тек од позива longjmp () прелази се на стек нове текуће нити. Ово није никакав проблем, јер тај део представља "ђубре" на стеку изнад границе која је запамћена у setjmp() . Приликом повратка контекста претходне нити, извршавање ће се наставити од запамћене границе стека, испод овог "ђубрета".

Напомене–Извршавање наставља са оног места где је позвана setjmp(), с тим да сада setjmp() враћа ону вредност која је достављена позиву longjmp()(то мора бити вредност различита од 0), самим тим вредност АX регистра је измењена – проблем код асинхроног преузимања.–Од тренутка чувања контекста помоћу setjmp(), до тренутка повратка помоћу longjmp(), извршавање у коме је setjmp() не сме да се врати из функције која непосредно окружује позив setjmp(), јер би се тиме стек нарушио, па повратак помоћу longjmp() доводи до краха система.

Задатак 8.

• Имплементирати функције setjmp и longjmp тако да раде на начин описан у претходном задатку:

" Функција setjmp прихвата показивач на бафер у којем чува тренутни контекст и враћа 0. Функција longjmp прихвата два параметра. Први параметар је показивач на структуру која садржи раније сачувани контекст (са setjmp) и који сада треба рестаурирати. Други параметар је врeдност коју функција треба да врати."

Решењеstruct cnt_buf{ // pomeraj

unsigned sp; // +0unsigned ax; // +2unsigned bx; // +4unsigned cx; // +6unsigned dx; // +8unsigned pc; // +10unsigned bp; // +12

};

struct PCB{ cnt_buf* context; unsigned* stack;};

unsigned _setjmp(cnt_buf *b, unsigned i) {asm { push bxmov bx, [bp+4] //bx = b; mov bx[2], axpop WORD PTR [bx+4] //bxmov bx[6], cxmov bx[8], dxmov ax, bp[0]mov bx[12], ax //BP nitimov ax, bp[2]mov bx[10], ax //PC nitimov [bx], sp //running->sp = SP//skida se sa steka i, b, PC, BPadd WORD PTR[bx], 8 }return 0;

}

Sadržaj steka(u odnosu na BP):

+6 i+4 b+2 PC+0 BP-2 BX

Решењеunsigned _longjmp(cnt_buf *b, unsigned i){ asm {

mov bx, [bp+4] //BX = b;mov ax, [bp+6] //AX = i;mov sp, [bx] //restauriramo stekpush axpush bxpush WORD PTR bx[10] //pcpush WORD PTR bx[12] //bpmov bp, sp// restauriramo AX, BX …mov ax, bx[2]push WORD PTR bx[4] //bxmov cx, bx[6]mov dx, bx[8]pop bx}return i;

}

Чест случај је да компајлери генеришу такав код да вредност враћају у

неком од регистара. За 8086 углавном важи да се вредност враћа у регистру

АХ. Зато се ова линија преводи у:

mov ax, [bp+6]pop BPret

Sadržaj Restauriranog Steka (potreban za povratak na setjmp):ibPCBP

Sadržaj steka(u odnosu na BP):

+6 i+4 b+2 PC+0 BP

Решење

void createThread(PCB *newPCB, void (*body)()){ newPCB->stack = new unsigned[1024]; newPCB->context = new cnt_buf;

newPCB->context->pc = FP_OFF(body); newPCB->context->sp = FP_OFF(newPCB->stack+1024);}

Задатак 9.

• Написати корутину dispatch() коришћењем функције:

void yield(unsigned* oldSP, unsigned* newSP); Прва половина функције чува контекст на

текућем стеку и потом памти тренутну врeдност регистра SP у локацији на коју показује oldSP.

Друга половина прво у регистар SP уписује садржај локације на коју показује newSP и потом са стека рестаурира контекст.

Решење

struct PCB{ unsigned SP;

};

void dispatch(){unsigned *oldSP = &running->SP;Scheduler::put(running);running = Scheduler::get();yield(oldSP, &running->SP);

}

Проблем: Потребно је познавати

имплементацију yield() f-je да би се креирао почетни

контекст.

Задатак 10.

• Прокоментарисати претходне задатке уколико се dispatch() позива из прекидне рутине. Шта је у том случају проблем и како се решава?

Решењеvoid _dispatch(){lock() asm { // cuva registre na steku

push ax //sta bi se desilo da ne cuvamo AX?push bxpush cxpush dxmov bx, running

mov [bx], sp //cuva sp } Scheduler::put(running); running = Scheduler::get(); asm {

mov bx, runningmov sp, [bx] // restauira sp

pop dx // restauira registre pop cx pop bx pop ax

}unlock()}

Чување контекстадо сада извршаване

нити

Рестаурација контекстанити која се наставља

На почетку се забрањује

преузимање процесора.

На крају се поново дозвољава

преузимање процесора.