RK3576開發(fā)板外設(shè)管理:Udev實現(xiàn)U盤自動掛載的標(biāo)準(zhǔn)化方案

原創(chuàng) 作者 Forlinx 2026-03-05 14:44:00 RK3576開發(fā)板

我是燕南無聲,一個在嵌入式領(lǐng)域摸爬滾打了12年的工程師。平時喜歡琢磨底層系統(tǒng)機(jī)制,也愛把實際項目中踩過的坑、填過的土記錄下來。今天要分享的是基于飛凌RK3576開發(fā)板(OK3576-C)Ubuntu24.04環(huán)境,深入解析Udev設(shè)備事件監(jiān)聽機(jī)制與Systemd服務(wù)協(xié)同邏輯,提供U盤自動掛載標(biāo)準(zhǔn)化實現(xiàn)方案。從Udev配置、規(guī)則編寫,到Systemd服務(wù)創(chuàng)建、掛載/卸載腳本開發(fā),全流程拆解,解決路徑亂碼、腳本執(zhí)行失效等行業(yè)常見問題,為RK3576開發(fā)板嵌入式項目提供可復(fù)用的外設(shè)管理模板。

項目背景與需求

本次項目是基于RK3576開發(fā)板(型號:OK3576-C)開發(fā)自助終端設(shè)備,客戶核心需求是 系統(tǒng)啟動后插入U盤可自動掛載到固定目錄,且目錄名無亂碼。Ubuntu 24.04桌面版自帶的文件管理器(如nautilus)雖支持U盤自動掛載,但掛載點路徑會包含UUID或中文卷標(biāo),在終端操作時極易出現(xiàn)亂碼,直接干擾后續(xù)自動化腳本的執(zhí)行。

RK3576開發(fā)板

由于終端環(huán)境對路徑的 固定性和可預(yù)測性要求極高,因此決定拋棄文件管理器的默認(rèn)掛載機(jī)制,改用udev接管U盤的插拔事件,自定義掛載/卸載腳本,實現(xiàn)標(biāo)準(zhǔn)化的U盤掛載管理。

實現(xiàn)思路與核心邏輯

首先明確核心實現(xiàn)思路:udev是Linux內(nèi)核設(shè)備管理器的用戶空間守護(hù)進(jìn)程,核心負(fù)責(zé)管理/dev目錄下的設(shè)備節(jié)點,并能在設(shè)備插入/移除時觸發(fā)自定義動作。我們的核心目標(biāo)是—— U盤插入時自動執(zhí)行掛載腳本,U盤拔出時自動執(zhí)行卸載腳本。

需要注意的是,Ubuntu 24.04這類高版本系統(tǒng)中,直接在udev規(guī)則里調(diào)用shell腳本大概率會失效:一方面udev事件觸發(fā)的運行環(huán)境極度受限,另一方面腳本執(zhí)行為異步模式,易出現(xiàn)競爭條件導(dǎo)致執(zhí)行失敗。Linux官方推薦的最優(yōu)方案是 通過systemd服務(wù)包裝腳本,在udev規(guī)則中通過TAG+="systemd"和ENV{SYSTEMD_WANTS}觸發(fā)服務(wù),讓腳本在systemd管理的干凈環(huán)境中運行,從根本上規(guī)避權(quán)限、路徑等問題。

基于此,梳理出本次改造需要修改/新增的核心文件,所有操作均基于飛凌RK3576開發(fā)板的Ubuntu 24.04環(huán)境:

  • /etc/udev/udev.conf:確認(rèn)udev基礎(chǔ)配置,指向正確的規(guī)則目錄;
  • /etc/udev/rules.d/95-usb-mount.rules:編寫udev核心規(guī)則,捕獲U盤分區(qū)插拔事件,觸發(fā)對應(yīng)systemd服務(wù);
  • /etc/systemd/system/mount.service/umount.service:創(chuàng)建兩個systemd服務(wù)單元,分別承載U盤掛載、卸載邏輯;
  • /etc/systemd/system/mount.sh/umount.sh:編寫實際的掛載、卸載腳本,由上述systemd服務(wù)調(diào)用。

分步實操實現(xiàn)方案

以下為分步實現(xiàn)步驟,所有配置命令、腳本代碼均經(jīng)過實際環(huán)境驗證,直接復(fù)制即可使用,僅在格式上做縮進(jìn)和注釋優(yōu)化,未改動任何核心參數(shù)。

