编辑
2025-03-30
记录知识
0

我们之前构建了RTEMS操作系统,这次继续构建此实时系统的应用程序,以hello kylin为例

一、准备仓库

为了编写应用程序以及管理,需要提前准备一个本地git仓库,如下

# mkdir -p app/hello # cd app/hello # git init .

二、waf构建工具环境

waf作为rtems的构建工具,我们需要为应用程序下载此工具,如下:

# cp ../../waf . # git submodule add https://gitlab.rtems.org/rtems/tools/rtems_waf.git rtems_waf

三、编写应用

编写应用前,需要为RTEMS操作系统提供init.c,如下

/* * Simple RTEMS configuration */ #define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER #define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER #define CONFIGURE_UNLIMITED_OBJECTS #define CONFIGURE_UNIFIED_WORK_AREAS #define CONFIGURE_RTEMS_INIT_TASKS_TABLE #define CONFIGURE_INIT #include <rtems/confdefs.h>

然后创建hello.c应用程序如下

/* * Hello world example */ #include <rtems.h> #include <stdlib.h> #include <stdio.h> rtems_task Init( rtems_task_argument ignored ) { printf( "\nHello Kylin\n" ); exit( 0 ); }

最后为hello.c和init.c编写waf构建脚本,如下

# # Hello world Waf script # from __future__ import print_function rtems_version = "6" try: import rtems_waf.rtems as rtems except: print('error: no rtems_waf git submodule') import sys sys.exit(1) def init(ctx): rtems.init(ctx, version = rtems_version, long_commands = True) def bsp_configure(conf, arch_bsp): # Add BSP specific configuration checks pass def options(opt): rtems.options(opt) def configure(conf): rtems.configure(conf, bsp_configure = bsp_configure) def build(bld): rtems.build(bld) bld(features = 'c cprogram', target = 'hello.exe', cflags = '-g -O2', source = ['hello.c', 'init.c'])

四、构建

此时一切准备完成之后,构建应用程序和构建操作系统的命令一致,如下:

# ./waf configure --rtems=~/work/rtems/out/ --rtems-bsp=aarch64/zynqmp_qemu # ./waf

五、运行

通过rtems-run可以带起qemu运行应用程序,如下:

# rtems-run --rtems-bsps=zynqmp_qemu build/aarch64-rtems6-zynqmp_qemu/hello.exe

此时运行结果如下

RTEMS Testing - Run, 6.0.not_released Command Line: /root/work/rtems/out/bin/rtems-run --rtems-bsps=zynqmp_qemu build/aarch64-rtems6-zynqmp_qemu/hello.exe Host: Linux tf 6.8.0-47-generic #47-Ubuntu SMP PREEMPT_DYNAMIC Fri Sep 27 21:40:26 UTC 2024 x86_64 Python: 2.7.18 (default, Jan 31 2024, 16:23:13) [GCC 9.4.0] Host: Linux-6.8.0-47-generic-x86_64-with-Ubuntu-20.04-focal (Linux tf 6.8.0-47-generic #47-Ubuntu SMP PREEMPT_DYNAMIC Fri Sep 27 21:40:26 UTC 2024 x86_64 x86_64) Hello Kylin [ RTEMS shutdown ] CPU: 0 RTEMS version: 6.0.0.87bf49b7156b9ddf45c218e5d4fa01f27b283db7 RTEMS tools: 13.3.0 20240521 (RTEMS 6, RSB b1aec32059aa0e86385ff75ec01daf93713fa382-modified, Newlib 1b3dcfd) executing thread ID: 0x0a010001 executing thread name: UI1 Run time : 0:00:00.756775

至此,一个最简单的rtems的应用程序编写完成

六、提交代码

最后提交相关代码即可

# git add init.c hello.c wscript # git commit -m "My first RTEMS application."
编辑
2025-03-30
记录知识
0

rtems可以支持capture任务的执行情况,如下演示效果

一、运行capture示例

构建通过waf执行,如下

# ./waf configure --prefix=/root/work/rtems/out # ./waf

至此,我们可以运行capture示例如下

# qemu-system-aarch64 -no-reboot -nographic -s -serial mon:stdio -machine xlnx-zcu102 -m 4096 -kernel build/aarch64/zynqmp_qemu/testsuites/samples/capture.exe

此时的信息如下

*** BEGIN OF TEST CAPTURE ENGINE *** *** TEST VERSION: 6.0.0.87bf49b7156b9ddf45c218e5d4fa01f27b283db7-modified *** TEST STATE: USER_INPUT *** TEST BUILD: RTEMS_POSIX_API RTEMS_SMP *** TEST TOOLS: 13.3.0 20240521 (RTEMS 6, RSB b1aec32059aa0e86385ff75ec01daf93713fa382-modified, Newlib 1b3dcfd) Press any key to start capture engine (20s remaining) Press any key to start capture engine (19s remaining) Monitor ready, press enter to login. 1-rtems $

二、相关命令

