cuốn sách gpt4 ai đã làm

[rCore Study Notes 016] Triển khai ứng dụng

In lại Tác giả: Sahara Thời gian cập nhật: 2024-07-20 13:00:42 57 4
mua khóa gpt4 Nike

viết ở phía trước

Bài luận này được viết bởi một người rất mới. Nếu bạn có bất kỳ câu hỏi nào, xin vui lòng hỏi họ kịp thời.

Bạn có thể liên hệ: 1160712160@qq.com.

GitHhub: https://github.com/WindDevil (Hiện tại không có gì.

phương pháp thiết kế

Trên thực tế, sau khi hiểu cơ chế cấp đặc quyền, nếu muốn thiết kế một ứng dụng, bạn cần đảm bảo rằng nó đáp ứng các yêu cầu của chế độ U và không truy cập các chức năng ở chế độ S. Sau đó, những điểm chính khi triển khai nó là.

  1. Bố cục bộ nhớ ứng dụng
  2. Cuộc gọi hệ thống do ứng dụng đưa ra

thiết kế cụ thể

Những tính năng cần bổ sung

Trong quá trình thực hiện cụ thể, việc thiết kế cũng phải được thực hiện theo các điểm chính được đưa ra trong phương pháp thiết kế.

  1. Bố cục bộ nhớ ứng dụng
    1. Đặt vị trí nhập của thư viện người dùng
    2. Cấu hìnhlinker.ldFile, thiết lập người dùng khó khănromđịa chỉ ở
  2. Cuộc gọi hệ thống do ứng dụng đưa ra
    1. Việc thực hiện có thể gọigọi lạigiao diện

Các ứng dụng cần triển khai

Ứng dụng được biên dịch riêng để tạo tệp ELF và cắt nó thành tệp .bin. Để gọi nó, bạn chỉ cần liên kết nó với kernel trước rồi kernel sẽ tải nó vào bộ nhớ vào thời điểm thích hợp. để thực hiện nó trong phần giới thiệu của phần này

  • xin chào thế giới : In một dòng lên màn hình Xin chào thế giới từ chương trình chế độ người dùng!
  • cửa hàng_lỗi : Truy cập vào một địa chỉ vật lý bất hợp pháp để kiểm tra xem hệ thống xử lý hàng loạt có bị ảnh hưởng bởi lỗi này hay không.
  • quyền lực : Liên tục chuyển đổi cấp độ đặc quyền giữa các thao tác tính toán và thao tác in chuỗi

Tạo dự án

Đảm bảo rằng vị trí hiện tại là không gian làm việc.

cd -/không gian làm việc

Sử dụng hàng hóa để tạo dự án và tạo thư mục người dùng trong thư mục dự án để lưu trữ mã và giao diện chế độ người dùng.

hàng hóa mới ./user

Đã có user/src trong tệp dự án để lưu thư viện người dùng. Tiếp theo, tạo user/src/bin để lưu ứng dụng.

người dùng mkdir/src/bin

Thực hiện cấu hình bố trí bộ nhớ

Tạo tệp liên kết linker.ld trong user/src.

liên kết liên kết.ld

Vì địa chỉ mục nhập của lớp người dùng ứng dụng là 0x80400000 nên linker.ld được cung cấp trong Chương 1 cần được sửa đổi và BASE_ADDRESS được đặt thành 0x80400000.

OUTPUT_ARCH(riscv) ENTRY(_start) BASE_ADDRESS = 0x80400000; SECTIONS { . = BASE_ADDRESS; skernel = .; stext = .; (4K); etext = .; .rodata : { *(.rodata .rodata.*) *(.srodata .srodata.*) } = ALIGN(4K); erodata = .; sdata = .; ) *(.sdata .sdata.*) } = ALIGN(4K); edata = .bss : { *(.bss.stack) sbss = .; *(.bss .bss.*) *(.sbss .sbss.*) } . = ALIGN(4K); ebss = .; *(.eh_frame) } }

Bố cục bộ nhớ được thể hiện bằng tệp liên kết này như trong hình. Hãy chú ý đến vị trí của địa chỉ thấp và địa chỉ cao trong hình.

  • Phân đoạn dữ liệu được khởi tạo lưu trữ dữ liệu toàn cục được khởi tạo trong chương trình và được chia thành .rodata Và .data Hai phần. Cái trước lưu trữ dữ liệu toàn cầu chỉ đọc, thường là một số hằng số hoặc chuỗi không đổi, v.v. trong khi cái sau lưu trữ dữ liệu toàn cầu có thể sửa đổi.
  • Phân đoạn dữ liệu chưa được khởi tạo .bss Lưu dữ liệu chung chưa được khởi tạo trong chương trình, dữ liệu này thường được khởi tạo bằng 0 bởi trình tải của chương trình, nghĩa là xóa vùng này theo từng byte;
  • tràn Vùng (heap) được sử dụng để lưu trữ dữ liệu được phân bổ động khi chương trình đang chạy. Ví dụ: dữ liệu được phân bổ bởi malloc/new trong C/C++ được đặt trong vùng heap và nó sẽ tăng dần lên các địa chỉ cao hơn;
  • chồng Vùng (ngăn xếp) không chỉ được sử dụng để lưu và khôi phục ngữ cảnh lệnh gọi hàm mà các biến cục bộ trong phạm vi của từng hàm cũng được trình biên dịch đặt trong khung ngăn xếp của nó và nó phát triển về các địa chỉ thấp hơn.

