2000字范文,分享全网优秀范文,学习好帮手!
2000字范文 > linux U盘插拔检测

linux U盘插拔检测

时间:2023-06-13 01:00:15

相关推荐

linux U盘插拔检测

因为最终要在tiny210上实现此功能,最终选择了hotplug。

/hdy5200075/item/7751f48647f3d12a100ef3f6这里是hotplug检测U盘的源码,我在qt里将其写到一个hostplug.h文件里。

[cpp]view plain copy print ? #ifndefHOSTPLUG_H #defineHOSTPLUG_H #include<iostream> #include<stdio.h> #include<stdlib.h> #include<string.h> #include<ctype.h> #include<sys/un.h> #include<sys/ioctl.h> #include<sys/socket.h> #include<linux/types.h> #include<linux/netlink.h> #include<errno.h> #include<fcntl.h> staticintinit_hotplug_sock(void) { structsockaddr_nlsnl; constintbuffersize=16*1024*1024; intretval; memset(&snl,0x00,sizeof(structsockaddr_nl)); snl.nl_family=AF_NETLINK; snl.nl_pid=getpid(); snl.nl_groups=1; inthotplug_sock=socket(PF_NETLINK,SOCK_DGRAM,NETLINK_KOBJECT_UEVENT); if(hotplug_sock==-1) { printf("Errorgettingsocket;%s\n",strerror(errno)); return-1; } /*setreceivebuffersize*/ setsockopt(hotplug_sock,SOL_SOCKET,SO_RCVBUFFORCE,&buffersize,sizeof(buffersize)); <strong><u><spanstyle="color:#ff0000;">intflags=fcntl(hotplug_sock,F_GETFL,0); fcntl(hotplug_sock,F_SETFL,flags|O_NONBLOCK);</span></u></strong> retval=bind(hotplug_sock,(structsockaddr*)&snl,sizeof(structsockaddr_nl)); if(retval<0) { printf("bindfailed:%s",strerror(errno)); close(hotplug_sock); hotplug_sock=-1; return-1; } returnhotplug_sock; } #endif//HOSTPLUG_H 然后添加一个定时器,定时器时间为500ms,即每500ms扫描一次,如下:[cpp]view plain copy print ? voidWidget::timerEvent(QTimerEvent*event) { staticintn=0; charbuf[1024]={0};//UEVENT_BUFFER_SIZE*2 if(event->timerId()==timer.timerId()) { recv(hotplug_sock,&buf,sizeof(buf),0);//useatimertoquerysocketfromcore-netlink QStringresult=buf; rectFlag=result; qDebug()<<result; if(result.contains("add")) { n++; if(n>10) n=10; ui->progressBar->setValue(n); } elseif(result.contains("remove")) { n--; if(n<0) n=0; ui->progressBar->setValue(n); } } else QWidget::timerEvent(event); } 注意这里最关键的就是[cpp]view plain copy print ? recv(hotplug_sock,&buf,sizeof(buf),0);

这个函数,接收消息存至buf里。但默认的hotplug_sock是阻塞的,也就是当执行到recv时,程序就会停在这里,直到再次接收到内核新的消息,程序才会往下执行。为此,程序必须改动,一种思路是开一个线程,专门运行recv,停在那也无所谓;另外一种思路是将这个sock改成非阻塞的,改动部分见.h文件里画红线部分!

int flags=fcntl(hotplug_sock, F_GETFL,0);

fcntl(hotplug_sock, F_SETFL, flags | O_NONBLOCK);

当内核没有消息时,recv()之后的buf是空的。 交叉编译后,程序至Tiny210里运行良好!

上一张效果图:

但遗憾的是,工业是这么做是很不高明的,为了扫描一个U盘要开一个定时器在那扫描,因此最终采用判断/proc/scsi/usb-storage是否存在来判断u盘是否插入。曾考虑过U盘插入后,挂载点/udisk是否存在来判断。但当用户在/udisk目录下时,这时突然拔掉U盘。/udisk就会存在,而且ls查看的结果是报错。这时因为未推出U盘目录就拔掉,linux无法正常卸载造成的。当U盘插入良好时,usb-storage文件夹里会有三个文件,当卸载不成功时,会有两个文件。当卸载成功时,usb-storage这个文件夹会消失。采用像/udisk里写测试文件来判断/udisk是否可用,不可用的话就提示给用户:(自己是写了一个自动挂载的规则,见博客Linux上用udev自动识别并挂载U盘,然后判断usb-storage文件夹里文件的个数,是3的话就是有U盘插上了,否则就是U盘拔掉了。)

