传统SysV风格启动脚本):
创建并编辑run_hello脚本
- /etc/init.d/run_hello
bash
#!/bin/sh /etc/rc.common
# OpenWRT.org
START=95
STOP=05
start() {
echo "running hello start."
}
stop() {
echo "running hello stop."
}
restart() {
echo "running hello restart."
}
reload() {
echo "running hello reload."
}
help() {
echo "running hello help."
}


传统SysV风格示例2:
bash
#!/bin/sh /etc/rc.common
START=90
STOP=10
# 额外命令
EXTRA_COMMANDS="status test reload"
EXTRA_HELP=" status Show service status
test Test configuration
reload Reload configuration"
# 启动
start() {
echo "Starting service"
# 使用 start-stop-daemon
start-stop-daemon -S -b -m \
-p /var/run/myservice.pid \
-x /usr/sbin/myservice \
-- --daemon --config /etc/myservice.conf
return $?
}
# 停止
stop() {
echo "Stopping service"
start-stop-daemon -K \
-p /var/run/myservice.pid \
-x /usr/sbin/myservice
[ -f /var/run/myservice.pid ] && rm -f /var/run/myservice.pid
return $?
}
# 重启
restart() {
stop
sleep 1
start
}
# 重新加载
reload() {
echo "Reloading configuration"
[ -f /var/run/myservice.pid ] && {
kill -HUP $(cat /var/run/myservice.pid)
echo "Configuration reloaded"
} || {
echo "Service not running"
return 1
}
}
# 状态检查
status() {
if [ -f /var/run/myservice.pid ]; then
local pid=$(cat /var/run/myservice.pid)
if kill -0 "$pid" 2>/dev/null; then
echo "Service is running (PID: $pid)"
return 0
else
echo "Service is not running (stale PID file)"
rm -f /var/run/myservice.pid
return 1
fi
else
echo "Service is not running"
return 3
fi
}
# 配置测试
test() {
echo "Testing configuration"
# 验证配置文件
/usr/sbin/myservice --test --config /etc/myservice.conf
return $?
}
基于procd风格
- OpenWrt 的启动脚本基于 BusyBox init 系统,结合 procd 进程管理框架,提供了一套标准的启动脚本规范。
- OpenWrt的启动脚本遵循LSB(Linux Standard Base)init脚本的规范,但使用自己的procd进程管理工具(可选)来管理服务。
- 一个典型的OpenWrt启动脚本位于/etc/init.d/目录下,其基本结构如下:
- 脚本开头使用shebang指定解释器:#!/bin/sh /etc/rc.common
- 使用/etc/rc.common提供的函数
- 定义START和STOP变量,表示启动和停止的顺序
- 可选地使用USE_PROCD=1来启用procd
- 定义启动、停止、重启等函数
启动脚本的安装与使用
启动脚本需要可执行权限。安装后,可以使用以下命令来管理服务:
/etc/init.d/script start
/etc/init.d/script stop
/etc/init.d/script restart
/etc/init.d/script reload
/etc/init.d/script enable
/etc/init.d/script disable
其中,enable和disable用于设置服务是否在启动时自动启动。实际上,enable命令会在/etc/rc.d/目录下创建一个指向/etc/init.d/脚本的符号链接,而disable则删除这个链接。
基本脚本结构
最小启动脚本示例:
bash
#!/bin/sh /etc/rc.common
START=95
STOP=05
USE_PROCD=1
start_service() {
procd_open_instance
procd_set_param command /usr/bin/my_service_command
procd_append_param command --my-argument 123
procd_set_param respawn
procd_set_param stdout 1
procd_set_param stderr 1
procd_close_instance
}
stop_service() {
# 如果停止服务需要额外的操作,可以在这里定义
# 否则,procd会自动停止服务
}
启动顺序控制
-
标准启动顺序(启动数字范围:1-99)
- 1-20: 内核和系统初始化
- 21-40: 基础服务 (network, firewall)
- 41-60: 系统服务 (dns, dhcp)
- 61-80: 应用服务 (web, file sharing)
- 81-99: 用户应用
-
常见服务的 START 值:
- sysctl: 10
- network: 20
- firewall: 40
- dnsmasq: 50
- uhttpd: 80
- cron: 90
- 自定义服务: 95-99
-
注意事项:
- 启动脚本的顺序:START和STOP变量的值决定了启动和停止的顺序。注意,启动顺序是从小到大,停止顺序是从小到大(即数字越小,在启动时越早执行,停止时也越早执行,但通常停止顺序与启动顺序相反)。
脚本的调试:如果脚本有问题,可以查看系统日志(使用logread命令)来获取错误信息。
procd进程管理
启用procd : USE_PROCD=1
bash
# 服务启动(procd 风格)
start_service() {
procd_open_instance
# 命令和参数
procd_set_param command /usr/sbin/myservice
procd_append_param command --config /etc/myservice.conf
procd_append_param command --log /var/log/myservice.log
# 运行用户和组
procd_set_param user nobody
procd_set_param group nogroup
# 标准输出/错误
procd_set_param stdout 1
procd_set_param stderr 1
# PID 文件
procd_set_param pidfile /var/run/myservice.pid
# 进程监控和重启
procd_set_param respawn
procd_set_param respawn_retry 3600 5 5
# 3600秒内重启5次后放弃,重启间隔5秒
# 环境变量
procd_set_param env LANG=C
procd_set_param env HOME=/tmp
# 资源限制
procd_set_param limits core="unlimited"
procd_set_param limits nofile="1024"
# 优先级
procd_set_param nice -10
procd_close_instance
}
# 服务停止(可选,默认会自动停止)
stop_service() {
# 自定义停止逻辑
echo "Custom stop operations"
[ -f /var/run/myservice.pid ] && {
kill $(cat /var/run/myservice.pid)
rm -f /var/run/myservice.pid
}
}
# 重新加载配置
reload_service() {
# 发送 HUP 信号重新加载配置
[ -f /var/run/myservice.pid ] && {
kill -HUP $(cat /var/run/myservice.pid)
}
}
# 配置变化触发器
service_triggers() {
# 配置文件变化时重载
procd_add_reload_trigger /etc/config/myservice
# 网络接口变化时重载
procd_add_interface_trigger "network.interface.*" /etc/init.d/myservice reload
# 无线接口变化时重载
procd_add_wifi_trigger "wireless.radio.*" /etc/init.d/myservice restart
}
procd_set_param 参数详解
| 参数 | 说明 | 示例 |
|---|---|---|
| command | 主命令 | procd_set_param command /usr/bin/nginx |
| append_param | 追加参数 | procd_append_param command -g "daemon off;" |
| user | 运行用户 | procd_set_param user nobody |
| group | 运行组 | procd_set_param group nogroup |
| pidfile | PID文件 | procd_set_param pidfile /var/run/nginx.pid |
| stdout | 标准输出 | procd_set_param stdout 1(1=启用) |
| stderr | 标准错误 | procd_set_param stderr 1(1=启用) |
| respawn | 自动重启 | procd_set_param respawn |
| respawn_retry | 重启策略 | procd_set_param respawn_retry 3600 5 5 |
| env | 环境变量 | procd_set_param env PATH=/bin:/sbin |
| nice | 优先级 | procd_set_param nice -10 |
| limits | 资源限制 | procd_set_param limits nofile="1024" |
| umask | 文件掩码 | procd_set_param umask 022 |
| netdev | 网络设备 | procd_set_param netdev eth0 |
| file | 文件描述符 | procd_set_param file /var/etc/nginx.conf |
| watch | 监控资源 | procd_set_param watch network.interface.lan |
| error | 错误处理 | procd_set_param error /dev/console |
脚本位置与命名
位置:/etc/init.d/(运行时)和 files/etc/init.d/(开发时)
命名:小写字母,通常与服务名称相同,如network、firewall
依赖关系控制
bash
# 在脚本开头定义依赖
REQUIRES="network"
PROVIDES="myservice"
NEEDS="network.interface"
# 在 start() 中检查依赖
start() {
# 检查网络是否就绪
/etc/init.d/network enabled || {
echo "Network service is required"
return 1
}
# 检查端口是否可用
if netstat -tln | grep -q ":8080 "; then
echo "Port 8080 is already in use"
return 1
fi
}
示例:读取UCI配置
bash
#!/bin/sh /etc/rc.common
# UCI 配置集成示例
USE_PROCD=1
START=90
STOP=10
# 加载 UCI 配置
load_config() {
config_load myservice
config_get_bool enabled "$1" enabled
config_get port "$1" port
config_get log_level "$1" log_level
config_get interface "$1" interface
}
# 启动服务
start_service() {
# 加载配置
config_load myservice
config_foreach load_config myservice
# 检查是否启用
[ "$enabled" = "1" ] || {
echo "Service is disabled in UCI config"
return 0
}
procd_open_instance
procd_set_param command /usr/sbin/myservice
procd_append_param command --port "$port"
procd_append_param command --log-level "$log_level"
# 网络接口
[ -n "$interface" ] && procd_set_param netdev "$interface"
procd_set_param respawn
procd_close_instance
}
# 配置变化处理
service_triggers() {
procd_add_reload_trigger myservice
}