$ sudo apt install git csh g++ $ git clone https://github.com/connlabtw/NachOS.git $ cd NachOS $ sudo cp -r usr / $ cd code $ make
Test
然後就能來測試能不能正常運行了 進到 NachOS/code/userprog 裡面
1
$ ./nachos –e ../test/test1
如果正常就應該能看到這樣的結果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Total threads number is 1 Thread ../test/test1 is executing. Print integer:9 Print integer:8 Print integer:7 Print integer:6 return value:0 No threads ready or runnable, and no pending interrupts. Assuming the program completed. Machine halting!
Ticks: total 200, idle 66, system 40, user 94 Disk I/O: reads 0, writes 0 Console I/O: reads 0, writes 0 Paging: faults 0 Network I/O: packets received 0, sent 0
我照網路上也去試試同時跑兩個 process (要記得先去 Makefile 裡面加上 test2 再 make test2)
1
$ ./nachos -e ../test/test1 -e ../test/test2
MultiProgramming
看了很多 blog 都有提到這問題,我一開始卻沒遇到,我才剛慶幸我沒有遇到這問題結果在測試我的 Sleep() 就遇到了,有夠…
反正當我想同時跑兩個不同時間的 Sleep() 並交互使用 PrintInt 時發現這兩個 thread 竟然互相干擾,我只想印十遍的內容竟然重複了近三十遍,每到記數變數 i 快要接近終結的 10 時,都會跳掉,很明顯就是兩個 thread 用到同一塊記憶體了,所以以下就是根據 Morris 的 blog 改的
// The following class defines a software alarm clock. classAlarm : public CallBackObj { public: Alarm(bool doRandomYield); // Initialize the timer, and callback // to "toCall" every time slice. ~Alarm() { delete timer; }
voidWaitUntil(int x); // suspend execution until time > now + x SleepList sleepList; // for implementing Sleep() function.
private: Timer *timer; // the hardware timer device
voidCallBack(); // called when the hardware // timer generates an interrupt };
// 多加上兩個條件,一個是檢查是否 woken,一個是檢查還有沒有正在 sleep 的 thread // 若是都沒有就可以關掉 timer 了 if (status == IdleMode && !woken && sleepList.IsEmpty()) { // is it time to quit? if (!interrupt->AnyFutureInterrupts()) { timer->Disable(); // turn off the timer } } else { // there's someone to preempt interrupt->YieldOnReturn(); } }
// implement WaitUntil function. void Alarm::WaitUntil(int x) { // save previous setting as oldLevel and disable interrupt. // SetLevel 是決定這個 thread 能不能被 interrupt // 這裡是把原本的 level 存起來然後把當前設定成不能被 interrupt IntStatus oldLevel = kernel->interrupt->SetLevel(IntOff); Thread* t = kernel->currentThread; sleepList.PutToSleep(t, x); // put current thread to sleep list. kernel->interrupt->SetLevel(oldLevel); // recover old interrupt state. }
// check if there is still thread sleeping boolSleepList::IsEmpty() { return threadlist.size() == 0; }
// put the thread into sleep list voidSleepList::PutToSleep(Thread*t, int x) { // check if it cannot be interrupt. ASSERT(kernel->interrupt->getLevel() == IntOff); threadlist.push_back(SleepThread(t, counter + x)); // put into the list t->Sleep(false); }
// will be call in callback boolSleepList::PutToReady() { bool woken = false; counter ++;
// check all thread in the list if there are thread already finish sleeping for(std::list<sleepthread>::iterator it = threadlist.begin(); it != threadlist.end(); ) { // 'when' 就是被創造時的 counter 加上他要 sleep 的時間,也就是他應該醒來的時間 // 所以我們檢查 when 跟 counter 來判斷他該不該醒來 // 'when' is time the thread should wake up // if counter >= when, this thread will be ready to run. if(counter >= it->when) { // 若是他該醒來就把從睡覺 list 中去掉,然後把他叫醒 (用 ReadyToRun) woken = true; kernel->scheduler->ReadyToRun(it->sleeper); it = threadlist.erase(it); } // if the thread is not ready to run, keep checking next thread in the list. else { it++; } } return woken; }