1

確認(rèn)udev基礎(chǔ)配置

首先檢查并確認(rèn)/etc/udev/udev.conf配置文件,確保udev的規(guī)則目錄、設(shè)備根目錄指向正確,無需新增配置,僅啟用必要項即可:

# /etc/udev/udev.conf
# 參考udev.conf(5)獲取詳細(xì)配置說明
# udevd會在initrd中啟動,修改此文件后建議重建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/"
# 日志級別設(shè)為錯誤,減少冗余日志
udev_log="err"

核心關(guān)注udev_rules參數(shù),確保其指向/etc/udev/rules.d/,保持默認(rèn)配置即可,無需額外修改。

2

編寫udevU盤插拔檢測規(guī)則

在/etc/udev/rules.d/目錄下創(chuàng)建95-usb-mount.rules文件(95為規(guī)則優(yōu)先級,數(shù)值越大優(yōu)先級越低),編寫U盤插拔的觸發(fā)規(guī)則,核心實現(xiàn) 插入創(chuàng)目錄+啟掛載服務(wù),拔出啟卸載服務(wù)

# /etc/udev/rules.d/95-usb-mount.rules
# 為U盤主設(shè)備(sd[a-z])創(chuàng)建符號鏈接usb%m,%m為內(nèi)核次要設(shè)備號,保證設(shè)備標(biāo)識唯一
KERNEL=="sd[a-z]", NAME="%k", SYMLINK+="usb%m", OPTIONS="last_rule"
# U盤分區(qū)(sd[a-z][0-9])插入事件:創(chuàng)建符號鏈接
ACTION=="add", KERNEL=="sd[a-z][0-9]", SYMLINK+="usb%m", NAME="%k"
# U盤分區(qū)插入事件:自動創(chuàng)建掛載點目錄,路徑為/mnt/分區(qū)名(如/mnt/sda1)
ACTION=="add", KERNEL=="sd[a-z][0-9]", RUN+="/bin/mkdir -p /mnt/%k"
# U盤分區(qū)插入事件:核心觸發(fā)掛載服務(wù),僅匹配USB塊設(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盤分區(qū)移除事件:創(chuàng)建符號鏈接(保持與插入規(guī)則一致性,可省略)
ACTION=="remove", KERNEL=="sd[a-z][0-9]", SYMLINK+="usb%m", NAME="%k"
# U盤分區(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)鍵說明:ENV{ID_BUS}=="usb"精準(zhǔn)匹配USB設(shè)備,徹底排除本地SATA、NVMe等存儲設(shè)備;RUN+="/bin/mkdir -p /mnt/%k"自動創(chuàng)建掛載點;TAG+="systemd"告訴udev通過systemd管理后續(xù)觸發(fā)的服務(wù)。

3

創(chuàng)建systemd掛載/卸載服務(wù)單元

由于高版本Ubuntu無法直接在udev規(guī)則中執(zhí)行腳本,因此創(chuàng)建兩個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ù)啟動時機(jī):在網(wǎng)絡(luò)服務(wù)后啟動(掛載無需網(wǎng)絡(luò),僅為通用習(xí)慣,可省略)
After=network.target
[Service]
# 服務(wù)類型:forking表示腳本會以后臺進(jìn)程模式運行
Type=forking
# 執(zhí)行用戶/用戶組:必須為root,掛載/卸載操作需要最高權(quán)限
User=root
Group=root
# 工作目錄:掛載點根目錄
WorkingDirectory=/mnt
# 服務(wù)執(zhí)行命令:調(diào)用掛載腳本,--log-target=journal讓日志寫入systemd日志,可通過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ù)啟動時機(jī):在網(wǎng)絡(luò)服務(wù)后啟動
After=network.target
[Service]
# 服務(wù)類型:forking表示腳本會以后臺進(jìn)程模式運行
Type=forking
# 執(zhí)行用戶/用戶組:root最高權(quán)限
User=root
Group=root
# 工作目錄:掛載點根目錄
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í)行以下命令為兩個服務(wù)文件添加可執(zhí)行權(quán)限:

chmod 777 /etc/systemd/system/mount.service
chmod 777 /etc/systemd/system/umount.service
4

編寫掛載/卸載核心腳本

