编辑
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 ); } }

四、参考文献

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

RTEMS支持多种调度算法,默认情况下是基于优先级的调度器,为了了解这些调度器算法,本文列出RTEMS支持的调度器种类,方便后续逐一进行调度器的测试研究

一、简单优先级调度

Simple Priority Scheduler是基于优先级调度的一种简化算法,其初始化如下:

#define CONFIGURE_SCHEDULER_TABLE_ENTRIES \ RTEMS_SCHEDULER_TABLE_SIMPLE( dflt, CONFIGURE_SCHEDULER_NAME ) #define SCHEDULER_SIMPLE_ENTRY_POINTS \ { \ _Scheduler_simple_Initialize, /* initialize entry point */ \ _Scheduler_simple_Schedule, /* schedule entry point */ \ _Scheduler_simple_Yield, /* yield entry point */ \ _Scheduler_simple_Block, /* block entry point */ \ _Scheduler_simple_Unblock, /* unblock entry point */ \ _Scheduler_simple_Update_priority, /* update priority entry point */ \ _Scheduler_default_Map_priority, /* map priority entry point */ \ _Scheduler_default_Unmap_priority, /* unmap priority entry point */ \ SCHEDULER_DEFAULT_SMP_OPERATIONS \ _Scheduler_default_Node_initialize, /* node initialize entry point */ \ _Scheduler_default_Node_destroy, /* node destroy entry point */ \ _Scheduler_default_Release_job, /* new period of task */ \ _Scheduler_default_Cancel_job, /* cancel period of task */ \ _Scheduler_default_Start_idle /* start idle entry point */ \ SCHEDULER_DEFAULT_SET_AFFINITY_OPERATION \ }

二、优先级调度

Priority Scheduler是默认的基于优先级的调度器,其初始化如下

#define RTEMS_SCHEDULER_TABLE_PRIORITY( name, obj_name ) \ { \ &SCHEDULER_PRIORITY_CONTEXT_NAME( name ).Base.Base, \ SCHEDULER_PRIORITY_ENTRY_POINTS, \ RTEMS_ARRAY_SIZE( \ SCHEDULER_PRIORITY_CONTEXT_NAME( name ).Ready \ ) - 1, \ ( obj_name ) \ SCHEDULER_CONTROL_IS_NON_PREEMPT_MODE_SUPPORTED( true ) \ } #define SCHEDULER_PRIORITY_ENTRY_POINTS \ { \ _Scheduler_priority_Initialize, /* initialize entry point */ \ _Scheduler_priority_Schedule, /* schedule entry point */ \ _Scheduler_priority_Yield, /* yield entry point */ \ _Scheduler_priority_Block, /* block entry point */ \ _Scheduler_priority_Unblock, /* unblock entry point */ \ _Scheduler_priority_Update_priority, /* update priority entry point */ \ _Scheduler_default_Map_priority, /* map priority entry point */ \ _Scheduler_default_Unmap_priority, /* unmap priority entry point */ \ SCHEDULER_DEFAULT_SMP_OPERATIONS \ _Scheduler_priority_Node_initialize, /* node initialize entry point */ \ _Scheduler_default_Node_destroy, /* node destroy entry point */ \ _Scheduler_default_Release_job, /* new period of task */ \ _Scheduler_default_Cancel_job, /* cancel period of task */ \ _Scheduler_default_Start_idle /* start idle entry point */ \ SCHEDULER_DEFAULT_SET_AFFINITY_OPERATION \ }

三、最早截止时间优先调度

Earliest Deadline First调度按照任务的截止时间来确定任务优先级,其初始化如下

