笔者刚解决完这个问题,前期有些忘了,一点一点回忆。会慢慢更新。
笔者使用的是Alpha出厂系统,也就是用Yocto做的根文件系统。这个系统里包含了QT和很多其他必要的库,可以不用在开发板上再安装了。
正点原子提供的出厂系统中已经内置了用udev自动挂载u盘的脚本,在/etc/udev 文件夹下有个脚本文件夹scripts,里面有个自动挂载的脚本mount.sh,部分内容如下:
...
automount() {
name="`basename "$DEVNAME"`"
! test -d "/mnt/$name" && mkdir -p "/mnt/$name"
# Silent util-linux's version of mounting auto
if [ "x`readlink $MOUNT`" = "x/bin/mount.util-linux" ] ;
then
MOUNT="$MOUNT -o silent"
fi
# If filesystem type is vfat, change the ownership group to 'disk', and
# grant it with w/r/x permissions.
case $ID_FS_TYPE in
vfat|fat)
MOUNT="$MOUNT -o umask=007,gid=`awk -F':' '/^disk/{print $3}' /etc/group`"
;;
# TODO
*)
;;
esac
if ! $MOUNT -t auto $DEVNAME "/mnt/$name"
then
logger "mount.sh/automount" "$MOUNT -t auto $DEVNAME \"/mnt/$name\" failed!"
rm_dir "/mnt/$name"
else
logger "mount.sh/automount" "Auto-mount of [/mnt/$name] successful"
touch "/tmp/.automount-$name"
fi
}
...
basename 是是去掉文件明前面的目录并打印出来,比如/dev/sda1 打印出来就是sda1。
$DEVNAME是由udev提供的系统环境变量,表示正在处理的块设备的名称。这个系统环境变量可以通过输入命令udevadm test /sys/class/block/sda查看。
DEVLINKS=/dev/disk/by-id/usb-USB_Disk_3727637990721561-0:0 /dev/disk/by-path/platform-ci_hdrc.1-usb-0:1.4:1.0-scsi-0:0:0:0
DEVNAME=/dev/sda
DEVPATH=/devices/platform/soc/2100000.aips-bus/2184200.usb/ci_hdrc.1/usb1/1-1/1-1.4/1-1.4:1.0/host2/target2:0:0/2:0:0:0/block/sda
DEVTYPE=disk
脚本里用到了这些命令:
test -d 文件名: 如果文件存在且为目录则为真。
mkdir -p 文件夹名: 确保目录名称存在,不存在的就建一个。
mount -o auto、-o noauto:打开/关闭自动挂上模式。
开机后会自动执行这个脚本,u盘插入就会挂上,挂载到/mnt下
使用df -h命令或者mount能查看
root@ATK-IMX6U:~# df -h
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 120M 1.7M 119M 2% /mnt/sda1
root@ATK-IMX6U:~# mount
/dev/sda1 on /mnt/sda1 type vfat (rw,relatime,gid=6,fmask=0007,dmask=0007,allow_utime=0020,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro)
在QT工程中做如下改动
#include
确保系统上安装了libudev库。如果尚未安装,使用 Linux 发行版的包管理器进行安装。在Ubuntu 的系统上,使用以下命令进行安装:
sudo apt-get install libudev-dev
在.pro文件中增加一行 LIBS += -ludev
udev = udev_new();
if (!udev) {
qFatal("Failed to initialize libudev.");
}
monitor = udev_monitor_new_from_netlink(udev, "udev");
udev_monitor_filter_add_match_subsystem_devtype(monitor, "block", NULL);
udev_monitor_enable_receiving(monitor);
int fd = udev_monitor_get_fd(monitor);
QSocketNotifier *notifier = new QSocketNotifier(fd, QSocketNotifier::Read, this);
connect(notifier, &QSocketNotifier::activated, this, &viva::usbDiskInserted);
通过添加-ludev到LIBS变量,编译程序将在链接过程中搜索库并解析对函数udev_new和其他libudev函数的引用。
确保系统上安装了libudev库。如果尚未安装,使用 Linux 发行版的包管理器进行安装。在Ubuntu 的系统上,使用以下命令进行安装:
sudo apt-get install libudev-dev
struct udev *udev;
udev = udev_new();
if (!udev) {
qFatal("初始化libudev失败");
}
struct udev_monitor *monitor = udev_monitor_new_from_netlink(udev, "udev"); //创建一个udev对象来监听设备事件
udev_monitor_filter_add_match_subsystem_devtype(monitor, "block", NULL); //指定设备子系统(如块设备的block)
udev_monitor_enable_receiving(monitor); //将监视器添加到事件循环中
int fd = udev_monitor_get_fd(monitor);
QSocketNotifier *notifier = new QSocketNotifier(fd, QSocketNotifier::Read, this);
connect(notifier, &QSocketNotifier::activated, this, &MyClass::usbDiskInserted); //连接信号槽
上面代码设置了一个监视器来过滤块设备并将其接收QSocketNotifier发来的通知并转发到usbDiskInserted
养成良好习惯,避免内存泄漏
udev_monitor_unref(monitor);
udev_unref(udev);
这里设置成只要有u盘插入,查找并读取里面的target.txt文件
void mainwindow::usbDiskInserted()
{
struct udev_device *dev = udev_monitor_receive_device(monitor);
if (dev) {
const char *action = udev_device_get_action(dev);
if (action && strcmp(action, "add") == 0) { //如果是插入u盘执行下面
const char *devpath = udev_device_get_devpath(dev);
const char *devname = udev_device_get_devnode(dev);
qDebug() << "u盘路径:" << devpath << "节点名称:" << devname;
QString mountPoint = getUsbDiskMountPoint(devname); //返回设备节点名称获取挂载路径
// 在u盘中寻找target.txt
QString targetFile = searchFileInUsbDisk(mountPoint, "target.txt");
if (!targetFile.isEmpty()) {
qDebug() << "目标文件路径: " << targetFile;
// 此处可以写对target.txt的处理
} else {
qDebug() << "未找到文件";
}
}
else if(action && strcmp(action, "remove") == 0) //如果是拔出u盘执行下面
qDebug() << "u盘已经拔出";
udev_device_unref(dev); //用于释放对特定udev_device对象的引用
}
}
其中获取挂载路径函数getUsbDiskMountPoint
QString mainwindow::getUsbDiskMountPoint(const QString &usbDiskPath)
{
// 相当于在命令行输入mount命令获取u盘挂载路径
QProcess process;
process.start("mount");
process.waitForFinished(-1);
QByteArray output = process.readAllStandardOutput();
QString mountOutput = QString::fromUtf8(output);
// 读取mount on 后的字段,也就是挂载路径的文本
QString searchString = usbDiskPath + " on "; //相当于找这段文本/dev/sda1 on /mnt/sda1
int index = mountOutput.indexOf(searchString);
if (index != -1) {
// 如果找到把on后面的文本/mnt/sda1提取出来
QString mountPath = mountOutput.mid(index + searchString.length());
mountPath = mountPath.left(mountPath.indexOf(' '));
qDebug() << "挂载路径:" << mountPath;
return mountPath;
} else {
qDebug() << "未找到路径";
return QString();
}
}
另一种获取挂载路径函数getUsbDiskMountPoint2,这个函数不用输入参数,更为简单,但有失败的可能
QString mainwindow::getUsbDiskMountPoint2()
{
QString mountPath;
foreach (const QStorageInfo &storage, QStorageInfo::mountedVolumes())
{
if (storage.isValid() && storage.isReady())
{
if (storage.rootPath().contains("sd")) {
mountPath = storage.rootPath();
qDebug() << "挂载路径:" << mountPath;
return mountPath;
}
else {
qDebug() << "未找到路径";
return QString();
}
} else {
qDebug() << "未找到路径";
return QString();
}
}
}
其中查找特定文件函数searchFileInUsbDisk
QString mainwindow::searchFileInUsbDisk(const QString &usbDiskPath, const QString &fileName)
{
//使用QT遍历器,遍历读取u盘中的所有文件
QDirIterator it(usbDiskPath, QDir::Files | QDir::NoSymLinks, QDirIterator::Subdirectories);
while (it.hasNext()) {
QString filePath = it.next();
QFileInfo fileInfo(filePath);
if (fileInfo.fileName() == fileName) {
return fileInfo.filePath(); //找到返回文件路径
}
}
return QString(); // 文件未找到
}
上述情况是在进入App后插入u盘,触发QSocketNotifier找到u盘。
如果在进入App前就有u盘插入,可以使用上面的getUsbDiskMountPoint2()