Mar 31, 2013

Lập trình syscall trên nachos

Trong file hướng dẫn của thầy không thực sự cụ thể, và như vậy rất khó cho mọi người thực hành theo, vậy mình sẽ viết lại các qui trình một chút_
Một chút tìm hiểu về quá trình thực thi 1 hàm
Qui trình thực thi các interrupt sẽ được đề cập sau
  1. Xây dựng trong kernel space
    Để xác định user đang gọi cái interrupt nào. Ta cho nó 1 định danh là 1 số nào đó. Khi kernel nhận được số đó thì hiểu là user yêu cầu chuyện gì.
    Ví dụ: Nếu user đưa vô số 11 tức là yêu cầu đọc 1 số nguyên từ console.
  2. Tạo macro:
    Để đỡ phải quên trong việc lập trình, ta sẽ define số đó thành 1 macro.
    Trong syscall.h:
    define SC_ReadInt 11
    Khai báo interface cho end-user:
    Cũng trong syscall.h, ta tạo một hàm để người dùng (end-user) giao tiếp với hệ điều hành, ở đây ta khai báo hàm ReadInt();
    int ReadInt();

    Định nghĩa hàm trong file assembler.
    Khi người dùng gửi đến 1 thông điệp, ví dụ như “đọc 1 số từ console”, đến với hệ điều hành – lúc này hiểu thông điệp là 11 chẳng hạn. Để hệ điều hành nhận được số 11 đó và truyền đến chỗ xử lý các thông điệp, ta cần pass giá trị này qua. Giống như gọi 1 hàm xxx(11) vậy. Trong nachos, công việc này tức là truyền 11 vào biến $2 được qui định trước đó và gọi system call.

    Ta mở các file sau: code/test/start.ccode/test/start.s:
    .globl ReadInt
    .ent ReadInt
    ReadInt:
        addiu $2, $0, SC_ReadInt
        syscall
        j $31
        .end ReadInt
    

    Như vậy, thông điệp “đọc 1 số từ console” đã đến tai hệ điều hành. Giờ là lúc bắt tay vào xử lý công việc đó. Và vì đây là software interrupts, hay còn gọi là 1 exceptions. Nên dĩ nhiên, ta sẽ thêm mã vào file usrprog/exceptions.cc
    ///....
    switch(which)
    {
        case SystemcallException:
            switch(type)
            {
                case SC_ReadInt:
                // Đoạn mã thực thi
                break;
                // Các xử lý khác ...
            }
            / Tăng biến pc lên 1 đơn vị
            machine->Registers[PrevPCReg] = machine->Registers[PCReg];
            machine->Registers[PCReg] = machine->Registers[NextPCReg];
            machine->Registers[NextPCReg] += 4;  
    }
    ///....
    
    
  3. Một số lớp và phương thức được thêm vào trong đồ án:
    1. SynchConsole
      Được cài đặt ở thư mục threads/ giúp truy xuất giữa console và người dùng.
      Trong file threads/system.h, phần định nghĩa macro USER_PROGRAM ta include header file của SynchConsole. Đồng thời khai báo extern cho biến gSynchConsole;
      $ifdef USER_PROGRAM
      #include "synchcons.h"
      extern SynchConsole *gSynchConsole;
      #endif
      Trong file threads/system.cc, cũng trong phần khai báo macro USER_PROGRAM ta khai báo biến gSynchConsole (chỉ khai báo, không có khóa extern).
    2. User2System và System2User

      Mã code của 2 phương thức này được trình bày ở link này: Click vào đây

      Hai hàm này sẽ trở thành phương thức public trong lớp Machine. Khai báo hàm nằm ở machine/machine.h và phần định nghĩa sẽ nằm ở machine/machine.cc hoặc có thể là trong translate.cc. Mình nghĩ là nên đặt vào trong translate.cc hơn, vì nó phần nào thể hiện bản chất: các phương thức liên quan đến vùng nhớ sẽ được đặt ở đó.
  4. Test hàm và chạy thử nghiệm
    Trong thư mục test/, ta tạo một tập tin .c để kiểm tra tính đúng đắn của exception vừa viết. Đó là gọi hàm đó theo tham số đã đề xuất.
    Cấu hình Makefile:
    Để có thể sử dụng lớp SynchConsole, ta mở file Makefile.common và thêm các thông số sau:
    • Tại THREAD_H : ‘../threads/synchcons.h’
    • Tại THREAD_C : ‘../threads/synchcons.cc’
    • Tại THREAD_O : ‘../threads/synchcons.o’
      Cấu hình file test
      Ví dụ ta muốn chạy đoạn mã test.c, qui trình biên dịch và chạy như sau:
    • Trong file test/Makefile:
      all: ... test
      Thêm vào cuối file
      test.o: test.c
          $(CC) $(CFLAGS) -c test.c
      test: test.o test.o
          $(LD) $(LDFLAGS) start.o test.o -o test.coff
          ../bin/coff2noff test.coff test
      
      

      Sau khi make xong, ta chạy lệnh ./usrprog/nachos -rs 1023 -x [Đường dẫn đến file thực thi].

5 comments:

  1. cảm ơn anh, bài của anh rất bổ ích, thật sự em đang rất đau đầu với môn này,trong đồ án em có 1 câu hỏi như sau, mong anh có thể phân tích và hướng dẫn sơ lược cho em các bước tiến hành cài đặt để em có thể hiểu rõ hơn:

    Cài đặt system call OpenFileID Open(char *name, int type) và int Close(OpenFileID id). User program có thể mở 2 loại file, file chỉ đọc và file đọc và ghi. Mỗi tiến trình sẽ được cấp một bảng mô tả file với kích thước cố định. Đồ án này, kích thước của bảng mô tả file là có thể lưu được đặc tả của 10 files. Trong đó, 2 phần tử đầu, ô 0 và ô 1 để dành cho console input và console output. System call mở file phải làm nhiệm vụ chuyển đổi địa chỉ buffer trong user space khi cần thiết và viết hàm xử ly’ phù hợp trong kernel. Bạn sẽ phải dùng đối tượng filesystem trong thư mục filesys. System call Open sẽ trả về id của file (OpenFileID = một số nguyên), hoặc là -1 nếu bị lỗi. Mở file có thể bị lỗi như trường hợp là không tồn tại tên file hay không đủ ô nhớ trong bảng mô tả file. Tham số type = 0 cho mở file đọc và ghi, = 1 cho file chỉ đọc. Nếu tham số truyền bị sai thì system call phải báo lỗi. System call sẽ trả về -1 nếu bị lỗi và 0 nếu thành công.

    em xin cảm ơn!

    ReplyDelete
    Replies
    1. Chào bạn, mình sẽ sớm post hướng dẫn cho các bài tập liên quan đến system calls của hệ thống lưu trữ tập tin :)

      Delete
  2. Cảm ơn bài viết của anh, hi vọng anh sớm ra những bài hướng dẫn tiếp theo về nachos. =))

    ReplyDelete
  3. Hi bạn, mình chỉ giải đáp một số vướng mắc khi làm thôi. Còn phần chính thì các bạn phải tự code vậy. Đây là đồ án kinh điển môn HĐH của HCMUS :D

    ReplyDelete
  4. em sử dụng hàm synchcons-> read thì nó báo lỗi segment fault, tra google là truy cập vùng nhớ không được phép, anh có thể giải thích thêm được không

    ReplyDelete