• Rust嵌入式编程---panic处理和异常处理


    panic处理

    我们之前学习过panic,这个部分对于Rust是非常重要的,例如索引等内置操作在运行时进行内存安全检查,当尝试超出边界索引时,这会导致恐慌。在标准库(Std)中,panic有一个定义的行为:它展开 panic 线程的堆栈,除非用户选择在 panic 时中止程序,否则会进行错误位置的一个堆栈定位。
    但是,在没有标准库的程序中,恐慌行为是未定义的。可以通过声明函数来选择行为。此函数必须在程序的依赖关系图中只出现一次,并且必须具有以下签名:,其中 PanicInfo 是包含有关 panic 位置的信息的结构。#[panic_handler] fn(&PanicInfo) -> !
    对于panic,有如下的一些常用种类:
    1.panic_halt:死机会导致程序或当前线程通过进入无限循环而停止。
    2.panic_abort:紧急导致中止指令被执行。
    3.panic_itm:使用 ITM(一种特定于 ARM cortex-M 的外设)记录死机消息。
    4.panic_semihosting:使用半主机技术将死机消息记录到主机,这个常用在qemu中,可以打印出错误信息。
    这些panic的使用方法如下:
    use panic_halt as _
    use panic_semihosting as _
    如果不重命名,编译器会警告说你有一个未使用的导入,当然不是错误,可以编译通过。
    下面通过一个例子来理解一下,panic的使用方法,是一个数组访问越界的一个错误。

    #![no_main]
    #![no_std]
    
    use panic_semihosting as _;
    use cortex_m_rt::entry;
    #[entry]
    fn main() -> ! {
        let xs = [0, 1, 2];
        let i = xs.len() + 1;
        let _y = xs[i]; //访问越界,加下划线变量就是说未使用不警告。
        loop {}
    }
    

    出现以下运行结果,用的是semihosting,会打印输出在这里插入图片描述
    这里可以看到,对于panic,是程序运行时自动判断的,整个代码也就加了个semihosting的处理,使用起来很方便。你也可以试试其他类型的panic,观察结果的不同,其实也就是不同的panic处理方式。

    异常处理

    异常和中断是处理器处理异步事件和致命错误(例如执行无效指令)的硬件机制。异常意味着抢占,并涉及异常处理程序,即响应触发事件的信号而执行的子例程,这就是和。是硬件上的,而panic理解成软件上的,比如访问文件不存在啊、访问越界啊、除0操作等等,而且panic一般没有那么多处理方式,没有抢占和触发panic处理程序等等。cortex-m-rt crate 库提供了一个异常属性标志#[exception]用来声明一个异常处理。
    SysTick是系统定时器,能产生周期性的中断。每当中断产生时,就执行处理函数SysTick()。系统定时器要能够产生定时中断,就要先初始化,设置时钟源,设置计数值,启动计数器,开启中断。
    详细代码如下:

    #![deny(unsafe_code)]
    #![no_main]
    #![no_std]
    
    use panic_halt as _;
    use core::fmt::Write;
    
    use cortex_m::peripheral::syst::SystClkSource;
    use cortex_m_rt::{entry, exception};
    use cortex_m_semihosting::{
        debug,
        hio::{self, HStdout},
    };
    
    #[entry]
    fn main() -> ! {
        let p = cortex_m::Peripherals::take().unwrap();
        let mut syst = p.SYST;
    
        //将系统计时器配置为每秒触发一次SysTick异常
        syst.set_clock_source(SystClkSource::Core);
        syst.set_reload(12_000_000);//周期 = 1s
        syst.clear_current();
        syst.enable_counter();//使能systick计数器
        syst.enable_interrupt();//使能中断
        loop {}
    }
    
    #[exception]
    fn SysTick() {
        static mut COUNT: u32 = 0;
        static mut STDOUT: Option<HStdout> = None;
    
        *COUNT += 1;
    
        // Lazy initialization
        if STDOUT.is_none() {
            *STDOUT = hio::hstdout().ok();
        }
    
        if let Some(hstdout) = STDOUT.as_mut() {
            write!(hstdout, "{}", *COUNT).ok();
        }
        if *COUNT == 9 {
           //qemu中运行到第九秒暂停退出
            debug::exit(debug::EXIT_SUCCESS);
        }
    }
    
    

    终端打印123456789后退出。

    默认异常处理程序

    该属性的实际作用是覆盖特定异常的默认异常处理程序。如果不重写特定异常的处理程序,则该函数将由函数处理,该函数默认为:exceptionDefaultHandler

    fn DefaultHandler() {
        loop {}
    }
    

    你可以在“默认处理程序”上放置断点并捕获未处理的异常,当然也可以重写和覆盖。

    硬故障处理程序

    当程序进入无效状态时,将触发此异常,因此其处理程序无法返回,因为这可能导致未定义的行为。此外,运行时crate在调用用户定义的处理程序之前会执行一些工作,以提高可调试性。处理程序的参数是指向由异常推送到堆栈中的寄存器的指针。这些寄存器是触发异常时处理器状态的快照,可用于诊断硬故障。HardFaultfn(&ExceptionFrame) -> !
    下面是一个执行非法操作的示例:读取不存在的内存位置。该程序在QEMU上不起作用,即它不会崩溃,因为不会检查内存负载,并且会在读取无效内存时返回。

    #![no_main]
    #![no_std]
    
    use panic_halt as _;
    use core::fmt::Write;
    use core::ptr;
    use cortex_m_rt::{entry, exception, ExceptionFrame};
    use cortex_m_semihosting::hio;
    
    #[entry]
    fn main() -> ! {
        // 读取不存在的内存地址,注意写法
        unsafe {
            ptr::read_volatile(0x3FFF_FFFE as *const u32);
        }
        loop {}
    }
    
    #[exception]
    fn HardFault(ef: &ExceptionFrame) -> ! {
        if let Ok(mut hstdout) = hio::hstdout() {
            writeln!(hstdout, "{:#?}", ef).ok();
        }
        loop {}
    }
    

    处理程序打印值。如果运行此命令,您将在 OpenOCD 控制台上看到类似的内容。
    在这里插入图片描述
    该值是发生异常时程序计数器的值,它指向触发异常的指令,所以可以看一下pc的值指向的指令就是错误的地方。
    在这里插入图片描述
    这里可以看到,是因为ldr指令出现错误,也就是加载读取指令。

  • 相关阅读:
    docker部署Golang程序
    DNSPod十问董文辉:我们为什么要拥抱云原生安全?
    Cy3/5/7标记多肽/PEG/聚合物/磷脂----为华生物
    java-net-php-python-jsp校园招聘管理系统计算机毕业设计程序
    【STL】string各种函数的应用
    一种解决问题E: Unable to locate package python-vcstool的方法
    JTS:04 读取数据库数据
    MNE绘制自定义通道位置及名字的脑地形图
    Http协议
    112.HBase Endpoint类型的Coprocessor开发与部署
  • 原文地址:https://blog.csdn.net/phthon1997/article/details/127101677