người dùng, những buffer này có thể không hợp lệ bất cứ lúc nào nên cần phải cẩn thận khi thực hiện các thao tác với chúng Cần phải xử lý trường hợp một
3.3 Yêu cầu triển kha
Mã nguồn Pintos đã có sẵn trình xử lí các cuộc gọi hệ thống (system calls handler) dưới dạng ngắt 0x30 nằm trong userprog/syscall.c. Khi gọi cuộc gọi hệ thống thì trình xử lí sử dụng số cuộc gọi hệ thống (system call number). Sau đó kiểm tra tính hợp lệ của con trỏ trong danh sách tham số đầu vào, con trỏ này sẽ chỏ tới vùng người dùng thì hợp lệ. Theo quy ước gọi, system call sẽ được gọi bới một giá trị 4 byte được trỏ bởi con trỏ esp. Nó có thể lấy bằng cách tham chiếu đến đến giá trị trong stack user (địa chỉ esp + 4, esp + 8,…). Cuối cùng trình xử lí cuộc gọi hệ thống sẽ sao chép các đối số trên user stack vào kernel và lưu kết quả của lệnh gọi hệ thống trên thanh ghi EAX. Các cuộc gọi hệ thống được định nghĩa ở src/lib/syscall_nr.h.
Hình 8 Sơ đồ xử lí các cuộc gọi hệ thống
Theo như yêu cầu của vấn đề System Calls (project 2 Pintos) đề ra , ta sẽ cần phải xây dựng các lệnh system calls cho hệ thống với những yêu cầu cụ thể cho từng lệnh gọi như sau :
System Call: void halt (void): Lệnh system call này để kết thúc Pintos.
System Call: void exit (int status): Chấm dứt chương trình người dùng hiện tại, trả lại hiện trạng cho kernel. Nếu vẫn đang có tiến trình cha (parent process) nào đó đang chờ thì sẽ trả về trạng thái, trạng thái 0 cho biết thành công và còn giá trị khác không sẽ cho biết rằng có lỗi.
System Call: pid_t exec (const char * cmd_line): Tạo tiến trình con (child process) và thực thi chương trình tương ứng với cmd_line .Chuyền vào giá trị đối số nào đó và nhận được kết quả trả về pid (process's program id ).Kết quả trả về phải là pid -1, nếu không thì đó là pid không hợp lệ, nguyên nhân có thể chương trình không thể tải hoặc không chạy vì lí do gì đó. Khi ấy, tiến trình cha không thể trả về từ trình thực thi khi tiến trình con chưa thực thi xong. System call: int wait (pid_t pid): Chờ tiến trình con xử lí pid và trả về trạng thái pid đến exit. Nếu pid vẫn chưa kết thúc, cần đợi kết thúc mới thôi. Sau đó, trả về trạng thái mà pid khi hoàn thành để thoát. Nếu pid không gọi exit () để thoát, nhưng đã bị kernel kết thúc (có thể do ngoại lệ xảy ra), thì wait (pid) sẽ trả về -1. Một tiến trình cha có thể gọi đợi tiến trình con đã kết thúc và trả về trạng thái exit của tiến trình con hoặc biết được tiến trình con đó đã bị kết thúc bởi kernel. wait () thất bại và trả về -1 nếu:
+ pid không tham chiếu trực tiếp tới tiến trình con trong chuỗi gọi. + Tiến trình cần gọi wait vốn đã được gọi wait() thành công trên pid.
Lưu ý: Tất cả tài nguyên của một tiến trình, bao gồm struct thread của nó , phải được giải phóng cho dù tiến trình cha của nó có đợi nó hay không bất kể việc tiến trình con thoát trước hay sau tiến trình cha.
Hình 9 Sơ đồ luồng điều khiển
System Call: bool create (const char * file, unsigned initial_size): Tạo một file mới với đối số đầu vào là tên file và kích thước khởi tạo ban đầu ( đơn vị byte). Kết quả trả về true nếu thành công, trả về false nếu thất bại. Việc tạo file mới sẽ không theo kèm luôn việc mở file mới đó. Mở file mới tạo là một thao tác riêng biệt sẽ yêu cầu lệnh gọi hệ thống mở.
System call: bool remove (const char * file): Thực hiện xoá file ,đối số đầu vào là tên file cần xoá. Kết quả trả về sau gọi hàm là true nếu thành công, trả về false nếu thất bại. Một file có thể bị xóa bất kể nó đang mở hay đóng và việc xóa file đang mở sẽ không đóng file đó. Xem xóa file đang mở để biết chi tiết. System call: int open (const char * file): Thực hiện mở file. Trả về một giá trị nguyên không âm được gọi là "file descriptor" (fd) hoặc trả về -1 nếu không thể mở file (Các fd được đánh số 0 và 1 thể hiện cho bộ điều khiển: fd 0
(STDIN_FILENO) là đầu vào tiêu chuẩn, fd 1 (STDOUT_FILENO) là đầu ra tiêu chuẩn. Lệnh gọi hệ thống mở file sẽ không trả về fd 0 hay fd 1. Mỗi tiến trình
có một fd độc lập. Các tiến trình con sẽ không được kế thừa fd từ tiến trình cha. Khi một file được mở nhiều lần, dù được mở bởi một tiến trình hay nhiều tiến trình thì mỗi lần mở sẽ trả về một fd mới. Có thể có nhiều fd khác nhau cho một file giúp việc gọi lệnh đóng file là độc lập và tách biệt khi mà file đó đang mở ở nhiều tiến trình)
System call: int filesize (int fd): Trả về kích thước file tính theo byte. Đối số đầu vào của filesize() là fd của file cần tính kích thước.
System call: int read (int fd, void * buffer, unsigned size): Đọc các byte kích thước từ file đang mở ( dựa vào fd để xác định file đang mở cần đọc) vào bộ đệm. Trả về số bytes thực sự được đọc (0 ở cuối file) hoặc -1 nếu file không thể đọc được. Với fd =0 sẽ thực hiện đọc kí tự từ bàn phím.
System call: int write (int fd, const void * buffer, unsigned size): Ghi kích thước bytes từ bộ đệm buffer vào file đang mở. Trả về số bytes được ghi (có thể nhỏ hơn kích thước vì có thể có một số byte không ghi được). Nếu ghi vào phần cuối file sẽ làm mở rộng kích thước file (trước khi sửa đổi mã nguồn, việc mở rộng file như vậy thì hệ thống file cơ bản (file system) của mã nguồn gốc không thực hiện được) .Nhiệm vụ lệnh write là ghi càng nhiều bytes càng tốt vào cuối file và trả về số lượng byte thực tế được ghi hoặc trả về 0 nếu không ghi được byte nào. Với fd =1 sẽ ghi dữ liệu ra console , cần đảm bảo kích thước ghi không lớn hơn vài trăm byte. Nếu các bộ đệm lớn quá có thể thực hiện chia nhỏ, nếu không, các dòng văn bản xuất ra từ các tiến trình khác nhau có thể bị xen kẽ trên console, gây nhầm lẫn cho cả người đọc hoặc gây lỗi khi thực hiện các lệnh tiếp theo.
System call: void seek (int fd, unsigned position): Lệnh gọi tìm kiếm seek, 2 đối số đầu vào là fd của file và vị trí cần tìm kiếm trong file đó. Thực hiện bằng cách thay đổi byte tiếp theo được đọc hoặc ghi trong file đang mở thành vị trí xác định của byte đó. Vị trí 0 được tính là vị trí bắt đầu file. Nếu vị trí tìm kiếm cần tìm nằm ngoài cả dữ liệu cuối cùng của file thì sẽ thu được 0 bytes, ta cũng sẽ hiểu đó là vị trí cuối của file. Việc ghi sau đó sẽ gây mở rộng file, khoảng trằng cuối file sẽ được lấp đầy bằng các số 0 .( Tại project 2 này thì ta không thay đổi được trong file Pintos vì file có độ dài cố định, nếu muốn thì sau khi project 4 hoàn thành mới thực hiện được )
System call: unsigned tell (int fd): Trả về vị trí của byte tiếp theo được đọc hoặc ghi trong file đang mở, vị trí trả về tính theo bytes (tính từ đầu file là 0).
System call: void close (int fd): Đóng file fd. Việc thoát hoặc kết thúc một tiến trình sẽ đóng lần lượt tất cả fd đang mở.
3.4 Triển khai mã nguồn
Các hàm và các biến được triển khai trong file:
threads/thread.c và thread.h userprog/process.c
userprog/syscall.c và syscall.h