Nhớ lại hướng dẫn mà chúng tôi đã sử dụng để flash tệp nhị phân vào QEMU, chúng tôi thực sự đã đặt kernel đã biên dịch ở 0x80200000, đây là phần .text.

qemu-system-riscv64 \ -machine virt \ -nographic \ -bios ../bootloader/rustsbi-qemu.bin \ -device Loader,file=target/riscv64gc-unknown-none-elf/release/os.bin,addr= 0x80200000

Đặt .text.entry trong đó _start nằm ở đầu toàn bộ chương trình. Nghĩa là, hệ thống hàng loạt đã nhập điểm vào của thư viện người dùng miễn là nó nhảy tới 0x80400000 sau khi tải và sẽ chuyển đến ứng dụng sau khi khởi tạo Logic chính...

Ở đây lưu ý rằng ENTRY(_start) là lối vào chương trình cài đặt. Nhìn lại nội dung của entry.asm trong Chương 1, .section .text.entry được đặt ở đây và Global_asm!(include_str!("entry. asm" )), trích dẫn mã này

# os/src/entry.asm .section .text.entry .globl _start _start: la sp, boot_stack_top gọi Rust_main .section .bss.stack .globl boot_stack_low_bound boot_stack_low_bound: .space 4096 * 16 .globl boot_stack_top boot_stack_top:

Cung cấp địa chỉ bắt đầu và kết thúc của phần .bss của tệp thực thi được tạo cuối cùng để hỗ trợ việc sử dụng hàm clear_bss.

Quan sát ~/App/rCore-Tutorial-v3/user/src/linker.ld