copen <buffer-size>: 此命令默认设置capture的buffersize byte单位 cwceil <priority-value>: 此命令过滤低于优先级的所有事件 cwfloor <priority-value>: 此命令过滤高于优先级的所有事件 cwglob <on/off>: 此命令启动或关闭监视器 ctset [-?] type [to name/id] [from] [from name/id]: 此命令设置触发器类型,这里RMON是监视自身 cenable: 此命令启用capture功能 cdisable: 此命令禁用capture功能 ctrace: 此命令trace出日志 cwadd <task-name>: 此命令为特定的任务添加监视 cwtctl <task-name> <on/off>: 此命令启动或禁用特定任务 ctlist: 此命令列出当前的task

三、演示

此时我们进入captrue.exe,开启capture如下

1-rtems $ ctrace error: trace read failed: RTEMS_NOT_CONFIGURED 1-rtems $ 1-rtems $ cenable error: enable failed: RTEMS_UNSATISFIED 1-rtems $ copen 50000 capture engine opened. 1-rtems $ cwceil 100 watch ceiling is 100. 1-rtems $ cwfloor 102 watch floor is 102. 1-rtems $ cwglob on global watch enabled. 1-rtems $ ctset RMON trigger set. 1-rtems $ cenable capture engine enabled.

然后我们运行capture内置的测试程序test1,如下

1-rtems $ test1

等任务完成之后,我们关闭capture,然后查看trace如下

1-rtems $ cdisable capture engine disabled. 1-rtems $ ctrace 0 0:05:12.132637246 0a010003 CT1a 102 102 102 8192 TASK_RECORD 0 0:05:12.132782702 0 0a010003 CT1a 102 102 CREATED 0 0:05:12.133041342 258640 0a010003 CT1a 102 102 STARTED 0 0:05:12.133258110 216768 0a010003 CT1a 102 102 SWITCHED_IN 0 0:05:12.133502894 244784 0a010003 CT1a 102 102 BEGIN 0 0:05:12.134015374 512480 0a010003 CT1a 102 102 SWITCHED_OUT 0 0:05:13.130163838 0a010004 CT1b 101 101 101 8192 TASK_RECORD 0 0:05:13.130167454 996152080 0a010004 CT1b 101 101 CREATED 0 0:05:13.130238478 71024 0a010004 CT1b 101 101 STARTED 0 0:05:13.130302190 63712 0a010004 CT1b 101 101 SWITCHED_IN 0 0:05:13.130313246 11056 0a010004 CT1b 101 101 BEGIN 0 0:05:14.129783230 999469984 0a010004 CT1b 101 101 SWITCHED_OUT 0 0:05:14.129853182 0a010005 CT1c 100 100 100 8192 TASK_RECORD 0 0:05:14.129854974 71744 0a010005 CT1c 100 100 CREATED 0 0:05:14.129888638 33664 0a010005 CT1c 100 100 STARTED 0 0:05:14.129931806 43168 0a010005 CT1c 100 100 SWITCHED_IN 0 0:05:14.129936494 4688 0a010005 CT1c 100 100 BEGIN 0 0:05:14.130633246 696752 0a010005 CT1c 100 100 SWITCHED_OUT 0 0:05:14.130634638 1392 0a010004 CT1b 101 101 SWITCHED_IN 0 0:05:14.629816846 499182208 0a010004 CT1b 101 101 SWITCHED_OUT 0 0:05:14.629819598 2752 0a010003 CT1a 102 100 SWITCHED_IN 0 0:05:14.630445950 626352 0a010003 CT1a 102 102 SWITCHED_OUT

我们查看当前的任务列表,如下

1-rtems $ ctlist uptime: 0:22:34.490255513 total 2 09010001 IDLE 255 255 255 READY a--g 0a010002 RMON 1 1 1 READY a---

至此,capture的示例演示完成

编辑
2025-03-30
记录知识
0

rtems提供了trace的基本功能,如下演示此功能情况

一、trace配置文件

对于rtems的trace功能,可以通过官方提供的fileio-trace.ini示例文件配置,如下

; ; RTEMS Trace Linker FileIO Trace Configuration ; ; Copyright 2015 Chris Johns <chrisj@rtems.org> ; ;-------------------------------------------------------------------------- [tracer] name = File IO tracer ; ; The configuration ; options = fileio-options traces = fileio defines = fileio enables = fileio triggers = fileio functions = fileio-funcs, rtems-api, rtems-posix, libc-heap include = rtems.ini, rtld-base.ini, rtld-trace-buffer.ini, libc-heap.ini ;-------------------------------------------------------------------------- [fileio-options] dump-on-error = true ; ; Tools ; prefix = /opt/rtems/4.11 rtems-path = /opt/rtems/kernel/4.11 rtems-bsp = sparc/sis ; ; Generator options. ; gen-enables = enable gen-triggers = enable ;-------------------------------------------------------------------------- [fileio] generator = trace-buffer-generator define = '#define RTLD_TRACE_BUFFER_SIZE (1UL * 1024 * 1024)' trace = rtems_shell_init trace = malloc, calloc, realloc, free traces = rtems-api-semaphore, rtems-posix-mutex enable = rtems_shell_init enable = malloc, calloc, realloc, free enables = rtems-api-semaphore, rtems-posix-mutex trigger = rtems_shell_init ;-------------------------------------------------------------------------- [fileio-funcs] headers = fileio-headers signatures = fileio-signatures [fileio-headers] header = '#include <rtems/shell.h>"' [fileio-signatures] rtems_shell_init = rtems_status_code, const char*, size_t, rtems_task_priority, const char*, bool, bool, rtems_shell_login_check_t

