linux reboot flow

本文深入解析Linux内核重启机制,包括机器描述表、初始化过程、核心重启函数及其工作流程,详细解释了如何通过板级配置文件(如vmlinux.lds.S)定义机器描述,并阐述了在内核重启过程中,从调用reboot函数到系统关闭的整个流程,以及设备驱动、系统核心服务如何响应并执行设备卸载、系统清理等操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1,  little machine_desc struture

1-1, first eye on arch/arm/include/asm/mach/arch.h

/*
 * Machine type table - also only accessible during boot
 */
extern const struct machine_desc __arch_info_begin[], __arch_info_end[];
#define for_each_machine_desc(p)                        \
        for (p = __arch_info_begin; p < __arch_info_end; p++)

#define DT_MACHINE_START(_name, _namestr)               \
static const struct machine_desc __mach_desc_##_name    \
 __used                                                 \
 __attribute__((__section__(".arch.info.init"))) = {    \
        .nr             = ~0,                           \
        .name           = _namestr,

#endif

1-2, put machine_desc variable in arch.info.init section, whose begining is __arch_info_begin and ending is __arch_info_end. they defined at file arch/arm/kernel/vmlinux.lds.S

.init.arch.info : {
                __arch_info_begin = .;
                *(.arch.info.init)
                __arch_info_end = .;
        }
1-3,  grep "DT_MACHINE_START"  will help you find your board's related machine_desc, which will stored at section: arch.info.init, something like:

static const char *xxx_dt_match[] __initconst = {
        "xxx,yyyy",
        NULL
};

DT_MACHINE_START(xxx_DT, "Generic xxx (Flattened Device Tree)")
        .smp            = smp_ops(xxx_smp_ops),
        .map_io         = xxx_map_io,
        .init_machine   = xxx_init_mach,
        .init_late      = xxx_init_late,
        .dt_compat      = xxx_dt_match,
        .restart = xxxx_xxx_restart,
MACHINE_END

2,  who use the machine_desc variable

arch/arm/kernel/setup.c

start_kernel -> setup_arch -> setup_machine_fdt -> of_flat_dt_match_machine(mdesc_best, arch_get_next_mach) 

static const void * __init arch_get_next_mach(const char *const **match)
{
        static const struct machine_desc *mdesc = __arch_info_begin;
        const struct machine_desc *m = mdesc;

        if (m >= __arch_info_end)
                return NULL;

        mdesc++;
        *match = m->dt_compat;
        return m;
}

at last setup_machine_fdt will return a machine_desc variable at last which is your board used and assign arm_pm_restart at last by the follow statement:

if (mdesc->restart)
                arm_pm_restart = mdesc->restart;


3, what flow reboot will go?

in kernel/reboot.c there is a function:

SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
                void __user *, arg)
