Trong quá trình học tập học phần Hệ điều hành do thầy Phạm VănTiến giảng dạy chúng em đã được hướng dẫn, trợ giáo để hoàn thành việc thực hànhcác dự án trong khóa học về hệ điều hành Pin
Trang 1TRƯỜNG ĐẠI HỌC BÁCH KHOA HÀ NỘI
VIỆN ĐIỆN TỬ - VIỄN THÔNG
BÁO CÁO BÀI TẬP LỚN
HỆ ĐIỀU HÀNH
Đề tài: Project 1: Alarm clock và Project 2: System Calls
Sinh viên thực hiện : Nguyễn Đình Hùng 20182555
: Nguyễn Anh Đức 20182433 : Phạm Huy Hoàng 20182545 Giảng viên hướng dẫn : TS Phạm Văn Tiến
Trang 2PHẠM NGỌC LÂM 20182628
Hà Nội, 12-2021
download by : skknchat@gmail.com
Trang 3LỜI NÓI ĐẦU
Hiện nay, khoa học kĩ thuật trên thế giới ngày càng phát triển mạnh mẽ Khoa học
kĩ thuật đóng vai trò không thể thiếu trong đời sống hiện đại ngày nay của chúng ta nóhiện diện trong từng sản phẩm, từng vật dụng mà chúng ta sử dụng hàng ngày Vì vậyviệc học tập và đạo tạo, kế thừa và phát triển những thành tựu khoa học cho thế hệ mớingày nay là vô cùng quan trọng đặc biệt là thế hệ sinh viên Việc tiếp cận đó cần phảitiếp cận cả về mặt lí thuyết lẫn thực hành, như vậy việc học tập và đào tạo mới có thểđạt được hiệu quả tốt nhất, đáp ứng được cho xã hội nguồn nhân lực khoa học kĩ thuậtchất lượng cao Trong quá trình học tập học phần Hệ điều hành do thầy Phạm VănTiến giảng dạy chúng em đã được hướng dẫn, trợ giáo để hoàn thành việc thực hànhcác dự án trong khóa học về hệ điều hành Pintos
Sau khi trải qua một khoảng thời gian, nhóm chúng em đã cơ bản hoàn thành việclập trình để hiểu hơn về hệ điều hành Pintos, giải quyết được một vấn đề trong hệ điềuhành này Trong bài báo cáo này, nhóm chúng em sẽ đi trình bày cụ thể những gì đãlàm được trong những tuần vừa qua thông qua 4 chương sau:
Chương 1: Giới thiệu hệ điều hành Pintos
Chương 2: Alarm Clock
Chương 3: System Calls
Chương 4: Kết luận
Nhóm xin chân thành cảm ơn TS Phạm Văn Tiến đã tận tâm hướng dẫn chúng emtrong quá trình tìm hiểu và hoàn thiện báo cáo này!
Trang 4MỤC LỤC
DANH MỤC HÌNH VẼ i
BẢNG PHÂN CÔNG CÔNG VIỆC ii
CHƯƠNG 1 GIỚI THIỆU HỆ ĐIỀU HÀNH PINTOS 3
1.1 Tổng quan về Pintos 3
1.2 Cây thư mục nguồn 3
1.3 Tổng quan về Threads 4
CHƯƠNG 2 ALARM CLOCK 6
2.1 Vấn đề Alarm Clock 6
2.2 Mô tả mục tiêu 7
2.3 Phương pháp giải quyết 7
2.4 Triển khai mã nguồn 8
2.5 Mô tả mã nguồn đã sửa đổi, bổ sung 8
2.6 Kết quả 9
CHƯƠNG 3 SYSTEM CALLS 10
3.1 Đặt vấn đề 10
3.2 Mô tả mục tiêu 11
3.3 Yêu cầu triển khai 12
3.4 Triển khai mã nguồn 15
3.5 Sửa đổi mã nguồn và bổ sung 16
3.6 Kết quả 19
CHƯƠNG 4 KẾT LUẬN 20
TÀI LIỆU THAM KHẢO 21
download by : skknchat@gmail.com
Trang 5DANH MỤC HÌNH VẼ
Hình 1 Sơ đồ FSM của một threads 4
Hình 2 Sơ đồ hàm chuyển trạng thái của threads 5
Hình 3 Mô tả chức năng của hàm thread_yield() 7
Hình 4 Cơ chế sleep and wake up 8
Hình 5 Kết quả sau khi make check test alarm-clock 9
Hình 6 Sơ đồ tương tác giữa chương trình người dùng và OS 10
Hình 7 Cấu trúc thành phần của layout Virtual Memory 11
Hình 8 Sơ đồ xử lí các cuộc gọi hệ thống 12
Hình 9 Sơ đồ luồng điều khiển 14
Hình 10 Kết quả sau khi đã make check test system calls 19
Trang 6Các vấn đề Nguyễn Đình Hùng Nguyễn Anh Đức Phạm Huy Hoàng
- Sửa mã nguồn và chạy thử nghiệm cuối cùng (đãpass hết)
- Phân tích chức năng các hàm cần sửa đổi mã nguồn
- Triển khai cơ chế sleep and wake up
- Sửa mã nguồn và chạy thử nghiệm (đã pass )
2 System
Calls
- Phân tích chức năng, chỉnh sửa mã nguồn, kiểm thử các hàm liên quan đến các tiến trình process: halt, exit, exec, wait(đã pass hết)
- Phân tích chức năng, chỉnh sửa mã nguồn và kiểm thử các hàm liên quan đến filesystem:
create, remove, open, filesize (đã pass hết)
- Phân tích chức năng, chỉnh sửa mã nguồn và kiểm thử các hàm liên quan đến filesystem: read, write, seek, tell, close (đã pass hết)
BẢNG PHÂN CÔNG CÔNG VIỆC
iidownload by : skknchat@gmail.com
Trang 7CHƯƠNG 1 GIỚI THIỆU HỆ ĐIỀU HÀNH PINTOS
Chương này giới thiệu tổng quan về hệ điều hành Pintos và tổng quan về cây thưmục nguồn của hệ điều hành Pintos
Stanford đưa ra 4 Project dựa trên hệ điều hành Pintos:
Project 1: Threads – Luồng
Project 2: User Programs – Chương trình người dùng
Project 3: Virtual Memory – Bộ nhớ ảo
Project 4: File Systems – Hệ thống file
Ngoài ra Stanford đưa ra CS140 Problem Set 0: Synchronization – Đồng bộ hóa đểsinh viên làm quen với mã nguồn của Pintos Khi người dùng triển khai thành công(PASS các bài TEST) các nội dung được đề ra này, hệ điều hành Pintos sẽ hoạt độnghiệu quả, hiệu suất cao, tiết kiêm các tài nguyên của bộ nhớ, năng lượng và thời giancủa máy tính Trong báo cáo sẽ sử dụng QEMU là trình mô phỏng
1.2 Cây thư mục nguồn
Quan sát vào bên trong mã nguồn Đây là cấu trúc thư mục trong master/src”
Trang 9Hình 1 Sơ đồ FSM của một threads
Trong Pintos, các trạng thái của luồng được khai báo bởi cấu trúc “enum thread_status”
THREAD_RUNNING: Luồng đang chiếm CPU và làm việc trong CPU.
THREAD_READY: Luồng sẵn sàng làm việc nhưng chưa được đưa vào CPU,
được đặt trong hàng đợi ready queue chờ đến lượt
THREAD_BLOCKED: Luồng đang chờ một sự kiện nào đó xảy ra mới tiếp tục làm việc Khi đó luồng được đưa vào trạng thái THREAD_BLOCKED để
nhường CPU cho các luồng khác trong ready queue làm việc
THREAD_DYING: Luồng đã thực hiện xong việc và được TERMINATED Nó
sẽ được tiêu hủy
Các hàm chuyển đổi giữa các trạng thái của luồng được mô tả ở hình dưới Chúngđược khởi tạo trong “src/threads/thread.c”
Hình 2 Sơ đồ hàm chuyển trạng thái của threads
Trang 10Cấu trúc của luồng được khai báo trong “threads/thread.h”:
tid_t_tid: Thread identifier
enum thread_status status: Thread state
char name[16]: Name (for debugging purpose)
uint8_t *stack: Saved stack pointer
int priority: Priority (from 0 to 63)
int base_priority: Base priority for priority donation
struct list_elem allelem: List element for all threads list
struct list_elem elem: List element
unit32_t *pagedir: Page directory
usigned magic: Detects stack overflow
6download by : skknchat@gmail.com
Trang 11CHƯƠNG 2 ALARM CLOCK
Chương này mô tả mục tiêu, phương pháp, triển khai mã nguồn, mô tả mã nguồn
đã sửa đổi, bổ sung giải quyết vấn đến Alarm Clock trong Project 1 của Pintos
2.1 Vấn đề Alarm Clock
Thực hiện lại timer_sleep(), được đặt trong “devices/timer.c” Mặc dù nó đã hoạt động nhưng nó “busy wait”, tức là nó quay trong một vòng lặp để kiểm tra thời gian hiện tại và gọi thread_yield() cho đến khi đủ thời gian trôi qua Yêu cầu thực hiện lại
để tránh “busy waiting”.
Chức năng của void timer_sleep (int64_t ticks) : tạm dừng thực hiện chuỗi cuộc
gọi cho đến khi thời gian trôi qua ít nhất x ticks timer Trừ khi idle_thread, luồng không cần thức dậy sau đúng x ticks timer Chỉ cần đặt nó vào hàng đợi ready queue
sau khi nó đợi đúng khoảng thời gian
Vòng lặp gây ra “busy waiting”
while (timer_elapsed (start) < ticks)
thread_yield ();
Vòng lặp này sẽ được thực hiện liên tục cho đến khi số ticks trong timer_sleep() lớn hơn số ticks yêu cầu được trả về từ timer_elapsed (start) Nó chỉ thực hiện duy nhất một việc kiểm tra điều kiện và gọi hàm thread_yield() khi thỏa mãn điều kiện Chức năng của thread_yield() trong “threads/thread.c”, sau khi gọi hàm, luồng
đang chạy được đưa về trạng thái THREAD_READY Cụ thể, hàm này sẽ kiểm tra
luồng hiện tại có phải “idle_thread” không Nếu không, luồng sẽ được thêm vào hàng đợi “ready list” Sau đó, luồng được đưa về trạng thái THREAD_READY và nhường CPU cho schedule()
Trang 12Hình 3 Mô tả chức năng của hàm thread_yield().
Do sử dụng vòng lặp while() nên luồng ra vào hàng đợi ready_list liên tục đến khi
vòng lặp kết thúc Việc này gây ảnh hưởng đến hiệu năng của hệ điều hành
2.2 Mô tả mục tiêu
Thực hiện lại timer_sleep(), được đặt trong “devices/timer.c” Mặc dù nó đã hoạt động nhưng nó “busy wait”, tức là nó quay trong một vòng lặp để kiểm tra thời gian hiện tại và gọi thread_yield() cho đến khi đủ thời gian trôi qua Yêu cầu thực hiện lại
để tránh “busy waiting”.
2.3 Phương pháp giải quyết
Đối với mỗi luồng trong Pintos, luồng sẽ chuyển sang trạng thái sleep nếu không
có nhu cầu sử dụng CPU và sẽ được đánh thức sau một khoảng thời gian Cụ thể, mỗi
luồng có một thời gian gian wakeup_ticks để ngủ trước khi nó được đánh thức Đầu tiên, luồng được khởi tạo wakeup_ticks, sau đó thêm vào hàng đợi sleeping_list và sắp
xếp theo thứ tự tăng dần thời gian ngủ Khi đó luồng được đưa sang trạng thái
Trang 13Hình 4 Cơ chế sleep and wake up.
2.4 Triển khai mã nguồn
Các hàm cần triển khai cho quá trình sửa đổi mã nguồn nằm trong file thread.c, thread.h trong thư mục “src/threads/” và “src/devices/timer.c”.
thread_block(): Cho luồng hiện tại đi ngủ (trạng thái THREAD_BLOCKED).
Nó sẽ không được schedule() cho đến khi được đánh thức bởi thread_unblock().
thread_unblock(): Đưa luồng đang ở trạng thái THREAD_BLOCKED sang trạng thái ready-to-run (trạng thái THREAD_READY).
thread_tick(): Được gọi bởi timer_interrupt tại mỗi timer_tick Hàm này chạy
vượt quá time slice chưa (time slice được khai báo là 4 ticks), nếu đã hết thời gian nó
sẽ gọi hàm intr_yield_on_return() để thực hiện context switch, sau đó gọi hàm thread_unblock() đánh thức luồng đang ngủ Việc đưa luồng vào trạng thái ngủ được thực hiện trong hàm timer_sleep() như được mô tả trong mục 2.4
2.5 Mô tả mã nguồn đã sửa đổi, bổ sung
Trang 14 int64_t sleeping_ticks: Thời gian ngủ cho đến khi được đánh thức
của luồng
Các hàm được thêm vào threads/thread.c:
thread_wakeup(): Đánh thức các threads không bận.
thread_sleep(): Đặt chế độ ngủ để tránh việc busy waiting.
thread_insert_sleep_list(): đẩy vào cuối hàng đợi ready_list để chờ
làm việc với CPU
Các biến và hàm thêm vào threads/thread.c:
static struct list sleeping_list: Danh sách chờ của các luồng trong
timer_sleep.
Các hàm được chỉnh sửa theo giải pháp trên:
threads/thread.c: thread_init(), thread_tick()
Trang 15Hình 5 Kết quả sau khi make check test alarm-clock
CHƯƠNG 3 SYSTEM CALLS
Chương này mô tả mục tiêu, phương pháp, triển khai mã nguồn, mô tả mã nguồn
đã sửa đổi, bổ sung giải quyết vấn đề System Calls trong Project 2 của Pintos.
3.1 Đặt vấn đề
Khi làm việc trên Pintos với các phần của hệ thống mà cho phép chạy chương trìnhngười dùng, ta nhận thấy mã nguồn cơ bản của hệ thống chỉ hỗ tải và chạy chươngtrình người dùng nhưng lại không thể thực hiện tương tác hoặc các thiết lập vào ra I/O
Do đó cần thông qua các các cuộc gọi hệ thống (System Calls) ta sẽ kích hoạt cácchương trình tương tác với hệ điều hành
Trang 16Hình 6 Sơ đồ tương tác giữa chương trình người dùng và OS
Để cài đặt các system call, trước hết cần tìm hiểu thao tác đọc và ghi một cách an toàn các dữ liệu đang có trong không gian bộ nhớ ảo của các tiến trình cấp độ người dùng Các tham số của system call nằm trong stack của tiến trình người dùng, ngay bên trên con trỏ stack
Hình 7 Cấu trúc thành phần của layout Virtual Memory
Chú ý rằng, nếu con trỏ stack không hợp lệ khi một chương trình gọi một system call, nhân của hệ điều hành sẽ không thể bị crash Một số tham số của system call là con trỏ chỉ đến buffer bên trong không gian địa chỉ của tiến trình
12download by : skknchat@gmail.com
Trang 17ngườ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 system call không được thực thi trọn vẹn vì lỗi truy cập bộ nhớ không hợp lệ Các lỗi này bao gồm con trỏ null, con trỏ không hợp lệ (trỏ đến vùng nhớ chưa được ánh xạ) hoặc con trỏ của vùng nhớ nhân hệ điều hành Đặc biệt, có thể xảy
ra trường hợp một vùng nhớ 4 byte chứa 2 byte nhớ hợp lệ và 2 byte nhớ không hợp lệ, nếu vùng nhớ này nằm trên biên của bảng phân trang Khi xảy ra trường hợp này, cần phải chấm dứt (terminate) tiến trình người dùng.
3.3 Yêu cầu triển khai
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ínhhợ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ườidùng thì hợp lệ Theo quy ước gọi, system call sẽ được gọi bới một giá trị 4 byte đượctrỏ 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 ghiEAX Các cuộc gọi hệ thống được định nghĩa ở src/lib/syscall_nr.h.
Trang 18Hì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ảixâ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ệnhgọ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 chakhô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:
14download by : skknchat@gmail.com
Trang 19+ 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ìnhcon 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