Buildroot 增加自启动项
- 概述
- 增加模块
- 源码结构
- 编写测试程序
- 编译测试
- 增加系统自启动
- 一个问题
- 解决方案:显式指定输入设备
- 其他
- /etc/init.d 目录下的 SXXxxx 文件作用解析
概述
Buildroot 是一款轻量级、高度可定制的开源工具集,专为嵌入式系统打造。它通过自动化构建流程(交叉编译工具链、Linux内核、根文件系统、引导程序),将复杂的嵌入式开发简化为“菜单配置”(make menuconfig)。开发者无需手动处理依赖与兼容性问题,只需勾选所需软件包,即可生成完整的可烧录镜像。在嵌入式场景中,服务进程的自动加载是刚需, 通常将启动脚本置于/etc/init.d/(SysV init)或编写systemd单元文件,实现服务的全自动托管。
增加模块
参考: Buildroot 添加自定义模块-内置文件到文件系统
–本文不再复述–
源码结构
package/auto_run/
├── CMakeLists.txt
├── Config.in
├── dumpsys.cpp
├── auto_run.mk
└── S99dumpsys
编写测试程序
dumpsys.cpp
#include <stdio.h>
#include <string.h>
#include <sys/statvfs.h>
#include <unistd.h>
void print_memory() {
FILE *meminfo = fopen("/proc/meminfo", "r");
if (!meminfo) {
printf("无法读取内存信息\n");
return;
}
char line[256];
unsigned long total_mem = 0, free_mem = 0;
while (fgets(line, sizeof(line), meminfo)) {
if (strncmp(line, "MemTotal:", 9) == 0) {
sscanf(line + 9, "%lu", &total_mem);
} else if (strncmp(line, "MemFree:", 8) == 0) {
sscanf(line + 8, "%lu", &free_mem);
}
}
fclose(meminfo);
printf("可用内存: %lu MB / 最大内存: %lu MB\n", free_mem / 1024, total_mem / 1024);
}
void print_disk() {
struct statvfs stat;
if (statvfs("/", &stat) != 0) {
printf("无法读取磁盘信息\n");
return;
}
unsigned long free_space = (stat.f_bfree * stat.f_frsize) / (1024 * 1024);
unsigned long total_space = (stat.f_blocks * stat.f_frsize) / (1024 * 1024);
printf("可用空间: %lu MB / 最大空间: %lu MB\n", free_space, total_space);
}
int main() {
char input[10];
while (1) {
printf("输入命令 (mem/disk/exit): ");
if (!fgets(input, sizeof(input), stdin)) {
continue;
}
// 移除换行符
input[strcspn(input, "\n")] = 0;
if (strcmp(input, "mem") == 0) {
print_memory();
} else if (strcmp(input, "disk") == 0) {
print_disk();
} else if (strcmp(input, "exit") == 0) {
break;
} else {
printf("无效命令\n");
}
}
return 0;
}
编译测试
编译完成后, 可以直接将dumpsys 程序拷贝到/usr/data/dumpsys
运行测试:
# /usr/data/dumpsys
输入命令 (mem/disk/exit): mem
可用内存: 397 MB / 最大内存: 447 MB
输入命令 (mem/disk/exit): exit
增加系统自启动
auto_run.mk
AUTO_RUN_SITE = $(TOPDIR)/package/auto_run
AUTO_RUN_SITE_METHOD = local
AUTO_RUN_INSTALL_STAGING = YES
# dumpsys
define AUTO_RUN_INSTALL_INIT_SYSV
$(INSTALL) -D -m 0755 $(AUTO_RUN_SITE)/S99dumpsys $(TARGET_DIR)/etc/init.d/S99dumpsys
endef
AUTO_RUN_POST_INSTALL_TARGET_HOOKS += AUTO_RUN_INSTALL_INIT_SYSV
$(eval $(cmake-package))
S99dumpsys
#!/bin/sh
check_run(){
local file="/usr/data/dumpsys"
local max_attempts=10
local attempt=1
while [ $attempt -le $max_attempts ]; do
if [ -f "$file" ]; then
echo "File $file found, starting dumpsys..."
/usr/data/dumpsys &
return 0
else
echo "Attempt $attempt: File $file not found, waiting..."
sleep 1
attempt=$((attempt + 1))
fi
done
echo "file not found and exit"
return 1
}
case "$1" in
start)
echo "Starting dumpsys..."
check_run || exit 1
;;
stop)
echo "Stopping dumpsys..."
killall dumpsys
;;
restart)
$0 stop
$0 start
;;
*)
echo "Usage: $0 {start|stop|restart}"
exit 1
;;
esac
exit 0
一切准备就绪! 打包烧录!
一个问题
为了方便测试, 默认/usr/data/
目录下没有dumpsys, 通过命令拷贝并重启.
意外出现:
输入命令 (mem/disk/exit): 输入命令 (mem/disk/exit): 输入命令 (mem/disk/exit): 输入命令 (mem/disk/exit): 输入命令 (mem/disk/exit): 输入命令 (mem/disk/exit): 输入命令 (mem/disk/exit): 输入命令 (mem/disk/exit): ...
循环输出LOG: 输入命令 (mem/disk/exit):
调试发现问题出在
if (!fgets(input, sizeof(input), stdin))
原因是从stdin
读取输入出现了问题.
在Buildroot环境中,fgets()
在通过init脚本启动时失败但在手动启动时正常,这通常与标准输入(stdin)的环境差异有关。
由于 后台运行(&)的影响:
&
使程序在后台运行,可能导致stdin被关闭或重定向- init系统可能进一步处理了标准流
解决方案:显式指定输入设备
修改程序代码:
FILE *input_stream = fopen("/dev/console", "r");
if (!input_stream) {
input_stream = fopen("/dev/tty", "r");
}
if (!input_stream) {
input_stream = stdin;
}
if (!fgets(input, sizeof(input), input_stream)) {
// 错误处理
}
根本原因是初始化环境和交互式shell环境的差异。在系统启动时,标准输入可能没有绑定到有效的终端设备,而手动启动时则会有完整的终端环境。
其他
/etc/init.d 目录下的 SXXxxx 文件作用解析
文件命名规则与核心作用
-
S 前缀:表示 启动(Start) 服务,与之对应的 K 前缀表示 停止(Kill) 服务。
XX 数字:决定脚本执行顺序,数值小的优先执行(如 S10network 先于 S20apache 执行),用于解决服务间的依赖关系(例如网络接口需先于 Web 服务启动)。
xxx 服务名:标识具体服务(如 S50ssh 表示 SSH 服务)。 -
SXXxxx 文件是 SysVinit/BusyBox 初始化系统中,通过数字排序和符号链接机制实现服务按需启动的核心配置单元,直接影响嵌入式设备或服务器在特定运行级别的服务状态。