there is switch case:
switch (cmd) {
        case LINUX_REBOOT_CMD_RESTART:
                kernel_restart(NULL);
                break;

void kernel_restart(char *cmd)
{
        kernel_restart_prepare(cmd);
        migrate_to_reboot_cpu();
        syscore_shutdown();
        if (!cmd)
                pr_emerg("Restarting system\n");
        else
                pr_emerg("Restarting system with command '%s'\n", cmd);
        kmsg_dump(KMSG_DUMP_RESTART);
        machine_restart(cmd);
}

void kernel_restart_prepare(char *cmd)
{
        blocking_notifier_call_chain(&reboot_notifier_list, SYS_RESTART, cmd); // notify event of SYS_RESTART, others like "systemd" will capture it do some process.
        system_state = SYSTEM_RESTART;
        usermodehelper_disable();
        pr_info("%s will call device shutdown\n", __func__);
        device_shutdown();// device's shut down will be called
}

/**
 * syscore_shutdown - Execute all the registered system core shutdown callbacks.
 */
void syscore_shutdown(void)
{
        struct syscore_ops *ops;

        mutex_lock(&syscore_ops_lock);

        list_for_each_entry_reverse(ops, &syscore_ops_list, node)
                if (ops->shutdown) {
                        if (initcall_debug)
                                pr_info("PM: Calling %pF\n", ops->shutdown);
                        ops->shutdown();// registered by register_syscore_ops
                        pr_info("%s\n", __func__);
                }

        mutex_unlock(&syscore_ops_lock);
}

/*
 * Restart requires that the secondary CPUs stop performing any activity
 * while the primary CPU resets the system. Systems with a single CPU can
 * use soft_restart() as their machine descriptor's .restart hook, since that
 * will cause the only available CPU to reset. Systems with multiple CPUs must
 * provide a HW restart implementation, to ensure that all CPUs reset at once.
 * This is required so that any code running after reset on the primary CPU
 * doesn't have to co-ordinate with other CPUs to ensure they aren't still
 * executing pre-reset code, and using RAM that the primary CPU's code wishes
 * to use. Implementing such co-ordination would be essentially impossible.
 */
void machine_restart(char *cmd)
{
        local_irq_disable();
        smp_send_stop();


        /* Flush the console to make sure all the relevant messages make it
         * out to the console drivers */
        arm_machine_flush_console();

        if (arm_pm_restart) {
                pr_info("%s arm_pm_restart\n", __func__);
                arm_pm_restart(reboot_mode, cmd);
        } else {
                pr_info("%s do_kernel_restart\n", __func__);
                do_kernel_restart(cmd);
        }
        /* Give a grace period for failure to restart of 1s */
        mdelay(1000);

        /* Whoops - the platform was unable to reboot. Tell the user! */
        printk("Reboot failed -- System halted\n");
        local_irq_disable();
        while (1);
}


4, test result flow

[  148.550000] systemd-shutdown[1]: All DM devices detached.
[  148.560000] systemd-shutdown[1]: Rebooting.
[  148.560000] reboot: SYSC_reboot LINUX_REBOOT_CMD_RESTART //SYSCALL_DEFINE4(reboot
[  148.570000] reboot: kernel_restart_prepare will call device shutdown //kernel_restart_prepare
[  148.580000] mmc mmc3:0001: shutdown //device_shutdown
[  148.580000] mmcblk mmc1:1234: shutdown
[  148.580000] xxx_tsc 10d80000.adc:tsc: shutdown
[  148.710000] i2c i2c-2: shutdown
[  148.710000] i2c i2c-1: shutdown
[  148.710000] xxx_vdss sirfsoc_vdss.0: shutdown
[  148.710000] pca954x 0-0070: shutdown
[  148.720000] i2c 0-0045: shutdown
[  148.720000] i2c 0-0049: shutdown
[  148.720000] i2c 0-004b: shutdown
[  148.730000] i2c 0-0038: shutdown
[  148.730000] sx150x 0-0020: shutdown
[  148.730000] i2c i2c-0: shutdown
[  148.740000] pwm-backlight sirf-backlight: shutdown
[  148.740000] pvrsrvkm 12000000.graphics: shutdown
[  148.750000] xxx-wdt 10dc0000.timer: shutdown //device_shutdown
[  148.750000] syscore_shutdown //syscore_shutdown
[  148.750000] reboot: Restarting system
[  148.760000] machine_restart arm_pm_restart // machine_restart

<think>好的,用户现在问的是如何在Linux下使用minicom查看STM32F4发送的“abc”。之前他问过如何在代码里修改以发送“abc”,现在需要实际操作接收部分。首先,我需要回想之前给出的方案,确保这里的回答和之前的一致。比如,用户可能已经按照之前的三种方法修改了代码,现在需要验证是否正确发送。 接下来,我需要考虑用户可能的环境。他使用的是Linux系统,所以需要指导安装minicom,配置串口,连接STM32F4的UART到电脑,可能通过USB转TTL模块。用户可能对Linux下的串口设备名称不太熟悉,比如/dev/ttyUSB0或/dev/ttyACM0,需要解释如何确认设备。 然后,需要指导用户设置minicom的参数,比如波特率、数据位、停止位、校验位,这些必须和STM32的配置一致。之前的代码里用的是115200的波特率,所以这里要提醒用户检查是否匹配。同时,用户可能遇到权限问题,无法访问串口设备,所以要提醒使用sudo或者修改用户组。 另外,用户可能在连接硬件时有误,比如RX/TX线接反,或者没有共地,需要指出常见的硬件问题。在测试时,如果minicom没有显示,可能需要检查代码是否正确发送了换行符,比如之前的例子中添加了\r\n,这样在终端上才能正确换行显示。 还要考虑用户可能的调试步骤,比如用示波器或逻辑分析仪检查信号,或者使用其他工具如screen或stty验证串口通信。可能用户没有正确烧录程序到STM32,所以需要提醒重新编译和下载固件。此外,如果用户使用了不同的UART实例(比如UART1或其他),需要确保minicom连接的串口对应正确的引脚。 最后,总结步骤,确保用户能一步步操作,并解决常见问题。需要以清晰的结构呈现,分步骤说明安装、配置、连接、测试和调试,让用户能够跟随操作。同时,强调关键点,如波特率一致,硬件连接正确,权限问题,这些都是新手容易出错的地方。</think>在 Linux 系统下使用 `minicom` 查看 STM32F4 发送的 "abc",需完成以下关键步骤: --- ### **一、硬件准备** 1. **连接方式** ```bash STM32F4 UART_TX → USB转TTL模块的RX STM32F4 GND ↔ USB转TTL模块的GND ``` - 推荐使用 CH340/CP2102/FT232 等 USB 转串口芯片模块 - 确认开发板 UART 引脚(如 USART1 的 PA9(TX)/PA10(RX)) 2. **设备识别** 插入 USB 后执行: ```bash dmesg | tail # 查看新识别的串口设备(如 /dev/ttyUSB0) ls /dev/tty* # 确认设备存在 ``` --- ### **二、软件配置** #### 1. 安装 minicom ```bash sudo apt install minicom # Ubuntu/Debian sudo yum install minicom # CentOS/RHEL ``` #### 2. 配置串口参数(与STM32代码一致) ```bash sudo minicom -s # 进入配置模式 ``` 按以下顺序设置: ``` Serial port setup → Serial Device: /dev/ttyUSB0 Bps/Par/Bits: 115200 8N1 Hardware Flow Control: No Software Flow Control: No 保存为默认配置(Save setup as dfl) ``` --- ### **三、关键操作流程** #### 1. 启动监听 ```bash sudo minicom -D /dev/ttyUSB0 -b 115200 # 手动指定设备及波特率 ``` #### 2. 验证通信(STM32端需满足): - **代码配置正确性**(参考你的发送代码): ```c // 确保波特率匹配(如 115200) huart1.Init.BaudRate = 115200; // 发送时包含回车换行符 HAL_UART_Transmit(&huart1, (uint8_t*)"abc\r\n", 5, 1000); ``` #### 3. 观测结果: - 成功时 minicom 窗口显示连续 `abc` - 无显示时按 `Ctrl+A → Z → C` 清屏后再试 --- ### **四、故障排查指南** #### 1. 硬件问题 | 现象 | 检查点 | |-----------------------|--------------------------------| | 无任何数据 | RX/TX 线序是否反接 | | 乱码 | 波特率误差(示波器测量实际速率)| | 间歇性断连 | USB 供电不足(外接电源) | #### 2. 权限问题 ```bash sudo usermod -aG dialout $USER # 将当前用户加入串口组 reboot # 重启生效 ``` #### 3. 高级调试 ```bash # 1. 使用 stty 验证端口配置 stty -F /dev/ttyUSB0 -a # 2. 十六进制原始数据查看 sudo cat /dev/ttyUSB0 | hexdump -C # 3. 替代工具测试 sudo screen /dev/ttyUSB0 115200 ``` --- ### **五、最佳实践建议** 1. **代码优化**: 添加发送完成回调函数,确保数据完整: ```c void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) { if(huart == &huart1) { HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin); // LED指示发送完成 } } ``` 2. **实时监控**: 结合 `tmux` 分屏,持续监视串口: ```bash tmux new-session -s uart_monitor tmux split-window -h "sudo minicom -D /dev/ttyUSB0 -b 115200" ``` --- **最终效果**:正确配置后,minicom 将实时显示 STM32 周期性发送的 `abc` 字符串,波特率误差应小于 2%(可通过逻辑分析仪验证时序)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值