这里关于rtems-path和rtems-bsp主要配置为当前rtems的安装路径和bsp路径,如下

prefix = /root/work/rtems/out rtems-path = /root/work/rtems/out rtems-bsp = aarch64/zynqmp_qemu

二、编译.o文件

这里以fileio为例,我们需要先将init.c编译成.o文件,如下

# /root/work/rtems/out/bin/aarch64-rtems6-gcc -MMD -Wall -Wmissing-prototypes -Wimplicit-function-declaration -Wstrict-prototypes -Wnested-externs -mno-outline-atomics -mcpu=cortex-a53 -mfix-cortex-a53-835769 -mfix-cortex-a53-843419 -O2 -g -fdata-sections -ffunction-sections -Icpukit/include -I../../../cpukit/include -Icpukit/score/cpu/aarch64/include -I../../../cpukit/score/cpu/aarch64/include -Ibsps/include -I../../../bsps/include -Ibsps/aarch64/include -I../../../bsps/aarch64/include -Ibsps/aarch64/xilinx-zynqmp/include -I../../../bsps/aarch64/xilinx-zynqmp/include -Itestsuites/support/include -I../../../testsuites/support/include ../../../testsuites/samples/fileio/init.c -c -o/root/work/rtems/rtems/build/aarch64/zynqmp_qemu/testsuites/samples/fileio/fileio-init.o -DHAVE_CONFIG_H=1 -DTEST_STATE_USER_INPUT=1

此命令来源于构建是waf的verbose参数,如下可以看到waf verbose参数. 值得注意的是,我将原来构建的init.c.672.o名字重写成了fileio-init.o

# rm -rf build/aarch64/zynqmp_qemu/testsuites/samples/fileio # ./waf -v Waf: Entering directory `/root/work/rtems/rtems/build' Waf: Leaving directory `/root/work/rtems/rtems/build' 'build' finished successfully (0.045s) Waf: Entering directory `/root/work/rtems/rtems/build/aarch64/zynqmp_qemu' 08:11:11 runner 'git ls-files --modified' 08:11:11 runner 'git rev-parse HEAD' [3710/4847] Compiling testsuites/samples/fileio/init.c 08:11:13 runner ['/root/work/rtems/out/bin/aarch64-rtems6-gcc', '-MMD', '-Wall', '-Wmissing-prototypes', '-Wimplicit-function- declaration', '-Wstrict-prototypes', '-Wnested-externs', '-mno-outline-atomics', '-mcpu=cortex-a53', '-mfix-cortex-a53-835769' , '-mfix-cortex-a53-843419', '-O2', '-g', '-fdata-sections', '-ffunction-sections', '-Icpukit/include', '-I../../../cpukit/inc lude', '-Icpukit/score/cpu/aarch64/include', '-I../../../cpukit/score/cpu/aarch64/include', '-Ibsps/include', '-I../../../bsps /include', '-Ibsps/aarch64/include', '-I../../../bsps/aarch64/include', '-Ibsps/aarch64/xilinx-zynqmp/include', '-I../../../bs ps/aarch64/xilinx-zynqmp/include', '-Itestsuites/support/include', '-I../../../testsuites/support/include', '../../../testsuit es/samples/fileio/init.c', '-c', '-o/root/work/rtems/rtems/build/aarch64/zynqmp_qemu/testsuites/samples/fileio/init.c.672.o', '-DHAVE_CONFIG_H=1', '-DTEST_STATE_USER_INPUT=1'] [4847/4847] Linking build/aarch64/zynqmp_qemu/testsuites/samples/fileio.exe 08:11:14 runner ['/root/work/rtems/out/bin/aarch64-rtems6-gcc', 'testsuites/samples/fileio/init.c.672.o', '-o/root/work/rtems/ rtems/build/aarch64/zynqmp_qemu/testsuites/samples/fileio.exe', '-Wl,-Bstatic', '-L.', '-lrtemscpu', '-lrtemsbsp', '-lrtemstes t', '-Wl,-Bdynamic', '-qrtems', '-mno-outline-atomics', '-mcpu=cortex-a53', '-mfix-cortex-a53-835769', '-mfix-cortex-a53-84341 9', '-Wl,--gc-sections', '-L/root/work/rtems/rtems/bsps/aarch64/shared/start', '-L/root/work/rtems/rtems/bsps/aarch64/xilinx-z ynqmp/start', '-Wl,--wrap=printf', '-Wl,--wrap=puts', '-Wl,--wrap=putchar'] Waf: Leaving directory `/root/work/rtems/rtems/build/aarch64/zynqmp_qemu' 'build_aarch64/zynqmp_qemu' finished successfully (2.606s)

三、执行tld

根据抓到的日志信息,我们可以拿到ld的命令如下:

# /root/work/rtems/out/bin/aarch64-rtems6-gcc testsuites/samples/fileio/init.c.672.o -o/root/work/rtems/rtems/build/aarch64/zynqmp_qemu/testsuites/samples/fileio.exe -Wl,-Bstatic -L. -lrtemscpu -lrtemsbsp -lrtemstest -Wl,-Bdynamic -qrtems -mno-outline-atomics -mcpu=cortex-a53 -mfix-cortex-a53-835769 -mfix-cortex-a53-843419 -Wl,--gc-sections -L/root/work/rtems/rtems/bsps/aarch64/shared/start -L/root/work/rtems/rtems/bsps/aarch64/xilinx-zynqmp/start -Wl,--wrap=printf -Wl,--wrap=puts -Wl,--wrap=putchar

