VxWorks 6.9下开机脚本启动程序的printf调试信息重定向问题
我来帮你拆解这个问题并给出可行的解决方案——你遇到的核心问题其实和SHELL_MAX_SESSIONS关系不大,而是VxWorks中shell会话的I/O隔离机制导致的。
为什么会出现这个现象?
VxWorks里每个shell会话(不管是串口初始化的,还是后来的Telnet连接)都是独立的实例,拥有自己的标准输入/输出/错误文件描述符。当你通过开机脚本启动C程序时,程序会继承启动它的那个初始串口shell的stdio上下文,所以所有printf输出只会发送到那个串口会话的fd上;而手动在Telnet会话启动程序时,程序继承的是当前Telnet会话的stdio,自然能在当前会话看到输出。SHELL_MAX_SESSIONS只是控制允许同时存在的会话数量,完全不负责同步不同会话的输出,所以调整它的数值根本解决不了问题。
可行的解决方案
要让程序的输出同步到所有已打开的shell会话,有两种比较直接的思路:
1. 自定义多会话输出函数替代printf
这是最灵活的方案,不需要修改内核配置,只需要在你的C程序里实现一个遍历所有活跃shell会话并发送输出的函数,替换掉原来的printf。
首先,你需要确保内核配置了INCLUDE_SHELL_INFO(默认可能没开,需要在Wind River Workbench的内核配置里勾选),这个选项允许我们通过shellInfoGet()函数获取所有活跃shell的信息。
下面是一个示例实现:
#include <shellLib.h> #include <stdio.h> #include <stdarg.h> #include <unistd.h> #include <string.h> // 定义最大支持的会话数,根据你的实际场景调整 #define MAX_ACTIVE_SESSIONS 32 void multi_printf(const char *format, ...) { va_list args; char output_buf[2048]; // 缓冲区大小按需调整 SHELL_INFO session_list[MAX_ACTIVE_SESSIONS]; int session_count; // 第一步:把格式化内容写入缓冲区 va_start(args, format); vsnprintf(output_buf, sizeof(output_buf), format, args); va_end(args); // 第二步:获取所有活跃的shell会话信息 session_count = shellInfoGet(session_list, MAX_ACTIVE_SESSIONS); for (int i = 0; i < session_count; i++) { // 跳过无效会话(比如未初始化的shell) if (session_list[i].shellId == 0 || session_list[i].fdStdout == -1) { continue; } // 将缓冲区内容写入当前会话的stdout write(session_list[i].fdStdout, output_buf, strlen(output_buf)); } // 兼容原有逻辑:同时输出到程序继承的原始stdout printf("%s", output_buf); }
使用方法很简单:把你程序里所有printf("xxx")替换成multi_printf("xxx")即可。这样不管程序是从哪个会话启动的,输出都会同步到所有当前活跃的串口/Telnet会话。
2. 利用系统日志机制转发输出
如果你不想修改程序代码,可以考虑把程序的printf输出重定向到VxWorks的系统日志,然后配置日志输出到所有shell会话。
- 首先,在程序启动时,将stdout重定向到日志设备:
// 在程序初始化时执行 dup2(logFdGet(), STDOUT_FILENO); dup2(logFdGet(), STDERR_FILENO); - 然后,编写一个辅助函数(或者在开机脚本中调用C函数),遍历所有活跃shell会话,把它们的stdout文件描述符添加到日志输出集合中:
#include <shellLib.h> #include <logLib.h> void log_to_all_shells(void) { SHELL_INFO session_list[MAX_ACTIVE_SESSIONS]; int session_count = shellInfoGet(session_list, MAX_ACTIVE_SESSIONS); for (int i = 0; i < session_count; i++) { if (session_list[i].shellId != 0 && session_list[i].fdStdout != -1) { logFdAdd(session_list[i].fdStdout); } } } - 最后,在开机脚本中启动程序前调用这个函数,或者把它作为系统初始化任务的一部分。
不过这种方法有个小缺点:logMsg(也就是系统日志)的输出格式会带有时间戳等前缀,如果你需要和原来的printf完全一致的输出格式,第一种方案更合适。
注意事项
- 使用
shellInfoGet()时,要注意会话的动态变化:如果有新的Telnet会话在程序启动后连接,你的multi_printf每次调用都会重新获取最新的会话列表,所以新会话也能看到后续的输出。 - 缓冲区大小要根据你实际的输出内容调整,避免截断长输出。
内容的提问来源于stack exchange,提问作者accpnt




