制作Linux映像和Kernel的启动

十七.linux开发之Kernel移植——内核的启动过程分析 有道云笔记地址: 详情看这里链接,记录太多,就不一一排版了。 http://note.youdao.com/noteshare?id=e68caf68c259bc3491dd7f8c4ac4bb99&sub=435C54F4A4C444B995888BCC71CDB607 2.6.35.7版本kernel。 参考:http://blog.chinaunix.net/uid-26... 阅读详情

进入bootm命令分析之前,先来看看README里面的几段话,简单翻译之

制作Linux映像
============

使用uboot时,内核通常生成的文件"zImage"或"bzImage"是没用的。较新一些的内核原码会
生成"uImage", 这个可以为uboot使用。
"uImage"全用了一个工具"tools/mkimage"来封装压缩后的映像文件,在其头部添加一
些信息以及crc32校验等。

我们需要做如下几件事情:

* 制作一个标准的内核映像文件"vmlinux"(这个是ELF格式的)
* 将其转换为二进制映像
        arm-linux-objcopy -O binarty \
                -R .note -R .comment \
                -S vmlinux linux.bin

* 压缩这个二进制映像
        gzip -9 linux.bin
* 封装这个已压缩的映像
        mkimage -A arm -O linux -T kernel -C gzip \
                -a 0 -e 0 -n "Linux Kernel Image" \
                -d linux.bin.gz uImage

For i.MX51 BBG Board:
    $ ~/myandroid/bootable/bootloader/uboot-imx/tools/mkimage -A arm -O linux -T kernel -C none -a 0x90008000 -e 0x90008000 -n "Android Linux Kernel" -d ./zImage ./uImage

    During boot, when uboot try to load above "uImage", it will know to load it (without image head added by mkimage tool) into 0x90008000 (specified by "-a"), and then jump to 0x90008000  (specified by "-e") to execute it. "-C none" means no compression when generating "uImage". This is because the original zImage is already a compressed one.


mkimage 也可以用来制作ramdisk映像。
mkimage 在映像的头部添加了64字节的信息,用来指明映像文件用于的体系结构,操作系统, 映像类型, 压缩方式,入口地址,时间戳,crc32校验等。

mkimage 一般用于两种情况: 为映像添加头信息 和 列出文件的头信息
         tools/mkimage -l image
             -l ==> 列出映像的头信息

添加头信息时
    tools/mkimage -A arch -O os -T type -C comp -a addr -e ep \
              -n name -d data_file image
      -A ==> set architecture to 'arch' 
体系结构
      -O ==> set operating system to 'os' 
操作系统
      -T ==> set image type to 'type' 
映像类型
      -C ==> set compression type 'comp' 
压缩方式
      -a ==> set load address to 'addr' (hex) 
加载地址
      -e ==> set entry point to 'ep' (hex) 
入口地址
      -n ==> set image name to 'name' 
映像文件名
      -d ==> use image data from 'datafile' 
制作映像的时间

----------------------------------------------
bootm
=====
这个命令用于启动一个操作系统映像。它会从映像文件的头部取得一些信息,这些信息包括:
映像文件的基于的cpu架构、其操作系统类型、映像的类型、压缩方式、映像文件在内存中的加载地址、
映像文件运行的入口地址、映像文件名等。
紧接着bootm将映像加载到指定的地址,如果需要的话,还会解压映像并传递必要有参数给内核,最后
跳到入口地址进入内核。

bootm的第一个参数是映像存储的地址。

例如Linux,可以附带一个参数。此参数会被认为是一个initrd的起始地址,此时bootm命令有三个
步骤:首先解压Linux内核映像并将其复制到ram中,然后将ramdisk映像加载到ram中,最后将控制
权交给内核,并向内核传递ramdisk的位置和大小等信息.

单单用来启动Linux内核,而没有initrd时,可用如下命令:
 => bootm ${kernel_addr}

如果还有initrd,则可使用下面的命令:
 => bootm ${kernel_addr} ${ramdisk_addr}

