Đồ án 2. Đa chương và những công việc kì lạ - ngankhanh98/nachos GitHub Wiki
Tài liệu này được tạo ra cốt là để giải thích cơ bản lý thuyết, kỹ thuật dùng trong Đồ án này.
Lý thuyết
✍ Kịch bản:
Bạn có một file ping.c
, bạn muốn exec file này.
👍 NachOS thỏa mãn bạn như nầy:
- NachOS biên dịch
ping.c
thànhping.noff
hay đại loại là thứ bạn không dễ dàng hiểu, nhưng máy tính có cách hiểu và thứ này tối ưu cho việc thực thi. - Thủa còn đơn chương, nachos đơn giản nạp toàn bộ
ping.noff
vào main memory (RAM thật) để chuẩn bị lên CPU xử lý.
✍ Kịch bản đa chương:
Bạn có một file scheduler.c
và ping.c
và pong.c
Có scheduler.c
:
#include "syscall"
void main()
{
Exec("ping.c");
Exec("pong.c");
}
⚠ Bây giờ xảy ra vấn đề này:
Sau khi tạo ra file và load vào main memory scheduler.noff
, theo lệnh gọi Exec, file ping.noff
cũng được generate và load. Nếu viết CS_Exec đúng (chỉ chỉnh trong exception.cc) thì ping được gọi chạy bình thường. Nhưng, Exec ("pong.c");
tạo ra .noff
, file này lúc load vào main memory thì ghi đè lên vùng của ping.noff
. Ghi đè rồi thì làm ăn gì được.
🧠 Giải pháp trước mắt là:
Phải thay đổi một cái bảng hay cấu trúc gì đó, mà đã đánh dấu vùng nào trên main memory đã là hoa có chủ, tránh nạp đè pong.noff
vào vùng mà ping.noff
đã giành.
Kỹ thuật:
Lý thuyết ở trên được triển khai trong source code như thế nào?
SpaceId Exec(char* filename);
Gọi lệnh Exec trong ./test/scheduler.c
đơn chương
#include "syscall.h"
void main()
{
Exec("./test/ping.c");
}
Cài đặt syscall Exec trong ./userprog/exception.cc
với tư tưởng:
OpenFile *execution = fileSystem->Open(filename);
...
thread *t = new thread(execution);
t->space = new Addrspace(execution); // ghi chú
t->Fork(StartMyProcess, 0);
...
delete[] filename;
ghi chú: Đây là cách một .noff
được load vào trong main memory
Để ý thấy:
- Hiểu nôm na,
.noff
được chia thành nhiều "phần" để lưu vào main memory (tương tự copy một file vào một file vậy). pageTable
giống như một cuốn từ điển, virtualPage[i] đại diện cho một "phần" thứ i của.noff
, physicalPage[i] đại diện cho vùng nhớ (page) thứ i trong main memory. Bây giờ thì, dữ liệu nguồn đã có (.noff
), địa chỉ đích và nguồn đã xác định, chỉ cần chép tuần tự toàn bộ.noff
vào main memory thôi.
Để thực hiện đa chương, cần chỉnh sửa gì
thay đổi pageTable
Trong 1 tiến trình, physicalPage[i] nào đã nạp dữ liệu, thì không được nạp đè lên. Muốn như vậy, từ lúc bắt đầu chạy tiến trình, phải
- Xây dựng pageTable; Khi đó, mỗi lần dùng physicalPage[i] lưu
.noff
, phải có flag đánh dấu.- Clear hết physicalPage trong main mem
- Sao chép
.noff
vào main memory
Kỹ thuật đánh dấu physicalPage nhờ lớp BitMap
Một chút về lớp BitMap,
- Có thể hiểu một đối tượng thuộc lớp BitMap là một mảng các bit.
- Tại sao nên dùng BitMap? Vì lớp này được hỗ trợ sẵn những hàm như
Find()
tìm phần tử trống đầu tiên,Mark(id)
đánh dấu phần tử id vừa được dùng.
Như vậy, có thể sửa code trong addrspace.cc
như sau:
- Xây dựng pageTable; Khi đó, mỗi lần dùng physicalPage[i] lưu
.noff
, phải có flag đánh dấu.
...
// pageTable[i].physicalPage = i;
// đã tự khai báo đối tượng toàn cục gBitMapPhysPage rồi nhé
slot = gBitMapPhysPage->Find();
pageTable[i].physicalPage = slot;
gBitMapPhysPage->Mark(slot);
...
- Clear hết physicalPage trong main mem, phải có chiến thuật, page nào dùng (dựa vào pageTable) mới clear, không nên liều mà clean hết như trước được.
//bzero(machine->mainMemory, size);
for (i = 0; i < numPages; i++) {
bzero(&(machine->mainMemory[pageTable[i].physicalPage*PageSize]), PageSize);
}
- Sao chép
.noff
vào main memory Đoạn code sao chép.noff
vào main memory, mình không biết diễn đạt cho dễ hiểu thế nào, tốt như nên đọc code, chạy code tay trên sơ đồ mình đã vẽ, bạn mình của chỉ mình bằng sơ đồ và nó hiệu quả kinh ngạc.
*Mà mình thấy, nó y như ý tưởng chép file sang file: chép một đoạn từ file nguồn sang, đánh dấu vị trí trong file nguồn, xem đã chép filesize bao nhiêu để tính toán địa chỉ (vị trí) trong file đích để tiếp tục sao chép, dừng khi hết file nguồn.
Vấn đề tồn tại:
Nếu bạn test thử, chỉ in được 1972 kí tự A và B, thay vì 2000 như yêu cầu.
Vấn đề này, mình nghĩ liên quan tới đồng bộ hóa. Một trong hai tiến trình kết thúc trước nên ngắt luôn main, nghĩa là sau khi in xong 1000 'B', tiến trình pong kết thúc, làm kết thúc luôn main trong scheduler.c
, nên 'A' mới in được 972 lần đã dừng.
Mình có một chút ý tưởng về thêm lớp thread, nó nên có thêm
List children
thread *parent
và một số phương thức hỗ trợ việc thread cha sẽ chờ nếu thread con là LIVING_CHILD. Nhưng nó là chuyện SC_Join
và SC_Exit
sẽ giải quyết, nên source có vài đoạn chỉnh sửa cho việc này, đừng hiểu lầm là vô ích mà xóa đi.
Cảm ơn sự giúp đỡ của
My kind friend: nkhang DePault Univerity Operating Systems lectures: http://condor.depaul.edu/glancast/546class/docs/lec7.html Introducing User Programs into Nachos: https://users.cs.duke.edu/~raw/cps110.s04/Lectures/NachosUserProgs.pdf CSE 120 Nachos Project 2: Multiprogramming: http://cseweb.ucsd.edu/classes/fa14/cse120-b/projects/multi.html Căn bản và rất căn bản về hệ điều hành NachOS ☺: http://read.pudn.com/downloads161/ebook/733633/Nachos_CanBan/Nachos_CanBan.pdf