此时我们根据gcc的verbose参数,可以得到ld的调用参数如下

# aarch64-rtems6-ld -plugin /root/work/rtems/out/libexec/gcc/aarch64-rtems6/13.3.0/liblto_plugin.so -plugin-opt=/root/work/rtems/out/libexec/gcc/aarch64-rtems6/13.3.0/lto-wrapper -plugin-opt=-fresolution=/tmp/ccBjkjvt.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lrtemsbsp -plugin-opt=-pass-through=-lrtemscpu -plugin-opt=-pass-through=-latomic -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc -EL -X -maarch64elf --fix-cortex-a53-835769 --fix-cortex-a53-843419 -o /root/work/rtems/rtems/build/aarch64/zynqmp_qemu/testsuites/samples/fileio.exe /root/work/rtems/out/lib/gcc/aarch64-rtems6/13.3.0/nooa/a53/fix835769/fix843419/crti.o /root/work/rtems/out/lib/gcc/aarch64-rtems6/13.3.0/nooa/a53/fix835769/fix843419/crtbegin.o -L. -L/root/work/rtems/rtems/bsps/aarch64/shared/start -L/root/work/rtems/rtems/bsps/aarch64/xilinx-zynqmp/start -L/root/work/rtems/out/lib/gcc/aarch64-rtems6/13.3.0/nooa/a53/fix835769/fix843419 -L/root/work/rtems/out/lib/gcc/aarch64-rtems6/13.3.0/../../../../aarch64-rtems6/lib/nooa/a53/fix835769/fix843419 -L/root/work/rtems/out/lib/gcc/aarch64-rtems6/13.3.0 -L/root/work/rtems/out/lib/gcc/aarch64-rtems6/13.3.0/../../../../aarch64-rtems6/lib testsuites/samples/fileio/init.c.672.o -Bstatic -lrtemscpu -lrtemsbsp -lrtemstest -Bdynamic --gc-sections --wrap=printf --wrap=puts --wrap=putchar -lgcc --start-group -lrtemsbsp -lrtemscpu -latomic -lc -lgcc --end-group -lgcc /root/work/rtems/out/lib/gcc/aarch64-rtems6/13.3.0/nooa/a53/fix835769/fix843419/crtend.o /root/work/rtems/out/lib/gcc/aarch64-rtems6/13.3.0/nooa/a53/fix835769/fix843419/crtn.o -T linkcmds

这里清晰的描述了fileio.exe的ld过程,我们将其替换成rtems-tld的调用方式如下

# rtems-tld -C build/aarch64/zynqmp_qemu/fileio-trace.ini -- -B/root/work/rtems/out/aarch64-rtems6/zynqmp_qemu/lib -qrtems -mcpu=cortex-a53 -O2 -g -ffunction-sections -fdata-sections -Wall -Wmissing-prototypes -Wimplicit-function-declaration -Wstrict-prototypes -Wnested-externs -Wl,--gc-sections -mcpu=cortex-a53 -o /root/work/rtems/rtems/build/aarch64/zynqmp_qemu/testsuites/samples/fileio.exe /root/work/rtems/rtems/build/aarch64/zynqmp_qemu/testsuites/samples/fileio/fileio-init.o -lrtemstest

这里如果是通过ld链接,则系统无法进行trace,如果通过rtems-tld链接,则系统可被trace。效果如下

四、未开启trace的现象

如果通过标准ld,如下验证

# qemu-system-aarch64 -no-reboot -nographic -s -serial mon:stdio -machine xlnx-zcu102 -m 4096 -kernel build/aarch64/zynqmp_qemu/testsuites/samples/fileio.exe *** BEGIN OF TEST FILE I/O *** *** TEST VERSION: 6.0.0.87bf49b7156b9ddf45c218e5d4fa01f27b283db7 *** TEST STATE: USER_INPUT *** TEST BUILD: RTEMS_POSIX_API RTEMS_SMP *** TEST TOOLS: 13.3.0 20240521 (RTEMS 6, RSB b1aec32059aa0e86385ff75ec01daf93713fa382-modified, Newlib 1b3dcfd) Press any key to start file I/O sample (20s remaining) ========================= RTEMS FILE I/O Test Menu ========================= p -> part_table_initialize f -> mount all disks in fs_table l -> list file r -> read file w -> write file s -> start shell Enter your selection ==>s Creating /etc/passwd and group with four useable accounts: root/pwd test/pwd rtems/NO PASSWORD chroot/NO PASSWORD Only the root user has access to all available commands. ========================= starting shell ========================= Welcome to rtems-6.0.0 (AArch64/AArch64-LP64/zynqmp_qemu) Copyright (C) 1989, 2021 RTEMS Project and contributors Login into RTEMS /dev/foobar login: root Password: RTEMS Shell on /dev/foobar. Use 'help' to list commands. SHLL [/] # rtrace status No trace buffer generated code in the application; see rtems-tld