OUTPUT_ARCH(riscv) ENTRY(_start) BASE_ADDRESS = 0x80400000; SECTIONS { . = BASE_ADDRESS; .text : { *(.text.entry) *(.text .text.*) } .rodata : { *(.rodata .rodata. *) *(.srodata .srodata.*) } .data : { *(.data .data.*) *(.sdata .sdata.*) } .bss : { start_bss = .; *(.bss .bss.*) *(.sbss .sbss.*) end_bss = .; /DISCARD/ : { *(.eh_frame) *(.debug*) } }

Bạn có thể thấy rằng so với file link.ld mà chúng ta xây dựng theo file thì có ít mô tả về xxx(label) = .; và offset của = ALIGN(4K); hơn.

xxx(label) = .; được sử dụng để xác định ký hiệu xxx và đặt vị trí con trỏ hiện tại cho anh ta.

. = ALIGN(4K) được sử dụng để kiểm tra xem vị trí liên kết hiện tại có được căn chỉnh theo ranh giới 4K hay không. Nếu vị trí liên kết hiện tại không phải là bội số của ranh giới 4K, trình liên kết sẽ lấp đầy đủ byte cho đến ranh giới 4K tiếp theo.

Do đó, chúng ta có thể biết rằng trong linker.ld gốc, một số nhãn cần được đặt để chúng ta có thể lấy các con trỏ này thông qua extern C để biết kích thước của từng phần sau khi biên dịch. Và mỗi khi một phần được đặt, nó cần. được kiểm tra để phù hợp với ranh giới 4K ..

chi tiết (vui lòng đọc kỹ phần này, đặc biệt là BỎ QUA)

  • OUTPUT_ARCH(riscv): Xác định rằng kiến trúc đầu ra mục tiêu là RISC-V.
  • NHẬP(_bắt đầu): Chỉ định điểm vào của chương trình là_bắt đầuchức năng. Đây là nơi thực hiện chương trình bắt đầu.
  • BASE_ADDRESS = 0x80200000;: Đặt địa chỉ cơ sở của chương trình thành 0x80200000, đây là địa chỉ bắt đầu của chương trình được tải vào bộ nhớ.
  • PHẦN: Bắt đầu xác định bố cục của phân đoạn bộ nhớ.
  • . = CƠ SỞ_ĐỊA CHỈ;: Đặt địa chỉ hiện tại làm địa chỉ cơ sở.
  • da = .;: Ghi lại địa chỉ bắt đầu của phân đoạn kernel.
  • văn bản = .;: Ghi địa chỉ bắt đầu của đoạn văn bản (đoạn mã).
  • .chữ: Xác định một đoạn văn bản, chứa mã thực thi.*(.text.entry)*(.text .text.*)có nghĩa là tất cả.chữ.chữ.*Nội dung phần được liên kết ở đây.
  • . = CĂN HỘ(4K);: Căn chỉnh địa chỉ hiện tại theo ranh giới 4K (4096 byte).
  • etext = .;: Ghi địa chỉ cuối của đoạn văn bản.
  • srodata = .;: Ghi lại địa chỉ bắt đầu của phân đoạn dữ liệu chỉ đọc.
  • .rodata: Xác định phân đoạn dữ liệu chỉ đọc, bao gồm các hằng số và dữ liệu chỉ đọc.*(.rodata .rodata.*)*(.srodata .srodata.*)có nghĩa là tất cả.rodata.srodataNội dung phần được liên kết ở đây.
  • erodata = .;: Ghi lại địa chỉ cuối của phân đoạn dữ liệu chỉ đọc.
  • sdata = .;: Ghi lại địa chỉ bắt đầu của đoạn dữ liệu khởi tạo.
  • .data: Xác định phân đoạn dữ liệu khởi tạo, bao gồm các biến toàn cục được khởi tạo.*(.data .data.*)*(.sdata .sdata.*)có nghĩa là tất cả.data.sdataNội dung phần được liên kết ở đây.
  • edata = .;: Ghi lại địa chỉ cuối của đoạn dữ liệu khởi tạo.
  • .bss: Xác định phân đoạn dữ liệu chưa được khởi tạo (phân đoạn BSS), bao gồm các biến toàn cục chưa được khởi tạo.*(.bss.stack)*(.bss .bss.*)cũng như*(.sbss .sbss.*)có nghĩa là tất cả.bss,.sbss.bss.stackNội dung phần được liên kết ở đây.sss = .;Ghi lại địa chỉ bắt đầu của phân đoạn BSS.
  • ebss = .;: Ghi lại địa chỉ cuối của đoạn dữ liệu chưa được khởi tạo.
  • hạt nhân = .;: Ghi lại địa chỉ cuối của toàn bộ phân đoạn kernel.
  • /BỎ BỎ/: Xác định phần loại bỏ để loại trừ các phần không cần thiết, ở đây quy định là không bao gồm.eh_framephần này, thông thường phần này chứa thông tin khung xử lý ngoại lệ.

Tương ứng với việc tạo các chức năng nhập và khởi tạo hệ thống

Tạo mô-đun lib.rs

chạm vào lib.rs

Giống như main.rs của os, khi tạo mục nhập hàm, hãy sử dụng #[no_mangle] để đảm bảo tên hàm không được tối ưu hóa và sử dụng macro mới #[link_section = ".text.entry"] để tạo _start mã này. mã hợp ngữ đã biên dịch được đặt trong một tệp có tên .text.entry Trong đoạn mã, thuận tiện cho chúng ta điều chỉnh vị trí của nó trong quá trình liên kết tiếp theo để nó có thể đóng vai trò là lối vào thư viện người dùng. Ở đây cần lưu ý rằng chúng ta vẫn chỉ có thể sử dụng thư viện lõi nên chúng ta phải sử dụng. #![no_std] macro

#![no_std] #[no_mangle] #[link_section = ".text.entry"] pub extern "C" fn _start() -> ! { clear_bss(); sys_exit!"); }

Tương ứng với Chương 1, bạn cũng cần xóa phần .bss và sử dụng macro hoảng loạn!

#![feature(panic_info_message)] fn clear_bss() { extern "C" { fn start_bss(); fn end_bss(); } (start_bss as usize..end_bss as usize).for_each(|addr| không an toàn { (addr as *mut u8).write_volatile(0); } }

Và dùng giao diện exit để gọi hàm main. Giao diện exit ở đây chỉ có thể thực hiện được thông qua ecall sau này đối với hàm main nếu có ký hiệu main trong thư mục bin thì chương trình có thể liên kết bình thường, nhưng khi đó chúng ta không thể liên kết được. tìm chính, chúng tôi cũng cần phải có một Đảm bảo, điều này liên quan đến các liên kết yếu. Nếu không tìm thấy chính, hãy liên kết đến chính.

#![feature(linkage)] #[linkage = "weak"] #[no_mangle] fn main() -> i32 { hoảng loạn!("Không thể tìm thấy main!" }

Tạo mô-đun cuộc gọi hệ thống

Tạo mô-đun syscall. Tệp này được tạo trong tệp người dùng/src.

touchsyscall.rs

Chúng tôi sử dụng mã hợp ngữ nhúng của Rust để gọi ecall nhằm bắt đầu các cuộc gọi hệ thống ở chế độ người dùng.

Khi một tiến trình thực thi lệnh ecall, bộ xử lý sẽ kích hoạt một ngoại lệ, khiến điều khiển chuyển sang trình xử lý ngoại lệ kernel đặt trước. Tại thời điểm này, kernel có thể kiểm tra ngữ cảnh đã kích hoạt ecall và cung cấp các dịch vụ phù hợp dựa trên các tham số được truyền vào, chẳng hạn như mở tệp, tạo tiến trình, phân bổ bộ nhớ, v.v.

Bản thân lệnh ecall không mang bất kỳ tham số nào, nhưng nó có thể truy cập các giá trị trong các thanh ghi chung và chuyển chúng vào kernel dưới dạng tham số. Thông thường, các thanh ghi sau được sử dụng để truyền tham số:

  • x10(a0): tham số đầu tiên
  • x11(a1): tham số thứ hai
  • x12(a2): Tham số thứ ba
  • x13(a3): Tham số thứ tư
  • x14(a4): Tham số thứ năm
  • x15(a5): Tham số thứ sáu
  • x16(a6): Tham số thứ bảy
  • x17(a7): Tham số thứ tám, cũng đóng vai trò là số cuộc gọi hệ thống

Sau đó, bạn có thể tạo giao diện trong tệp syscall.rs

// user/src/syscall.rs use core::arch::asm; fn syscall(id: usize, args: [usize; 3]) -> isize { let mut ret: isize không an toàn { asm!( " ecall ", inlateout("x10") args[0] => ret, in("x11") args[1], in("x12") args[2], in("x17") id } ret }

Format macro asm!

  1. Đầu tiên, dòng 6 chính là đoạn mã hợp nhất mà chúng tôi muốn chèn ở đây, chúng tôi chỉ chèn một dòng. gọi lại hướng dẫn, nhưng nó có thể hỗ trợ chèn nhiều hướng dẫn cùng một lúc.
  2. Bắt đầu từ dòng 7, chúng tôi liên kết các biến đầu vào/đầu ra với các thanh ghi với sự trợ giúp của trình biên dịch dịch.
  3. Ví dụ: dòng 8 trong ("x11") tranh luận [1] Có nghĩa là các thông số sẽ được nhập lập luận[1] kill with gọi lại ghi đầu vào x11 Instant setting a1 , trình biên dịch sẽ tự động chèn các lệnh liên quan và chắc chắn rằng gọi lại đăng ký trước khi thực hiện lệnh này a1 Giá trị của lập luận[1] như nhau.
  4. Theo cách tương tự, chúng tôi có thể đặt các tham số đầu vào lập luận[2] Và NHẬN DẠNG Liên kết với các thanh ghi tương ứng a2 Và a7 ở giữa.
  5. Điều đặc biệt ở đây là a0 Thanh ghi, đóng vai trò vừa đầu vào vừa là đầu ra, vì vậy chúng ta sẽ TRONG Change thành công vào sau và trong phần biến ở dòng cuối cùng, hãy sử dụng {in_var} => {out_var} format, ở đâu {in_var} Và {out_var} Giao diện cho các biến đầu vào và đầu ra biến trong bối cảnh tương ứng.

Trong chương trình này, hai lệnh gọi hệ thống sau đây được thống nhất giữa chương trình ứng dụng và hệ thống xử lý hàng loạt theo API cấu hình:

/// Chức năng: Ghi dữ liệu vào bộ nhớ đệm vào tệp /// Tham số: `fd` đại diện cho tệp mô tả của file. được ghi; /// `buf` đại diện cho địa chỉ bắt đầu của bộ đệm trong bộ nhớ /// `len` đại diện cho chiều dài; /// ID tòa nhà: 64 fn sys_write(fd: usize, buf: *const u8, len: usize) -> isize; /// Chức năng: Thoát khỏi ứng dụng và thông báo cho hệ thống xử lý hàng hóa về giá trị trả về /// Tham số: `exit_code` biểu tượng /// ID tòa nhà: 93 fn sys_exit(exit_code: useize) -> !;

Tương tự như vậy, chúng tôi có thể sử dụng syscall để phát triển API này

// user/src/syscall.rs const SYSCALL_WRITE: usize = 64; const SYSCALL_EXIT: usize = 93; pub fn sys_write(fd: usize, buffer: &[u8]) -> isize { syscall(SYSCALL_WRITE, [fd, buffer .as_ptr() as usize, buffer.len()]) } pub fn sys_exit(xstate: i32) -> isize { syscall(SYSCALL_EXIT, [xstate as usize, 0, 0]) }

Lưu ý rằng sys_write sử dụng lát lát &[u8] để mô tả bộ đệm, đó là một con trỏ béo (Con trỏ béo), chứa cả địa chỉ chỉ bắt đầu bộ đệm và độ dài của bộ đệm. ứng dụng và sử dụng chúng để tạo ra một tham số gọi hệ thống thực tế một cách độc lập.

Đóng gói bổ sung giao diện

Để gói gọn hơn nữa hai lệnh gọi hệ thống trên trong thư viện người dùng user_lib, để gần với giao diện lệnh gọi hệ thống thực tế trên Linux và các nền tảng khác, hãy sửa đổi tệp lib.rs và nhập vào

sử dụng syscall mod; ) }