使用时确保地址参数正确。

当待启动的映像文件已经被加载于RAM时(例如用tftp下载到sdram上),需要对内存部局更加小心。
需要确保映像文件(可能是已被压缩的映像文件)加载的地址不会与解压后的内核位置重叠。例如,如果
将一个ramdisk映像加载于内存的低地址,则在Linux内核加载时可能会覆盖它。这将导致未知的系统
崩溃。

上面几段是README里的内容,介绍了内核映像的制作及bootm命令。下面来看具体代码



do_bootm

static bootm_headers_t images; 这个静态全局变量就是头信息结构了。注意这里是
bootm_headers_t而不是image_header_t.


typedef struct bootm_headers {
    image_header_t    *legacy_hdr_os;        /* image header pointer */
    image_header_t    legacy_hdr_os_copy;    /* header copy */
    ulong        legacy_hdr_valid;
    int        verify;        /* getenv("verify")[0] != 'n' */
    struct lmb    *lmb;        /* for memory mgmt */
} bootm_headers_t;


    mem_start = getenv_bootm_low(); //获得内存sdram的起始地址
1. 取环境变量中的boom_low
2. 没有boom_low,取则配置时的CFG_SDRAM_BASE
3. 没有CFG_SDRAM_BASE则取 gd->bd->bi_dram[0].start
4. 若还没有,则取 0

    mem_size = getenv_bootm_size(); //获得内存sdram的大小
1. 取环境变量中的bootm_size
2. 若没有bootm_size,则取 gd->bd->bi_dram[0].size


    lmb_add(&lmb, (phys_addr_t)mem_start, mem_size);
//

os_hdr = boot_get_kernel (cmdtp, flag, argc, argv,
            &images, &os_data, &os_len);

/******************************************************************************
******* 进入   boot_get_kernel       ********************************************
*******************************************************************************/



    if (argc < 2) {
        img_addr = load_addr;
        debug ("*  kernel: default image load address = 0x%08lx\n",
                load_addr);
    } else {
        img_addr = simple_strtoul(argv[1], NULL, 16);
        debug ("*  kernel: cmdline image address = 0x%08lx\n", img_addr);
    }

//argc < 2, 即直接输入bootm命令,此时映像存储地址为load_addr; 定义处有
//ulong load_addr = CFG_LOAD_ADDR; 即初始的映像存储地址,但在u-boot运行期间,load_addr的
//值会被环境变量所更改。
//若bootm后面还带了一参数,如bootm 30000,则映像的存储地址为0x30000
//接下来要从nand中读出kernel image时,就从地址0x3000开始读

    img_addr = genimg_get_image (img_addr);

// 从存储介质中将image读出,img_addr是前面获得的映像存储地址。这个函数体内被CONFIG_HAS_DATAFLASH
// 包围,是指kernel存于atmel的数据flash中.由于我这里只有nand,所以这个函数相当于空。
// 在运行bootm命令之前,一般使用tftp or nand read.jffs2 将kernel image 先读到sdram中,所以此时
// kernel image 已经在sdram中了。

genimg_get_format ((void *)img_addr)) 

//判断imgae的格式,通过判断头信息中的幻数来确断格式,格式有三种
// IMAGE_FORMAT_LEGACY
// IMAGE_FORMAT_FIT   这里不支持,在预定义里就没有定义 CONFIG_FIT这项
// IMAGE_FORMAT_INVALID 无效的格式

hdr = image_get_kernel (img_addr, images->verify);
// 检查头信息,函数内做了以下几件事
// image_check_magic 再次检查幻数
// image_check_hcrc  头信息校验
// image_print_contents (hdr); 打印头信息
// image_check_dcrc 数据校验,这里是校验kernel实际的数据
// image_check_target_arch 检查运行的cpu架构