此时我们rtrace status无法看到trace buffer,故此系统无法trace

五、开启trace的现象

我们通过rtems-tld链接的系统,如下现象

# qemu-system-aarch64 -no-reboot -nographic -s -serial mon:stdio -machine xlnx-zcu102 -m 4096 -kernel build/aarch64/zynqmp_qemu/testsuites/samples/fileio.exe *** BEGIN OF TEST FILE I/O *** *** TEST VERSION: 6.0.0.87bf49b7156b9ddf45c218e5d4fa01f27b283db7 *** TEST STATE: USER_INPUT *** TEST BUILD: RTEMS_POSIX_API RTEMS_SMP *** TEST TOOLS: 13.3.0 20240521 (RTEMS 6, RSB b1aec32059aa0e86385ff75ec01daf93713fa382-modified, Newlib 1b3dcfd) Press any key to start file I/O sample (20s remaining) Press any key to start file I/O sample (19s remaining) ========================= RTEMS FILE I/O Test Menu ========================= p -> part_table_initialize f -> mount all disks in fs_table l -> list file r -> read file w -> write file s -> start shell Enter your selection ==>s Creating /etc/passwd and group with four useable accounts: root/pwd test/pwd rtems/NO PASSWORD chroot/NO PASSWORD Only the root user has access to all available commands. ========================= starting shell ========================= Welcome to rtems-6.0.0 (AArch64/AArch64-LP64/zynqmp_qemu) Copyright (C) 1989, 2021 RTEMS Project and contributors Login into RTEMS /dev/foobar login: root Password: RTEMS Shell on /dev/foobar. Use 'help' to list commands. SHLL [/] # rtrace status RTEMS Trace Bufferring: status Running: yes Triggered: yes Level: 0% Traces: 24 SHLL [/] #

此时我们可以看到rtrace的状态

六、总结

至此,我们开启了rtems的trace系统。需要备注的是rtems的默认账号密码是root/pwd

编辑
2025-03-30
记录知识
0

为了更清楚的了解rtems的shell功能,这里以两个例子来演示shell功能。

一、设置登录账号

根据login_check中的rtems_shell_login_check函数,可以发现通过getpwnam_r获取passwd,这里实际调用是getpw_r,如下:

static int getpw_r( const char *name, int uid, struct passwd *pwd, char *buffer, size_t bufsize, struct passwd **result ) { FILE *fp; int match; _libcsupport_pwdgrp_init(); if ((fp = fopen("/etc/passwd", "r")) == NULL) rtems_set_errno_and_return_minus_one( EINVAL ); for(;;) { if (!_libcsupport_scanpw(fp, pwd, buffer, bufsize)) goto error_einval; if (name) { match = (strcmp(pwd->pw_name, name) == 0); } else { match = (pwd->pw_uid == uid); } if (match) { fclose(fp); *result = pwd; return 0; } } error_einval: fclose(fp); rtems_set_errno_and_return_minus_one( EINVAL ); }

所以我们知道rtems的账号信息在passwd文件,我们可以在Init中发现其创建了此文件

writeFile( "/etc/passwd", 0644, "root:$6$$FuPOhnllx6lhW2qqlnmWvZQLJ8Thr/09I7ESTdb9VbnTOn5.65" "/Vh2Mqa6FoKXwT0nHS/O7F0KfrDc6Svb/sH.:0:0:root::/:/bin/sh\n" "rtems::1:1:RTEMS Application::/:/bin/sh\n" "test:$1$$oPu1Xt2Pw0ngIc7LyDHqu1:2:2:test account::/:/bin/sh\n" "tty:*:3:3:tty owner::/:/bin/false\n" "chroot::4:2:chroot account::/chroot:/bin/sh\n" );

这里可以发现,如果我们新增一个用户,则在这里写入信息即可。需要注意的是密码被加密了。我们需要留意加密方式代码如下

crypt_add_format(&crypt_md5_format); crypt_add_format(&crypt_sha512_format); struct crypt_format crypt_md5_format = CRYPT_FORMAT_INITIALIZER(crypt_md5_r, "$1$"); struct crypt_format crypt_sha512_format = CRYPT_FORMAT_INITIALIZER(crypt_sha512_r, "$6$");

也就是我们可以通过md5和sha512两种方式加密

通过python3我们可以测试如下,假设我们设置密码是kylinos,则

>>> print(crypt.crypt("kylinos", "$1$")) $1$$PZwUkYqm0zRLtkgaIFzoq/ >>> print(crypt.crypt("kylinos", "$6$")) $6$$FXse0c1.Rfb8DWjwHmjUZNzufS4shiUUIVwzonK6LcfAlEWAYXPyxwfZhjV0C5.7tlU/ZUgh5e8GI2MPo7W02.

所以我们可以为rtems设置两个账户,一个是rtems,使用md5加密,一个是kylin,使用sha512加密