Thay đổibảng điều khiểnhiện thực hóa

Chúng tôi đã thay đổi Stdout::write_str trong mô-đun con bảng điều khiển thành triển khai dựa trên ghi và đặt tham số fd đến thành 1, đại diện cho đầu ra tiêu chuẩn, nghĩa là xuất ra màn hình. Chúng ta không cần phải xem xét các tình huống lựa chọn fd khác vào lúc này. Bằng cách này, macro println! của ứng dụng sẽ khả dụng thông qua lệnh gọi hệ thống.

Tạo tệp console.rs. Nội dung phù hợp với mô-đun trong Chương 1. Chỉ sửa đổi việc triển khai tính năng Ghi của Stdout.

// user/src/console.rs const STDOUT: usize = 1; impl Viết cho Stdout { fn write_str(&mut self, s: &str) -> fmt::Result { write(STDOUT, s.as_bytes()); (()) } }

Nội dung tập tin cuối cùng là

sử dụng super::write; sử dụng core::fmt::{self, Write}; const STDOUT: usize = 1; impl Viết cho Stdout { fn write_str(&mut self, s: &str) -> fmt::Result { write(STDOUT, s.as_bytes()); Ok(()) } } pub fn print(args: fmt::Arguments) { Stdout.write_fmt(args).unwrap(); } #[macro_export] macro_rules! print { ($fmt: chữ $(, $($arg: tt)+)?) => { $crate ::console::print(format_args!($fmt $(, $($arg)+)?)); #[macro_export] macro_rules! println { ($fmt: chữ $(, $($arg: tt)+)?) => { $crate::console::print(format_args!(concat!($fmt, "\ n") $(, $($arg)+)?)); } }