switch (image_get_type (hdr)) {
        case IH_TYPE_KERNEL:
            *os_data = image_get_data (hdr);
            *os_len = image_get_data_size (hdr);
            break;
        case IH_TYPE_MULTI:
            image_multi_getimg (hdr, 0, os_data, os_len);
            break;
                default:
            printf ("Wrong Image Type for %s command\n", cmdtp->name);
            show_boot_progress (-5);
            return NULL;
        }

// 这里先检查映像的类型,有kernel, ramdisk, multi, firmware, script等,
// 根据不同的类型来获得真正的image data(即不包括头信息的imgae)的起始地址(os_data) 和大小(os_len)

memmove (&images->legacy_hdr_os_copy, hdr, sizeof(image_header_t));

// 再次来看下images结构

typedef struct bootm_headers {
    image_header_t    *legacy_hdr_os;        /* image header pointer */
    image_header_t    legacy_hdr_os_copy;    /* header copy */
    ulong        legacy_hdr_valid;
    int        verify;        /* getenv("verify")[0] != 'n' */
    struct lmb    *lmb;        /* for memory mgmt */
} bootm_headers_t;

// 可知上面的memmove实际上是将头信息都保存到images的 legacy_hdr_os_copy成员中

        images->legacy_hdr_os = hdr;
// 保存kernel image头信息的地址

        images->legacy_hdr_valid = 1;
// 映像文件是有效的

return (void *)img_addr;
//最后返回映像文件的地址


/******************************************************************************
******* 退出   boot_get_kernel       ********************************************
*******************************************************************************/
//小结:从上面的分析可以看出 boot_get_kernel 的作用就是找到kernel image,并通过头信息的检查
//来判断其是否有效。最后返回kernel image的地址给do_bootm。


