RK3576開(kāi)發(fā)板外設(shè)管理:Udev實(shí)現(xiàn)U盤(pán)自動(dòng)掛載的標(biāo)準(zhǔn)化方案
Forlinx
2026-03-05 14:44:00
RK3576開(kāi)發(fā)板
我是燕南無(wú)聲,一個(gè)在嵌入式領(lǐng)域摸爬滾打了12年的工程師。平時(shí)喜歡琢磨底層系統(tǒng)機(jī)制,也愛(ài)把實(shí)際項(xiàng)目中踩過(guò)的坑、填過(guò)的土記錄下來(lái)。今天要分享的是基于飛凌RK3576開(kāi)發(fā)板(OK3576-C)Ubuntu24.04環(huán)境,深入解析Udev設(shè)備事件監(jiān)聽(tīng)機(jī)制與Systemd服務(wù)協(xié)同邏輯,提供U盤(pán)自動(dòng)掛載標(biāo)準(zhǔn)化實(shí)現(xiàn)方案。從Udev配置、規(guī)則編寫(xiě),到Systemd服務(wù)創(chuàng)建、掛載/卸載腳本開(kāi)發(fā),全流程拆解,解決路徑亂碼、腳本執(zhí)行失效等行業(yè)常見(jiàn)問(wèn)題,為RK3576開(kāi)發(fā)板嵌入式項(xiàng)目提供可復(fù)用的外設(shè)管理模板。
項(xiàng)目背景與需求
本次項(xiàng)目是基于RK3576開(kāi)發(fā)板(型號(hào):OK3576-C)開(kāi)發(fā)自助終端設(shè)備,客戶核心需求是 系統(tǒng)啟動(dòng)后插入U(xiǎn)盤(pán)可自動(dòng)掛載到固定目錄,且目錄名無(wú)亂碼。Ubuntu 24.04桌面版自帶的文件管理器(如nautilus)雖支持U盤(pán)自動(dòng)掛載,但掛載點(diǎn)路徑會(huì)包含UUID或中文卷標(biāo),在終端操作時(shí)極易出現(xiàn)亂碼,直接干擾后續(xù)自動(dòng)化腳本的執(zhí)行。
由于終端環(huán)境對(duì)路徑的 固定性和可預(yù)測(cè)性要求極高,因此決定拋棄文件管理器的默認(rèn)掛載機(jī)制,改用udev接管U盤(pán)的插拔事件,自定義掛載/卸載腳本,實(shí)現(xiàn)標(biāo)準(zhǔn)化的U盤(pán)掛載管理。
實(shí)現(xiàn)思路與核心邏輯
首先明確核心實(shí)現(xiàn)思路:udev是Linux內(nèi)核設(shè)備管理器的用戶空間守護(hù)進(jìn)程,核心負(fù)責(zé)管理/dev目錄下的設(shè)備節(jié)點(diǎn),并能在設(shè)備插入/移除時(shí)觸發(fā)自定義動(dòng)作。我們的核心目標(biāo)是—— U盤(pán)插入時(shí)自動(dòng)執(zhí)行掛載腳本,U盤(pán)拔出時(shí)自動(dòng)執(zhí)行卸載腳本。
需要注意的是,Ubuntu 24.04這類(lèi)高版本系統(tǒng)中,直接在udev規(guī)則里調(diào)用shell腳本大概率會(huì)失效:一方面udev事件觸發(fā)的運(yùn)行環(huán)境極度受限,另一方面腳本執(zhí)行為異步模式,易出現(xiàn)競(jìng)爭(zhēng)條件導(dǎo)致執(zhí)行失敗。Linux官方推薦的最優(yōu)方案是 通過(guò)systemd服務(wù)包裝腳本,在udev規(guī)則中通過(guò)TAG+="systemd"和ENV{SYSTEMD_WANTS}觸發(fā)服務(wù),讓腳本在systemd管理的干凈環(huán)境中運(yùn)行,從根本上規(guī)避權(quán)限、路徑等問(wèn)題。
基于此,梳理出本次改造需要修改/新增的核心文件,所有操作均基于飛凌RK3576開(kāi)發(fā)板的Ubuntu 24.04環(huán)境:
- /etc/udev/udev.conf:確認(rèn)udev基礎(chǔ)配置,指向正確的規(guī)則目錄;
- /etc/udev/rules.d/95-usb-mount.rules:編寫(xiě)udev核心規(guī)則,捕獲U盤(pán)分區(qū)插拔事件,觸發(fā)對(duì)應(yīng)systemd服務(wù);
- /etc/systemd/system/mount.service/umount.service:創(chuàng)建兩個(gè)systemd服務(wù)單元,分別承載U盤(pán)掛載、卸載邏輯;
- /etc/systemd/system/mount.sh/umount.sh:編寫(xiě)實(shí)際的掛載、卸載腳本,由上述systemd服務(wù)調(diào)用。
分步實(shí)操實(shí)現(xiàn)方案
以下為分步實(shí)現(xiàn)步驟,所有配置命令、腳本代碼均經(jīng)過(guò)實(shí)際環(huán)境驗(yàn)證,直接復(fù)制即可使用,僅在格式上做縮進(jìn)和注釋優(yōu)化,未改動(dòng)任何核心參數(shù)。
確認(rèn)udev基礎(chǔ)配置
首先檢查并確認(rèn)/etc/udev/udev.conf配置文件,確保udev的規(guī)則目錄、設(shè)備根目錄指向正確,無(wú)需新增配置,僅啟用必要項(xiàng)即可:
# /etc/udev/udev.conf # 參考udev.conf(5)獲取詳細(xì)配置說(shuō)明 # udevd會(huì)在initrd中啟動(dòng),修改此文件后建議重建initrd使配置生效 #udev_log=info #children_max= #exec_delay= #event_timeout=180 #timeout_signal=SIGKILL #resolve_names=early # 設(shè)備根目錄 udev_root="/dev/" # udev規(guī)則文件存放目錄 udev_rules="/etc/udev/rules.d/" # 日志級(jí)別設(shè)為錯(cuò)誤,減少冗余日志 udev_log="err"
核心關(guān)注udev_rules參數(shù),確保其指向/etc/udev/rules.d/,保持默認(rèn)配置即可,無(wú)需額外修改。
編寫(xiě)udevU盤(pán)插拔檢測(cè)規(guī)則
在/etc/udev/rules.d/目錄下創(chuàng)建95-usb-mount.rules文件(95為規(guī)則優(yōu)先級(jí),數(shù)值越大優(yōu)先級(jí)越低),編寫(xiě)U盤(pán)插拔的觸發(fā)規(guī)則,核心實(shí)現(xiàn) 插入創(chuàng)目錄+啟掛載服務(wù),拔出啟卸載服務(wù):
# /etc/udev/rules.d/95-usb-mount.rules
# 為U盤(pán)主設(shè)備(sd[a-z])創(chuàng)建符號(hào)鏈接usb%m,%m為內(nèi)核次要設(shè)備號(hào),保證設(shè)備標(biāo)識(shí)唯一
KERNEL=="sd[a-z]", NAME="%k", SYMLINK+="usb%m", OPTIONS="last_rule"
# U盤(pán)分區(qū)(sd[a-z][0-9])插入事件:創(chuàng)建符號(hào)鏈接
ACTION=="add", KERNEL=="sd[a-z][0-9]", SYMLINK+="usb%m", NAME="%k"
# U盤(pán)分區(qū)插入事件:自動(dòng)創(chuàng)建掛載點(diǎn)目錄,路徑為/mnt/分區(qū)名(如/mnt/sda1)
ACTION=="add", KERNEL=="sd[a-z][0-9]", RUN+="/bin/mkdir -p /mnt/%k"
# U盤(pán)分區(qū)插入事件:核心觸發(fā)掛載服務(wù),僅匹配USB塊設(shè)備,避免識(shí)別SATA等本地設(shè)備
ACTION=="add",KERNEL=="sd[a-z][0-9]",SUBSYSTEM=="block",ENV{ID_BUS}=="usb",TAG+="systemd", PROGRAM="/bin/systemd-escape -p %k", ENV{SYSTEMD_WANTS}+="mount.service"
# U盤(pán)分區(qū)移除事件:創(chuàng)建符號(hào)鏈接(保持與插入規(guī)則一致性,可省略)
ACTION=="remove", KERNEL=="sd[a-z][0-9]", SYMLINK+="usb%m", NAME="%k"
# U盤(pán)分區(qū)移除事件:觸發(fā)卸載服務(wù),僅匹配USB塊設(shè)備
ACTION=="remove", KERNEL=="sd[a-z][0-9]", SUBSYSTEM=="block", ENV{ID_BUS}=="usb", PROGRAM="/usr/bin/systemctl start umount.service", TAG+="systemd"
規(guī)則關(guān)鍵說(shuō)明:ENV{ID_BUS}=="usb"精準(zhǔn)匹配USB設(shè)備,徹底排除本地SATA、NVMe等存儲(chǔ)設(shè)備;RUN+="/bin/mkdir -p /mnt/%k"自動(dòng)創(chuàng)建掛載點(diǎn);TAG+="systemd"告訴udev通過(guò)systemd管理后續(xù)觸發(fā)的服務(wù)。
創(chuàng)建systemd掛載/卸載服務(wù)單元
由于高版本Ubuntu無(wú)法直接在udev規(guī)則中執(zhí)行腳本,因此創(chuàng)建兩個(gè)systemd服務(wù)單元mount.service和umount.service,分別調(diào)用掛載、卸載腳本,服務(wù)文件存放于/etc/systemd/system/目錄。
掛載服務(wù):mount.service
# /etc/systemd/system/mount.service [Unit] # 服務(wù)描述 Description=mountservice # 服務(wù)啟動(dòng)時(shí)機(jī):在網(wǎng)絡(luò)服務(wù)后啟動(dòng)(掛載無(wú)需網(wǎng)絡(luò),僅為通用習(xí)慣,可省略) After=network.target [Service] # 服務(wù)類(lèi)型:forking表示腳本會(huì)以后臺(tái)進(jìn)程模式運(yùn)行 Type=forking # 執(zhí)行用戶/用戶組:必須為root,掛載/卸載操作需要最高權(quán)限 User=root Group=root # 工作目錄:掛載點(diǎn)根目錄 WorkingDirectory=/mnt # 服務(wù)執(zhí)行命令:調(diào)用掛載腳本,--log-target=journal讓日志寫(xiě)入systemd日志,可通過(guò)journalctl查看 ExecStart=/etc/systemd/system/mount.sh start --log-target=journal [Install] # 服務(wù)安裝目標(biāo):多用戶模式下啟用 WantedBy=multi-user.target
卸載服務(wù):umount.service
# /etc/systemd/system/umount.service [Unit] # 服務(wù)描述 Description=umountservice # 服務(wù)啟動(dòng)時(shí)機(jī):在網(wǎng)絡(luò)服務(wù)后啟動(dòng) After=network.target [Service] # 服務(wù)類(lèi)型:forking表示腳本會(huì)以后臺(tái)進(jìn)程模式運(yùn)行 Type=forking # 執(zhí)行用戶/用戶組:root最高權(quán)限 User=root Group=root # 工作目錄:掛載點(diǎn)根目錄 WorkingDirectory=/mnt # 服務(wù)執(zhí)行命令:調(diào)用卸載腳本 ExecStart=/etc/systemd/system/umount.sh start --log-target=journal [Install] # 服務(wù)安裝目標(biāo):多用戶模式下啟用 WantedBy=multi-user.target
執(zhí)行以下命令為兩個(gè)服務(wù)文件添加可執(zhí)行權(quán)限:
chmod 777 /etc/systemd/system/mount.service chmod 777 /etc/systemd/system/umount.service
編寫(xiě)掛載/卸載核心腳本
創(chuàng)建與systemd服務(wù)對(duì)應(yīng)的掛載腳本mount.sh和卸載腳本umount.sh,存放于/etc/systemd/system/目錄,腳本為實(shí)際執(zhí)行U盤(pán)掛載、卸載的核心邏輯。
掛載腳本:mount.sh
#!/bin/bash
# /etc/systemd/system/mount.sh
# U盤(pán)自動(dòng)掛載腳本,掛載路徑/mnt/設(shè)備名(如/dev/sda1 → /mnt/sda1)
# 初始變量,后續(xù)會(huì)被遍歷結(jié)果覆蓋
var2="/mnt/sda"
# 遍歷所有/dev目錄下的USB分區(qū)設(shè)備
for V in $(ls /dev/sd[a-z][0-9])
do
# 打印當(dāng)前設(shè)備名(調(diào)試用)
echo $V
# 賦值當(dāng)前設(shè)備路徑給變量
var2=$V
# 截取設(shè)備名(如從/dev/sda1中截取sda1)
echo ${var2:5:4}
# 執(zhí)行掛載:將設(shè)備掛載到/mnt/設(shè)備名目錄
/bin/mount $V /mnt/${var2:5:4}
done
腳本關(guān)鍵說(shuō)明:掛載點(diǎn)由udev規(guī)則提前創(chuàng)建,腳本直接執(zhí)行掛載即可;使用/bin/mount絕對(duì)路徑執(zhí)行,避免udev/systemd環(huán)境中PATH變量缺失導(dǎo)致的命令找不到問(wèn)題。
卸載腳本:umount.sh
#!/bin/sh
# /etc/systemd/system/umount.sh
# U盤(pán)自動(dòng)卸載腳本,檢測(cè)設(shè)備不存在則卸載并刪除掛載點(diǎn)
# 定義需監(jiān)控的USB設(shè)備節(jié)點(diǎn)
usb_device_1="/dev/sda1"
usb_device_2="/dev/sdb1"
usb_device_3="/dev/sdc1"
usb_device_4="/dev/sdd1"
# 定義設(shè)備對(duì)應(yīng)的掛載點(diǎn)目錄
mount_dir_1="/mnt/sda1"
mount_dir_2="/mnt/sdb1"
mount_dir_3="/mnt/sdc1"
mount_dir_4="/mnt/sdd1"
# 延時(shí)1秒,等待設(shè)備徹底移除,避免競(jìng)爭(zhēng)條件導(dǎo)致卸載失敗
sleep 1
# 檢測(cè)設(shè)備1是否存在,不存在則卸載并刪除掛載點(diǎn)
if [ ! -e "$usb_device_1" ];then
umount $usb_device_1 > /dev/null 2>&1
rm -rf $mount_dir_1 > /dev/null 2>&1
fi
# 檢測(cè)設(shè)備2是否存在,不存在則卸載并刪除掛載點(diǎn)
if [ ! -e "$usb_device_2" ];then
umount $usb_device_2 > /dev/null 2>&1
rm -rf $mount_dir_2 > /dev/null 2>&1
fi
# 檢測(cè)設(shè)備3是否存在,不存在則卸載并刪除掛載點(diǎn)
if [ ! -e "$usb_device_3" ];then
umount $usb_device_3 > /dev/null 2>&1
rm -rf $mount_dir_3 > /dev/null 2>&1
fi
# 檢測(cè)設(shè)備4是否存在,不存在則卸載并刪除掛載點(diǎn)
if [ ! -e "$usb_device_4" ];then
umount $usb_device_4 > /dev/null 2>&1
rm -rf $mount_dir_4 > /dev/null 2>&1
fi
腳本關(guān)鍵說(shuō)明:sleep 1為核心延時(shí)操作,等待內(nèi)核完成設(shè)備移除的底層操作;> /dev/null 2>&1重定向所有輸出和錯(cuò)誤到空設(shè)備,保持系統(tǒng)日志干凈。
執(zhí)行以下命令為兩個(gè)腳本添加可執(zhí)行權(quán)限:
chmod 777 /etc/systemd/system/mount.sh chmod 777 /etc/systemd/system/umount.sh
重載配置并測(cè)試驗(yàn)證
完成所有配置和腳本編寫(xiě)后,無(wú)需重啟系統(tǒng),手動(dòng)重載udev規(guī)則和systemd配置即可生效:
# 重載udev規(guī)則,讓新編寫(xiě)的U盤(pán)插拔規(guī)則生效 udevadm control --reload-rules # 重載systemd配置,讓新創(chuàng)建的掛載/卸載服務(wù)生效 systemctl daemon-reload
測(cè)試步驟:
- 插入U(xiǎn)盤(pán)(單分區(qū)/多分區(qū)均可),執(zhí)行l(wèi)s /mnt/,可看到自動(dòng)創(chuàng)建的掛載點(diǎn)目錄(如sda1),執(zhí)行df -h可驗(yàn)證U盤(pán)已成功掛載;
- 拔出U盤(pán),執(zhí)行l(wèi)s /mnt/,對(duì)應(yīng)的掛載點(diǎn)目錄已被自動(dòng)刪除,執(zhí)行df -h可驗(yàn)證U盤(pán)已成功卸載;
- 若需查看服務(wù)運(yùn)行日志,可執(zhí)行journalctl -u mount.service或journalctl -u umount.service。
驗(yàn)證總結(jié)
在飛凌RK3576開(kāi)發(fā)板的Ubuntu 24.04桌面版環(huán)境中,經(jīng)過(guò)多次實(shí)際插拔測(cè)試,該方案可實(shí)現(xiàn) U盤(pán)插入自動(dòng)掛載到/mnt/設(shè)備名固定目錄,拔出自動(dòng)卸載并刪除掛載點(diǎn),掛載點(diǎn)路徑無(wú)任何UUID或中文卷標(biāo),徹底解決了默認(rèn)掛載的亂碼問(wèn)題,完全滿足自助終端設(shè)備的自動(dòng)化腳本操作需求。
執(zhí)行以下調(diào)試命令,可驗(yàn)證掛載/卸載服務(wù)是否已正常啟用:
# 查看掛載服務(wù)啟用狀態(tài) systemctl is-enabled mount.service # 查看卸載服務(wù)啟用狀態(tài) systemctl is-enabled umount.service
正常情況下,兩個(gè)命令的輸出均為enabled,表示服務(wù)已成功啟用并隨系統(tǒng)開(kāi)機(jī)自啟。
經(jīng)驗(yàn)反思
本次基于udev+systemd實(shí)現(xiàn)U盤(pán)自動(dòng)掛載,踩過(guò)了高版本Ubuntu的環(huán)境坑,也積累了嵌入式Linux外設(shè)管理的實(shí)用經(jīng)驗(yàn),核心總結(jié)三點(diǎn):
對(duì)于飛凌嵌入式RK3576開(kāi)發(fā)板,Ubuntu 24.04桌面版的整體運(yùn)行表現(xiàn)穩(wěn)定,結(jié)合udev的設(shè)備事件監(jiān)聽(tīng)和systemd的服務(wù)管理,可靈活定制USB、串口、網(wǎng)卡等各類(lèi)外設(shè)的自動(dòng)處理邏輯。本次U盤(pán)自動(dòng)掛載的改造方案,也為后續(xù)嵌入式項(xiàng)目中的外設(shè)管理提供了可直接復(fù)用的模板。
咨詢立即獲得專(zhuān)屬報(bào)價(jià)
華北區(qū)負(fù)責(zé)人
華東區(qū)負(fù)責(zé)人
華南區(qū)負(fù)責(zé)人
中西區(qū)負(fù)責(zé)人
相關(guān)產(chǎn)品 >
-
FET3576-C核心板
飛凌嵌入式RK3576核心板集成了強(qiáng)大的處理器和豐富的接口,提供出色的計(jì)算能力和擴(kuò)展性。RK3576核心板以其卓越的性能、低功耗和穩(wěn)定性,成為工業(yè)、AIoT、邊緣計(jì)算、智能移動(dòng)終端等領(lǐng)域的理想選擇。無(wú)論是數(shù)據(jù)處理還是邊緣計(jì)算,RK3576都能為項(xiàng)目提供強(qiáng)大的硬件支持。核心板推薦選擇飛凌嵌入式瑞芯微系列RK3576J業(yè)級(jí)核心板、RK3576高性能核心板。 了解詳情
-
OK3576-C開(kāi)發(fā)板
RK3576開(kāi)發(fā)板CPU選用瑞芯微RK3576,采用核心板+底板分體式設(shè)計(jì),采用4個(gè)100Pin板對(duì)板連接器的方式將處理器的功能引腳以最便利的方式全部引出,并針對(duì)不同的功能做了深度優(yōu)化,方便用戶二次開(kāi)發(fā)的同時(shí)簡(jiǎn)化用戶設(shè)計(jì),為您的項(xiàng)目提供良好的評(píng)估及設(shè)計(jì)依據(jù)。RK3576是瑞芯微專(zhuān)為AIoT市場(chǎng)打造的一款高算力、高性能、低功耗的國(guó)產(chǎn)化應(yīng)用處理器,集成了4個(gè)ARM Cortex-A72和4個(gè) ARM Cortex-A53高性能核;內(nèi)置6TOPS超強(qiáng)算力NPU;嵌入式3D GPU加之帶有MMU的專(zhuān)用2D硬件引擎,最大限度提升顯示性能;H.265超清硬解碼,最高支持8K分辨率。 了解詳情