[cpp]view plain copy print ? boolWidget::checkSaveFile() { QStringfileName="/proc/scsi/usb-storage/a.txt"; QFilefile(fileName); if(!file.open(QFile::WriteOnly|QFile::Text)) returnfalse; else {file.close(); file.remove(); returntrue; } } 查询U盘状态的槽函数:[cpp]view plain copy print ? voidWidget::on_queryButton_clicked() { QDirdir("/proc/scsi/usb-storage");//在板子上,如果检测挂载点,改为:QDirdir("/udisk")检测挂载点 QMessageBoxbox; QStringmess; box.setWindowTitle(tc->toUnicode("U盘状态")); qDebug()<<"dir.count()="<<dir.count(); if(rectFlag.contains("add")) mess=tc->toUnicode("正在识别,请稍等-----"); elseif(rectFlag.contains("remove")) mess=tc->toUnicode("正在删除您的U盘-------"); elseif(dir.exists()) { if(dir.count()>2) { //if(checkSaveFile()) mess=tc->toUnicode("U盘已连接!"); //else //mess=tc->toUnicode("您的U盘已插入,但挂载点有问题,不能正常使用!建议拔掉U盘,然后重启!"); } elseif((dir.count()==2)) { QDirdirMount("/udisk"); if(dirMount.exists()) { intfd=system("umount/udisk"); qDebug()<<"fd="<<fd; } else mess=tc->toUnicode("/udiskNoExist!"); //if(checkSaveFile()) //mess=tc->toUnicode("您未插入U盘。但挂载点可写,不影响使用。如果需要请插入U盘!");//这种情况从逻辑上讲不可能出现。 //else //mess=tc->toUnicode("您未插入U盘。但当前挂载点有问题,建议重启后再插入U盘!"); } else mess=tc->toUnicode("U盘连接有故障,请重启后再插入U盘!"); } else mess=tc->toUnicode("U盘未连接!"); //process->start("ls/mnt\n"); //QStringtest=QString::number(a,10); //QStringtest=process->readAllStandardOutput(); //ui->getTextEdit->insertPlainText(test+"\n"); ui->getTextEdit->insertPlainText(mess+"\n"); autoScroll();[cpp]view plain copy print ? } 这样就能正常检测U盘了,如果想加进度条就加上。不加,也能正常检测。

问题又来了,上面采用向/udisk里写测试文件来检测/udisk是否可用,但有时用户会将U盘进行写保护。我试遍所有的方法,用open、opendir、access、stat等来检查异常情况下/udisk的属性与正常状态有何不同,最终也没查出来。也用了ls /udisk > /a.txt,截取ls的内容。但当/udisk出现异常时,报错的内容是板子上报的,并不是ls显示的内容。ls此时显示结果为空。

其实,与其判断这种误拔U盘的行为,倒不如防止。经过我研究发现,当ls出现/udisk fatal error,只要执行/umount /udisk,手动将这个文件夹卸载,再次插上U盘就可以了。为此,大家看到我上面程序里,当检测dir.count == 2时,检查/udisk是否存在,如果存在则将/udisk卸载。

这样做基本算完美解决问题了。美中不足的是,当异常出现时,如果板子程序一直在运行,则拔掉U盘再插上无事。如果此时板子重启,在板子重启前就将U盘再次插入到板子,这时候因为咱们的应用程序还未运行,还没有执行 umount /udisk,这个时候程序就检测不出来了。

要避免这个问题,就采用往U盘里写数据的方法判断,或者如果允许扫描用hostplug查询出来的信息可以得知usb的注册情况,这种思路应该也可以。一个小小的U盘检测,终于告一段落了,实现了x86下、arm平台均可用的qt检测U盘!----------------yanzi1225627

这里给一个源码资源,是老外写的,用qtcpsocket来监听netlink的消息,老外写的代码就是不一样啊,大家参考吧:

/detail/yanzi1225627/4514740

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。