Nuttx romfs与启动脚本rcS

ARM系统上电后,系统将flash地址映射到零地址处,处理器从零地址处开始运行第一条指令。而在零地址处,一般是系统复位中断向量,此处存放的是一条跳转指指令,通过该条换指令,处理器运行复位服务函数。Cortext-M系列的实现稍有不同,它的零地址处放置的栈指针,接着才是复服务函数地址。

复位服务函数开始处是由汇编语言编写,借助汇编函数调用C函数,将系统带到C函数中。Nuttx的C入口函数便是os_start(),在os_start()执行操作系统地初始化。

Nuttx操作系统初始化完成后,首先创建第一个进程,idle线程便是这第一个进程的线程。有了第一个进程,系统接着创建用户进程,即在os_bringup()函数中,调用os_do_appstart()创建init进程。init进程的第一个默认线程是nsh_main。nsh_main调用nsh_consolemain()函数,先运行romfs文件系统上的启动脚本,然后nsh_main线程进入nsh交互界面。至此,操作系统完全启动。其他进程,或者线程可以通过启动脚本启动运行,或者通过nsh启动。。

在默认情形下,nsh_main线程将运行romfs中“/etc/init.d/rcS”这个启动脚本上的命令。romfs启动脚本有它自己的语法,Nuttx中的sh可以解析这些语法,并执行相关的命令。这些命令可以设置进程环境变量,读取环境变量,或者启动其他线程。

这一节将说明Nuttx制作romfs的方法,接着说明如何定制romfs,即让Nuttx在启动的过程中能够运行我们制作的启动脚本。最后我们对启动脚本的语法进行简要说明。

1. romfs简介

romfs,顾名思义是在rom中实现的一种文件系统,linux 2.1.21版本中最早支持该文件系统。这种文件系统是基于块存储介质的只读文件系统,具有体积小、读取速度快、可靠性高的优点。它的缺点是,这种文件系统是只读的,而且对文件的描述信息很少,没有访问权限限制。

romfs通常用在嵌入式系统中,用来存储系统模块,只读的文件等,将其部署在EEPROM或者其他块存储设备中。

更多关于romfs文件系统的模块结构和协议,以及驱动程序,可以在网上搜索。

2. 制作romfs

在linux系统下使用“genromfs”工具来生成romfs映像文件,该映像文件可以被挂载到系统中作为只读文件系统。

2.1 生成romfs

这里在linux系统下演示一下生成并挂载romfs文件系统。首先主机上创建我们所要生成romfs的目录和文件,比如目录romfs_dir。通过”tree”命令查看该目录结构

$ tree romfs_dir
romfs
|——dir_a
|    |——a.txt   
|——dir_b
     |——b.txt

2 directories, 2 file

写入字符串”this is a.txt”到a.txt文件,写入字符串”this is b.txt”到b.txt文件。

使用”genromfs”命令将romfs_dir文件夹制作成一个romfs映像,并挂载到系统”/mnt”。”genromfs”命令使用方法可以通过”genromfs -h”查看。在romfs_dir所在目录下执行命令:

$ genromfs –f romfs.img –d romfs_dir –v –V “romfstest”

上面genromfs命令工具以文件夹romfs_dir为输入,生成romfs.img二进制映像文件,映像卷名为”romfstest”。”-v”参数使得”genromfs”输出详细信息。

我们通过”mount”命令将生成的romfs挂载到系统”/mnt”下,并查看其内容是否符合预期。

$ sudo mount romfs.img  /mnt –o loop
mount: warning: /mnt seems to be mounted read-only
$ tree /mnt
/mnt
|——dir_a
|    |——a.txt   
|——dir_b
     |——b.txt

2 directories, 2 file

2.2 制作romfs头文件

Nuttx系统中注册romfs文件系统,需要romfs文件系统头指针,以便将文件系统编译链接进可执行文件。所以,需要将romfs映像文件生成一个头文件。

