Blog
Implementierungshinweise zum PID Lookup
2022-07-07
PID-Thread Lookup
Für IPC muss irgendwie die PID zum Prozess (Thread-Pointer) aufgelöst werden. Es ist in unserem Fall vollkommen in Ordnung, da einfach eine Liste zu verwenden und die durchzulaufen.
Aber Achtung:
die
ready
-Queue des Schedulers ist nicht ausreichend – denn nicht jeder Thread ist da drin, sondern könnte ja gerade in einem Waitingroom sein (sogar ziemlich wahrscheinlich, wenn ihr euch die IPC-Implementierung anschaut)ein
Queue<Thread> processlist
könnt ihr nicht verwenden – denn Thread erbt von Queue::Node , was lediglich einennext
-Zeiger als Attribut anfügt. Und dieser eine Zeiger würde dann gleichzeitig von derready
-Queue UND der processlist-Queue verwendet werden, was natürlich zu Fehlern führt.Siehe dazu auch die Warnung in der Dokumentation:
One instance of a class inheriting from Queue::Node can be at most in one Queue
kommt besser nicht auf die Idee, die PID gleich der Threadadresse zu setzen (und wild zu casten). Da kann viel schief gehen, u.a. nicht vertrauenswürdige Zeiger aus dem Userspace und sehr schnelle erneute PID Belegung (bei
delete
undnew
)!man kann vector verwenden, aber schaut euch dazu die Implementierung an, sie ist momentan etwas von der STL inspiriert und sehr stark vereinfacht (z.B. bzgl Iteratoren). Je nachdem wie ihr das verwenden wollt müsst ihr vielleicht die Methoden anpassen – z.B. um beliebige
pos
auch jenseits der aktuellen Kapazität (_capacity
) einfügen zu können, empfiehlt sich eine Erweiterung uminline void insert(size_t pos, const T& value) { if (pos == _capacity) { expand(); } else if (pos > _capacity) { // Reserve capacity is the next power of 2 size_t r = pos; for (size_t p = 1; p < sizeof(uintptr_t) * 8; p *= 2) { r |= r >> p; } reserve(r + 1); } // ...
(man könnte alternativ auch schlicht
reserve(pos + 1)
machen, was aber zu vielen Speicherkopien bei subsequenteninsert
s führen kann – deshalb wird im Beispiel immer die nächste Zweierpotenz verwendet)
Map Systemaufruf (Nachtrag)
In der Aufgabenstellung wird der map
-Systemaufruf zusammen mit dem Allokator für den Userspace als Beispiel gezeigt. Der Allokator verwendet intern (versteckt hinter einer Templatevariable) beim ersten Aufruf map(nullptr, ...)
, lässt sich also vom Kernel eine Adresse geben. Und fordert bei Bedarf dann weiteren Speicher an – braucht aber unbedingt einen zusammenhängenden Speicherbereich (und verwendet deshalb in den folgenden map
-Aufrufen auch explizite Adressen).
Das bedeutet unter Umständen, dass ihr, wenn ihr den Allokator so wie in der Vorgabe verwendet, aber auch noch zusätzlich in eurer Anwendung map(nullptr, ...)
aufrufen wollt, der Allokator keinen weiteren Speicher mehr rausgeben wird.
Mögliche Workarounds:
entweder einfach
map
mit expliziten Zieladressen (jenseits von0x5000000
[Userspace + App + Allokator]) in eurer Anwendung aufrufenoder
alloc_buddy.h
im Userspace (jedoch nicht im Kernel) patchen, zum Beispiel den Code in Zeile 355zu etwas wie
if ((base_ptr = reinterpret_cast<uintptr_t>(RESERVE(reinterpret_cast<void*>(0x1000000000), size))) == NULL) { return NULL; }
zu ändern, um den Heap ab
0x1000000000
starten zu lassen.oder beim Aufruf mit Parameter
nullptr
schlicht ganz zufällige (noch unbelegte) Adressen rauszugeben. Grundlegende Idee:void* map(void* addr, size_t size) { if (addr == nullptr) { static uintptr_t nextPtr = 88172645463325252ull; // TODO: Eigener Wert do { nextPtr ^= nextPtr << 13; nextPtr ^= nextPtr >> 7; nextPtr ^= nextPtr << 17; addr = reinterpret_cast<void*>(nextPtr); } while (addr < USER_SPACE || mapping->in_use(addr, size)); } // ... }