ОС1/Модификације октобар 2025 — разлика између измена
(Уклоњена целокупна садржина странице) ознака: страница испражњена |
Нема описа измене ознака: ручно враћање |
||
| Ред 1: | Ред 1: | ||
== Modifikacija: joinAll == | |||
Dodati sistemske pozive <code>thread_add_child</code> i <code>thread_join_all</code>. | |||
<code>thread_add_child</code> registruje dete-nit za tekuću (pozivajuću) nit. | |||
<code>thread_join_all</code> blokira tekuću nit dok se sva njena deca ne izvrše. | |||
U okviru C++ API dodati nestatičke metode <code>addChild(Thread* child)</code> i <code>joinAll()</code> u klasu <code>Thread</code>. | |||
Napisati test program: nit A pravi 3×B i 1×C, svaki B pravi 3×C. Roditelj čeka svu svoju decu pre nego što nastavi. | |||
=== Rešenje === | |||
==== TCB.hpp ==== | |||
Dodato na vrhu fajla (forward declaration): | |||
<syntaxhighlight lang="cpp"> | |||
class Semaphore; | |||
</syntaxhighlight> | |||
Nova polja u <code>private</code> sekciji: | |||
<syntaxhighlight lang="cpp"> | |||
TCB* parent; // pokazivač na roditelja (nullptr ako nema) | |||
int childCount; // broj nezavršene dece | |||
Semaphore* childrenSem; // semafor na koji roditelj čeka u joinAll | |||
</syntaxhighlight> | |||
Nove metode u <code>public</code> sekciji: | |||
<syntaxhighlight lang="cpp"> | |||
static void addChild(TCB* parent, TCB* child); | |||
static void joinAllChildren(); | |||
</syntaxhighlight> | |||
==== TCB.cpp ==== | |||
Dodat include na vrhu: | |||
<syntaxhighlight lang="cpp"> | |||
#include "../h/Semaphore.hpp" | |||
</syntaxhighlight> | |||
U konstruktoru, inicijalizacija novih polja (posle <code>this->finished = false;</code>): | |||
<syntaxhighlight lang="cpp"> | |||
this->parent = nullptr; | |||
this->childCount = 0; | |||
this->childrenSem = nullptr; | |||
</syntaxhighlight> | |||
Izmenjen <code>dispatch()</code> — else grana proširena da obavesti roditelja kad dete završi: | |||
<syntaxhighlight lang="cpp"> | |||
void TCB::dispatch() { | |||
TCB *old = running; | |||
if (!old->isFinished()) { | |||
Scheduler::put(old); | |||
} else { | |||
if (old->parent != nullptr) { | |||
old->parent->childCount--; | |||
if (old->parent->childCount == 0 && old->parent->childrenSem != nullptr) { | |||
old->parent->childrenSem->sem_signal(); | |||
} | |||
} | |||
} | |||
running = Scheduler::get(); | |||
contextSwitch(&old->context, &running->context); | |||
} | |||
</syntaxhighlight> | |||
Nove funkcije: | |||
<syntaxhighlight lang="cpp"> | |||
void TCB::addChild(TCB* parent, TCB* child) { | |||
child->parent = parent; | |||
parent->childCount++; | |||
if (parent->childrenSem == nullptr) { | |||
Semaphore::createSemaphore(&parent->childrenSem, 0); | |||
} | |||
} | |||
void TCB::joinAllChildren() { | |||
if (running->childCount <= 0) return; | |||
running->childrenSem->sem_wait(); | |||
} | |||
</syntaxhighlight> | |||
==== syscall_c.hpp ==== | |||
Dodate deklaracije: | |||
<syntaxhighlight lang="cpp"> | |||
void thread_add_child(thread_t child); | |||
void thread_join_all(); | |||
</syntaxhighlight> | |||
==== syscall_c.cpp ==== | |||
<syntaxhighlight lang="cpp"> | |||
void thread_add_child(thread_t child) { | |||
if (child == nullptr) return; | |||
do_syscall(0x16, (uint64)child); | |||
} | |||
void thread_join_all() { | |||
do_syscall(0x17); | |||
} | |||
</syntaxhighlight> | |||
==== RISCV.cpp (handleSupervisorTrap) ==== | |||
Dodate nove case grane u switch (posle case 0x13 dispatch): | |||
<syntaxhighlight lang="cpp"> | |||
case 0x16: { | |||
TCB *child = (TCB*)arg1; | |||
TCB::addChild(TCB::running, child); | |||
break; | |||
} | |||
case 0x17: { | |||
TCB::joinAllChildren(); | |||
break; | |||
} | |||
</syntaxhighlight> | |||
==== syscall_cpp.hpp ==== | |||
Dodate metode u klasu <code>Thread</code> (public sekcija): | |||
<syntaxhighlight lang="cpp"> | |||
void addChild(Thread* child); | |||
void joinAll(); | |||
</syntaxhighlight> | |||
==== syscall_cpp.cpp ==== | |||
<syntaxhighlight lang="cpp"> | |||
void Thread::addChild(Thread* child) { | |||
thread_add_child(child->getMyHandle()); | |||
} | |||
void Thread::joinAll() { | |||
thread_join_all(); | |||
} | |||
</syntaxhighlight> | |||
==== Test primer ==== | |||
<syntaxhighlight lang="cpp"> | |||
#include "../h/syscall_cpp.hpp" | |||
#include "../h/syscall_c.hpp" | |||
#include "../h/printing.hpp" | |||
class ThreadC : public Thread { | |||
public: | |||
ThreadC(int id) : Thread(), id(id) {} | |||
private: | |||
int id; | |||
void run() override { | |||
printString(" C"); | |||
printInt(id); | |||
printString(" started\n"); | |||
volatile int sum = 0; | |||
for (int i = 0; i < 3000; i++) | |||
for (int j = 0; j < 3000; j++) | |||
sum += j; | |||
printString(" C"); | |||
printInt(id); | |||
printString(" finished\n"); | |||
} | |||
}; | |||
class ThreadB : public Thread { | |||
public: | |||
ThreadB(int id) : Thread(), id(id) {} | |||
private: | |||
int id; | |||
void run() override { | |||
printString(" B"); | |||
printInt(id); | |||
printString(" started, creating 3 C children\n"); | |||
ThreadC* c[3]; | |||
for (int i = 0; i < 3; i++) { | |||
c[i] = new ThreadC(id * 10 + i); | |||
c[i]->start(); | |||
this->addChild(c[i]); | |||
} | |||
printString(" B"); | |||
printInt(id); | |||
printString(" waiting for children...\n"); | |||
this->joinAll(); | |||
printString(" B"); | |||
printInt(id); | |||
printString(" all children done!\n"); | |||
for (int i = 0; i < 3; i++) delete c[i]; | |||
} | |||
}; | |||
void joinAllTest() { | |||
printString("A started, creating 3 B and 1 C\n"); | |||
ThreadB* b[3]; | |||
for (int i = 0; i < 3; i++) { | |||
b[i] = new ThreadB(i); | |||
b[i]->start(); | |||
thread_add_child(b[i]->getMyHandle()); | |||
} | |||
ThreadC* c = new ThreadC(99); | |||
c->start(); | |||
thread_add_child(c->getMyHandle()); | |||
printString("A waiting for all children...\n"); | |||
thread_join_all(); | |||
printString("\n=== A: ALL children done! ===\n"); | |||
for (int i = 0; i < 3; i++) delete b[i]; | |||
delete c; | |||
} | |||
</syntaxhighlight> | |||
'''Napomena:''' <code>start()</code> mora biti pozvan PRE <code>addChild()</code> jer <code>start()</code> interno poziva <code>thread_create()</code> koji postavlja <code>myHandle</code>. Bez toga, <code>getMyHandle()</code> vraća <code>nullptr</code>. | |||
==== Očekivan output ==== | |||
<pre> | |||
A started, creating 3 B and 1 C | |||
A waiting for all children... | |||
B0 started, creating 3 C children | |||
B0 waiting for children... | |||
B1 started, creating 3 C children | |||
B1 waiting for children... | |||
B2 started, creating 3 C children | |||
B2 waiting for children... | |||
C99 started | |||
C99 finished | |||
C0 started | |||
C0 finished | |||
C1 started | |||
C1 finished | |||
...svi C završe... | |||
B0 all children done! | |||
B1 all children done! | |||
B2 all children done! | |||
=== A: ALL children done! === | |||
</pre> | |||
==== Kako radi ==== | |||
Svaki TCB ima polje <code>parent</code> (ko je roditelj) i <code>childCount</code> (koliko dece još radi). Roditelj se blokira na semaforu (<code>childrenSem</code>, inicijalno 0). Kad nit završi, u <code>dispatch()</code> se dekrementira <code>parent->childCount</code>. Tek kad poslednje dete spusti brojač na 0, poziva se <code>sem_signal()</code> koji deblokirara roditelja. | |||
Верзија на датум 9. март 2026. у 18:32
Modifikacija: joinAll
Dodati sistemske pozive thread_add_child i thread_join_all.
thread_add_child registruje dete-nit za tekuću (pozivajuću) nit.
thread_join_all blokira tekuću nit dok se sva njena deca ne izvrše.
U okviru C++ API dodati nestatičke metode addChild(Thread* child) i joinAll() u klasu Thread.
Napisati test program: nit A pravi 3×B i 1×C, svaki B pravi 3×C. Roditelj čeka svu svoju decu pre nego što nastavi.
Rešenje
TCB.hpp
Dodato na vrhu fajla (forward declaration):
class Semaphore;
Nova polja u private sekciji:
TCB* parent; // pokazivač na roditelja (nullptr ako nema)
int childCount; // broj nezavršene dece
Semaphore* childrenSem; // semafor na koji roditelj čeka u joinAll
Nove metode u public sekciji:
static void addChild(TCB* parent, TCB* child);
static void joinAllChildren();
TCB.cpp
Dodat include na vrhu:
#include "../h/Semaphore.hpp"
U konstruktoru, inicijalizacija novih polja (posle this->finished = false;):
this->parent = nullptr;
this->childCount = 0;
this->childrenSem = nullptr;
Izmenjen dispatch() — else grana proširena da obavesti roditelja kad dete završi:
void TCB::dispatch() {
TCB *old = running;
if (!old->isFinished()) {
Scheduler::put(old);
} else {
if (old->parent != nullptr) {
old->parent->childCount--;
if (old->parent->childCount == 0 && old->parent->childrenSem != nullptr) {
old->parent->childrenSem->sem_signal();
}
}
}
running = Scheduler::get();
contextSwitch(&old->context, &running->context);
}
Nove funkcije:
void TCB::addChild(TCB* parent, TCB* child) {
child->parent = parent;
parent->childCount++;
if (parent->childrenSem == nullptr) {
Semaphore::createSemaphore(&parent->childrenSem, 0);
}
}
void TCB::joinAllChildren() {
if (running->childCount <= 0) return;
running->childrenSem->sem_wait();
}
syscall_c.hpp
Dodate deklaracije:
void thread_add_child(thread_t child);
void thread_join_all();
syscall_c.cpp
void thread_add_child(thread_t child) {
if (child == nullptr) return;
do_syscall(0x16, (uint64)child);
}
void thread_join_all() {
do_syscall(0x17);
}
RISCV.cpp (handleSupervisorTrap)
Dodate nove case grane u switch (posle case 0x13 dispatch):
case 0x16: {
TCB *child = (TCB*)arg1;
TCB::addChild(TCB::running, child);
break;
}
case 0x17: {
TCB::joinAllChildren();
break;
}
syscall_cpp.hpp
Dodate metode u klasu Thread (public sekcija):
void addChild(Thread* child);
void joinAll();
syscall_cpp.cpp
void Thread::addChild(Thread* child) {
thread_add_child(child->getMyHandle());
}
void Thread::joinAll() {
thread_join_all();
}
Test primer
#include "../h/syscall_cpp.hpp"
#include "../h/syscall_c.hpp"
#include "../h/printing.hpp"
class ThreadC : public Thread {
public:
ThreadC(int id) : Thread(), id(id) {}
private:
int id;
void run() override {
printString(" C");
printInt(id);
printString(" started\n");
volatile int sum = 0;
for (int i = 0; i < 3000; i++)
for (int j = 0; j < 3000; j++)
sum += j;
printString(" C");
printInt(id);
printString(" finished\n");
}
};
class ThreadB : public Thread {
public:
ThreadB(int id) : Thread(), id(id) {}
private:
int id;
void run() override {
printString(" B");
printInt(id);
printString(" started, creating 3 C children\n");
ThreadC* c[3];
for (int i = 0; i < 3; i++) {
c[i] = new ThreadC(id * 10 + i);
c[i]->start();
this->addChild(c[i]);
}
printString(" B");
printInt(id);
printString(" waiting for children...\n");
this->joinAll();
printString(" B");
printInt(id);
printString(" all children done!\n");
for (int i = 0; i < 3; i++) delete c[i];
}
};
void joinAllTest() {
printString("A started, creating 3 B and 1 C\n");
ThreadB* b[3];
for (int i = 0; i < 3; i++) {
b[i] = new ThreadB(i);
b[i]->start();
thread_add_child(b[i]->getMyHandle());
}
ThreadC* c = new ThreadC(99);
c->start();
thread_add_child(c->getMyHandle());
printString("A waiting for all children...\n");
thread_join_all();
printString("\n=== A: ALL children done! ===\n");
for (int i = 0; i < 3; i++) delete b[i];
delete c;
}
Napomena: start() mora biti pozvan PRE addChild() jer start() interno poziva thread_create() koji postavlja myHandle. Bez toga, getMyHandle() vraća nullptr.
Očekivan output
A started, creating 3 B and 1 C
A waiting for all children...
B0 started, creating 3 C children
B0 waiting for children...
B1 started, creating 3 C children
B1 waiting for children...
B2 started, creating 3 C children
B2 waiting for children...
C99 started
C99 finished
C0 started
C0 finished
C1 started
C1 finished
...svi C završe...
B0 all children done!
B1 all children done!
B2 all children done!
=== A: ALL children done! ===
Kako radi
Svaki TCB ima polje parent (ko je roditelj) i childCount (koliko dece još radi). Roditelj se blokira na semaforu (childrenSem, inicijalno 0). Kad nit završi, u dispatch() se dekrementira parent->childCount. Tek kad poslednje dete spusti brojač na 0, poziva se sem_signal() koji deblokirara roditelja.