- Cần một system call
pgaccess(base, len, mask)báo về các trang bộ nhớ user trong dải bắt đầu tạibase, dàilentrang, đã được truy cập (accessed) kể từ lần kiểm tra trước. - Kết quả trả về trong một biến 64-bit tại địa chỉ
maskcủa user: bit i tương ứng trang i; bit=1 nếu trang đã được truy cập. - Sau khi ghi nhận, kernel xóa cờ truy cập của trang để lần gọi kế tiếp chỉ phản ánh truy cập mới phát sinh.
- Giới hạn tối đa 64 trang mỗi lần gọi (tương ứng mặt nạ 64-bit).
- RISC-V và xv6 dùng bit PTE_A (Accessed) trong Page Table Entry để đánh dấu một trang đã được truy cập (do phần cứng đặt khi đọc/ghi/thực thi lần đầu).
- Trong kernel, với mỗi trang trong phạm vi yêu cầu, tra PTE tương ứng:
- Bỏ qua nếu PTE không hợp lệ (
!PTE_V) hoặc không phải trang user (!PTE_U). - Nếu PTE có
PTE_A: bật bit i trongmaskvà xóaPTE_Ađể “reset” cho lần kiểm tra sau.
- Bỏ qua nếu PTE không hợp lệ (
- Sao chép
maskvề user quacopyout. Giao diện người dùng sử dụng stub syscall sinh bởiusys.pl.
Lợi ích: triển khai đơn giản, tuyến tính theo số trang; không cần sửa đổi phần cứng hay cơ chế TLB. Xóa PTE_A giúp thu được semantics kiểu “edge-triggered” (chỉ truy cập mới).
3.1) Khai báo số hiệu syscall
- File: kernel/syscall.h
- Thêm/đảm bảo tồn tại:
#define SYS_pgaccess 30 - Ý nghĩa: gán một số hiệu duy nhất để user stub nạp vào thanh ghi
a7trước khiecall.
- Thêm/đảm bảo tồn tại:
3.2) Stub và khai báo phía user
- File: user/usys.pl
- Có mục:
entry("pgaccess"); - Ý nghĩa: sinh ra stub trong
usys.Sđể nạpa7 = SYS_pgaccessrồiecall.
- Có mục:
- File: user/user.h
- Khai báo:
int pgaccess(void *base, int len, void *mask); - Ý nghĩa: cho phép chương trình user gọi hàm
pgaccess()như một API bình thường.
- Khai báo:
3.3) Đăng ký handler trong kernel
- File: kernel/syscall.c
- Khai báo:
extern uint64 sys_pgaccess(void); - Ánh xạ trong bảng syscall:
[SYS_pgaccess] sys_pgaccess,(đi kèm#ifdef LAB_PGTBL). - Ý nghĩa: khi trap
ecallvớia7=30, kernel gọi đúng handlersys_pgaccess().
- Khai báo:
3.4) Định nghĩa cờ PTE_A
- File: kernel/riscv.h
- Có:
#define PTE_A (1L << 6) - Ý nghĩa: cờ do phần cứng đặt khi trang được truy cập; kernel đọc/xóa để theo dõi truy cập.
- Có:
3.5) Handler sys_pgaccess
- File: kernel/sysproc.c (bên trong
#ifdef LAB_PGTBL) - Chức năng: nhận tham số user, quét bảng trang, lập
mask, trả về kết quả, và xóa cờPTE_Ađã quan sát. - Ý nghĩa từng bước chính trong code:
- Đọc tham số syscall
argaddr(0, &uaddr);→ địa chỉ bắt đầubaseargint(1, &npages);→ số tranglenargaddr(2, &umask);→ địa chỉ user để ghi kết quả- Kiểm tra
npages < 0→ lỗi; cắtnpagesvề 64 nếu lớn hơn.
- Duyệt từng trang và tra PTE
- Tính
va = uaddr + i * PGSIZEcho từngi. pte = walk(p->pagetable, va, 0)để lấy con trỏ PTE.- Bỏ qua nếu
pte == 0hoặc!(*pte & PTE_V)(chưa map) hoặc!(*pte & PTE_U)(không phải của user).
- Tính
- Ghi nhận và reset cờ truy cập
- Nếu
*pte & PTE_Akhác 0:mask |= (1ULL << i)và*pte &= ~PTE_Ađể xóa cờ. - Ý nghĩa: bật bit trong kết quả và “reset” để lần sau chỉ thấy truy cập mới.
- Nếu
- Trả kết quả về user
copyout(p->pagetable, umask, &mask, sizeof(mask))→ lỗi thì trả-1, thành công trả0.
- Đọc tham số syscall
3.6) Kiểm thử với chương trình user
- File: user/pgtbltest.c
- Cấp phát
buf = malloc(32 * PGSIZE); lần 1 gọipgaccess(buf, 32, &abits)kỳ vọngabits=0. - Ghi 1 byte vào các offset trang 1, 2, 30:
buf[PGSIZE * k] += 1;. - Lần 2 gọi
pgaccess(buf, 32, &abits)và kiểm:abits == (1<<1) | (1<<2) | (1<<30). - Ý nghĩa: viết 1 byte đảm bảo phần cứng đặt
PTE_Acho các trang tương ứng.
- Cấp phát
3.7) Cách chạy và chấm điểm
-
Chạy thủ công trong QEMU:
cd source make clean && make qemu
Trong shell của xv6 sau khi boot:
pgtbltestKỳ vọng:
pgaccess_test: OKvàpgtbltest: all tests succeeded. -
Chạy script chấm điểm (nếu toolchain đã sẵn sàng):
cd source ./grade-lab-pgtbl
3.8) Lưu ý biên và bảo mật
basekhông cần thẳng hàng trang; bit 0 ứng với trang chứabase(khuyến nghị căn trang để dễ suy luận).- Bắt buộc kiểm tra
PTE_Uđể chỉ báo cáo trang user (tránh lộ thông tin vùng kernel). - Trang chưa map hoặc không hợp lệ bị bỏ qua (bit = 0), không gây lỗi toàn bộ.
Tham chiếu nhanh (điểm tựa khi vấn đáp):
- Số hiệu syscall: kernel/syscall.h →
SYS_pgaccess - Đăng ký syscall: kernel/syscall.c →
extern sys_pgaccess+syscalls[] - Bit truy cập: kernel/riscv.h →
PTE_A - Handler: kernel/sysproc.c →
sys_pgaccess() - Stub và khai báo user: user/usys.pl, user/user.h
- Bài test: user/pgtbltest.c