بیایید یک درایور UART بنویسیم
این ماشین QEMU 'virt' یک PL011 بهعنوان UART دارد، پس بیایید یک درایور برای آن بنویسیم.
const FLAG_REGISTER_OFFSET: usize = 0x18; const FR_BUSY: u8 = 1 << 3; const FR_TXFF: u8 = 1 << 5; /// Minimal driver for a PL011 UART. #[derive(Debug)] pub struct Uart { base_address: *mut u8, } impl Uart { /// Constructs a new instance of the UART driver for a PL011 device at the /// given base address. /// /// # Safety /// /// The given base address must point to the 8 MMIO control registers of a /// PL011 device, which must be mapped into the address space of the process /// as device memory and not have any other aliases. pub unsafe fn new(base_address: *mut u8) -> Self { Self { base_address } } /// Writes a single byte to the UART. pub fn write_byte(&self, byte: u8) { // Wait until there is room in the TX buffer. while self.read_flag_register() & FR_TXFF != 0 {} // SAFETY: We know that the base address points to the control // registers of a PL011 device which is appropriately mapped. unsafe { // Write to the TX buffer. self.base_address.write_volatile(byte); } // Wait until the UART is no longer busy. while self.read_flag_register() & FR_BUSY != 0 {} } fn read_flag_register(&self) -> u8 { // SAFETY: We know that the base address points to the control // registers of a PL011 device which is appropriately mapped. unsafe { self.base_address.add(FLAG_REGISTER_OFFSET).read_volatile() } } }
- توجه داشته باشید که
Uart::new
ناامن یا unsafe است در حالی که متدهای دیگر ایمن هستند. این بهخاطر این است که تا زمانی که تماس گیرندهUart::new
تضمین کند که الزامات ایمنی آن برآورده شده است (یعنی فقط یک نمونه از درایور برای یک UART مشخص وجود دارد و هیچ چیز دیگری نام مستعار فضای آدرس آن را ندارد)، پس همیشه میتوانwrite_byte
را بعداً فراخوانی کرد زیرا میتوانیم پیششرطهای لازم را فرض کنیم. - ما میتوانستیم این کار را به صورت دیگری انجام دهیم ( ساخت
new
را ایمن کنیم، اماwrite_byte
را ناامن کنیم)، اما استفاده از آن بسیار راحتتر خواهد بود، زیرا هر مکانی کهwrite_byte
را صدا میزند باید در مورد ایمنی یا safety استدلال کند. - This is a common pattern for writing safe wrappers of unsafe code: moving the burden of proof for soundness from a large number of places to a smaller number of places.