linux 移动存储设备弹出操作详解

news/2024/11/28 4:27:39/

本文开发环境搭建:

库名deb系rpm系
glib2/gioapt install libglib2.0-devyum install glib2-devel

1、基础信息简述:

1、常见的移动存储设备有:udisk(U盘)、DVD(光盘)、Mobile Hard disk(移动硬盘)
2、选择udisk为例,一个udisk实物在操作系统层面可以抽象出三个对象:Drive(驱动)、Mount(挂载点)、Volume(卷设备)
3、使用lsblk命令可以看到系统的磁盘信息,如果某个设备已经弹出,那么它的/dev设备是存在的,但是lsblk不会显示。

简单一句:linux内核肯定有处理udisk的驱动,驱动再可以检测是否存在卷设备,再者就是卷设备是否已经挂载、以及挂载点信息。

2、Mobile Hard disk的特殊之处

假设某个移动硬盘的设备是/dev/sdb1,它的挂载点是/media/user/aaa
1、如果是udisk设备,我们用以下2个命令均可以弹出udisk,但是运行命令后移动硬盘只会卸载,不会弹出

sudo eject /dev/sdb1
sudo eject /media/user/aaa

2、但是如果使用linux的文件管理器 右键菜单中的弹出按钮,发现移动硬盘可以弹出成功
3、原因(移动硬盘弹出原理):文件管理器内部处理移动设备的弹出操作时,udisk与DVD均是操作的Volume,但是移动硬盘操作的是Drive,移动硬盘需要关闭它的驱动连接才能真正的弹出它。

3、代码理论铺垫

1、在上述论据的基础上,我们将udisk与DVD分为一类,将移动硬盘单独分为一类,读者会问:代码中怎么区分这2类设备?
2、针对移动硬盘的特殊性质,我们可以使用如下函数进行区分
gboolean g_drive_can_eject(GDrive* drive) //udisk与DVD使用该函数即可判断是否可弹出
gboolean g_drive_can_stop(GDrive* drive) //移动硬盘使用该函数即可判断其Drive是否可停止
3、系统每插入一个移动设备,均会存在一个对应的computer:///xxx.drive的文件,我们使用该文件比较udisk与移动硬盘的不同点(以开机后再插入设备为例)
移动设备的驱动对比图

4、Mobile Hard disk的特殊之处2

事实证明,存在2种情况:

  • 开机插入移动硬盘,针对移动硬盘使用g_drive_can_stop()返回TRUE。
  • 开机插入移动硬盘,针对移动硬盘使用g_drive_can_stop()返回FALSE

大胆猜测与分析:
1、移动硬盘的驱动确实与其他移动存储设备的驱动有所不同,导致开机前插入的话会被默认当作系统硬盘而不是移动盘
2、该特殊问题如何解决?我猜测该问题目前应该无解或者触及了知识盲区。

5、C代码实例

1、代码逻辑讲解:

  • 代码内的 xxx.drive 是gvfs-ls computer:///命令的结果,这个文件是插入的udisk与移动硬盘在内存中的一种表示方式(linux一切皆文件的道理?)
  • GMainLoop g_main_loop_new(GMainContext,gboolean) 和 void g_main_loop_run(GMainLoop*) 分表表示创建和运行一个主循环,类似于守护进程的概念。
  • 增加主循环主要是为了保证glib2/gio API能够成功执行完成,保证用户能看到期待的结果,因为我们的程序是单线程的,而glib2库全部的回调函数均是通过信号触发的
  • func() 内部对udisk与移动硬盘的弹出均做了处理,读者只需要插入移动存储设备后将代码内的xxx.drive替换为gvfs-ls computer:///命令的某个结果,重新编译运行即可。

2、代码分块
1)功能代码 (if 和else if两个不同的分支分别处理udisk与移动硬盘设备)

void func(){gboolean canEject,canStop;//布尔变量,取值TRUE或FALSEchar *devFile;//GFile *file = g_file_new_for_uri("computer:///Kingston DataTraveler 3.0.drive");//创建金士顿设备文件对象GFile *file = g_file_new_for_uri("computer:///S5170-25 6.drive");//创建移动硬盘设备文件对象GFileInfo *fileInfo = g_file_query_info(file,"mountable::*",     /*查询挂载相关的所有信息*/G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,//不允许符号链接NULL,NULL);//查询具体属性值canEject = g_file_info_get_attribute_boolean(fileInfo, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_EJECT);//可弹出属性canStop = g_file_info_get_attribute_boolean(fileInfo, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_STOP);  //可停止属性if(canEject)            //如果可以弹出则正常弹出设备,udisk一般走这个分支g_file_eject_mountable_with_operation(file,G_MOUNT_UNMOUNT_NONE,NULL,NULL,NULL,NULL);else if(canStop){       //如果不可以正常弹出则尝试通过停止驱动的方式来弹出设备,移动硬盘一般走这个分支devFile = g_file_info_get_attribute_as_string(fileInfo, G_FILE_ATTRIBUTE_MOUNTABLE_UNIX_DEVICE_FILE);//查询驱动设备对应的dev设备文if(devFile){ejectDeviceByStopDrive(devFile);//停止驱动以达到弹出的效果g_free(devFile);}}g_object_unref(file);           //释放节点内存g_object_unref(fileInfo);
}

2)弹出指定/dev设备(这个代码主要是供移动硬盘使用的)