#define RTEMS_SCHEDULER_TABLE_EDF( name, obj_name ) \ { \ &SCHEDULER_EDF_CONTEXT_NAME( name ).Base, \ SCHEDULER_EDF_ENTRY_POINTS, \ SCHEDULER_EDF_MAXIMUM_PRIORITY, \ ( obj_name ) \ SCHEDULER_CONTROL_IS_NON_PREEMPT_MODE_SUPPORTED( true ) \ } #define SCHEDULER_EDF_ENTRY_POINTS \ { \ _Scheduler_EDF_Initialize, /* initialize entry point */ \ _Scheduler_EDF_Schedule, /* schedule entry point */ \ _Scheduler_EDF_Yield, /* yield entry point */ \ _Scheduler_EDF_Block, /* block entry point */ \ _Scheduler_EDF_Unblock, /* unblock entry point */ \ _Scheduler_EDF_Update_priority, /* update priority entry point */ \ _Scheduler_EDF_Map_priority, /* map priority entry point */ \ _Scheduler_EDF_Unmap_priority, /* unmap priority entry point */ \ SCHEDULER_DEFAULT_SMP_OPERATIONS \ _Scheduler_EDF_Node_initialize, /* node initialize entry point */ \ _Scheduler_default_Node_destroy, /* node destroy entry point */ \ _Scheduler_EDF_Release_job, /* new period of task */ \ _Scheduler_EDF_Cancel_job, /* cancel period of task */ \ _Scheduler_default_Start_idle /* start idle entry point */ \ SCHEDULER_DEFAULT_SET_AFFINITY_OPERATION \ }

四、恒定带宽调度

Constant Bandwidth Server调度是基于EDF的扩展,默认情况下,给任务分配固定的带宽(预算),然后再通过截止时间进行调度

#define RTEMS_SCHEDULER_TABLE_CBS( name, obj_name ) \ { \ &SCHEDULER_CBS_CONTEXT_NAME( name ).Base, \ SCHEDULER_CBS_ENTRY_POINTS, \ SCHEDULER_CBS_MAXIMUM_PRIORITY, \ ( obj_name ) \ SCHEDULER_CONTROL_IS_NON_PREEMPT_MODE_SUPPORTED( true ) \ } #define SCHEDULER_CBS_ENTRY_POINTS \ { \ _Scheduler_EDF_Initialize, /* initialize entry point */ \ _Scheduler_EDF_Schedule, /* schedule entry point */ \ _Scheduler_EDF_Yield, /* yield entry point */ \ _Scheduler_EDF_Block, /* block entry point */ \ _Scheduler_CBS_Unblock, /* unblock entry point */ \ _Scheduler_EDF_Update_priority, /* update priority entry point */ \ _Scheduler_EDF_Map_priority, /* map priority entry point */ \ _Scheduler_EDF_Unmap_priority, /* unmap priority entry point */ \ SCHEDULER_DEFAULT_SMP_OPERATIONS \ _Scheduler_CBS_Node_initialize, /* node initialize entry point */ \ _Scheduler_default_Node_destroy, /* node destroy entry point */ \ _Scheduler_CBS_Release_job, /* new period of task */ \ _Scheduler_CBS_Cancel_job, /* cancel period of task */ \ _Scheduler_default_Start_idle /* start idle entry point */ \ SCHEDULER_DEFAULT_SET_AFFINITY_OPERATION \ }

五、基于SMP的调度器扩展

为了支持SMP,简单优先级,优先级,EDF都做了SMP的扩展支持,如下

#define RTEMS_SCHEDULER_TABLE_SIMPLE_SMP( name, obj_name ) \ { \ &SCHEDULER_SIMPLE_SMP_CONTEXT_NAME( name ).Base.Base, \ SCHEDULER_SIMPLE_SMP_ENTRY_POINTS, \ SCHEDULER_SIMPLE_SMP_MAXIMUM_PRIORITY, \ ( obj_name ) \ SCHEDULER_CONTROL_IS_NON_PREEMPT_MODE_SUPPORTED( false ) \ } #define RTEMS_SCHEDULER_TABLE_PRIORITY_SMP( name, obj_name ) \ { \ &SCHEDULER_PRIORITY_SMP_CONTEXT_NAME( name ).Base.Base.Base, \ SCHEDULER_PRIORITY_SMP_ENTRY_POINTS, \ RTEMS_ARRAY_SIZE( \ SCHEDULER_PRIORITY_SMP_CONTEXT_NAME( name ).Ready \ ) - 1, \ ( obj_name ) \ SCHEDULER_CONTROL_IS_NON_PREEMPT_MODE_SUPPORTED( false ) \ } #define RTEMS_SCHEDULER_TABLE_EDF_SMP( name, obj_name ) \ { \ &SCHEDULER_EDF_SMP_CONTEXT_NAME( name ).Base.Base.Base, \ SCHEDULER_EDF_SMP_ENTRY_POINTS, \ SCHEDULER_EDF_MAXIMUM_PRIORITY, \ ( obj_name ) \ SCHEDULER_CONTROL_IS_NON_PREEMPT_MODE_SUPPORTED( false ) \ }