// 继续do_bootm中

    switch (genimg_get_format (os_hdr)) { //boot_get_kernel中做过一次,检查幻数,判断image
    case IMAGE_FORMAT_LEGACY:
        type = image_get_type (os_hdr); 
// 映像类型
        comp = image_get_comp (os_hdr); 
// 压缩方式
        os = image_get_os (os_hdr);   
// 操作系统类型

        image_end = image_get_image_end (os_hdr); 
// 结束地址
        load_start = image_get_load (os_hdr); 
// 头信息中定义的加载地址
        break;

    image_start = (ulong)os_hdr; 
// 映像起始地址,包括头信息
    load_end = 0; 
    type_name = genimg_get_type_name (type); 
// 映像名


    iflag = disable_interrupts(); 
// 关中断,这里为空,中断未使能


switch (comp) { 
//根据压缩方式来选择将要执行的动作
    case IH_COMP_NONE: // image
未压缩
        if (load_start == (ulong)os_hdr) { 
// 头信息中的加载地址是否等于现在所在的地址
            printf ("   XIP %s ... ", type_name);
        } else {
            printf ("   Loading %s ... ", type_name);

            memmove_wd ((void *)load_start, 
// 若不等的话,还要先将image复制到
                   (void *)os_data, os_len, CHUNKSZ); 
// 头信息指定的加载地址处
        }
        load_end = load_start + os_len; 
// 加载后,整个image的结束地址
        puts("OK\n");
        break;
    case IH_COMP_GZIP: // gzip
的压缩方式
        printf ("   Uncompressing %s ... ", type_name);
        if (gunzip ((void *)load_start, unc_len,   
 // 先将image全部解压到头信息中指定
                    (uchar *)os_data, &os_len) != 0) { 
//的加载地址处
            puts ("GUNZIP: uncompress or overwrite error "
                "- must RESET board to recover\n");
            show_boot_progress (-6);
            do_reset (cmdtp, flag, argc, argv);
        }

        load_end = load_start + os_len; // 
加载后,整个image的结束地址
        break;



if ((load_start < image_end) && (load_end > image_start)) {
        if (images.legacy_hdr_valid) {
            if (image_get_type (&images.legacy_hdr_os_copy) == IH_TYPE_MULTI)
                puts ("WARNING: legacy format multi component "
                    "image overwritten\n");
        } else {
            puts ("ERROR: new format image overwritten - "
                "must RESET the board to recover\n");
            show_boot_progress (-113);
            do_reset (cmdtp, flag, argc, argv);
        }
    }

// load_start load_end 是头信息中预定的
// image_start image_end 
是映像文件在内存中放的位置
// 
这里主要判断存放地址和加载地址是否有重叠,因为重叠会造成未知的系统崩溃


    lmb_reserve(&lmb, load_start, (load_end - load_start));

switch (os) {
    default:            /* handled by (original) Linux case */
    case IH_OS_LINUX:
        do_bootm_linux (cmdtp, flag, argc, argv, &images);
        break;

    case IH_OS_NETBSD:
        do_bootm_netbsd (cmdtp, flag, argc, argv, &images);
        break;

    case IH_OS_RTEMS:
        do_bootm_rtems (cmdtp, flag, argc, argv, &images);
        break;
}

// 上面可以很清楚的看到,根据头信息中定义的操作系统类型,进入相应的入口中
// 这里Linux就是 do_bootm_linux(cmdtp, flag, argc, argv, &images)



do_bootm_linux() 位于 lib_arm/bootm.c 中

void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],
             bootm_headers_t *images)
{
    ulong    initrd_start, initrd_end;
    ulong    ep = 0;
    bd_t    *bd = gd->bd;
    char    *s;
    int    machid = bd->bi_arch_number;
    void    (*theKernel)(int zero, int arch, uint params);
    int    ret;

#ifdef CONFIG_CMDLINE_TAG
    char *commandline = getenv ("bootargs"); 
// 启动时的命令行参数
#endif

    /* find kernel entry point */
    if (images->legacy_hdr_valid) {
        ep = image_get_ep (&images->legacy_hdr_os_copy); 
               
 // 获得内核入口地址,即头地址+0x40, 跳过头信息
    } else {

        puts ("Could not find kernel entry point!\n");
        goto error;
    }
    theKernel = (void (*)(int, int, uint))ep; 
// 终于看到theKernel了,给函数指针赋地址值

    s = getenv ("machid"); 
// 目标板的ID, 如smdk2410为193
    if (s) {
        machid = simple_strtoul (s, NULL, 16);
        printf ("Using machid 0x%x from environment\n", machid);
    }

    // 
检查是否还有ramdisk,如果有的话,则将其加载到指定地址
    ret = boot_get_ramdisk (argc, argv, images, IH_ARCH_ARM,
            &initrd_start, &initrd_end);
    if (ret)
// 只有当有ramdisk,且加载ramdisk失败时才返回1
        goto error;

    // taglist 
向内核传递的参数的设置
    // 
这里先去掉预编译,加载memory 和 command line 参数

    setup_start_tag (bd);
    
    setup_memory_tags (bd);
    setup_commandline_tag (bd, commandline);

    setup_end_tag (bd);

    /* we assume that the kernel is in place */
    printf ("\nStarting kernel ...\n\n");

    /*
     * 
在进入内核前要做几件事
     * 
关中断 关I/D-cache 
     */

    cleanup_before_linux ();

    /*
     * 
进入内核,注意传递的几个参数
     * 1. 0
     * 2. mach id 
     * 3. 
参数taglist地址
     */

    theKernel (0, machid, bd->bi_boot_params);
    /* does not return */
    return;

error:
    do_reset (cmdtp, flag, argc, argv);
    return;
}


/*
 * 
重点来看一下taglist, 因为这个之前在vivi中没有。vivi中使用的是param struct
 * 
来传递参数的。新的内核都推荐用taglist来传递参数
 */

static struct tag *params;
struct tag {
        struct tag_header hdr;
        union {
                struct tag_core        core;
                struct tag_mem32    mem;
                struct tag_videotext    videotext;
                struct tag_ramdisk    ramdisk;
                struct tag_initrd    initrd;
                struct tag_serialnr    serialnr;
                struct tag_revision    revision;
                struct tag_videolfb    videolfb;
                struct tag_cmdline    cmdline;

