一、 前言与目标
最近在折腾手里的这块 RV1126 + AP6212 的开发板,想用它搭一个WiFi热点,方便其他设备能快速接入进行调试。本以为这是个很简单的任务,准备用Linux下最经典的组合 hostapd
和 dnsmasq
来实现。结果没想到,从最开始的配置文件,到最后的启动顺序,一路上踩了一连串的坑。
这里把整个调试过程记录下来,希望能给后来遇到同样问题的朋友们一些参考。
二、 调试之旅:我的“踩坑”与“填坑”记录
第一坑:配置文件里的“幽灵空格”与端口冲突
一开始,我满怀信心地写好了三个核心文件:hostapd.conf
, dnsmasq.conf
和启动脚本 wifi_apmode.sh
。结果第一次运行脚本就直接罢工了,报了两个莫名其妙的错:
-
一个是
dnsmasq
的Address already in use
(地址已被占用)。 -
另一个是
hostapd
的invalid driver
(无效驱动)。
查了半天,第一个问题是 dnsmasq.conf
里我自己画蛇添足地加了一行 port=111
,想指定端口,结果和系统其他服务冲突了。把它删掉,dnsmasq
就不报这个错了。
第二个 hostapd
的问题就更坑了,我逐字对比配置,最后才发现是driver=nl80211
和 hw_mode=g
这两行的值后面,不小心多敲了一个空格!对于配置文件来说,这自然就成了不认识的非法值。把这两个“幽灵空格”删掉后,服务总算能启动了。
第二坑:热点能连,但就是不给IP
解决了初级错误后,新问题来了。我的手机和电脑都能搜到 alientek_softap
这个热点,也能输入密码连接上,但就是一直卡在“正在获取IP地址...”,最后宣告失败。
这一下就把问题锁定在了 dnsmasq
身上。既然WiFi能连上,说明hostapd
已经把链路层的工作干好了,问题肯定出在DHCP分配IP的环节。
我回头仔细检查我的 wifi_apmode.sh
启动脚本,才发现犯了个致命的逻辑错误:
# 错误的顺序
dnsmasq -C dnsmasq.conf # 我先启动了DHCP服务
ifconfig wlan0 192.168.4.3 # 然后才给网卡配IP
这怎么行呢?DHCP服务员(dnsmasq
)自己都不知道餐厅(wlan0
接口)的地址,怎么给客人(手机)安排座位(分配IP)?
正确的逻辑应该是先让接口拥有IP地址,再启动依赖于这个接口的服务。把这两行命令的顺序换过来,这个问题就解决了。
第三坑:时灵时不灵的IP分配,需要更“霸道”的配置
虽然调整顺序后能获取到IP了,但我发现有时候连接还是会慢,或者不稳定。我猜想是不是因为我的板子上还有一个 eth0
网口,dnsmasq
被搞糊涂了,不知道到底该听谁的。
为了让它老实点,我决定把它的配置写死,不给它任何“自由发挥”的空间。我修改了 dnsmasq.conf
,增加了三个关键选项:
-
bind-interfaces
: 强制它只在我指定的接口上工作。 -
listen-address=192.168.4.3
: 强制它只在这个IP地址上提供服务。 -
dhcp-authoritative
: 告诉所有客户端,我才是这里的老大,唯一的DHCP服务器。
最后的收尾:两个意想不到的小冲突
本以为万事大吉,结果运行优化后的配置,又冒出两个新错误:dnsmasq
报127.0.0.1
地址冲突,hostapd
说我没设密码。
这两个问题也很好解决:
-
Dnsmasq冲突:是因为它默认还想当DNS服务器,监听了
127.0.0.1
的53端口,和系统其他服务撞车了。在dnsmasq.conf
里加上port=0
,把它DNS的功能彻底禁用,只做纯粹的DHCP服务,问题解决。 -
Hostapd没密码:是我在之前优化
hostapd.conf
的加密方式时,追求安全(只用WPA2),结果不小心把wpa_passphrase=...
这一行给删了。把它加回去就好了。
三、 总结与最终配置
经过这么一番折腾,总算是把这个AP功能搞定了。从最初的语法错误,到核心的逻辑顺序问题,再到服务冲突和配置细节,每一步都是一次宝贵的经验积累。下面贴出最终能在我这块 RV1126 + AP6212 板子上完美工作的配置,给有需要的朋友们“抄个作业”。
[root@ATK-DLRV1126:/wifiAP]# cat dnsmasq.conf
# 强制dnsmasq只绑定在下面指定的接口上,不再监听其他接口(如eth0)
bind-interfaces
# 只在我们指定的wlan0接口上工作
interface=wlan0
# 监听的IP地址,必须和wlan0的静态IP一致
listen-address=192.168.4.3
# DHCP地址池
dhcp-range=192.168.4.2,192.168.4.254,255.255.255.0,24h
# 声明自己是此网段唯一的、权威的DHCP服务器,可以加快客户端获取IP的速度
dhcp-authoritative
port=0
[root@ATK-DLRV1126:/wifiAP]# cat hostapd.conf
interface=wlan0
ssid=alientek_softap
driver=nl80211
channel=6
hw_mode=g
ignore_broadcast_ssid=0
auth_algs=1
wpa=2
wpa_key_mgmt=WPA-PSK
wpa_passphrase=12345678
rsn_pairwise=CCMP
[root@ATK-DLRV1126:/wifiAP]# cat wifi_apmode.sh
#!/bin/sh
echo 0 > /sys/class/rfkill/rfkill1/state
sleep 2
echo 1 > /sys/class/rfkill/rfkill1/state
sleep 2
if [[ "$(pidof dnsmasq)" != "" ]]
then
kill -9 $(pidof dnsmasq)
fi
if [[ "$(pidof hostapd)" != "" ]]
then
kill -9 $(pidof hostapd)
fi
connmanctl disable wifi
rfkill unblock all
ifconfig wlan0 down
sleep 1
ifconfig wlan0 up
sleep 1
# --- 以下是修改过的部分 ---
# 1. 先为wlan0接口配置静态IP地址
ifconfig wlan0 192.168.4.3
echo "wlan0 IP address set to 192.168.4.3"
# 2. 再启动dnsmasq,它现在知道要在哪个网段上提供DHCP服务了
dnsmasq -C dnsmasq.conf
echo "Dnsmasq service started"
# 3. 最后启动hostapd,创建WiFi热点
hostapd hostapd.conf -B
echo "Hostapd service started"
[root@ATK-DLRV1126:/wifiAP]# ./wifi_apmode.sh
Disabled wifi
wlan0 IP address set to 192.168.4.3
Dnsmasq service started
Configuration file: hostapd.conf
Using interface wlan0 with hwaddr 9c:b8:b4:0d:54:44 and ssid "alientek_softap"
wlan0: interface state UNINITIALIZED->ENABLED
wlan0: AP-ENABLED
Hostapd service started
[root@ATK-DLRV1126:/wifiAP]#
四、 最终一步:设置开机自启动,让热点“自动化”
热点虽然能通过手动运行脚本来启动了,但这还不够,总不能每次开机都SSH上去手动敲一遍命令吧?所以,我们这趟旅程的最后一站,就是把它加入到系统的开机自启动流程里,实现真正的“自动化AP”。
1. 分析系统启动方式
要设置自启动,得先看看我这个Buildroot构建的Linux系统用的是什么启动管理器。我 ls /etc/init.d/
了一下,看到了满屏的 S
开头的脚本(比如S45connman
, S50dropbear
),这很明显是老派但稳定可靠的 System V init 系统。
那就好办了,我们只需要写一个自己的启动脚本,再把它放到这个目录里就行。
2. 文件归档:给配置文件安个家
我的 hostapd.conf
, dnsmasq.conf
和 wifi_apmode.sh
现在还扔在一个临时目录里,这不清真。为了方便管理和后续维护,我决定把它们统一放到一个永久的位置。
首先,我创建了一个专门的目录:
mkdir -p /usr/bin/wifiAP
然后,把三个文件都移进去:
mv wifi_apmode.sh hostapd.conf dnsmasq.conf /usr/bin/wifiAP/
3. 编写init.d启动脚本
第一次尝试:标准的 init.d
脚本
接下来就是重头戏:编写 init.d
启动脚本。我在 /etc/init.d/
目录下新建了一个名为 S60wifi_ap
的文件。
-
命名考量:
S
代表Start(启动),60
是一个启动优先级。我看了一下,系统里网络服务(S40network
,S45connman
)的优先级大概在40-45,我选60
是为了确保它在网络相关的底层服务之后运行,保证wlan0
接口等硬件已经准备就绪。
我在 S60wifi_ap
文件里写入了以下内容,这是一个很标准的 init.d
脚本模板,可以处理 start
, stop
, restart
等参数:
#!/bin/sh
### BEGIN INIT INFO
# Provides: wifi_ap
# Required-Start: $network $local_fs
# Required-Stop: $network $local_fs
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Start WiFi AP mode using hostapd and dnsmasq
### END INIT INFO
# AP脚本和配置文件的存放路径
AP_PATH="/usr/local/bin/wifiAP"
AP_SCRIPT="wifi_apmode.sh"
case "$1" in
start)
echo "Starting WiFi AP mode..."
# 调用我们原来的主脚本来启动服务
# 使用 sh 命令执行,并确保在正确的目录下运行
(cd $AP_PATH && sh ./$AP_SCRIPT)
;;
stop)
echo "Stopping WiFi AP mode..."
# 停止服务最简单的方法就是杀掉进程
killall hostapd > /dev/null 2>&1
killall dnsmasq > /dev/null 2>&1
# 可以选择性地关闭wlan0接口
ifconfig wlan0 down
;;
restart)
$0 stop
sleep 2
$0 start
;;
*)
echo "Usage: $0 {start|stop|restart}"
exit 1
;;
esac
exit 0
(注意:我在start
命令里加了(cd $AP_PATH && ...)
,这是一个好习惯,可以确保脚本总是在正确的目录下寻找配置文件。)
将新建的/etc/init.d/S60wifi_ap加上执行权限,然后
然后我信心满满的重启开发板,等待AP自己启动,可是没能达到我的预期效果,试了几次也不行,看来还有问题,那就继续排查吧。。。
第二次尝试:日志与时序竞争
-
问题现象:手动执行
/etc/init.d/S60wifi_ap start
完美成功,但开机就是不启动。 -
调试思路:这明显是遇到了嵌入式开发中最经典的“时序竞争”或“环境差异”问题。开机时脚本的运行环境和时机,与我手动执行时大不相同。为了搞清楚开机时到底发生了什么,我修改了
S60wifi_ap
脚本,在开头加上了日志记录功能exec > /tmp/wifi_ap_boot.log 2>&1
,并增加了一个sleep 10
的延时,想看看日志里会说些什么。 -
修改后的
S60wifi_ap脚本内容如下
-
#!/bin/sh # AP脚本和配置文件的存放路径 AP_PATH="/usr/bin/wifiAP" AP_SCRIPT="wifi_apmode.sh" LOG_FILE="/tmp/wifi_ap_boot.log" # 将所有输出重定向到日志文件,方便调试 exec > $LOG_FILE 2>&1 case "$1" in start) echo "------ Starting WiFi AP at $(date) ------" # 关键步骤:等待5-10秒,给WiFi硬件和驱动充分的初始化时间 echo "Waiting for WiFi hardware to settle..." sleep 10 echo "Executing AP script..." # 先切换到AP_PATH目录,然后再执行脚本 (cd ${AP_PATH} && sh ./${AP_SCRIPT}) echo "------ WiFi AP script finished ------" ;; stop) echo "------ Stopping WiFi AP at $(date) ------" killall hostapd > /dev/null 2>&1 killall dnsmasq > /dev/null 2>&1 ifconfig wlan0 down ;; restart) $0 stop sleep 2 $0 start ;; *) echo "Usage: $0 {start|stop|restart}" exit 1 ;; esac exit 0
-
关键发现:再次重启后,我登录系统,准备查看日志,结果
cat /tmp/wifi_ap_boot.log
提示No such file or directory
!
这个发现是整个调试过程的最关键突破口。日志文件根本没有被创建,这无可辩驳地证明了:系统在开机过程中,压根就没运行我的 S60wifi_ap
脚本! 问题不是我的脚本执行失败了,而是它被完全忽略了。
最终的解决方案:简单粗暴,但有效
既然系统不“自动发现”并执行我的脚本,那我就只能用更直接的方法了。我怀疑在这个精简的系统中,init
进程可能只执行了一个核心的 rcS
脚本。
我的最终解决方案是:放弃调用外部脚本,将核心命令直接写入系统的主启动脚本 /etc/init.d/rcS
中。
-
我用
vi
打开了/etc/init.d/rcS
文件。 -
在文件的最末尾(在
done
循环之后),我添加了以下几行代码:# ------------------- FINAL ATTEMPT: Direct WiFi AP Startup ------------------- # This is the final attempt to start the AP service directly from rcS # 1. Wait for 15 seconds to ensure all hardware is fully initialized. sleep 15 # 2. Directly execute the core commands, ensuring correct working directory. (cd /usr/bin/wifiAP && ifconfig wlan0 192.168.4.3) (cd /usr/bin/wifiAP && dnsmasq -C dnsmasq.conf) (cd /usr/bin/wifiAP && hostapd hostapd.conf -B) # -----------------------------------------------------------------------------
这个方法的核心在于:
-
直接:不再有任何中间调用,也就是不在通过/etc/init.d/rcS去调用S60wifi_ap,而是让rcS直接执行最根本的三个命令。
-
延时:通过
sleep 15
给予系统充足的时间,让所有硬件和底层服务都准备就绪,避免了任何时序问题。 -
可靠:
rcS
是系统无论如何都会执行的脚本,把命令放在这里可以保证它一定会被执行。
-
-
保存文件,重启开发板。
六、 结语
终于,随着开发板的重启,开机后自动散发了热点,熟悉的 alientek_softap
热点终于自动出现了,手机连上后也顺利获取到了IP地址。至此,整个WiFi AP热点的配置与调试工作画上了圆满的句号。
从最初的配置文件语法错误,到核心的服务启动逻辑顺序问题,再到最后与系统启动流程的“斗智斗勇”,整个过程虽然曲折,但每解决一个问题,对嵌入式Linux系统服务和启动流程的理解就更深了一层。希望这篇踩坑记录能对后来者有所帮助
希望这篇踩坑记录能对大家有所帮助!