六、其他调度器

除了上述调度器,还有基于CPU亲和性优先级的SMP调度器,如RTEMS_SCHEDULER_TABLE_PRIORITY_AFFINITY_SMP,他会根据CPU的亲和性来调整基于优先级的调度器,可以确保某个任务只允许运行在某个CPU上

还有基于抢占式的SMP调度器,并支持CPU的亲和性设置

七、系统默认调度器

系统默认调度器是优先级调度,其定义如下:

#if !defined(CONFIGURE_SCHEDULER_CBS) \ && !defined(CONFIGURE_SCHEDULER_EDF) \ && !defined(CONFIGURE_SCHEDULER_EDF_SMP) \ && !defined(CONFIGURE_SCHEDULER_PRIORITY) \ && !defined(CONFIGURE_SCHEDULER_PRIORITY_AFFINITY_SMP) \ && !defined(CONFIGURE_SCHEDULER_PRIORITY_SMP) \ && !defined(CONFIGURE_SCHEDULER_SIMPLE) \ && !defined(CONFIGURE_SCHEDULER_SIMPLE_SMP) \ && !defined(CONFIGURE_SCHEDULER_STRONG_APA) \ && !defined(CONFIGURE_SCHEDULER_USER) #if defined(RTEMS_SMP) && _CONFIGURE_MAXIMUM_PROCESSORS > 1 #define CONFIGURE_SCHEDULER_EDF_SMP #else #define CONFIGURE_SCHEDULER_PRIORITY #endif #endif

其配置作用如下:

#ifdef CONFIGURE_SCHEDULER /* * Ignore these warnings: * * - invalid use of structure with flexible array member * * - struct has no members */ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpedantic" CONFIGURE_SCHEDULER; #pragma GCC diagnostic pop #endif const Scheduler_Control _Scheduler_Table[] = { CONFIGURE_SCHEDULER_TABLE_ENTRIES };

然后我们将宏定义展开,如下:

static struct { Scheduler_priority_Context Base; Chain_Control Ready[ ( 255 + 1 ) ]; } _Configuration_Scheduler_priority_dflt; const Scheduler_Control _Scheduler_Table[] = { { &_Configuration_Scheduler_priority_dflt.Base.Base, { _Scheduler_priority_Initialize, _Scheduler_priority_Schedule, _Scheduler_priority_Yield, _Scheduler_priority_Block, _Scheduler _priority_Unblock, _Scheduler_priority_Update_priority, _Scheduler_default_Map_priority, _Scheduler_default_Unmap_priority,

可以看到我们配置的默认调度器就是优先级调度,且有且就1个。还记得之前查看初始化代码的时候,我们在rtems_initialize_data_structures函数,会调用_Scheduler_Handler_initialization,其实现如下

void _Scheduler_Handler_initialization(void) { size_t n; size_t i; n = _Scheduler_Count; for ( i = 0 ; i < n ; ++i ) { const Scheduler_Control *scheduler; #if defined(RTEMS_SMP) Scheduler_Context *context; #endif scheduler = &_Scheduler_Table[ i ]; #if defined(RTEMS_SMP) context = _Scheduler_Get_context( scheduler ); #endif _ISR_lock_Initialize( &context->Lock, "Scheduler" ); ( *scheduler->Operations.initialize )( scheduler ); } }

这里会直接调用调度器的initialize函数。至此就开展调度器的初始化了

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

rtems作为操作系统,支持任务的调度,对应于程序而言,我们需要清楚的知道任务的调度场景,本文结合rtems的调度结构体,描述一下rtems支持的调度场景

一、RTEMS结构体

默认结构体如下:

typedef struct { /** @see _Scheduler_Handler_initialization() */ void ( *initialize )( const Scheduler_Control * ); /** @see _Scheduler_Schedule() */ void ( *schedule )( const Scheduler_Control *, Thread_Control *); /** @see _Scheduler_Yield() */ void ( *yield )( const Scheduler_Control *, Thread_Control *, Scheduler_Node * ); /** @see _Scheduler_Block() */ void ( *block )( const Scheduler_Control *, Thread_Control *, Scheduler_Node * ); /** @see _Scheduler_Unblock() */ void ( *unblock )( const Scheduler_Control *, Thread_Control *, Scheduler_Node * ); /** @see _Scheduler_Update_priority() */ void ( *update_priority )( const Scheduler_Control *, Thread_Control *, Scheduler_Node * ); /** @see _Scheduler_Map_priority() */ Priority_Control ( *map_priority )( const Scheduler_Control *, Priority_Control ); /** @see _Scheduler_Unmap_priority() */ Priority_Control ( *unmap_priority )( const Scheduler_Control *, Priority_Control ); #if defined(RTEMS_SMP) /** * @brief Ask for help operation. * * @param[in] scheduler The scheduler instance to ask for help. * @param[in] the_thread The thread needing help. * @param[in] node The scheduler node. * * @retval true Ask for help was successful. * @retval false Otherwise. */ bool ( *ask_for_help )( const Scheduler_Control *scheduler, Thread_Control *the_thread, Scheduler_Node *node ); /** * @brief Reconsider help operation. * * @param[in] scheduler The scheduler instance to reconsider the help * request. * @param[in] the_thread The thread reconsidering a help request. * @param[in] node The scheduler node. */ void ( *reconsider_help_request )( const Scheduler_Control *scheduler, Thread_Control *the_thread, Scheduler_Node *node ); /** * @brief Withdraw node operation. * * @param[in] scheduler The scheduler instance to withdraw the node. * @param[in] the_thread The thread using the node. * @param[in] node The scheduler node to withdraw. * @param[in] next_state The next thread scheduler state in case the node is * scheduled. */ void ( *withdraw_node )( const Scheduler_Control *scheduler, Thread_Control *the_thread, Scheduler_Node *node, Thread_Scheduler_state next_state ); /** * @brief Makes the node sticky. * * This operation is used by _Thread_Priority_update_and_make_sticky(). It * is only called for the scheduler node of the home scheduler. * * Uniprocessor schedulers schould provide * _Scheduler_default_Sticky_do_nothing() for this operation. * * SMP schedulers should provide this operation using * _Scheduler_SMP_Make_sticky(). * * The make and clean sticky operations are an optimization to simplify the * control flow in the update priority operation. The update priority * operation is used for all scheduler nodes and not just the scheduler node * of home schedulers. The update priority operation is a commonly used * operations together with block and unblock. The make and clean sticky * operations are used only in specific scenarios. * * @param scheduler is the scheduler of the node. * * @param[in, out] the_thread is the thread owning the node. * * @param[in, out] node is the scheduler node to make sticky. */ void ( *make_sticky )( const Scheduler_Control *scheduler, Thread_Control *the_thread, Scheduler_Node *node ); /** * @brief Cleans the sticky property from the node. * * This operation is used by _Thread_Priority_update_and_clean_sticky(). It * is only called for the scheduler node of the home scheduler. * * Uniprocessor schedulers schould provide * _Scheduler_default_Sticky_do_nothing() for this operation. * * SMP schedulers should provide this operation using * _Scheduler_SMP_Clean_sticky(). * * @param scheduler is the scheduler of the node. * * @param[in, out] the_thread is the thread owning the node. * * @param[in, out] node is the scheduler node to clean the sticky property. */ void ( *clean_sticky )( const Scheduler_Control *scheduler, Thread_Control *the_thread, Scheduler_Node *node ); /** * @brief Pin thread operation. * * @param[in] scheduler The scheduler instance of the specified processor. * @param[in] the_thread The thread to pin. * @param[in] node The scheduler node of the thread. * @param[in] cpu The processor to pin the thread. */ void ( *pin )( const Scheduler_Control *scheduler, Thread_Control *the_thread, Scheduler_Node *node, struct Per_CPU_Control *cpu ); /** * @brief Unpin thread operation. * * @param[in] scheduler The scheduler instance of the specified processor. * @param[in] the_thread The thread to unpin. * @param[in] node The scheduler node of the thread. * @param[in] cpu The processor to unpin the thread. */ void ( *unpin )( const Scheduler_Control *scheduler, Thread_Control *the_thread, Scheduler_Node *node, struct Per_CPU_Control *cpu ); /** * @brief Add processor operation. * * @param[in] scheduler The scheduler instance to add the processor. * @param[in] idle The idle thread of the processor to add. */ void ( *add_processor )( const Scheduler_Control *scheduler, Thread_Control *idle ); /** * @brief Remove processor operation. * * @param[in] scheduler The scheduler instance to remove the processor. * @param[in] cpu The processor to remove. * * @return The idle thread of the removed processor. */ Thread_Control *( *remove_processor )( const Scheduler_Control *scheduler, struct Per_CPU_Control *cpu ); #endif /** @see _Scheduler_Node_initialize() */ void ( *node_initialize )( const Scheduler_Control *, Scheduler_Node *, Thread_Control *, Priority_Control ); /** @see _Scheduler_Node_destroy() */ void ( *node_destroy )( const Scheduler_Control *, Scheduler_Node * ); /** @see _Scheduler_Release_job() */ void ( *release_job ) ( const Scheduler_Control *, Thread_Control *, Priority_Node *, uint64_t, Thread_queue_Context * ); /** @see _Scheduler_Cancel_job() */ void ( *cancel_job ) ( const Scheduler_Control *, Thread_Control *, Priority_Node *, Thread_queue_Context * ); /** @see _Scheduler_Start_idle() */ void ( *start_idle )( const Scheduler_Control *, Thread_Control *, struct Per_CPU_Control * ); #if defined(RTEMS_SMP) /** @see _Scheduler_Set_affinity() */ Status_Control ( *set_affinity )( const Scheduler_Control *, Thread_Control *, Scheduler_Node *, const Processor_mask * ); #endif } Scheduler_Operations;

针对结构体的成员,我们知道需要实现上述的函数回调,这些回调有RTEMS的应用程序和系统下发。下面主要介绍其调度的场景

二、调度器初始化

_Scheduler_Handler_initialization作为调度器的初始化函数,在RTEMS初始化流程中,我们知道rtems_initialize_data_structures会初始化调度器,其函数为_Scheduler_Handler_initialization,这里会根据_Scheduler_Table的值,对每种调度器进行初始化。

三、执行调度

_Scheduler_Schedule可以标记当前线程可以调度,这种情况下,修改当前线程的heir,并设置需要被调度。然后等待系统调度

四、主动让出

_Scheduler_Yield作为让出调度的函数,提供给用户程序用来主动让出当前任务,让调度器选择下一个任务

五、阻塞任务调度

_Scheduler_Block函数可以主动阻塞当前任务,并将自身任务从就绪队列移除,然后修改自身的heir,等待调度

六、恢复阻塞任务调度

_Scheduler_Unblock_Scheduler_Block成对,将自身添加到就绪队列中,然后修改自身的heir,等待调度

七、更新任务优先级

_Scheduler_Update_priority会遍历调度器的所有任务,修改其任务优先级。

八、映射优先级到调度器

_Scheduler_Map_priority将指定的优先级从应用程序域映射到调度器域

九、映射优先级到应用

_Scheduler_Unmap_priority将指定的优先级从调度器域映射到应用程序域

十、任务的调度器节点初始化/销毁

_Scheduler_Node_initialize在线程创建时,默认初始化本线程的调度节点

_Scheduler_Node_destroy在线程销毁时,销毁线程的调度节点

十一、周期任务创建和销毁

_Scheduler_Release_job将任务释放出来,用作周期任务

_Scheduler_Cancel_job将周期任务取消,用作周期任务

十二、启动空闲任务

_Scheduler_Start_idle为调度器启动空闲任务