Đọc mã của ba ứng dụng

Bạn có thể truy cập ~/App/rCore-Tutorial-v3, sử dụng git check ch2, chuyển sang mã trong Chương 2 để xem mã nguồn của ba ứng dụng. Và bạn có thể sao chép chúng vào thư mục user/src/bin của chúng tôi. dự án.

cd ~/App/rCore-Tutorial-v3 git kiểm tra ch2 cd user/src/bin cp 00hello_world.rs ~/workspace/user/src/bin cp 01store_fault.rs ~/workspace/user/src/bin cp 02power.rs ~ /không gian làm việc/người dùng/src/bin

xin chào thế giới

#![no_std] #![no_main] #[macro_use] thùng bên ngoài user_lib; #[no_mangle] fn main() -> i32 { println!("Xin chào thế giới!");

cửa hàng_lỗi

#![no_std] #![no_main] #[macro_use] extern thùng user_lib; #[no_mangle] fn main() -> i32 { println!("Trong quá trình kiểm tra store_fault, chúng tôi sẽ chèn một thao tác lưu trữ không hợp lệ...") ; println!("Kernel nên tắt ứng dụng này!"); core::ptr::null_mut::().write_volatile(0);

quyền lực

#![no_std] #![no_main] #[macro_use] thùng bên ngoài user_lib; const SIZE: usize = 10; const P: u32 = 3; const STEP: usize = 100000; const MOD: u32 = 10007; fn main() -> i32 { let mut pow = [0u32; SIZE]; đặt chỉ số thay đổi: usize = 0; pow[index] = 1; for i in 1..=STEP { let end = pow[index]; cuối cùng * P % MOD; nếu i % 10000 == 0 { println!("{}^{}={}(MOD {})", P, i, pow[index], MOD); } } println!("Kiểm tra nguồn ổn!");

Biên dịch và tạo mã nhị phân ứng dụng

Tạo tập lệnh xây dựng tự động

Mượn trực tiếp tập lệnh trong ~/App/rCore-Tutorial-v3/user và tạo Makefile trong thư mục người dùng