Linux系统下的”xxd”工具命令能够将标准输入或者给定的二进制文件输出到文件或者标准输出。关于”xxd”工具命令的详细使用方法可以通过”xxd -h”或者”man xxd”命令查看。如果使用”-i”参数,”xxd”可以输出C语言头文件形式的文件。

我们以上节中生成的romfs映像文件制作一个C语言头文件,头文件名为nsh_romfsimg.h, 而头文件nsh_romfsimg.h中的数组名为romfs_img。命令如下:

$ xxd –i romfs.img nsh_romfsimg.h

生成的头文件中数组名称由输入的romfs映像文件名决定,如果文件名中含有”.”分隔符,那么”.”将会被替换为”_”。

3. Nuttx系统romfs实验

Nuttx系统在nsh_main线程中,通过调用nsh_initialize()函数,进而调用nsh_romfsetc()向系统注册romfs,并挂载到默认的位置”/etc”目录。注意,nsh_romfsetc()需要配置CONFIG_NSH_ROMFSETC,该选项可以在”make menuconfig”界面中配置,或者直接在.config文件中手动配置。另外,也需要系统支持ROMFS,即CONFIG_FS_ROMFS文件系统选项需要启用。

我们需要做的是生成romfs头文件,将其放置到系统的某个位置(默认位置为apps/nshlib目录,如果配置了CONFIG_NSH_ARCHROMFS,则放置到config/< board >/inlclude目录),重新编译系统。将生成的romfs头文件nsh_romfsimg.h放置到apps/nshlib目录,确保头文件nsh_romfsimg.h中数组名为”romfs_img”,重新编译下载系统,并运行。通过nsh终端验证如下:

NuttxShell (NSH)
nsh> ls
/:
 dev/
 etc/
nsh> cd /etc
nsh> ls
/etc:
 .
 dir_a/
 dir_b/
nsh> cd dir_a
/etc/dir_a:
 .
 a.txt
nsh> cat a.txt
this is a.txt
nsh> 

4. 制作启动脚本rcS

通过前面两节,我们已经可以定制romfs文件系统的内容了,那么制作Nuttx系统下的启动脚本init.d/rcS就易如反掌。

我们重起炉灶,另外创建一个文件夹top_dir,在该文件夹下创建init.d文件夹,再在init.d文件夹下创建文件rcS,目录结构如下:

$ tree top_dir
top_dir
|——init.d
     |——rcS

1 directory, 1 file

作为练习,我们现在rcS文件中写入简单地命令,只是挂载proc文件系统而已。注意:rcS文件中以”#”字符开头的行将被sh解析为注释行。我们的rcS文件的内容如下:

$ cat top_dir/init.d/rcS
# this is rcS file

mount –t procfs /proc

要挂载proc文件系统,Nuttx系统需要启用CONFIG_FS_PROCFS文件系统选项。

按照上述两节中的步骤,将top_dir目录制作为一个romfs.img映像文件,然后再生成nsh_romfsimg.h的头文件,将这个头文件放置到apps/nshlib路径下,编译下载并运行,nsh终端输出如下:

NuttxShell (NSH)
nsh> ls
/:
 Dev/
 etc/
 proc/
nsh> cat /etc/init.d/rcS
# this is rcS file

mount –t procfs /proc

nsh> 

从nsh终端的输出可以看出,Nuttx运行了/etc/init.d/rcS启动脚本,将proc文件系统成功地挂载到系统。最后也打印出了/etc/init.d/rcS的内容,全都符合我们的期望。

5. APM中生成romfs

启动APM飞控,从nsh终端查看它的”/etc”目录,可以看到下面有四个目录,分别为bootloader, px4io, tones 和init.d。bootloader用于更新飞控的bootloader。px4io是飞控上面io控制器的固件,该固件在飞控启动后从主控(stm32f427)下载到io控制器(stm32f103)。tones是控制蜂鸣器发出报警声的源文件。最后一个init.d文件夹下面便是APM的启动脚本rcS。