創(chuàng)建與systemd服務(wù)對應(yīng)的掛載腳本mount.sh和卸載腳本umount.sh,存放于/etc/systemd/system/目錄,腳本為實際執(zhí)行U盤掛載、卸載的核心邏輯。

掛載腳本:mount.sh

#!/bin/bash
# /etc/systemd/system/mount.sh
# U盤自動掛載腳本,掛載路徑/mnt/設(shè)備名(如/dev/sda1 → /mnt/sda1)
# 初始變量,后續(xù)會被遍歷結(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)鍵說明:掛載點由udev規(guī)則提前創(chuàng)建,腳本直接執(zhí)行掛載即可;使用/bin/mount絕對路徑執(zhí)行,避免udev/systemd環(huán)境中PATH變量缺失導(dǎo)致的命令找不到問題。

卸載腳本:umount.sh

#!/bin/sh
# /etc/systemd/system/umount.sh
# U盤自動卸載腳本,檢測設(shè)備不存在則卸載并刪除掛載點
# 定義需監(jiān)控的USB設(shè)備節(jié)點
usb_device_1="/dev/sda1"
usb_device_2="/dev/sdb1"
usb_device_3="/dev/sdc1"
usb_device_4="/dev/sdd1"
# 定義設(shè)備對應(yīng)的掛載點目錄
mount_dir_1="/mnt/sda1"
mount_dir_2="/mnt/sdb1"
mount_dir_3="/mnt/sdc1"
mount_dir_4="/mnt/sdd1"
# 延時1秒,等待設(shè)備徹底移除,避免競爭條件導(dǎo)致卸載失敗
sleep 1
# 檢測設(shè)備1是否存在,不存在則卸載并刪除掛載點
if [ ! -e "$usb_device_1" ];then
        umount $usb_device_1 > /dev/null 2>&1
        rm -rf $mount_dir_1 > /dev/null 2>&1
fi
# 檢測設(shè)備2是否存在,不存在則卸載并刪除掛載點
if [ ! -e "$usb_device_2" ];then
        umount $usb_device_2 > /dev/null 2>&1
        rm -rf $mount_dir_2 > /dev/null 2>&1
fi
# 檢測設(shè)備3是否存在,不存在則卸載并刪除掛載點
if [ ! -e "$usb_device_3" ];then
        umount $usb_device_3 > /dev/null 2>&1
        rm -rf $mount_dir_3 > /dev/null 2>&1
fi
# 檢測設(shè)備4是否存在,不存在則卸載并刪除掛載點
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)鍵說明:sleep 1為核心延時操作,等待內(nèi)核完成設(shè)備移除的底層操作;> /dev/null 2>&1重定向所有輸出和錯誤到空設(shè)備,保持系統(tǒng)日志干凈。

執(zhí)行以下命令為兩個腳本添加可執(zhí)行權(quán)限:

chmod 777 /etc/systemd/system/mount.sh
chmod 777 /etc/systemd/system/umount.sh
5

重載配置并測試驗證

完成所有配置和腳本編寫后,無需重啟系統(tǒng),手動重載udev規(guī)則和systemd配置即可生效:

# 重載udev規(guī)則,讓新編寫的U盤插拔規(guī)則生效
udevadm control --reload-rules
# 重載systemd配置,讓新創(chuàng)建的掛載/卸載服務(wù)生效
systemctl daemon-reload

測試步驟

  1. 插入U盤(單分區(qū)/多分區(qū)均可),執(zhí)行l(wèi)s /mnt/,可看到自動創(chuàng)建的掛載點目錄(如sda1),執(zhí)行df -h可驗證U盤已成功掛載;
  2. 拔出U盤,執(zhí)行l(wèi)s /mnt/,對應(yīng)的掛載點目錄已被自動刪除,執(zhí)行df -h可驗證U盤已成功卸載;
  3. 若需查看服務(wù)運行日志,可執(zhí)行journalctl -u mount.service或journalctl -u umount.service。

驗證總結(jié)

在飛凌RK3576開發(fā)板的Ubuntu 24.04桌面版環(huán)境中,經(jīng)過多次實際插拔測試,該方案可實現(xiàn) U盤插入自動掛載到/mnt/設(shè)備名固定目錄,拔出自動卸載并刪除掛載點,掛載點路徑無任何UUID或中文卷標(biāo),徹底解決了默認(rèn)掛載的亂碼問題,完全滿足自助終端設(shè)備的自動化腳本操作需求。

