使用QT在linux上识别挂载多个U盘

背景:
对于U盘挂载本身linux就有udev这样的工具支持;但是由于依赖udev挂载规则,有时候不太可控(其实我碰到的问题就是在设备终端插着U盘开机,无法识别并且挂载U盘,需要重新插拔一次,才能识别U盘。),所以决定编写代码来检测并且挂载。

1.挂载U盘呢,无非就是mkdir创建目录,然后使用mount命令挂载;(这里假设U盘的设备节点是/dev/sda1)

mkdir -p /mnt/usb
mount /dev/sda1 /mnt/usb  // mount命令挂载根据需要加一些参数,例如指定挂载U盘的系统格式或者U盘内容显示字符编码等等

2.那如何检测U盘的热插拔呢?可以使用定时器轮询检测/dev目录下的设备节点的变化,从而去实现热插拔。这种方法可行,但是太耗资源,不推荐。我这里使用到的是QT自带的一个类-------QFileSystemWatcher
稍微说一下这个类哈。
QFileSystemWatcher通过监控指定路径的列表,监视文件系统中文件和目录的变更。调用addPath()函数可以监控一个特定的文件或目录。如果需要监控多个路径,可以使用addPaths()。通过使用removePath()和removePaths()函数来移除现有路径。QFileSystemWatcher检查添加到它的每个路径,已添加到QFileSystemWatcher的文件可以使用的files()函数进行访问,目录则使用directories()函数进行访问。当一个文件被修改、重命名或从磁盘上删除时,会发出fileChanged()信号。同样,当一个目录或它的内容被修改或删除时,会发射directoryChanged()信号。需要注意:文件一旦被重命名或从硬盘删除,目录一旦从磁盘上删除,QFileSystemWatcher将停止监控。也不能只是定义一个临时对象,需要new出一个对象,不然无法起到监测的效果。其实这个类依赖linux自己的一个inotify工具。

3.这样思路就很明显了。通过QFileSystemWatcher这个类监测/dev目录下的变化,然后通过检测到的信号去获取U盘的设备信息。然后拿到信息之后,对设备进行挂载或者卸载的操作。
4.那么,怎么获取U盘设备的信息呢?这里使用到的方法是,使用linux的blkid命令,将命令输出到控制台的U盘设备信息拿到,然后解析出所需要的U盘设备信息;

blkid -d -c /dev/null
/dev/mmcblk3p2: UUID="bbe3ba31-a0cb-4982-86dd-959f9e01d32c" TYPE="ext2" 
/dev/sda1: UUID="D87072FE7072E324" TYPE="ntfs" 
/dev/sdb4: LABEL="Ubuntu 16.0" UUID="B4FE-5315" TYPE="vfat" 
/dev/sdc1: LABEL="机密文件" UUID="12E2766CE27653C7" TYPE="ntfs" 

5.那执行命令后,如何去获取控制台信息呢?这里使用的QT的QProcess这个类;
6.那获取到控制台信息后,如何解析所需的U盘信息呢?这里使用的是QT的QRegExp,通过正则表达式来解析。
注意
a. blkid这个命令一定要 -c /dev/null这个参数。因为如果不加的话,blkid这个命令会在/dev目录下生成临时文件.blkid.tab .blkid.tab.old这就相当于在/dev目录下生成创建了文件,也就是修改了/dev目录,这个会混淆影响QFileSystemWatcher的监测。
b. 在设备开机的时候,最好是执行命令 mount -t tmpfs none /mnt 这样就把/mnt目录挂载在一个临时文件系统上。关机之后,/mnt目录下的所有文件都会清空。
c. 如果优盘是fat32格式的,那么如果卷标是中文,那么会在linux上显示乱码。(这个听说是可以通过转码解决掉的,但是我没有成功);如果是ntfs格式的U盘,那么中文卷标显示正常。所以,在这里如果是fat32的优盘,我回去显示它的UUID。ubuntu也是这么做的。

好啦,基本思路已经说完了,直接上代码吧。

