Raspberry Pi树莓派数据存储管理优化方案.docx
《Raspberry Pi树莓派数据存储管理优化方案.docx》由会员分享,可在线阅读,更多相关《Raspberry Pi树莓派数据存储管理优化方案.docx(36页珍藏版)》请在冰豆网上搜索。
RaspberryPi树莓派数据存储管理优化方案
1RaspberryPi树莓派数据存储管理优化方案
1.1整体优化方案
树莓派系统在使用过程中,难免需要存储各种数据信息,便是系统的存储空间是有限的,存储介质也包括有外接U盘或系统TF卡,因此需要有一套有效的数据存储管理方案以满足系统的长期稳定运行需求,避免因为存储空间不足或出现不可读写等现象而异致系统故障。
本方案设计了一套完整可靠的数据存储管理优化方案,可应用于有效管理树莓派系统或应用程序持续产出的数据文件,保障树莓派系统长期稳定运行,具体的优化方案描述如下。
1、首先,创建/home/storage目录,定义为数据存储空间,专门应用于管理系统及应用程序的产出数据。
2、当有外接U盘接入的情况下,启动自动挂载脚本优先将U盘自动挂载到/home/storage,此时主要以U盘作为数据存储介质。
自动挂载脚本的详细制作就参考下面章节。
3、当没有外接U盘时,自动挂载脚本将自动检测系统TF卡是否存在数据分区(自定义),如果存在则自动挂载到/home/storage,此时主要以数据分区作为数据存储介质。
关于数据分区的制作方法请参考下面章节。
4、当系统既没有外接U盘接入,也没有数据分区时,系统默认以/home/storage目录直接作为数据存储空间,数据直接保存在系统TF卡的系统主分区。
1.2U盘初始化方法
目前树莓派系统支持的U盘大小有16G、32G、64G等规格,系统对一般通用的U盘产品是自动识别的,不需要安装驱动,只要把U盘插入树莓派的USB接口,正常情况下,通过sudofdisk-l命令就可以查询到U盘的识别状态以及其属性信息,如下图所示。
如上图所示,系统识别出来了一个32GB大小的磁盘,格式为FAT32,这个就是U盘。
关于外接U盘的格式问题,建议在使用前统一格式化为Ext4的Linux格式,此格式对于Linux操作系统的兼容性是最好的。
当然保留FAT32格式也可以正常使用的,但经实践发现,如果使用FAT32格式,容易会出现U盘写保护的现象,这时U盘将无法正常进行写操作,即使重启设备也无法触决问题,需要人工修复。
本方案设计了一套U盘自动格式化脚本(formatUSB.py,脚本代码参考下面章节),可直接通过执行该脚本自动将U盘格式化为Ext4格式,如下图所示。
sudopythonformatUSB.pyauto
1.3U盘自动挂载方法
一般在实际的应用过程中,系统程序或应用程序保存数据时需要指定目录路径,比如本文案指定/home/storage为数据存储目录,因此我们需要先于相关程序启动前将数据存储目录与实际的存存介质关联起来(挂载),本章节设计的U盘自动挂载脚本就是实现此目的,根据实际的存储条件自动完成挂载功能。
自动挂载脚本为mountStorage.py,系统在启动时自动调用该脚本执行挂载。
由于系统及应用程序的产出数据目录需要指向/home/storage目录,因此自动挂载脚本应该在这些应用程序启动前执行挂载,由于存在这种先后的依赖关系,因此我们把自动挂载脚本做成了一个系统服务,并指定在这些应用程序前执行,如下图所示。
/etc/systemd/system/mountStorage.service
该服务的内容如下所示:
[Unit]
Description=RunMountservice
Before=apache2.service
#Before=rc.local.service
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/bin/python/home/pi/app/script/mountStorage.py
[Install]
WantedBy=multi-user.target
补充说明:
Before=apache2.service配置指明mountStorage.service在apache2.service之前先启动,因为apache2的日志目录需要指向storage目录,因此需要在apache2启动前完成自动挂载。
如果是新创建的系统服务,请执行以下命令使其生效。
pi@raspberrypi:
/etc/systemd/system#chmod+xmountStorage.service
pi@raspberrypi:
/etc/systemd/system#systemctldaemon-reload
pi@raspberrypi:
/etc/systemd/system#systemctlenablemountStorage.service
1.4U盘自动管理方法
本方案设定/home/storage目录作为系统的主要数据存储空间,如果没有合理的管理方案,随着数据的不断写入,总有一天会写满磁盘空间,异致不必要的异常出现。
本方案设计了一套自动存储管理脚本clearStorage.py。
由于/home/storage目录存储的数据类型并不是固定的,所以用户应该根据/home/storage目录的具体存储数据特点对clearStorage.py进行修改。
其中一个基本的设计原则是可以增加专用的配置语文件,定义好主要的数据存储目录允许保存的最长时间,自动存储管理脚本定期检查并清除过期数据。
另外一个基本的设计原则是定期检测存储空间的使用率,当总使用率大于80%时,强制删除历史数据(删除的数据类型需要在脚本中自行定义)。
自动存储管理脚本可以设置成系统定时任务crontab,由系统定期(每天执行一次)执行即可。
1.5系统分区制作方法
1、执行命令:
sudocfdisk/dev/mmcblk0
2、移动光标到最下面的标志为FreeSpace的分区,按回车键选中,此时选中的分区高亮(白底黒字)。
3、移动光标,选定底下的New命令然后按回车键,开始创建新的分区
4、接下去会提示你输入要创建的新分区大小。
默认会将所有剩余空间用于创建新分区,这里将新分区设为1G
5、接下去将提示你选择创建主分区还是逻辑分区。
默认选择主分区,按回车键接受默认选择。
5、同样的方法将剩下的所有空闲磁盘空间划分为另一个分区,如下图所示
6、然后,移动光标到Write菜单按回车键选择从而写入分区表。
7、此时将提示你是否确定要改变分区,输入yes并按回车键。
8、移动光标到Quit菜单按回车键退出cfdisk程序。
9、执行命令:
sudofdisk-l查看分区情况:
10、执行命令:
sudoreboot重启系统
11、重启后执行命令:
sudomkfs.ext4/dev/mmcblk0p3格式化分区
sudomkfs.ext4/dev/mmcblk0p4格式化分区
1.6优化脚体制作
1.6.1U盘自动格式化脚本
#!
/usr/bin/envpython
#-*-coding:
utf-8-*-
"""
用于自动格式化挂载的U盘的脚本
"""
importos
importsys
importre
importdatetime
importre
importtime
importlogging
importlogging.handlers
MOUNT_POINT='/home/storage'#测试的挂载目录
fmtlogger=logging.getLogger('fmtlogger')
fmtlogger.setLevel(logging.INFO)
log_dir='/home/pi/log/'
log_file='formatusb.log'
ifos.path.exists(log_dir):
#print'OK'
full_log_path=log_dir+log_file
ifos.path.exists(full_log_path):
os.system('chmod-R777%s'%(full_log_path))
#setsplitteris1M,andonly1backup
fh=logging.handlers.RotatingFileHandler(full_log_path,
maxBytes=1*10**6,backupCount=1)
fh.setLevel(logging.INFO)
formatter=logging.Formatter('%(asctime)s-%(name)s-%(levelname)s-%(message)s')
fh.setFormatter(formatter)
console=logging.StreamHandler()
console.setLevel(logging.DEBUG)
fmtlogger.addHandler(fh)
#fmtlogger.addHandler(console)
classUSBInfo:
def__init__(self,dev_name):
self._dev_name=dev_name
self._format=''
self._mount_point=''
self._is_mount=False
self._has_files=False
defgetDevName(self):
returnself._dev_name
defsetDevName(self,name):
self._dev_name=name
defgetFormat(self):
returnself._format
defisLinuxFormat(self):
returnself._format=='Linux'
defsetFormat(self,fmt):
self._format=fmt
defisMount(self):
returnself._is_mount
defsetMountPoint(self,path=''):
self._is_mount=Falseifpath==''elseTrue
self._mount_point=path
defgetMountPoint(self):
returnself._mount_point
defhasFile(self):
returnself._has_files
defsetChkFileRes(self,state):
ifisinstance(state,bool):
self._has_files=state
classUSBFmt:
OP_SUCCESS=1#操作成功
MNT_SUCCESS=2#挂载U盘成功
UMNT_SUCCESS=3#卸载U盘成功
FMT_SUCCESS=4#格式化U盘成功
OP_FAIL=0#操作失败
USB_NOT_EXIST=-1#没有USB
MNT_NOPOINT=-2#该路径不能挂载
MNT_POINT_OCCUPIED=-3#该路径已被挂载,冲突
MNT_FAIL=-4#挂载U盘失败
UMNT_FAIL=-5#卸载U盘失败
FMT_FAIL=-6#格式化U盘失败
def__init__(self):
self._usbInfoMap={}#U盘信息表
self.getDeviceMap()
defumountAllUsb(self):
"""
取消挂载所有的USB分区
"""
usb_list=self._findAvailUSB()
fordevinusb_list:
part1='%s1'%dev
try:
os.system('umount%s>/dev/null2>&1'%part1)
except:
pass
defgetDeviceMap(self):
"""
读取U盘设备的分区表类型,能否挂载,是否已拷贝文件的信息
"""
self._usbInfoMap={}#clean
usb_list=self._findAvailUSB()
#printusb_list
fordeviceinusb_list:
self._getUsbInfo(device)
returnself._usbInfoMap
def_findAvailUSB(self):
"""
尝试在/dev/sda-/dev/sdz中查找可用的usb
@return返回可用usb的list
"""
usb_list=[]
start=97#'a'asciicode
foriinxrange(start,start+26):
dev='/dev/sd%s'%chr(i)
cmd='fdisk-l|grep"%s"'%dev
ret=os.popen(cmd).readlines()
iflen(ret)>0:
usb_list.append(dev)
returnusb_list
def_getUsbInfo(self,device_name):
"""
读取U盘设备的分区类型
"""
part1='%s1'%device_name
devInfo=USBInfo(device_name)#addinfo
#checkformat
fmt=self._checkFormat(part1)
devInfo.setFormat(fmt)
#checkwhetherismounted
path=self._checkMounted(part1)
devInfo.setMountPoint(path)
self._usbInfoMap[device_name]=devInfo
returnself._usbInfoMap
def_checkFormat(self,part1):
#checkformat
cmd='fdisk-l|grep"^%s"'%part1
#printcmd
ret=os.popen(cmd).readlines()
iflen(ret)>0:
#addindevicemap
if'Linux'inrepr(ret):
return'Linux'
elif'FAT32'inrepr(ret):
return'FAT32'
else:
return'Other'
else:
return''
def_checkMounted(self,part1):
cmd='df-h|grep"^%s"'%part1
res=os.popen(cmd).readlines()
#print'checkmount:
',res
iflen(res)>0:
forlineinres:
pattern=r'^%s.+\s+(/\S+)$'%part1
result=re.search(pattern,line)
ifresultisnotNone:
#print'match',part1
returnresult.group
(1)
else:
#printpart1,'notmatch'
return''
return''
deftryMount(self,device_name,mount_point='/home/pi/testMount'):
"""
检查是否能挂载该设备的分区,
如果设备是/dev/sda,则尝试挂载/dev/sda
@return:
成功则返回True,失败则返回False
"""
ret=USBFmt.OP_FAIL
ifnotos.path.exists(mount_point):
os.system('mkdir-m=777-p%s'%mount_point)
ifnotos.path.exists(mount_point):
returnUSBFmt.MNT_NOPOINT
ifdevice_namenotinself._usbInfoMap:
returnUSBFmt.USB_NOT_EXIST
ifself._usbInfoMap[device_name].isMount():
fmtlogger.info('%shasmounted.'%device_name)
ret=self._umount(device_name)
ifret!
=USBFmt.OP_SUCCESS:
returnret
ifos.path.ismount(mount_point):
returnUSBFmt.MNT_POINT_OCCUPIED
ret=self._mount(device_name,mount_point)
ifret!
=USBFmt.OP_SUCCESS:
returnret
time.sleep(3)#delayforsystemresponse
ret=self._umount(device_name)
ifret!
=USBFmt.OP_SUCCESS:
returnret
returnUSBFmt.OP_SUCCESS
def_mount(self,device_name,mount_point):
part1='%s1'%device_name
res=''
try:
res=os.popen('mount%s%s2>&1'%(part1,mount_point)).read()
except:
returnUSBFmt.MNT_FAIL
iflen(res)>0:
fmtlogger.error('mounterror:
%s'%res)
returnUSBFmt.MNT_FAIL
fmtlogger.info('mount%ssucecss!
'%part1)
returnUSBFmt.OP_SUCCESS
def_umount(self,device_name):
part1='%s1'%device_name
cmd='umount%s2>&1'%part1
try:
res=os.popen(cmd).read()
except:
returnUSBFmt.UMNT_FAIL
iflen(res)>0:
fmtlogger.error('mounterror:
%s'%res)
returnUSBFmt.UMNT_FAIL
fmtlogger.info('umount%ssucecss!
'%part1)
returnUSBFmt.OP_SUCCESS
defformatUsb(self,device_name):
"""
取消挂载设备,并格式化为ext4格式
@device_name设备名
"""
self.getDeviceMap()
ifdevice_namenotinself._usbInfoMap:
returnUSBFmt.USB_NOT_EXIST
part1='%s1'%device_name
ret=USBFmt.OP_FAIL
ifself._checkMounted(part1):
ret=self._umount(device_name)
ifret!
=USBFmt.OP_SUCCESS:
returnUSBFmt.FMT_FAIL
#doformat
ret=self._formatDevice(device_name)
ifret!
=USBFmt.OP_SUCCESS:
returnUSBFmt.FMT_FAIL
returnUSBFmt.OP_SUCCESS
def_formatDevice(self,device_name):
"""
用dos分区覆盖原先分区表,创建1个默认最大的ext4分区
"""
part_cmd='(echoo;'\
'echon;'\
'echop;'\
'echo1;'\
'echo;'\
'echo;'\
'echow'\
')|fdisk%s2>&1'%device_name
#printpart_cmd
result=''
try:
result=os.popen(part_cmd).read()
except:
errmsg='%sFormaterror!
'%device_name
fmtlogger.error(errmsg)
returnUSBFmt.FMT_FAIL
iflen(result)>0:
fmtlogger.warning('fdiskresult:
%s'%result)
time.sleep(3)
part1='%s1'%device_name
fmtlogger.info('%smkfsstep.'%part1)
fmt_cmd='mkfs.ext4%s2>&1'%part1
try:
result=os.popen(fmt_cmd).read()
except:
errmsg='%sFormaterror!
'%device_name
fmtlogger.error(errmsg)
returnUSBFmt.FMT_FAIL
iflen(result)>0:
fmtlogger.warning('fdiskresult:
%s'%result)
time.sleep(3)
returnUSBFmt.OP_SUCCESS
defformatAllUsb(self):
fmtlogger.info('formatAll')
self.getDeviceMap()
fordev,infoinsorte