                /*
                 * Acorn specific
                 */
                struct tag_acorn    acorn;

                /*
                 * DC21285 specific
                 */
                struct tag_memclk    memclk;
        } u;
};


+=============+==================================================+ <= bd->bi_boot_params
| tag_header  |  hdr.tag = ATAG_CORE                                             |
|    hdr      |  hdr.size = tag_size(tag_core)                                   |
+-------------+------------------------------------------------------------------+
|             |  u.core.flags    = 0                                             |
| union u     |  u.core.pagesize = 0;                                            |
|             |  u.core.rootdev  = 0;                                            |
+=============+==================================================================+
| tag_header  |  hdr.tag = ATAG_MEM                                              |
|    hdr      |  hdr.size = tag_size(tag_mem32)                                  |
+-------------+------------------------------------------------------------------+
|             |  u.mem.start = bd->bi_dram[i].start                              |
| union u     |  u.mem.size = bd->bi_dram[i].size                                |
|             |                                                                  |
+=============+==================================================================+
| tag_header  |  hdr.tag = ATAG_CMDLINE                                          |
|    hdr      | hdr.size = (sizeof(struct tag_header) + strlen(p) + 1 + 4) >> 2; |
+-------------+------------------------------------------------------------------+
|             |                                                                  |      
| union u     |  strcpy(u.cmdline.cmdline, p)                                    |
|             |                                                              |      
+=============+==================================================================+
| tag_header  |  hdr.tag = ATAG_NONE                                             |
|    hdr      |  hdr.size = 0                                                    |
+-------------+------------------------------------------------------------------+
|             |                                                                  |      
| union u     |                                                                  |
|             |                                                              |      
+=============+==================================================================+


uboot load address、entry point、 bootm address以及kernel运行地址的意义及联系

按各地址起作用的顺序,uboot引导linux内核启动涉及到以下地址:

  1. load address:
  2. entry point: 这两个地址是mkimage时指定的
  3. bootm address:bootm为uboot的一个命令,以此从address启动kernel
  4. kernel运行地址:在具体mach目录中的Makefile.boot中指定,为kernel启动后实际运行的物理地址
mkimage -n 'linux-3.2.1' -A arm -O linux -T kernel -C none -a 0x30008000 -e 0x30008000 -d zImage uImage

理论上因为mkimage要为zImage加上0x40字节的header,所以entry point = load address + 0x40

但由于uboot 的bootm对uImage处理不是简单的go操作,其对前三个地址都有比较判断,所以在实际的操作中,就分为两种不同的情况:

1. bootm地址和load address一样

  此种情况下,bootm不会对uImage header后的zImage进行memory move的动作,而会直接go到entry point开始执行。因此此时的entry point必须设置为load address + 0x40。如果kernel boot过程没有到uncompressing the kernel,就可能是这里设置不对。

boom address == load address == entry point - 0x40