diff --git a/testsuites/samples/fileio/init.c b/testsuites/samples/fileio/init.c index 084d54d81a..a8e95874ef 100644 --- a/testsuites/samples/fileio/init.c +++ b/testsuites/samples/fileio/init.c @@ -584,10 +584,11 @@ static void fileio_start_shell(void) 0644, "root:$6$$FuPOhnllx6lhW2qqlnmWvZQLJ8Thr/09I7ESTdb9VbnTOn5.65" "/Vh2Mqa6FoKXwT0nHS/O7F0KfrDc6Svb/sH.:0:0:root::/:/bin/sh\n" - "rtems::1:1:RTEMS Application::/:/bin/sh\n" + "rtems:$1$$PZwUkYqm0zRLtkgaIFzoq/:1:1:RTEMS Application::/:/bin/sh\n" "test:$1$$oPu1Xt2Pw0ngIc7LyDHqu1:2:2:test account::/:/bin/sh\n" "tty:*:3:3:tty owner::/:/bin/false\n" "chroot::4:2:chroot account::/chroot:/bin/sh\n" + "kylin:$6$$FXse0c1.Rfb8DWjwHmjUZNzufS4shiUUIVwzonK6LcfAlEWAYXPyxwfZhjV0C5.7tlU/ZUgh5e8GI2MPo7W02.:5:5:KylinOS User::/:/bin/sh\n" );

构建验证即可。可以确认kylin 和 rtems两个账户的密码 kylinos都能登录。

二、shell脚本执行

为了使得rtems支持shell脚本执行,我们需要按照joel的方式编写脚本。我们可以在Init中通过writeScript和rtems_shell_write_file写入shell文件,如下

+ writeScript( + "/kylin", + "#! joel\n" + "echo Hello KylinOS\n" + ); + + rtems_shell_write_file( + "/kylin1", + "#! joel\n" + "echo Hello KylinOS\n" + );

编译后,运行如下:

SHLL [/] # ./kylin Hello KylinOS SHLL [/] # ./kylin1 Unable to execute //kylin1

可以发现rtems_shell_write_file的文件,及时内容正确,但是无法运行,我们需要为其添加权限,如下

SHLL [/] # chmod 0777 /kylin1 SHLL [/] # ./kylin1 Hello KylinOS

三、命令执行

为了内置rtems的命令,可以通过自己实现命令的方式,主要如下:

首先我们添加一个c文件,用作kylin命令,路径为cpukit/libmisc/shell/main_kylin.c内容为:

static int rtems_shell_main_kylin(int argc, char *argv[]) { printf("Kylin Say: Hello World!\n"); return 0; } rtems_shell_cmd_t rtems_shell_KYLIN_Command = { "kylin", /* name */ "kylin # show message", /* usage */ "system1", /* topic */ rtems_shell_main_kylin , /* command */ NULL, /* alias */ NULL /* next */ };

然后我们为其增加到cmd初始化的数组中,改动如下:

diff --git a/cpukit/include/rtems/shellconfig.h b/cpukit/include/rtems/shellconfig.h index 489f281400..2b51967f7b 100644 --- a/cpukit/include/rtems/shellconfig.h +++ b/cpukit/include/rtems/shellconfig.h @@ -95,6 +95,7 @@ extern rtems_shell_cmd_t rtems_shell_HEXDUMP_Command; extern rtems_shell_cmd_t rtems_shell_DEBUGRFS_Command; extern rtems_shell_cmd_t rtems_shell_DF_Command; extern rtems_shell_cmd_t rtems_shell_MD5_Command; +extern rtems_shell_cmd_t rtems_shell_KYLIN_Command; extern rtems_shell_cmd_t rtems_shell_RTC_Command; extern rtems_shell_cmd_t rtems_shell_SPI_Command; @@ -606,6 +607,7 @@ extern rtems_shell_alias_t * const rtems_shell_Initial_aliases[]; #if defined(CONFIGURE_SHELL_USER_COMMANDS) CONFIGURE_SHELL_USER_COMMANDS, #endif + &rtems_shell_KYLIN_Command, NULL };

最后我们添加kylin命令的编译,如下

diff --git a/spec/build/cpukit/objshell.yml b/spec/build/cpukit/objshell.yml index 2eaf4e17cf..09fdbaab8a 100644 --- a/spec/build/cpukit/objshell.yml +++ b/spec/build/cpukit/objshell.yml @@ -38,6 +38,7 @@ source: - cpukit/libmisc/shell/main_chdir.c - cpukit/libmisc/shell/main_chmod.c - cpukit/libmisc/shell/main_chroot.c +- cpukit/libmisc/shell/main_kylin.c - cpukit/libmisc/shell/main_cmdchmod.c - cpukit/libmisc/shell/main_cmdchown.c - cpukit/libmisc/shell/main_cmdls.c

编译运行后,我们可以直接调用kylin命令如下:

SHLL [/] # kylin Kylin Say: Hello World!
编辑
2025-03-30
记录知识
0

rtems支持简易的shell功能,本文基于rtems,进行shell的代码分析,了解其实现原理

一、初始化shell

为了让rtems支持shell,我们可以调用rtems_shell_init函数来初始化shell。这里举例如下:

rtems_shell_init( "SHLL", /* task_name */ RTEMS_MINIMUM_STACK_SIZE * 5, /* task_stacksize */ 100, /* task_priority */ "/dev/foobar", /* devname */ /* device is currently ignored by the shell if it is not a pty */rtems_shell_login_check false, /* forever */ true, /* wait */ rtems_shell_login_check /* login */ );

这里解释如下:

* @param task_name Name of the shell task. * @param task_stacksize The size of the stack. If 0 the default size is used. * @param task_priority The priority the shell runs at. * @param forever Repeat logins. * @param wait Caller should block until shell exits. * @param login_check User login check function, NULL disables login checks.

二、代码入口

而rtems_shell_init的实际实现是通过rtems_shell_run实现,如下:

return rtems_shell_run( task_name, /* task_name */ task_stacksize, /* task_stacksize */ task_priority, /* task_priority */ devname, /* devname */ forever, /* forever */ wait, /* wait */ "stdin", /* input */ "stdout", /* output */ false, /* output_append */ to_wake, /* wake_on_end */ false, /* echo */ login_check /* login check */ );

这里我们重点看两个函数:

sc = rtems_task_create( name, task_priority, task_stacksize, RTEMS_PREEMPT | RTEMS_TIMESLICE | RTEMS_NO_ASR, RTEMS_LOCAL | RTEMS_FLOATING_POINT, &task_id ); sc = rtems_task_start(task_id, rtems_shell_task, (rtems_task_argument) shell_env);

关于task的create和start,这里关于Init函数的时候解析过了。我们注意entry_point函数rtems_shell_task,其实现如下:

static rtems_task rtems_shell_task(rtems_task_argument task_argument) { rtems_shell_env_t *shell_env = (rtems_shell_env_t*) task_argument; rtems_id wake_on_end = shell_env->wake_on_end; rtems_shell_main_loop( shell_env ); rtems_shell_clear_shell_std_handles(); if (wake_on_end != RTEMS_INVALID_ID) rtems_event_send (wake_on_end, RTEMS_EVENT_1); rtems_task_exit(); }

这里重点函数是rtems_shell_main_loop,它调用

result = shell_main_loop(shell_env, interactive, line_editor_output);

shell_main_loop是shell的主要实现。

三、流程解析

3.1 登录提示

对于console,第一时间可以看到如下日志:

Welcome to rtems-6.0.0 (AArch64/AArch64-LP64/zynqmp_qemu) Copyright (C) 1989, 2021 RTEMS Project and contributors Login into RTEMS

此实现主要在rtems_shell_login函数,它会校验devname字段,如果不是pty,则打开issue文件,判断类型,issue文件内容如下:

static void rtems_shell_init_once(void) { struct passwd pwd; struct passwd *pwd_res; pthread_key_create(&rtems_shell_current_env_key, rtems_shell_env_free); /* dummy call to init /etc dir */ getpwuid_r(0, &pwd, NULL, 0, &pwd_res); rtems_shell_create_file("etc/issue", "\n" "Welcome to @V\\n" "Login into @S\\n"); rtems_shell_create_file("/etc/issue.net", "\n" "Welcome to %v\n" "running on %m\n"); rtems_shell_init_commands(); rtems_shell_register_monitor_commands(); }

这里rtems_shell_init_environment会被rtems_shell_run调用。同样的,我们可以在shell查看issue的值,如下:

SHLL [/] # cat etc/issue Welcome to @V\nLogin into @S\n

所以在rtems_shell_login中会读到V和S,如下:

static bool rtems_shell_login(rtems_shell_env_t *env, FILE * in,FILE * out) { FILE *fd; int c; time_t t; if (out) { if ((env->devname[5]!='p')|| (env->devname[6]!='t')|| (env->devname[7]!='y')) { fd = fopen("/etc/issue","r"); if (fd) { while ((c = fgetc(fd)) != EOF) { if (c=='@') { switch (c = fgetc(fd)) { ...... case 'S': fprintf(out,"RTEMS"); break; case 'V': fprintf( out, "%s\n%s", rtems_get_version_string(), rtems_get_copyright_notice() ); break; case '@': fprintf(out,"@"); break; default : fprintf(out,"@%c",c); break; } } else if (c=='\\') { switch(c=fgetc(fd)) { case '\\': fprintf(out,"\\"); break; case 'b': fprintf(out,"\b"); break; case 'f': fprintf(out,"\f"); break; case 'n': fprintf(out,"\n"); break; case 'r': fprintf(out,"\r"); break; case 's': fprintf(out," "); break; case 't': fprintf(out,"\t"); break; case '@': fprintf(out,"@"); break; } } else { fputc(c,out); } } fclose(fd); } } }

对于S,直接输出RTEMS即可,对于V,输出_RTEMS_version和_Copyright_Notice。这两个全局变量定义如下:

const char _RTEMS_version[] = "rtems-" RTEMS_VERSION " (" CPU_NAME "/" CPU_MODEL_NAME "/" RTEMS_XSTRING( RTEMS_BSP ) ")"; RTEMS_SECTION(".rtemsroset.copyright") const char _Copyright_Notice[] = "Copyright (C) 1989, 2021 RTEMS Project and contributors";

3.2 login输入

对于login,其显示如下:

/dev/asdasd login: root Password:

在上述提示完成之后,函数rtems_shell_login_prompt可显示如上信息。简要实现如下

for (i = 0; i < 3; ++i) { char user [32]; char passphrase [128]; fprintf( out, "%s login: ", device ); fflush( out ); result = rtems_shell_get_text( in, out, user, sizeof(user) ); if ( !result ) break; if (0 == strlen(user)) continue; fflush( in); fprintf( out, "Password: "); fflush( out); result = rtems_shell_get_text( in, NULL, passphrase, sizeof(passphrase) ); if ( !result ) break; fputc( '\n', out); result = check( user, passphrase ); if (result) break; fprintf( out, "Login incorrect\n\n"); sleep( 2); }

这里提供了3次机会,每次通过rtems_shell_get_text获取文本,rtems_shell_get_text是通过c库的getc获取字符,最后通过check回调检查是否登录正常。这里的check回调是rtems_shell_login_check函数,此函数校验密码。

3.3 等待用户输入

在登录完成之后,shell开始等待用户输入,其实现函数如下:

for (;;) { const char *c; int argc; char *argv[RTEMS_SHELL_MAXIMUM_ARGUMENTS]; /* Prompt section */ if (prompt) { rtems_shell_get_prompt(shell_env, prompt, RTEMS_SHELL_PROMPT_SIZE); } /* getcmd section */ cmd = rtems_shell_line_editor(cmds, cmd_count, RTEMS_SHELL_CMD_SIZE, prompt, stdin, line_editor_output); if (cmd == -1) continue; /* empty line */ if (cmd == -2) { result = false; break; /*EOF*/ } line++; if (shell_env->echo) fprintf(stdout, "%d: %s\n", line, cmds[cmd]); /* evaluate cmd section */ c = cmds[cmd]; while (*c) { if (!isblank((unsigned char)*c)) break; c++; } if (*c == '\0') /* empty line */ continue; if (*c == '#') { /* comment character */ cmds[cmd][0] = 0; continue; } if (!strcmp(cmds[cmd],"bye") || !strcmp(cmds[cmd],"exit")) { fprintf(stdout, "Shell exiting\n" ); break; } /* exec cmd section */ /* TODO: * To avoid user crash catch the signals. * Open a new stdio files with posibility of redirection * * Run in a new shell task background. (unix &) * Resuming. A little bash. */ memcpy (cmd_argv, cmds[cmd], RTEMS_SHELL_CMD_SIZE); if (!rtems_shell_make_args(cmd_argv, &argc, argv, RTEMS_SHELL_MAXIMUM_ARGUMENTS)) { int exit_code; rtems_shell_winsize(); exit_code = rtems_shell_execute_cmd(argv[0], argc, argv); if (shell_env->exit_code != NULL) *shell_env->exit_code = exit_code; if (exit_code != 0 && shell_env->exit_on_error) shell_env->exit_shell = true; } /* end exec cmd section */ if (shell_env->exit_shell) break; }

首先我们留意shell的左边,如下

SHLL [/] #

此代码实现为rtems_shell_get_prompt

void rtems_shell_get_prompt( rtems_shell_env_t *shell_env, char *prompt, size_t size ) { char buf[256]; char *cwd; /* XXX: show_prompt user adjustable */ cwd = getcwd(buf,sizeof(buf)); cwd = cwd != NULL ? cwd : "?"; snprintf(prompt, size - 1, "%s%s[%s] %c ", ((shell_env->taskname) ? shell_env->taskname : ""), ((shell_env->taskname) ? " " : ""), cwd, geteuid()?'$':'#'); }

然后通过rtems_shell_line_editor获取用户的输入信息,这里实现了rtems_shell_getchar封装的fgetc获取每行的输入

最后通过rtems_shell_execute_cmd执行代码,其实现如下

int rtems_shell_execute_cmd(const char *cmd, int argc, char *argv[]) { rtems_shell_cmd_t *shell_cmd; if (argv[0] == NULL) { return -1; } shell_cmd = rtems_shell_lookup_cmd(argv[0]); if (shell_cmd != NULL && !rtems_shell_can_see_cmd(shell_cmd)) { shell_cmd = NULL; } if (shell_cmd == NULL) { return rtems_shell_script_file(argc, argv); } else if (rtems_shell_can_execute_cmd(shell_cmd)) { return shell_cmd->command(argc, argv); } else { fprintf(stderr, "%s: Permission denied\n", cmd); return -1; } }

这里有脚本方式和命令行方式

3.4 脚本运行

脚本运行的主要函数是rtems_shell_script_file,它在rtems_shell_main_joel函数中根据rtems_shell_script来实现脚本运行,如下

result = rtems_shell_script( taskName, /* the name of the task */ stackSize, /* stack size */ taskPriority, /* task priority */ scriptFile, /* the script file */ outputFile, /* where to redirect the script */ 0, /* run once and exit */ 1, /* we will wait */ verbose /* do we echo */ );

3.5 命令运行

根据上面分析,脚本运行方式最后也是执行的命令,最终函数在shell_cmd->command(argc, argv);这里的command函数由自己实现在rtems_shell_Initial_commands/rtems_shell_Initial_aliases中。这里由rtems_shell_init_once调用时会主动调用rtems_shell_init_commands

static void rtems_shell_init_commands(void) { rtems_shell_cmd_t * const *c; rtems_shell_alias_t * const *a; for ( c = rtems_shell_Initial_commands ; *c ; c++ ) { rtems_shell_add_cmd_struct( *c ); } for ( a = rtems_shell_Initial_aliases ; *a ; a++ ) { rtems_shell_alias_cmd( (*a)->name, (*a)->alias ); } }

四、参考文献