MỤC TIÊU := riscv64gc-unknown-none-elf MODE := phát hành APP_DIR := src/bin TARGET_DIR := target/$(TARGET)/$(MODE) APPS := $(wildcard $(APP_DIR)/*.rs) ELFS := $(patsubst $(APP_DIR)/%.rs, $(TARGET_DIR)/%, $(APPS)) BINS := $(patsubst $(APP_DIR)/%.rs, $(TARGET_DIR)/%.bin, $(APPS)) OBJDUMP := Rust-objdump --arch-name=riscv64 OBJCOPY := Rust-objcopy --binary-architecture=riscv64 elf: @cargo build --release nhị phân: elf @$(foreach elf, $(ELFS), $(OBJCOPY) $(elf) --strip-all -O nhị phân $(patsubst $(TARGET_DIR)/%, $(TARGET_DIR)/%.bin, $(elf));) build: nhị phân

phân tích cú pháp

  1. định nghĩa biến:
    • MỤC TIÊU:Đặt làm kiến trúc đích riscv64gc-unknown-none-elf, đây là sự hỗ trợ của Rust dành cho kiến trúc RISC-V.
    • CÁCH THỨC:Đặt thành giải phóng, nghĩa là xây dựng bằng chế độ phát hành của Rust, chế độ này thường tạo ra các tệp nhị phân được tối ưu hóa.
    • APP_DIR: Thư mục mã nguồn, đây là src/bin, thường lưu trữ các tệp nguồn chương trình thực thi Rust.
    • TARGET_DIR: Xây dựng thư mục đầu ra, được xây dựng ở đây theo kiến trúc đích và chế độ xây dựng.
    • ỨNG DỤNG:sử dụng ký tự đại diện danh sách chức năng APP_DIR Tất cả trong thư mục .rs Tệp, tức là tệp nguồn Rust.
    • ELFS:vượt qua patsubst chuyển đổi chức năng ỨNG DỤNG List, chuyển đổi từng tệp nguồn sang đường dẫn tệp đích tương ứng ở định dạng ELF.
    • thùng: Sử dụng tương tự patsubst chức năng, ý chí ỨNG DỤNG Danh sách được chuyển đổi thành .bin Định dạng đường dẫn tệp nhị phân.
  2. Định nghĩa công cụ lệnh:
    • ĐỐI TƯỢNG:sử dụng gỉ-objdump công cụ, chỉ định lược đồ là riscv64, được sử dụng để xem cấu trúc bên trong của tệp mục tiêu.
    • ĐỐI TƯỢNG:sử dụng vật thể rỉ sét Tool, cũng chỉ định kiến trúc như riscv64, được sử dụng để chuyển đổi từ định dạng ELF sang các định dạng khác (chẳng hạn như nhị phân).
  3. Xác định mục tiêu:
    • yêu tinh: Mục tiêu này gọi xây dựng hàng hóa --phát hành lệnh, sử dụng Cargo (trình quản lý gói và công cụ xây dựng của Rust) để xây dựng dự án và tạo tệp thực thi ở định dạng ELF.
    • nhị phân: Mục tiêu này phụ thuộc vào yêu tinh mục tiêu, sau đó đi qua ELFS liệt kê, sử dụng ĐỐI TƯỢNG Chuyển đổi từng tệp ELF thành tệp nhị phân có biểu tượng.
    • xây dựng: Mục tiêu này phụ thuộc vào nhị phân Mục tiêu, có nghĩa là mục tiêu cuối cùng của quá trình xây dựng là tạo ra tệp nhị phân.

Thực hiện hướng dẫn xây dựng tự động

Nhập thư mục người dùng và thực thi lệnh xây dựng tự động của ứng dụng make build.

Báo cáo lỗi

error[E0583]: không tìm thấy tệp cho mô-đun `lang_items` --> src/lib.rs:7:1 | mod lang_items; để tạo module `lang_items`, tạo file "src/lang_items.rs" hoặc "src/lang_items/mod.rs" = lưu ý: nếu có `mod lang_items` ở nơi khác trong thùng, hãy nhập nó bằng `sử dụng thùng::...` thay vì lỗi: thanh ghi không hợp lệ `x10`: thanh ghi không xác định --> src/syscall.rs:11:13 inlateout("x10 | ") args[0] => ret, | ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^^ lỗi: đăng ký không hợp lệ `x11`: đăng ký không xác định --> src/syscall.rs:12:13 | in("x11") args[1], | ^^^^^^^^^^ lỗi: thanh ghi không hợp lệ `x12`: thanh ghi không xác định --> src/syscall.rs:13:13 | in("x12") args[2], | ^^ ^^^ ^^ ^^ ^^ ^^ ^^ ^^ lỗi: đăng ký không hợp lệ `x17`: đăng ký không xác định --> src/syscall.rs:14:13 | in("x17") id | ^^ ^^ ^^ ^^ ^^ ^^ Để biết thêm thông tin về lỗi này, hãy thử `rustc --explain E0583`. tạo: *** [Makefile:13: elf] Lỗi 101

Bạn có thể thấy mô-đun lang_items không tồn tại và tất cả các thanh ghi được đánh dấu là thanh ghi không hợp lệ.

Chúng tôi cũng kiểm tra việc triển khai trong ~/App/rCore-Tutorial-v3/user/src

#[panic_handler] fn Panic_info: &core::panic::PanicInfo) -> ! { let err = Panic_info.message().unwrap(); if let Some(location) = Panic_info.location() { println!( "Hoảng loạn tại {}:{}, {}", location.file(), location.line(), err } else { println!("Hoảng loạn: {}", err); } vòng lặp {} }

Bạn có thể thấy rằng giống như Chương 1, một hàm được chú thích bằng #[panic_handler] đã được triển khai, nhưng lệnh tắt do sbi cung cấp và macro error! do log cung cấp không được sử dụng. sử dụng macro hoảng loạn! Để triển khai một hàm có tên là hoảng loạn, chỉ cần triển khai hàm với chú thích này.

Sao chép tập tin này và sử dụng nó

cp lang_items.rs ~/workspace/user/src/

Biên dịch lại

cd ~/workspace/người dùng tạo bản dựng

Nhận thấy lỗi vẫn được báo cáo

lỗi: đăng ký không hợp lệ `x10`: đăng ký không xác định --> src/syscall.rs:11:13 | inlateout("x10") args[0] => ret, | ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ lỗi: thanh ghi không hợp lệ `x11`: thanh ghi không xác định --> src/syscall.rs:12:13 | 12 | in("x11") args[1], | ^^ ^^^ ^^ ^^ ^^ ^^ ^^ lỗi: đăng ký không hợp lệ `x12`: không xác định đăng ký --> src/syscall.rs:13:13 | 13 | in("x12") args[2], | ^^ ^^^ ^^ ^^ ^^ ^^ ^^ ^^ lỗi: đăng ký không hợp lệ `x17`: đăng ký không xác định --> src/syscall.rs:14:13 | in("x17") id | ^^ ^^ ^^ ^^ ^^ ^^ lỗi: không thể biên dịch `user` (lib) do 4 lỗi trước đó tạo ra: *** [Makefile:13: elf] Lỗi 101

Có thể có vấn đề với các tệp phụ thuộc. Chúng tôi nhận thấy rằng Rust-sbi dường như không phụ thuộc vào nó. Chúng tôi đã kiểm tra ~/App/rCore-Tutorial-v3/user/Cargo.toml và nhận thấy rằng đó không phải là vấn đề. sbi không có phần phụ thuộc, nhưng có phần phụ thuộc risc-v.

[phụ thuộc] riscv = { git = "https://github.com/rcore-os/riscv", tính năng = ["inline-asm"] } 

Tại thời điểm này, hãy chạy lại bản dựng dưới quyền người dùng và vẫn gặp lỗi.

lỗi: đăng ký không hợp lệ `x10`: đăng ký không xác định --> src/syscall.rs:11:13 | inlateout("x10") args[0] => ret, | ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ lỗi: thanh ghi không hợp lệ `x11`: thanh ghi không xác định --> src/syscall.rs:12:13 | 12 | in("x11") args[1], | ^^ ^^^ ^^ ^^ ^^ ^^ ^^ lỗi: đăng ký không hợp lệ `x12`: không xác định đăng ký --> src/syscall.rs:13:13 | 13 | in("x12") args[2], | ^^ ^^^ ^^ ^^ ^^ ^^ ^^ ^^ lỗi: đăng ký không hợp lệ `x17`: đăng ký không xác định --> src/syscall.rs:14:13 | in("x17") id | ^^ ^^ ^^ ^^ ^^ ^^ lỗi: không thể biên dịch `user` (lib) do 4 lỗi trước đó tạo ra: *** [Makefile:13: elf] Lỗi 101

Lúc này, xét nội dung của file Makefile và sử dụng hàng hóa để xây dựng, có vấn đề gì với cài đặt hàng hóa không? So sánh ~/App/rCore-Tutorial-v3/user, bạn có thể thấy thư mục .cargo tồn tại trong đó. nó. Trước đây chúng tôi cũng sử dụng thư mục này.

Trong môi trường lập trình Rust, .cargo/config.toml và Cargo.toml đều là các tệp cấu hình, nhưng mỗi tệp chịu trách nhiệm thực hiện các tác vụ khác nhau.

.cargo/config.toml Tệp này được sử dụng để định cấu hình hoạt động của chuỗi công cụ Rust (bao gồm cả Cargo). Nó cho phép người dùng đặt một số tùy chọn chung, chẳng hạn như kiến trúc mục tiêu biên dịch, hành vi mặc định của trình biên dịch (chẳng hạn như có bật thông tin gỡ lỗi, mức tối ưu hóa, v.v.), đường dẫn đến kho riêng, biến môi trường thời gian biên dịch, và nhiều tùy chọn cấu hình nâng cao khác, chẳng hạn như nguồn hình ảnh, lựa chọn chuỗi công cụ, v.v.

Tệp .cargo/config.toml thường nằm trong thư mục .cargo trong thư mục chính của người dùng, nhưng cũng có thể tạo một tệp có cùng tên trong thư mục gốc của dự án để ghi đè cấu hình mặc định sao cho cấu hình sẽ có hiệu lực cho một dự án cụ thể.

Tệp Cargo.toml là tệp kê khai dự án. Nó chứa siêu dữ liệu quan trọng về dự án Rust, bao gồm tên dự án, phiên bản, tác giả, giấy phép và các siêu thông tin khác của dự án và các yêu cầu về phiên bản của chúng; các thành viên của một dự án, tức là các dự án khác thuộc cùng một không gian làm việc; biên dịch các tính năng dành riêng cho cấu hình có thể bật hoặc tắt chức năng bổ sung của các tệp nhị phân, thư viện hoặc mô-đun thử nghiệm đích;

Tóm lại, Cargo.toml mô tả cấu trúc và yêu cầu của dự án, trong khi .cargo/config.toml kiểm soát cách Cargo xử lý các chi tiết khác nhau trong quá trình xây dựng dự án. Mọi dự án Rust đều có tệp Cargo.toml, trong khi .cargo/config.toml là tùy chọn và có thể được đặt trên toàn cầu hoặc ghi đè cục bộ trong thư mục dự án.

Sau đó, chúng tôi xem nội dung của tệp này và thấy rằng trình biên dịch và tệp liên kết đã được chỉ định

[build] target = "riscv64gc-unknown-none-elf" [target.riscv64gc-unknown-none-elf] Rustflags = [ "-Clink-args=-Tsrc/linker.ld", "-Cforce-frame-pointers= Đúng" ]

Chúng tôi tạo /user/.cargo/config.toml trong /user

chạm vào /user/.cargo/config.toml

Copy nội dung trên vào rồi chạy lại make build

error[E0463]: không thể tìm thấy thùng cho `user_lib` --> src/bin/01store_fault.rs:5:1 | 5 | thùng bên ngoài user_lib; ^^ ^^ ^^ ^^ không thể tìm thấy thùng lỗi[E0463]: không thể tìm thấy thùng cho `user_lib` --> src/bin/00hello_world.rs:5:1 | 5 | user_lib thùng bên ngoài; ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ không thể tìm thấy lỗi thùng: không thể tìm thấy macro `println` trong phạm vi này --> src/bin/01store_fault.rs:10:5 | 10 | ^^^^ ^^ | trợ giúp: xem xét việc nhập macro này | 4 + sử dụng user::println; không thể tìm thấy macro `println` trong phạm vi này --> src/bin/01store_fault.rs:9:5 | 9 | println!("Trong phần thử nghiệm store_fault, chúng tôi sẽ chèn một thao tác lưu trữ không hợp lệ..." | ^^^^^ | user::println; | lỗi: yêu cầu chức năng `#[panic_handler]`, nhưng không tìm thấy lỗi: không thể tìm thấy macro `println` trong phạm vi này --> src/bin/00hello_world.rs:9:5 | ("Xin chào thế giới!"); | ^^^^^ | trợ giúp: hãy xem xét việc nhập macro này | 4 + sử dụng user::println; `rustc --explain E0463` lỗi: không thể biên dịch `user` (bin "01store_fault") do 4 lỗi trước đó cảnh báo: quá trình xây dựng không thành công, đang chờ các công việc khác hoàn thành... lỗi: không thể biên dịch `user` ( bin "00hello_world") do 3 lỗi trước đó tạo ra: *** [Makefile:13: elf] Lỗi 101

Lỗi vẫn được báo cáo và người ta thấy rằng gói user_lib được yêu cầu không tồn tại và println là do thiếu sự phụ thuộc này. Hãy kiểm tra tài liệu chính thức tại đây: Thư viện bên ngoài này thực sự là lib.rs trong thư mục người dùng và một số thư mục khác. mô-đun con mà nó tham chiếu. Về lý do tại sao thư viện bên ngoài này được gọi là user_lib thay vì user, tên thư mục chứa lib.rs là do chúng ta đặt tên thư viện trong user/Cargo.toml: name="user_lib". Là thư viện người dùng mà chương trình nguồn trong thư mục bin phụ thuộc vào, nó tương đương với thư viện tiêu chuẩn được cung cấp bởi các ngôn ngữ lập trình khác.

Sau đó, chúng tôi sửa đổi tên trong user/Cargo.toml, name = "user_lib", rồi tạo bản dựng, không có lỗi nào được báo cáo.

Có thể tìm thấy trong ~/workspace/user/target/riscv64gc-unknown-none-elf/release,00hello_world,01store_fault,02power..

Chạy các ứng dụng trước khi triển khai hệ điều hành

Sử dụng qemu-riscv64, bạn có thể thực thi trực tiếp các ứng dụng bằng trình mô phỏng chế độ người dùng mà không cần hệ điều hành.

Chuyển đến thư mục ~/workspace/user/target/riscv64gc-unknown-none-elf/release và thử thực thi qemu-riscv64 ./00hello_world..

Tìm thấy một lỗi

Không tìm thấy lệnh 'qemu-riscv64', nhưng có thể được cài đặt bằng: sudo apt install qemu-user

thử cài đặt

sudo apt cài đặt qemu-người dùng

Sử dụng sau khi cài đặt thành công

qemu-riscv64 ./00hello_world - Xin chào thế giới! qemu-riscv64 ./01store_fault - Vào thử nghiệm store_fault, chúng tôi sẽ chèn một thao tác lưu trữ không hợp lệ... - Kernel sẽ tắt ứng dụng này! - Lỗi phân đoạn (core dumped) qemu-riscv64 . /02power - 3^10000=5079(MOD 10007) - 3^20000=8202(MOD 10007) - 3^30000=8824(MOD 10007) - 3^40000=5750(MOD 10007) - 3^50000=3824(MOD 10007) - 3^60000=8516(MOD 10007) - 3^70000=2510(MOD 10007) - 3^80000=9379(MOD 10007) - 3^90000=2621(MOD 10007) - 3^100000=2749(MOD 10007) - Kiểm tra nguồn OK!

Có thể thấy rằng sau khi thực thi 00hello_world, chế độ người dùng có thể thực thi bình thường. Sau khi thực thi 01store_fault, đã xảy ra lỗi do cố gắng ghi 0 vào con trỏ null.

Sau khi thực thi 02power, các phép tính ở chế độ người dùng và println! được chạy và println! thực sự gọi write và syscall. Có thể thấy rằng việc chuyển đổi lặp lại giữa chế độ người dùng và chế độ kernel cũng khả thi.

Theo mô tả trong tài liệu chính thức, có hai ứng dụng khác có thể được biên dịch và chạy: 03priv_inst và 04priv_csr, lần lượt xác minh hai tình huống "lý do tại sao các ứng dụng ở chế độ người dùng trực tiếp kích hoạt ngoại lệ từ chế độ người dùng sang chế độ kernel"

  1. Bản thân lệnh này là lệnh có mức đặc quyền cao, chẳng hạn như sret Lệnh (biểu thị việc quay lại từ chế độ S sang chế độ U)
  2. Lệnh truy cập vào một thanh ghi hoặc bộ nhớ chỉ có thể được truy cập ở mức đặc quyền của chế độ S, chẳng hạn như một thanh ghi hoặc bộ nhớ đại diện cho trạng thái hệ thống của chế độ S. thanh ghi trạng thái điều khiển trạng thái Chờ đợi

Lúc này cũng sao chép mã nguồn của 2 ứng dụng này.

cp 03priv_inst.rs ~/workspace/user/src/bin/ cp 04priv_csr.rs ~/workspace/user/src/bin/

Biên dịch tạo và chạy

cd ~/workspace/user make build cd ~/workspace/user/target/riscv64gc-unknown-none-elf/release qemu-riscv64 03priv_inst - Cố gắng thực thi lệnh đặc quyền ở Chế độ U - Kernel nên tắt ứng dụng này! - Hướng dẫn bất hợp pháp ( core dumped) qemu-riscv64 04priv_csr - Cố gắng truy cập CSR đặc quyền ở Chế độ U - Kernel sẽ bị hủy ứng dụng này! - Hướng dẫn bất hợp pháp (kết xuất lõi)

Cuối cùng, bài viết này về việc triển khai các ứng dụng [rCore Study Notes 016] kết thúc tại đây. Nếu bạn muốn biết thêm về việc triển khai các ứng dụng [rCore Study Notes 016], vui lòng tìm kiếm các bài viết về CFSDN hoặc tiếp tục duyệt các bài viết liên quan. Tôi hy vọng tất cả các bạn sẽ ủng hộ tôi. blog trong tương lai! .

57 4 0
Chứng chỉ ICP Bắc Kinh số 000000
Hợp tác quảng cáo: 1813099741@qq.com 6ren.com
Xem sitemap của VNExpress