具体细节可参看uboot代码common/cmd_bootm.c中bootm_load_os函数的实现:

        switch (comp) {
case IH_COMP_NONE:
if (load == blob_start || load == image_start) {
printf(" XIP %s ... ", type_name);
no_overlap = 1;
} else {
printf(" Loading %s ... ", type_name);
memmove_wd((void *)load, (void *)image_start,
image_len, CHUNKSZ);
}
*load_end = load + image_len;
puts("OK\n");
break;

2. bootm地址和load address不一样(但需要避免出现memory move时出现覆盖导致zImage被破坏的情况)

  此种情况下,bootm会把uImage header后的zImage move到load address(见上方代码),然后go到entry point开始执行。 由此知道此时的load address必须等于entry point。

boom address != load address == entry point

因此,在mkimage以及设置uboot boot command的时候需要注意到以上两种情况。

 

至于kernel的运行地址,其与前3个地址没有关系,除了要避免内存覆盖导致解压后kernel不完整的情况。

zImage的头部有地址无关的自解压程序,因此刚开始执行的时候,zImage所在的内存地址(entry point)不需要同编译kernel的地址相同。自解压程序会把kernel解压到编译时指定的物理地址,然后开始地址相关代码的执行。在开启MMU之前,kernel都是直接使用物理地址(可参看System.map)。


嵌入式 Linux 启动流程和 bootloader 介绍 目录 嵌入式 Linux 启动流程 简介 启动流程 Bootloader 简介 市面上可见的 bootloader 入式 Linux 启动流程 简介 对于一个 SoC 芯片而言,bootloader 必不可少。因为一个 SoC 芯片一定不是独立存在的,它的载体是经过严格设计的一块硬件电路板。从电路板上电到 SoC 可以执行 Linux Kernel 并最终挂载文件系统,需要在不同阶段执行不同的特定代码。 SoC 上电伊始,根据设定的启动方式,在合适的地方运行 BootRom 代码,用于对 阅读详情

相关推荐

Android系统启动流程2 ---linux内核的启动流程

1. 自解压内核映像 内核映像被加载到内存并获得控制权之后,内核启动流程开始。通常,内核映像以压缩形式存储,并不是一个可以执行的内核。因此,内核阶段的首要工作是自解压内核映像。 内核镜像和几个名词有关:vmliux、Image、zImage ARM32平台 编译一个压缩后的内核镜像: make zImage -》 LD vmlinux SYSMAP System.map SYSMAP .tmp_System.map OBJCOPY arch/arm/boot/Image K

weixin_40535588的博客 1759

LZMA ERROR 1 - must RESET board to recover错误分析

因为项目的需要,编译了MTK官方的4.1版SDK,最终生成了manfeel_uImage文件,刷入开发板之后重启,出现了这个LZMA ERROR 1的错误。是我运气太好,还是太背?上次也碰到过这个问题,改动了一下load address(由原来的0x80000000改成了0x80100000),就过了。然而,这次的问题貌似没那么简单。 只好深入uboot的lib_generic/lzmaDeco

释放“芯”中的小宇宙 9474

Linux内核分析(二)--qemu搭建kernel调试环境

本篇文章会简单介绍如何用qemu搭建一个kernel(v6.6.0)的调试环境,并且支持gdb调试

文艺小少年的博客 765

解决MT7620不断循环uboot(LZMA ERROR 1 - must RESET board to recover)

采用Ralink的SDK开发包,编译出的内核,以前烧录到MT7620上,可以正常运行。后来,编译出的内核,烧录之后,开发板反复重启,一直在不停的Uboot,陷入死循环,无法加载内核。 错误提示: stream version is not supported, outSize = FFFFFFFF LZMA ERROR 1 - must RESET board to recover 本文目的在于解决这个问题。 相关文档下载链接:http://download.youkuaiyun.com/det

xhoufei2010的专栏 9568

多智能体强化学习MASAC算法:从原理到PyTorch实战详解

深度强化学习通过智能体与环境的交互试错来学习最优策略,是解决复杂序列决策问题的核心技术。其核心原理在于利用价值函数或策略梯度来最大化累积奖励,在机器人控制、游戏AI等领域展现出巨大技术价值。当面对多个智能体需要协同或竞争的复杂动态环境时,单智能体算法因环境非平稳性而面临挑战,多智能体强化学习应运而生。Multi-Agent Soft Actor-Critic (MASAC) 算法融合了SAC的稳定高效与中心化训练去中心化执行的范式,通过维护中心化Critic网络和独立策略网络,有效解决了多智能体协作中的信用

weixin_30080745的博客 330

RNA-Seq数据分析实战:从FastQC到STAR比对的完整流程(附避坑指南)

本文提供了RNA-Seq数据分析从FastQC质控到STAR比对的完整实战流程与避坑指南。详细解读了FastQC/MultiQC报告的关键指标,并重点阐述了STAR比对工具的核心参数设置、索引构建及性能优化策略,旨在帮助用户获得准确可靠的比对结果,为下游基因表达分析奠定坚实基础。

weixin_29290259的博客 92

【linux】kernel 编译个人小结

kernel、module 编译搭建及编译相关错误

过得精彩,无所谓好坏 1595

VCS HVP与验证计划planner:数字IC验证环境搭建与收敛实践

在数字IC验证中,VCS 是广泛使用的编译型仿真器,HVP 通常指面向硬件验证平台的高性能编译选项集,用于开启 SystemVerilog 类、随机约束和 NTB 等能力;planner 则指验证计划管理,核心是把功能点与测试用例、覆盖点建立映射。理解 VCS 先编译后仿真的原理,有助于优化增量编译、调试访问和覆盖率采集,提升回归收敛效率。规划驱动让激励不再散落,设计改版时能快速定位受影响用例,交接和覆盖率收敛更有依据。无论是 UVM 环境搭建、编译参数选择,还是验证计划表维护,都需要把工具选项与规划方法结

weixin_42543046的博客 337

MTK APSOC 5.0.2 Build Process

Host gcc version : gcc (Ubuntu 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609 Target gcc version: mipsel-buildroot-linux-uclibc-gcc-4.6.3 下面是在ubuntu 16.64 x64上编译MTK_APSoC_SDK V5.0.2的过程. recipe for target ‘...

subplot的专栏 4586

CANoe基础讲解01:从零开始使用CANoe轻松搭建你的第一个CAN网络

本文是一篇CANoe软件入门实操指南,逐步演示了如何从零创建并运行一个CAN FD仿真工程。内容包括:启动软件、配置硬件通道与网络、加载DBC数据库、使用CAPL编写仿真节点发送CAN报文,最终启动测量并在Trace窗口中查看通信结果。通过七个清晰步骤,帮助初学者快速掌握CANoe基本操作,完成首个可运行的仿真网络搭建

车载网络小D的博客 1573

状态机设计与实现( stm32小车避障为例)

本文以STM32小车避障为例,介绍了状态机设计与实现方法。首先阐述了状态机的事件驱动特性、清晰结构和易扩展优势,并给出硬件组成框架。核心实现部分展示了状态枚举定义、状态转移图和主循环框架,通过switch-case结构实现状态切换。关键状态示例包括前方扫描和转向决策逻辑,通过传感器数据驱动状态转移。总结指出状态机设计需定义枚举状态、实现状态函数、使用switch跳转并更新全局状态。文章最后声明内容为学习笔记,欢迎读者指正交流。全文结构清晰,代码示例具体,为嵌入式系统状态机设计提供了实用参考。

2302_80219536的博客 1046

WRTNode(MT7620) 通过USB启动OpenWRT过程记录(上)

接上篇文章,现在开始启动U盘中的kernel,又碰到了很多问题! 用 fatload usb 0 0x80000000 uImage.lzm ; bootm 0x80000000 总是碰到下面的错误: MT7620 # bootm 0x80000000 ## Booting image at 80000000 ... Image Name: PandoraBox Lin

释放“芯”中的小宇宙 8786

ComfyUI工作流换脸节点报错ReActorFaceSwap

找到自己ComfyUI安装目录的custom_nodes下是否有这个comfyui-reactor-node-main。大家在借鉴别人工作流进行AI换脸的时候,如果遇到ReActorFaceSwap报错提示,莫慌,看我的,两步解决问题。这里千万注意不要嵌套文件夹,保证点击去这个文件就可以看到内置的配置文件!找到自己ComfyUI安装目录的facerestore_models。如果有这个文件,直接跳过,去下一步。如果没有,可以去Git官网下载。

qq_70810809的博客 9015

制作linux启动镜像内核,制作Linux映像和Kernel的启动

do_bootm_linux()位于lib_arm/bootm.c中void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],bootm_headers_t *images){ulong initrd_start, initrd_end;ulong ep = 0;bd_t *bd = gd-&g...

weixin_42393650的博客 300

LAMMPS后处理—如何构建二维云图-接触角呢?

计算接触角

Rockyoung 6523

Freeswitch DTMF 按键无法接收问题修复

freeswitch 在接收到 update后,会更新成新的 payload type,但是给的200响应,仍然是旧的,这时候就会导致手机侧认识需要发送旧的payload,所以发到 fs 后就会导致直接丢包,不去处理RTP包了。呼叫时需要设置通道变量 rtp_manual_rtp_bugs=ACCEPT_ANY_PAYLOAD ,这时候FS 就不会对包的payload进行判断,直接判定通过。应该修复 freeswitch 给响应时,应该根据收到的 payload 发送给手机,但是一直未找到正确的路径。

2301_81507069的博客 373

Linux内核Kernel启动过程

减少存储空间: 压缩内核映像可以显著减少其在存储设备上的占用空间。这对嵌入式系统、存储资源有限的设备以及需要快速分发和更新内核的环境尤其重要加快加载速度: 压缩文件占用的空间更小,这意味着启动加载程序从磁盘读取文件到内存中的时间会更短。虽然解压缩内核映像需要一些时间,但现代处理器的解压缩速度非常快,通常解压缩的时间比从存储设备读取更多数据的时间要少。这会整体上加快启动过程。提高传输效率: 在网络上传输内核映像时,压缩文件可以显著减少带宽使用量。这对于需要远程更新内核的系统(OTA)非常有利。

weixin_60669486的博客 3546

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

手斧Linux – 从LFS到Funtoo (121)

rm -rf CDroot fi mkdir CDroot mkdir CDroot/{isolinux,boot} #  -------用官方的syslinux5.00 cp -v syslinux-5.00/core/isolinux.bin CDroot/isolinux/ cp -v syslinux-5.00/com32/elflink/ldlinux/ldlinux.c

sanitywolf的专栏 545

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

Linux内核(Kernel)启动过程分析

Linux内核(Kernel)启动过程

Android应用开发工程师 2274

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

QEMU中启动RISCV Kernel

本文介绍了在Qemu中启动RISC-V Linux内核的完整流程,包括OpenSBI、U-Boot、Kernel和Buildroot的编译配置方法。文章详细说明了两种启动方式:直接启动内核(通过OpenSBI跳转至内核)和通过U-Boot启动内核。对于U-Boot启动方式,重点讲解了如何创建包含内核映像和设备树的分区磁盘镜像,以及如何设置U-Boot环境变量来加载内核。最后提供了自定义默认启动参数的配置建议,为开发者提供了从固件准备到系统启动的完整参考方案。

以梦为马不负韶华 1357

Linux内核映像的生成过程

本人水平相当有限,不当之处,欢迎指正。 在各种各样使用Linux的计算机系统中,系统启动的早期阶段,通常都会有这样一个环节, boot loader程序将内核映像加载到内存中,然后跳转到映像的起始位置开始执行。 接下来,内核就跑起来了。 那么,这个内核映像到底是个什么东东呢。本文就来简单介绍一下Linux内核映像的生成过程。 本文依据的内核版本 2.6.32 (一)

无忧老猪 1万+

linux内核映像查看,RockPI 4A Linux内核映像启动配置

在Debian系统中,通过修改启动配置文件/boot/extlinux/extlinux.conf,可以增加u-boot引导时的Linux内核映像选项,方便Linux内核调试。注:本文基于RockPI 4A单板Debian系统。一、extlinux.conf配置文件在Debian系统启动后,使用cat命令查看/boot/extlinux/extlinux.conf文件内容。root@linaro-...

weixin_42350506的博客 837

Linux kernel 启动流程分析2

Linux kernel 启动流程分析2---基本概述

ListQ的博客 1359
上一篇: 基于s3c2410的uboot1.2.0的分析以及移植
下一篇: uboot 环境变量
evenness
博客等级 码龄15年 99粉丝 8原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值