執(zhí)行以下調(diào)試命令,可驗證掛載/卸載服務(wù)是否已正常啟用:

# 查看掛載服務(wù)啟用狀態(tài)
systemctl is-enabled mount.service
# 查看卸載服務(wù)啟用狀態(tài)
systemctl is-enabled umount.service

正常情況下,兩個命令的輸出均為enabled,表示服務(wù)已成功啟用并隨系統(tǒng)開機(jī)自啟。

經(jīng)驗反思

本次基于udev+systemd實現(xiàn)U盤自動掛載,踩過了高版本Ubuntu的環(huán)境坑,也積累了嵌入式Linux外設(shè)管理的實用經(jīng)驗,核心總結(jié)三點:

規(guī)避udev直接調(diào)用腳本的坑 Ubuntu 20.04及以上版本中,udev的RUN指令運行環(huán)境受限,且受SELinux/AppArmor權(quán)限管控。通過systemd服務(wù)包裝腳本是官方推薦的穩(wěn)健方案。
卸載腳本的局限性需知曉 本次方案中的卸載腳本采用硬編碼設(shè)備節(jié)點方式,僅支持sda1~sdd1四個設(shè)備。若需同時插入多個U盤或U盤有多個分區(qū),可優(yōu)化為動態(tài)遍歷設(shè)備的方式。
固定路徑是解決亂碼的核心 默認(rèn)掛載的亂碼根源是路徑包含UUID/中文卷標(biāo),而udev規(guī)則中通過%k獲取設(shè)備名,掛載點固定為/mnt/%k,完全脫離卷標(biāo)和UUID的影響。

對于飛凌嵌入式RK3576開發(fā)板,Ubuntu 24.04桌面版的整體運行表現(xiàn)穩(wěn)定,結(jié)合udev的設(shè)備事件監(jiān)聽和systemd的服務(wù)管理,可靈活定制USB、串口、網(wǎng)卡等各類外設(shè)的自動處理邏輯。本次U盤自動掛載的改造方案,也為后續(xù)嵌入式項目中的外設(shè)管理提供了可直接復(fù)用的模板。

rk3576核心板

咨詢立即獲得專屬報價

華北區(qū)負(fù)責(zé)人二維碼

華北區(qū)負(fù)責(zé)人

華東區(qū)負(fù)責(zé)人二維碼

華東區(qū)負(fù)責(zé)人

華南區(qū)負(fù)責(zé)人二維碼

華南區(qū)負(fù)責(zé)人

中西區(qū)負(fù)責(zé)人二維碼

中西區(qū)負(fù)責(zé)人

相關(guān)產(chǎn)品 >

  • FET3576-C核心板

    飛凌嵌入式RK3576核心板集成了強(qiáng)大的處理器和豐富的接口,提供出色的計算能力和擴(kuò)展性。RK3576核心板以其卓越的性能、低功耗和穩(wěn)定性,成為工業(yè)、AIoT、邊緣計算、智能移動終端等領(lǐng)域的理想選擇。無論是數(shù)據(jù)處理還是邊緣計算,RK3576都能為項目提供強(qiáng)大的硬件支持。核心板推薦選擇飛凌嵌入式瑞芯微系列RK3576J業(yè)級核心板、RK3576高性能核心板 了解詳情
    FET3576-C核心板
  • OK3576-C開發(fā)板

    RK3576開發(fā)板CPU選用瑞芯微RK3576,采用核心板+底板分體式設(shè)計,采用4個100Pin板對板連接器的方式將處理器的功能引腳以最便利的方式全部引出,并針對不同的功能做了深度優(yōu)化,方便用戶二次開發(fā)的同時簡化用戶設(shè)計,為您的項目提供良好的評估及設(shè)計依據(jù)。RK3576是瑞芯微專為AIoT市場打造的一款高算力、高性能、低功耗的國產(chǎn)化應(yīng)用處理器,集成了4個ARM Cortex-A72和4個 ARM Cortex-A53高性能核;內(nèi)置6TOPS超強(qiáng)算力NPU;嵌入式3D GPU加之帶有MMU的專用2D硬件引擎,最大限度提升顯示性能;H.265超清硬解碼,最高支持8K分辨率。 了解詳情
    OK3576-C開發(fā)板

推薦閱讀 換一批 換一批