UdiskWatcher.cpp

	#include "UdiskWatcher.h"
	#include 
	#include 
	#include 
	#include 
	#include 

	// 定义设备挂载U盘的最大个数
	#define USB_COUNT               10
	
	USBDeviceInfo::USBDeviceInfo()
	    : strDev("")
	    , strUUID("")
	    , strLABEL("")
	    , strTYPE("")
	    , isBusy(false)
	{
	}

	bool USBDeviceInfo::operator==(const USBDeviceInfo &value)
	{
	    return (strUUID == value.strUUID);
	}
	
	UdiskWatcher::UdiskWatcher(QObject *parent)
	    : QObject(parent)
	    , m_systemWatcher(NULL)
	{
	    m_systemWatcher = new QFileSystemWatcher(this);
	    if (!m_systemWatcher->addPath("/dev"))
	    {
	        qDebug() << "fileSystemWatcher invalid path!";
	    }
	    // 由于QFileSystemWathcher初次开机时检测不到已经存在的U盘设备节点,所以不会挂载U盘;
	    // 所以选择在UdiskWatcher构造的时候尝试检测是否插入U盘,挂载一次U盘
	    udiskHandle();
	    connect(m_systemWatcher, &QFileSystemWatcher::directoryChanged, this, &UdiskWatcher::udiskHandle);
	}
	
	UdiskWatcher::~UdiskWatcher()
	{
	
	}
	
	void UdiskWatcher::udiskHandle()
	{
	    refreshUSBdeviceList();
	    int devListSize = m_usbDevList.size();
	    int count = 0;
	    for (int i = 0; i < devListSize; i++)
	    {
	        if (m_usbDevListMounted.contains(m_usbDevList.at(i)))
	        {
	            count++;
	        }
	    }
	    qDebug("count = %d , devListSize = %d , m_usbDevListMounted.size() = %d", count, devListSize, m_usbDevListMounted.size());
	    if ((count == m_usbDevListMounted.size()) && (count == devListSize))
	    {
	        qDebug("no usb changed");
	        return;                                   // 两个list相等,不需要操作
	    }
	    QList    usbDevListAdded;      // 刷新U盘设备节点列表后,新增的U盘设备列表
	    QList    usbDevListRemoved;    // 刷新U盘设备节点列表后,移除的U盘设备列表
	    usbDevListAdded.clear();
	    usbDevListRemoved.clear();

	    for (int j = 0; j < m_usbDevListMounted.size(); j++)
	    {
	        if (!m_usbDevList.contains(m_usbDevListMounted.at(j)))
	        {
	            usbDevListRemoved.append(m_usbDevListMounted.at(j));
	        }
	    }
	    qDebug("need to umount usb count = %d", usbDevListRemoved.size());
	    // 卸载已经移除的U盘设备
	    for (int j = 0; j < usbDevListRemoved.size(); j++)
	    {
	        umountUsb(usbDevListRemoved.at(j));
	    }
	
	    if (m_usbDevListMounted.size() < USB_COUNT)
	    {
	        for (int i = 0; i < devListSize; i++)
	        {
	            if (!m_usbDevListMounted.contains(m_usbDevList.at(i)))
	            {
	                usbDevListAdded.append(m_usbDevList.at(i));
	            }
	        }
	        qDebug("need to mount usb count = %d", usbDevListAdded.size());
	        // 挂载新增的U盘设备
	        for (int i = 0; i < usbDevListAdded.size(); i++)
	        {
	            mountUsb(usbDevListAdded.at(i));
	        }
	    }
	}

	QList UdiskWatcher::getCurrentUSBDevice()
	{
	    return m_usbDevListMounted;
	}
	
	bool UdiskWatcher::mountUsb(USBDeviceInfo dev)
	{
	    qDebug("DeviceManager::mountUsb\n");
	#ifndef _WIN32
	    QString output;
	    QString usbName;
	    // 只有U盘是ntfs类型的时候,中文卷标才能正常显示;所以当U盘类型是fat32或者其他类型的时候,显示UUID;这里与ubuntu的处理是一样的;
	    if (dev.strTYPE == "ntfs" && !dev.strLABEL.isEmpty())
	    {
	        usbName = "/mnt/" + dev.strLABEL;
	    }
	    else
	    {
	        usbName = "/mnt/" + dev.strUUID;
	    }
	    if (QFile::exists(usbName))
	    {
	        qDebug() << "derectory has exist!";
	        return true;
	    }
	    if (!myProcess("mkdir", QStringList() << "-p" << usbName, output))
	    {
	        qDebug() << "mkdir usb derectory failed!";
	        return false;
	    }
	    if (!myProcess("/bin/mount", QStringList() << "-o" << "sync" << "-o" << "shortname=mixed" << "-o" << "utf8=yes" << dev.strDev << usbName, output))
	    {
	        qDebug() << "mount usb failed!";
	        return false;
	    }
	    if (!myProcess("touch", QStringList() << usbName, output))
	    {
	        qDebug() << "touch usb derectory failed!";
	        return false;
	    }
	    m_usbDevListMounted.append(dev);
	    qDebug("DeviceManager::mountUsb success! m_usbDevListMounted.size = %d\n", m_usbDevListMounted.size());
	    return true;
	#else
	    Q_UNUSED(dev)
	    return false;
	#endif
	}
	
	bool UdiskWatcher::umountUsb(USBDeviceInfo dev)
	{
	    qDebug("DeviceManager::umountUsb\n");
	#ifndef _WIN32
	    QString output;
	    QString usbName;
	    if (dev.strTYPE == "ntfs" && !dev.strLABEL.isEmpty())
	    {
	        usbName = "/mnt/" + dev.strLABEL;
	    }
	    else
	    {
	        usbName = "/mnt/" + dev.strUUID;
	    }
	    if (!QFile::exists(usbName))
	    {
	        qDebug() << "derectory has not exist!";
	        return true;
	    }
	    if (!myProcess("/bin/umount", QStringList() << usbName, output))
	    {
	        qDebug() << "umount usb failed!";
	        return false;
	    }
	    if (!myProcess("rmdir", QStringList() << usbName, output))
	    {
	        qDebug() << "rmdir usb derectory failed!";
	        return false;
	    }
	    m_usbDevListMounted.removeAll(dev);
	    qDebug("DeviceManager::umountUsb success! m_usbDevListMounted.size = %d\n", m_usbDevListMounted.size());
	    return true;
	#else
	    return false;
	#endif
	}
	
	void UdiskWatcher::refreshUSBdeviceList()
	{
	#ifndef _WIN32
	    m_usbDevList.clear();
	    QString output;
	    myProcess("blkid", QStringList() << "-d" << "-c" << "/dev/null", output);
	    // 正则表达式,取出U盘设备节点列表
	    QStringList itemList;
	    myRegExp("/dev/sd[a-z].*\\n", output, itemList);
	    for (int i = 0; i < itemList.size(); i++)
	    {
	        USBDeviceInfo info;
	        QStringList tempList;
	        if (myRegExp("/dev/sd[a-z][1-9]", itemList.at(i), tempList) > 0)
	        {
	            info.strDev = tempList.at(0);
	            tempList.clear();
	        }
	        if (myRegExp("LABEL=\".*\"", itemList.at(i), tempList) > 0)
	        {
	            int size = tempList.at(0).size();
	            info.strLABEL = tempList.at(0).mid(7, (size-8));
	            tempList.clear();
	        }
	        if (myRegExp("UUID=\".*\"", itemList.at(i), tempList) > 0)
	        {
	            int size = tempList.at(0).size();
	            info.strUUID = tempList.at(0).mid(6, (size-7));
	            tempList.clear();
	        }
	        if (myRegExp("TYPE=\".*\"", itemList.at(i), tempList) > 0)
	        {
	            int size = tempList.at(0).size();
	            info.strTYPE = tempList.at(0).mid(6, (size-7));
	            tempList.clear();
	        }
	        m_usbDevList.append(info);
	    }
	    if (m_usbDevList.size() < 1)
	    {
	        qDebug() << "未检测到u盘设备!";
	        return;
	    }
	#endif
	}

	int UdiskWatcher::myRegExp(const QString pattern, const QString &text, QStringList &list)
	{
	    QRegExp regExp;
	    int capCount = 0;
	    int pos = 0;

	    if (pattern.isEmpty() || text.isEmpty())
	    {
	        return -1;
	    }
	    regExp.setMinimal(true);
	    regExp.setPattern(pattern);
	    regExp.indexIn(text);

	    list.clear();
	    while ((pos = regExp.indexIn(text, pos)) != -1)
	    {
	        list.push_back(regExp.cap(0));
	        pos += regExp.matchedLength();
	        capCount++;
	    }
	
	    return capCount;
	}

	bool UdiskWatcher::myProcess(QString cmd, QStringList param, QString &output)
	{
	    QProcess process;
	    process.start(cmd, param);
	    // 等待进程启动
	    if (!process.waitForStarted())
	    {
	        qDebug() <<  "命令执行开始失败\n";
	        return false;
	    }
	    process.closeWriteChannel();

	    // 用于保存进程的控制台输出
	    QByteArray procOutput;
	    while (!process.waitForFinished(300))
	    {
	        qDebug() <<  "命令执行结束失败\n";
	        return false;
	    }
	    procOutput = process.readAll();
	    output = procOutput;
	    return true;
	}

摘自:使用QT在linux上识别挂载多个U盘 - 代码先锋网

windows磁盘显示:https://www.freesion.com/article/93071348301/

https://www.cnblogs.com/gethope5/p/15662175.html

QT读写U盘文件 :https://baijiahao.baidu.com/s?id=1652637076957647136&wfr=spider&for=pc

ARM平台QT检测U盘插拔

Arm平台 Qt检测U盘插拔(三)-----蛋疼的recv()阻塞

你可能感兴趣的:(linux,qt)