目的很简单,插上U盘后能自动挂载,但加点小要求:第一,挂载的目录要在/media上。第二,挂载的目录名要和系统识别U盘的名字一样。比如,我有一个三个区的U盘,怼入后fdisk -l看一下看到它为/dev/sdb1\2\3,那么正常来讲,它应该挂载到/media/sdb1\2\3目录。
那基于这个目标,撸起袖子干。本来linux内置udev服务可以完美解决。但参考一堆教程配置来配置去有各种奇奇怪怪的问题达不到预期目的。外加上由于这个是小系统被剪过(从2G剪到550M的fedora...),缺了什么咱们还真不确定。
至此,一个低端的自动挂载程序想法悄咪咪的生成。但求能work out。
简单的说,就是不断检查系统硬盘分区,多了就加,少了就减。
当然这是个特例,因为目标系统定死了首先它只有一个硬盘装的是系统,也就是sda。其次它如果有新的硬盘进入系统,也只能是U盘。
所以分三块:
1. 流程控制,控制执行频率和挂载卸载执行分支;
2. 查看系统硬盘状态,并得到相应数据;
3. 生成硬盘信息数据结构,同步挂载卸载;
流程控制,简单的采用while(1)和sleep控制,采用数据对比每番进行判断;
硬盘状态,在/proc/partitions可以读取,再加个wc -l细化处理可以简单得到当前硬盘数;
数据结构,由于每个硬盘可能有几个区,而代表每个区的是字符串结构,符合这种直接用二维数组,再由于顺序没有特别的重要,善用字符串比较和下标就行,所以总的来说算是一个比较离散的二维数组吧。
上流程图:
由于也只有一条线程,也不用考虑数据的冲突,就一路往下跑就是了。
细想整个流程最重要的部分,也就是有一个全局的二维数组来记录着当前挂载了的硬盘。
每当硬盘有变动,这个二维数组就产生相应的变化,那具体的增删,是有点点类似队列但又不是,因为你无法判断是哪个先出(是哪个磁盘先拔出来),考虑过链表,不过总共才四五个USB口用数组它不香吗。
那么假设现在挂载了个三个分区的U盘B,sdb1,sdb2,sdb3,那么在这个全局数组中数据应该是这样的:
这时如果先插入两个分区的U盘C和一个分区的U盘D,它应该会进行添加即:
--->
添加的时候是从尾部添加,删除时及时移位进行前面的空位补全。其中有一个对应的全局变量记录着当前挂载的硬盘数,可以用作这个全局数组的下标进行处理。
从代码层级上说,从上往下简要的几个重要部份。
宏定义和全局,全局也就上述提到那俩~
#define PARTITION_CNT_MAX 50 //最大接入硬盘数,稍微给大点没事
#define PARTITION_LEN_SIZE 5 //最大分区名字长度
#define SLEEP_TIME_US 1000*500 //睡眠时间,等于是频率
#define BUFF_SIZE 1024*4 //取得数据中转缓存
#define CMD_SIZE 100 //存放Linux命令缓存
#define BUFF_SIZE_MINOR 10 //取得数据中转小缓存
#define LAST_DISK_POINT "sda1" //固有硬盘的最后一个,往下都是添加的
#define NORMAL_DISK_NUM 4 //没新接硬盘时的正常/proc/partitions行数
#define DEBUG //调试开关
char g_disk_mount[PARTITION_CNT_MAX][PARTITION_LEN_SIZE] = {0};
int g_disk_mount_cnt = 0;
一开始先检查硬盘数目,将数据转化成自己想要的信息:
int check_Disk_Num()
{
int disk_num;
char buff[BUFF_SIZE_MINOR] = {0};
char cmd[CMD_SIZE] = {0};
sprintf(cmd, "cat /proc/partitions | wc -l");
FILE *fp = popen(cmd, "r");
while( fgets(buff, BUFF_SIZE_MINOR-1, fp) != NULL)
{
continue;
}
disk_num = atoi(buff);
#ifdef DEBUG
printf("check disk num: %d\n", disk_num);
#endif
pclose(fp);
return disk_num;
}
每当检测到硬盘数目增加,会先取得增加的硬盘名字列表,保存在disk[]这个数组里:
char *p = strstr(buff,LAST_DISK_POINT);
if(p != NULL)
{
disk_partition_cnt = 0;
while(1)
{
if (disk_partition_cnt >= PARTITION_CNT_MAX)
break;
p = strstr(p + 4,"sd");
if(p != NULL)
{
//get mount disk list
memset(disk[disk_partition_cnt], 0, PARTITION_LEN_SIZE);
memcpy(disk[disk_partition_cnt], p, 4);
change_Enter(disk[disk_partition_cnt]);
disk_partition_cnt++;
}
else
{
break;
}
}
}
得到数组列表,判断下是否单分区是否已存在,进行全局数据添加操作和系统挂载:
//judge and add to global
disk_mount_cnt_temp = g_disk_mount_cnt;
for (int i = 0; i < disk_partition_cnt; i++)
{
if (disk_Is_Exit(disk[i]))
continue;
//if len=3 disk have no partition
if (strlen(disk[i]) == 3)
{
if ( !disk_Is_Single_Partition(disk[i]))
continue;
}
memcpy(g_disk_mount[g_disk_mount_cnt], disk[i], strlen(disk[i]));
g_disk_mount_cnt++;
}
run_Mount(g_disk_mount_cnt - disk_mount_cnt_temp);
根据获取的实际挂载磁盘信息和全局数组进行比对,如果发现实际的少了些,那要进行卸载和全局数组调整删除操作了:
for (int i = 0; i < g_disk_mount_cnt; i++)
{
if (strstr(buff,g_disk_mount[i]) == NULL)
{
memset(cmd, 0, CMD_SIZE);
sprintf(cmd,"umount /media/%s",g_disk_mount[i]);
if ( 0 != run_System_Cmd(cmd))
{
memset(cmd, 0, CMD_SIZE);
sprintf(cmd,"umount /media/%s -l",g_disk_mount[i]);
run_System_Cmd(cmd);
}
memset(cmd, 0, CMD_SIZE);
sprintf(cmd, "rm /media/%s -rf", g_disk_mount[i]);
run_System_Cmd(cmd);
disk_umount_pos[disk_umount_cnt] = i;
disk_umount_cnt++;
}
}
卸载系统里已经没有了的硬盘后,进行数组的清除和移位操作:
//clear the umount disk from global
for (int i = 0; i < disk_umount_cnt; i++)
{
if (disk_umount_pos[i] == -1)
continue;
memset(g_disk_mount[disk_umount_pos[i]], 0, PARTITION_LEN_SIZE);
}
//reset the global
g_disk_mount_cnt = g_disk_mount_cnt - disk_umount_cnt;
int temp_cnt = 0;
for (int i = 0; i < PARTITION_CNT_MAX; i++)
{
if (temp_cnt == g_disk_mount_cnt)
break;
if (strlen(g_disk_mount[i]) != 0)
{
memcpy(g_disk_mount[temp_cnt], g_disk_mount[i], PARTITION_LEN_SIZE);
temp_cnt++;
}
}
for (int i = temp_cnt; i < PARTITION_CNT_MAX; i++)
{
memset(g_disk_mount[i], 0, PARTITION_LEN_SIZE);
}
主要操作就这么多,当然还有很多细代码块,实现的方法也有很多。
还是那句话,能work就行。。。
是有点儿low的,不过的确也能按照要求完成任务,最起码baseline是达到的。
还是挺感概这个哈皮方法能上的了台~毕竟当初记得有一个作业,说把开发板作为一个电子相册,插入满是图片的U盘时开发版自动挂载U盘并显示图片出来。
哈哈哈如今竟然用到了,只不过别人变成了实际的用处,用在哪咱也不知道也不敢问,但完成底层的挂卸载管他怎么操作呢。
最后放上挂卸载时程序消耗图吧和一个服务检测这个程序会不会挂掉,挂掉就拉起来:
鹅还有哈皮代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define PARTITION_CNT_MAX 50
#define PARTITION_LEN_SIZE 5
#define SLEEP_TIME_US 1000*500
#define BUFF_SIZE 1024*4
#define CMD_SIZE 100
#define BUFF_SIZE_MINOR 10
#define LAST_DISK_POINT "sda1"
#define NORMAL_DISK_NUM 4
#define DEBUG
char g_disk_mount[PARTITION_CNT_MAX][PARTITION_LEN_SIZE] = {0}; // record the mounted disk
int g_disk_mount_cnt = 0; // record the mounted disk count
bool run_System_Cmd(char *cmd)
{
if (cmd == NULL)
return false;
#ifdef DEBUG
printf("run shell: %s\n", cmd);
#endif
bool ret = system(cmd);
//printf("ret = %d\n", ret);
return ret;
}
int check_Disk_Num()
{
int disk_num;
char buff[BUFF_SIZE_MINOR] = {0};
char cmd[CMD_SIZE] = {0};
sprintf(cmd, "cat /proc/partitions | wc -l");
FILE *fp = popen(cmd, "r");
while( fgets(buff, BUFF_SIZE_MINOR-1, fp) != NULL)
{
continue;
}
disk_num = atoi(buff);
#ifdef DEBUG
printf("check disk num: %d\n", disk_num);
#endif
pclose(fp);
return disk_num;
}
bool disk_Is_Single_Partition(char *disk)
{
if (disk == NULL)
return false;
unsigned char buff[BUFF_SIZE_MINOR] = {0};
unsigned char cmd[CMD_SIZE] = {0};
sprintf(cmd, "cat /proc/partitions | grep %s | wc -l", disk);
FILE *fp = popen(cmd, "r");
while( fgets(buff, BUFF_SIZE_MINOR-1, fp) != NULL)
{
continue;
}
pclose(fp);
if ( 1 == atoi(buff))
{
#ifdef DEBUG
printf("disk: %s is single partition\n",disk);
#endif
return true;
}
return false;
}
int get_Partition_Info(char *info)
{
char buff[BUFF_SIZE] = {0};
char cmd[CMD_SIZE] = {0};
sprintf(cmd, "cat /proc/partitions");
FILE *fp = popen(cmd, "r");
if (fp == NULL)
return -1;
while( fgets(buff, BUFF_SIZE-1, fp) != NULL)
{
strncat(info, buff, strlen(buff));
continue;
}
#ifdef DEBUG
printf("\n----partitions info----:\n %s\n", info);
#endif
pclose(fp);
return 0;
}
void run_Mount(int add)
{
unsigned char cmd[CMD_SIZE] = {0};
for (int i = g_disk_mount_cnt - add; i < g_disk_mount_cnt; i++)
{
memset(cmd, 0, CMD_SIZE);
sprintf(cmd, "mkdir /media/%s", g_disk_mount[i]);
run_System_Cmd(cmd);
memset(cmd, 0, CMD_SIZE);
sprintf(cmd,"mount -t auto /dev/%s /media/%s",g_disk_mount[i],g_disk_mount[i]);
run_System_Cmd(cmd);
}
}
bool disk_Is_Exit(char *disk)
{
for (int i = 0; i < g_disk_mount_cnt; i++)
{
if (strcmp(disk , g_disk_mount[i]) == 0)
{
return true;
}
}
return false;
}
void change_Enter(char *temp)
{
if (temp == NULL)
return ;
for (int i = 0; i < strlen(temp); i++)
{
if (temp[i] == '\n')
{
temp[i] = '\0';
}
}
}
bool check_Disk_Mount(char * buff)
{
if (buff == NULL)
return false;
for (int i = 0; i < g_disk_mount_cnt; i++)
{
//not in file buff
if (strstr(buff,g_disk_mount[i]) == NULL)
{
return false;
}
}
return true;
}
void main()
{
int disk_partition_cnt = 0;
int disk_num_temp;
int disk_num = NORMAL_DISK_NUM;
int disk_mount_cnt_temp;
char buff[BUFF_SIZE] = {0};
char disk[PARTITION_CNT_MAX][PARTITION_LEN_SIZE] = {0};
char cmd[CMD_SIZE] = {0};
int disk_umount_cnt = 0;
int disk_umount_pos[PARTITION_CNT_MAX];
for (int i = 0; i < PARTITION_CNT_MAX; i++)
{
disk_umount_pos[i] = -1;
}
while(1)
{
//check the /proc/partition
disk_num_temp = check_Disk_Num();
if (g_disk_mount_cnt != 0)
run_System_Cmd("sync");
if (disk_num != disk_num_temp)
{
#ifdef DEBUG
printf("different disk info\n");
#endif
//get partition buff
memset(buff,0,BUFF_SIZE);
if (0 != get_Partition_Info(buff))
{
usleep(SLEEP_TIME_US);
continue;
}
//disk add mount
if (disk_num < disk_num_temp)
{
#ifdef DEBUG
printf("disk add action\n");
#endif
char *p = strstr(buff,LAST_DISK_POINT);
if(p != NULL)
{
disk_partition_cnt = 0;
while(1)
{
if (disk_partition_cnt >= PARTITION_CNT_MAX)
break;
p = strstr(p + 4,"sd");
if(p != NULL)
{
//get mount disk list
memset(disk[disk_partition_cnt], 0, PARTITION_LEN_SIZE);
memcpy(disk[disk_partition_cnt], p, 4);
change_Enter(disk[disk_partition_cnt]);
disk_partition_cnt++;
}
else
{
break;
}
}
}
//judge and add to global
disk_mount_cnt_temp = g_disk_mount_cnt;
for (int i = 0; i < disk_partition_cnt; i++)
{
if (disk_Is_Exit(disk[i]))
continue;
//if len=3 disk have no partition
if (strlen(disk[i]) == 3)
{
if ( !disk_Is_Single_Partition(disk[i]))
continue;
}
memcpy(g_disk_mount[g_disk_mount_cnt], disk[i], strlen(disk[i]));
g_disk_mount_cnt++;
}
run_Mount(g_disk_mount_cnt - disk_mount_cnt_temp);
}
else
{
//find the umount disk list and umount
for (int i = 0; i < PARTITION_CNT_MAX; i++)
{
disk_umount_pos[i] = -1;
}
disk_umount_cnt = 0;
for (int i = 0; i < g_disk_mount_cnt; i++)
{
if (strstr(buff,g_disk_mount[i]) == NULL)
{
memset(cmd, 0, CMD_SIZE);
sprintf(cmd,"umount /media/%s",g_disk_mount[i]);
if ( 0 != run_System_Cmd(cmd))
{
memset(cmd, 0, CMD_SIZE);
sprintf(cmd,"umount /media/%s -l",g_disk_mount[i]);
run_System_Cmd(cmd);
}
memset(cmd, 0, CMD_SIZE);
sprintf(cmd, "rm /media/%s -rf", g_disk_mount[i]);
run_System_Cmd(cmd);
disk_umount_pos[disk_umount_cnt] = i;
disk_umount_cnt++;
}
}
#ifdef DEBUG
printf("get g_disk_umount_cnt: %d\n", disk_umount_cnt);
#endif
//clear the umount disk from global
for (int i = 0; i < disk_umount_cnt; i++)
{
if (disk_umount_pos[i] == -1)
continue;
memset(g_disk_mount[disk_umount_pos[i]], 0, PARTITION_LEN_SIZE);
}
//reset the global
g_disk_mount_cnt = g_disk_mount_cnt - disk_umount_cnt;
int temp_cnt = 0;
for (int i = 0; i < PARTITION_CNT_MAX; i++)
{
if (temp_cnt == g_disk_mount_cnt)
break;
if (strlen(g_disk_mount[i]) != 0)
{
memcpy(g_disk_mount[temp_cnt], g_disk_mount[i], PARTITION_LEN_SIZE);
temp_cnt++;
}
}
for (int i = temp_cnt; i < PARTITION_CNT_MAX; i++)
{
memset(g_disk_mount[i], 0, PARTITION_LEN_SIZE);
}
}
disk_num = disk_num_temp;
}
else if (disk_num_temp != NORMAL_DISK_NUM)
{
if (true != check_Disk_Mount(buff))
{
//refresh
disk_num = 0;
}
}
#ifdef DEBUG
printf("get g_disk_mount_cnt: %d\n", g_disk_mount_cnt);
for (int i = 0; i < g_disk_mount_cnt; i++)
{
printf("mount disk[%d]: %s\n", i, g_disk_mount[i]);
}
#endif
usleep(SLEEP_TIME_US);
}
}