Mình mặc định các bạn đã cài và biên dịch thành công nachos. Dưới đây là các file cần quan tâm: (bấm vào hình để xem kích thước gốc)
Khai báo biến toàn cục gSynchConsole trong file system.h và cấp phát bộ nhớ, delete nó (threads folder).
system.h:
#ifdef USER_PROGRAM
#include "machine.h"
#include "synchcons.h"
extern Machine* machine; // user program memory and registers
extern SynchConsole* gSynchConsole; // the simulate console
#endif
system.cc
#ifdef USER_PROGRAM // requires either FILESYS or FILESYS_STUB
Machine *machine; // user program memory and registers
SynchConsole *gSynchConsole;
#endif
#ifdef USER_PROGRAM
machine = new Machine(debugUserProg); // this must come first
gSynchConsole = new SynchConsole();
#endif
#ifdef USER_PROGRAM
delete machine;
delete gSynchConsole;
#endif
Bắt đầu với file syscall.h (userprog folder), define và khai báo các system call (SC):
#define SC_ReadInt 11
...
int ReadInt();
Ta viết lại cấu trúc điều khiển của để nhận các system call (SC) trong file exception.cc.
void ExceptionHandler(ExceptionType which)
{
int type = machine->ReadRegister(2);
switch (which)
{
case SyscallException:
switch (type)
{
case SC_Halt:
DEBUG('a', "Shutdown, initiated by user program.\n");
interrupt->Halt();
break;
...
case PageFaultException: // No valid translation found
printf("No valid translation found %d %d\n", which, type);
ASSERT(FALSE);
break;
...
machine->registers[PrevPCReg] = machine->registers[PCReg]; // for debugging, in case we
// are jumping into lala-land
machine->registers[PCReg] = machine->registers[NextPCReg];
machine->registers[NextPCReg] += 4;
break;
Với các exceptions khác được liệt kê trong machine.h thì chỉ cần printf() thông báo lỗi ra màn hình. Và tăng programcounter trước khi SC trả về kết quả.
Ta nên khai báo tất cả các SC trong start.c và start.s trước để tránh quên.
//start.c và //start.s khai báo giống nhau như sau
.globl ReadInt
.ent ReadInt
ReadInt:
addiu $2, $0,SC_ReadInt
syscall
j $31
.end ReadInt
Đối với các SC còn lại chỉ cần đổi tên.
Trước khi cài đặt 1 SC, các bạn phải hiểu cách truyền tham số và trả về giá trị của nó.
Tham số sẽ được đọc từ các thanh ghi, lần lượt từ 4 đến 7 (tham số nào ghi trước tiên ở thanh ghi 4, lần lượt tới thanh ghi 7).
Giá trị trả về được ghi lên thanh ghi 2.
Ngoài ra còn 2 hàm System2User và User2System đc sử dụng ở đây đối với con trỏ (char*, ...)
Ví dụ: int Func(char buff[], int length); // SC Func define trong syscall.h
//trong exception.cc
case SC_Halt(): ....
case SC_Func():
int buffAddr = machine->ReadRegister(4); // đọc địa chỉ chuỗi buff từ thanh ghi 4 (con trỏ giữ vùng nhớ buff)
int leng = machine->ReadRegister(5); // đọc giá trị length từ thanh ghi 5
char* buffer = new buffer[100];
buffer = machine->User2System(buffAddr, 100); // chuyển chuỗi trong con trỏ buffAddr sang vùng nhớ buffer
int value; // giá trị trả về của SC.
....
machine->WriteRegister(2, value); // trả về giá trị value vào thanh ghi 2
delete buffer;
Hàm User2System(buffAddr, 100) dùng để đọc giá trị chuỗi trong vùng nhớ mà buffAddr giữ vào chuỗi buffer với độ dài tối đa 100
Khi bạn nhập 1 chuỗi vào console (readstring) thì con trỏ của chuỗi bạn vừa nhập được ghi vào thanh ghi (thanh ghi 4 như ở trên). Bạn muốn lấy giá trị chuỗi vừa nhập để +-*/ trong khi viết SC thì phải dùng hàm này để lấy giá trị vào 1 chuỗi được cấp phát trong này (buffer).
Tương tự với System2User(buffAddr, sz, buffer), hàm này dùng để ghi chuỗi buffer được cấp phát với độ dài sz vào vùng nhớ mà buffAddr đang giữ.
Hai hàm này nằm trong lớp machine trong machine.h và machine.c (machine folder). Nếu không có các bạn tự thêm vào thành 1 phương thức của lớp này.
Lưu ý: Để hiểu HĐH làm việc như thế nào, chúng ta cần phải hiểu và phân biệt được kernel (system space) và user space. Mỗi chương trình trong hệ thống phải có các thông tin cục bộ của nó, bao gồm program counters, registers, stack pointers, và file system handler. Trước khi gọi một lệnh trong hệ thống thì các tham số truyền vào cần thiết phải được nạp vào các thanh ghi của CPU. Để chuyển một biến mang giá trị, tiến trình chỉ việc ghi giá trị vào thanh ghi. Để chuyển một biến tham chiếu, thì giá trị lưu trong thanh ghi đc xem như là “user space pointer”. Bởi vì userspace pointer không có ý nghĩa đối với kernel, mà chúng ta cần là chuyển nội dung từ user space vào kernel sao cho ta có thể xử lý dữ liệu này. Khi trả thông tin từ system về user space, thì các giá trị phải đặt trong các thanh ghi của CPU.
Bây giờ các bạn có thể cài đặt 1 SC theo các bước:
Đọc tham số từ thanh ghi (4 5 6 7)
Xử lý (viết theo c chuẩn, tham khảo trên cplusplus.com)
Trả về giá trị vào thanh ghi (2)
Để test SC bạn vừa viết, hãy viết 1 chương trình. Trong folder test, tạo 1 file.c
#include "syscall.h"
#include "copyright.h"
int main()
{
PrintString("Chao mung cac ban den voi nachos\n");
return 0;
}
Tiếp tục phải khai báo trong makefile (test folder) để nachos biên dịch file .c này thành các file chạy trên HĐH này (file.o .coff). Mở makefile và khai báo tại:
all: halt shell matmult sort help ascii echo createfile cat copys reverses
...
//cuối file ghi
help.o: help.c
$(CC) $(CFLAGS) -c help.c
help: help.o start.o
$(LD) $(LDFLAGS) start.o help.o -o help.coff
../bin/coff2noff help.coff help
Đối với các chương trình test khác chỉ việc đổi tên.
Sau khi hoàn thành, bạn mở terminal lên, cd đến folder code và gõ lệnh:
./userprog/nachos -rs 1023 -x ./test/readint
(thay readint bằng tên chương trình khác của bạn! ).
Chúc bạn hoàn thành tốt!
Bạn ơi, bạn có thể chỉ rõ hơn cách thêm hàm System2User và User2System ở 2 file machine.cc và machine.h được ko bạn?
ReplyDeleteMình cám ơn!