没有经过编译的APM的固件代码,用于生成romfs的文件夹只包含tones和init.d两个子文件夹,并没有bootloader和px4io,这两个文件夹是在编译时候生成的。这点可以从makefile文件中找到,以px4-io-v2为例。

px4-io-v2: $(PX4_ROOT)/Archives/px4io-v2.export
    $(v)+ $(MAKE) -C $(PX4_ROOT) -f $(PX4_ROOT)/Makefile.make px4io-v2_default EXTRADEFINES="-DARDUPILOT_BUILD"
    $(v) cp $(PX4_ROOT)/Images/px4io-v2_default.bin px4io-v2.bin
    $(v) cp $(PX4_ROOT)/Build/px4io-v2_default.build/firmware.elf px4io-v2.elf
    $(v) mkdir -p $(MK_DIR)/PX4/ROMFS/px4io/
    $(v) cp px4io-v2.bin $(MK_DIR)/PX4/ROMFS/px4io/px4io.bin
    $(v) mkdir -p $(MK_DIR)/PX4/ROMFS/bootloader/
       $(v) cp $(SKETCHBOOK)/mk/PX4/bootloader/px4fmuv2_bl.bin $(MK_DIR)/PX4/ROMFS/bootloader/fmu_bl.bin
    $(v) echo "PX4IOv2 Firmware is in px4io-v2.bin"

这段Makefile将生成的px4io-v2.bin文件复制到PX4/ROMFS/px4io/px4io.bin,将PX4/bootloader/px4fmuv2_bl.bin复制到PX4/ROMFS/bootloader/fmu_bl.bin。

制作APM romfs的文件准备好之后,将PX4/ROMFS文件夹制作成romfs.img二进制映像文件。

# Generate the ROMFS image from the root
$(ROMFS_IMG): $(ROMFS_SCRATCH) $(ROMFS_DEPS) $(GLOBAL_DEPS)
    @$(ECHO) "ROMFS:   $@"
    $(Q) $(GENROMFS) -f $@ -d $(ROMFS_SCRATCH) -V "NSHInitVol"

然后将romfs.img转换成romfs.o文件

# Turn the ROMFS image into an object file
$(ROMFS_OBJ): $(ROMFS_IMG) $(GLOBAL_DEPS)
    $(call BIN_TO_OBJ,$<,$@,romfs_img)

最后romfs.o会被链接进可执行文件,生成固件firmware.px4。

6. 启动脚本rcS语法介绍

rcS启动脚本实质上是Nuttx下实现的类似于linux中shell脚本的一种脚本文件,Nuttx中sh可以解释这种脚本的语法和执行命令。本小节简要说明Nuttx中支持的命令和基本语法。

6.1 基本命令

在Nuttx的脚本中可以运行命令,命令还可以带参数。脚本支持的命令是sh所支持的命令,可以在nsh终端输入help命令查看sh支持的命令。

nsh> help
help usage: help [-v] []
[               dirname     help            mh      set     unset
?               dd          hexdump     mount   sh      usleep
basename    df          kill            mv      sleep   xd
break       echo            ls          mw      test
cat         exec            mb          ps      time
cd          exit            mkdir       pwd     true
cp          false           mkfatfs     rm      uname
cmp         free            mkrd        rmdir   umount

命令”help -v”可以查看所有命令详细使用格式, 查看单个命令的详细使用格式,使用命令”help –v [< cmd >]”,比如查看命令”ls”的详细使用格式为”help –v ls”。

6.2 语法

6.2.1 if…else…

if… else… 条件语句的格式如下:

if  
then
        [sequence of ]
else
        [sequence of ]
fi

如果< cmd >结果为true,则执行then分支,否则,执行else分支。

6.2.2 while循环

while循环格式如下:

while  
do
        [sequence of ]
done

如果条件< cmd >返回为true,将循环执行do…done之间语句。

6.2.3 until循环

until循环格式如下:

until  
do
        [sequence of ]
done

如果条件< cmd >不为真,将循环执行do…done之间的语句。这点与while循环正好相反。

你可能感兴趣的:(Nuttx)