/* 通过停止驱动来弹出设备* @devFile 需要弹出的设备,如/dev/sdb1*/
void ejectDeviceByStopDrive(const char* devFile){GDrive* drive = getDriveFromSystem(devFile);//获取驱动if(!drive)return;if(g_drive_can_stop(drive))                 //驱动是否可以停止g_drive_stop(drive,G_MOUNT_UNMOUNT_NONE,NULL,NULL,NULL,NULL);//停止驱动g_object_unref(drive);                      //释放节点内存
}

3) 从系统磁盘监视器获取指定/dev设备的驱动

/* 从系统中获取指定设备的驱动* @devFile 如/dev/sdb1* @return @devFile在内存中对应的驱动对象*/
GDrive* getDriveFromSystem(const char* devFile){GVolumeMonitor *monitor = g_volume_monitor_get();               //系统磁盘监控器if(!monitor)return NULL;GList *allVolumes = g_volume_monitor_get_volumes(monitor);      //获取所有已连接的卷设备if(!allVolumes)return NULL;GList *l;       //glib2库的链表结构体GVolume *volume;//glib2库的卷设备结构体GDrive *drive;  //glib2库的驱动结构体const char *devPath;for(l = allVolumes; l != NULL; l = l->next){volume = l->data;//链表节点的数据域存放的是GVolume*对象devPath = g_volume_get_identifier(volume,"unix-device");//获取卷设备对象的dev设备标识符if(devPath && !strcmp(devFile,devPath)){drive = g_volume_get_drive(volume);             //获取卷设备对象的驱动g_free(devPath);break;}g_free(devPath);}g_list_foreach(allVolumes,(GFunc)g_object_unref,NULL);          //遍历链表,使用g_object_unref()释放每个链表节点g_list_free(allVolumes);                                        //释放掉链表的内存g_object_unref(monitor);return drive;
}

4)main函数(负责创建守护进程,保证API成功执行完成)

#include <stdio.h>
#include <glib.h>
#include <gio/gio.h>//函数原型声明
void func();
GDrive* getDriveFromSystem(const char* devFile);
void ejectDeviceByStopDrive(const char* devFile);int main(){GMainLoop *loop;func();loop = g_main_loop_new(NULL,FALSE);//创建主循环,NULL不需要传递进程上下文,FALSE现在不运行主循环g_main_loop_run(loop);             //运行主循环return 0;
}

6、编译运行

1)由于设备的驱动名可能不同,读者可能需要修改代码内的xxx.drive字符串
2)编译命令:gcc main.c -o main `pkg-config --cflags --libs glib-2.0 gio-2.0 `
3)运行:./main


http://www.ppmy.cn/news/907633.html

相关文章

使用组策略控制可移动存储访问

使用组策略控制可移动存储访问 为了保护公司的重要信息&#xff0c;微软河北技术支持中心定义了安全策略不允许把公司文档带出公司。实现这一措施的一种方法就是限制在公司计算机上使用可移动的存储设备。目前公司雇员是被允许在他们的计算机上使用可移动存储设备&#xff0c;例…

戴尔R730服务器,安装好系统后,多出一个OEMDRV的可移动存储设备。

戴尔R730服务器&#xff0c;借助光盘安装好系统之后&#xff0c;在资源管理器中看到一个名为OEMDRV的可移动存储设备。 这个是安装系统预留的分区&#xff0c;删除也没有关系&#xff0c;一般会在后期的运行过程中自动消失&#xff0c;或者开机进F10再退出也会消失。

可移动存储的设备格式化 - 文件系统

可移动存储的设备格式化 - 文件系统 通常系统默认使用的 FAT32 磁盘系统支持单个文件的容量上限为 4 GB&#xff0c;如果您需要存储高清视频等单个超过 4 GB 的文件&#xff0c;建议您将其重新格式化&#xff0c;设置为 NTFS 或 exFAT 文件系统&#xff0c;操作步骤如下&#x…

计算机配置中没有可移动存储设备,大神演示了在win10系统中禁用USB移动存储设备(例如U盘和移动硬盘)的设置技巧...

今天,编辑器告诉您如何在win10系统中禁用USB移动存储设备,例如U盘和移动硬盘. 许多用户可能不知道如何在win10系统中禁用USB移动存储设备,例如U盘和移动硬盘. 但是,当遇到在win10系统中禁用USB移动存储设备(例如U盘和移动硬盘)的问题时该怎么办?如果您遇到这种情况,请不要…

【MyBatis Plus 在线文档地址】

MyBatis plus 文档&#xff1a;&#xff08;点击跳转&#xff09; https://baomidou.com/pages/bab2db/#release

SpringFactoriesLoader解析

一、SpringFactoriesLoader 介绍 1.1 SpringFactoriesLoader 简介 SpringFactoriesLoader 工厂加载机制是 Spring 内部提供的一个约定俗成的加载方式&#xff0c;与 java spi 类似&#xff0c;只需要在模块的 META-INF/spring.factories 文件中&#xff0c;以 Properties 类型…

破解ZIP密码-13行代码

import zipfile f1open("5.txt") #打开密码字典 f2zipfile.ZipFile("11.zip") #打开待破解ZIP文件 for i in f1.readlines(): ii.strip() try: f2.extractall(pwdi.encode(utf-8)) print("该文件密码为:",i) …

破解EXCEL工作表保护密码

1、打开需要破解的excel表格 2、文件->新建空白工作表 3、在新建空白工作表的视图->宏->录制宏 4、停止录制宏 5、查看宏 6、编辑刚刚录制的宏&#xff0c;把下面代码复制过去 Public Sub 工作表保护密码破解() Const DBLSPACE As String vbNewLine & vbNe…