locate是Linux下实现快速查找文件的工具。
它的搜索速度要比find快很多,因为它在搜索时并没有去遍历文件系统查找,而是在一个索引数据库中进行查找。这个数据库文件在/var/lib/mlocate/mlocatedb(版本不同,会有所不同。有的版本位置是/var/lib/slocate/slocate.db,还有的是/var/cache/locate/locatedb)。此数据库中存储了系统的全部文件名和目录,locate命令就是在这个数据库内查找,所以速度非常快。但locate的查找并不是实时的,而是以数据库的更新为准。所以在查找前,可先运行”sudo updatadb”命令来更新数据库为最新。
整个locate工作其实是由四部分组成的:
updatedb主要用来更新数据库,这个工作可以通过crontab自动完成的;
mlocate/locate是完成查询功能的程序;
updatedb.conf用来配置数据库中要放入哪些目录和文件,排除哪些文件等;
mlocate.db则是存放文件信息的文件;
locate的所有查询操作全部在mlocate.db中进行的。对于mlocate.db,这是一个有一定格式的数据库文件,由文件头和数据组成,下面就结合locate源码详细讲解一下这个数据库的格式。
首先看一下使用hexdump打印出来的mlocate.db文件开始的一部分:
root@ubuntu:/etc# hexdump -c /var/lib/mlocate/mlocate.db | head -30
0000000 \0 m l o c a t e \0 \0 001 ) \0 001 \0 \0
0000010 / \0 p r u n e _ b i n d _ m o u
0000020 n t s \0 1 \0 \0 p r u n e f s \0 A
0000030 F S \0 A U T O F S \0 B I N F M T
0000040 _ M I S C \0 C I F S \0 C O D A \0
0000050 C U R L F T P F S \0 D E V F S \0
0000060 D E V P T S \0 D E V T M P F S \0
0000070 E C R Y P T F S \0 F T P F S \0 F
0000080 U S E . G L U S T E R F S \0 F U
0000090 S E . S S H F S \0 F U S E S M B
00000a0 \0 I S O 9 6 6 0 \0 L U S T R E \0
00000b0 M F S \0 N C P F S \0 N F S \0 N F
00000c0 S 4 \0 P R O C \0 R P C _ P I P E
00000d0 F S \0 S H F S \0 S M B F S \0 S Y
00000e0 S F S \0 T M P F S \0 U D F \0 U S
00000f0 B F S \0 V M H G F S \0 \0 p r u n
0000100 e n a m e s \0 \0 p r u n e p a t
0000110 h s \0 / h o m e / . e c r y p t
0000120 f s \0 / m e d i a \0 / t m p \0 /
0000130 v a r / s p o o l \0 \0 \0 \0 \0 \0 V
0000140 ~ 205 ٠024 D Ꞡ נ \0 \0 \0 \0 / \0 001 b i
0000150 n \0 001 b o o t \0 001 c d r o m \0 001
0000160 d e v \0 001 e t c \0 001 h o m e \0 \0
0000170 i n i t r d . i m g \0 \0 i n i t
0000180 r d . i m g . o l d \0 001 l i b \0
0000190 001 l o s t + f o u n d \0 001 m e d
00001a0 i a \0 001 m n t \0 001 o p t \0 001 p r
00001b0 o c \0 001 r o o t \0 001 r u n \0 001 s
00001c0 b i n \0 001 s r v \0 001 s y s \0 001 t
00001d0 m p \0 001 u s r \0 001 v a r \0 \0 v m
我已经将关键信息加粗显示,结合下面的介绍看的时候会更清楚。
整个文件头包括header和配置块两部分。mlocate.db文件的最前面的部分由一个db_header结构体表示。
1)header
struct db_header
{
uint8_t magic[8]; /* See DB_MAGIC below */
uint32_t conf_size; /* Configuration block size, in big endian */
uint8_t version; /* File format version, see DB_VERSION* below */
uint8_t check_visibility; /* Check file visibility in locate(1) */
uint8_t pad[2]; /* 32-bit total alignment */
/* Followed by NUL-terminated path of the root of the database */
};
其中:
uint8_t magic[8]: 文件开始的8byte是magic number,其值是”\0mlocate”,这个魔数定义在宏DB_MAGIC中:
#define DB_MAGIC { '\0', 'm', 'l', 'o', 'c', 'a', 't', 'e' }
uint32_t conf_size: 接下来的4byte是配置文件的大小,以大端模式存放。
uint8_t version: 1byte保存文件格式版本,目前为0。
uint8_t check_visibility: 可见性标识,1byte。
uint8_t pad[2]: 2byte填充字节和以NUL结尾的根目录。
2)配置块
header后面接下来是配置块,其作用是当配置文件更新后确保过期数据库不会被updabedb使用,对locate没有影响。配置块是一系列由变量名和变量值组成的键值对,其实就是updatedb.conf配置文件中的变量和值。变量值可以由多个字符串组成,每个值以NUL为结束符,整个变量的结束则是由两个NUL字符表示。
当前定义的变量有:
prune_bind_mounts: 单值,PRUNE_BIND_MOUNTS的值;
prunefs: PRUNEFS的值,多字符串,表示排除的文件系统;
prunenames: PRUNENAMES的值,多字符串,表示排除的文件或目录名;
prunepaths: PRUNEPATHS的值,多字符串,排除的路径;
我在上面dump出来的图表中将一些关键部分加粗表示,将上面的图表结合updatedb.conf文件内容对照着看可以看的更清楚。
updatedb.conf文件内容:
root@ubuntu:/etc# cat updatedb.conf
PRUNE_BIND_MOUNTS="yes"
\# PRUNENAMES=".git .bzr .hg .svn"
PRUNEPATHS="/tmp /var/spool /media /home/.ecryptfs"
PRUNEFS="NFS nfs nfs4 rpc_pipefs afs binfmt_misc proc smbfs autofs iso9660 ncpfs coda devpts ftpfs devfs mfs shfs sysfs cifs lustre tmpfs usbfs udf fuse.glusterfs fuse.sshfs curlftpfs ecryptfs fusesmb devtmpfs vmhgfs"
文件头后面接下来就是数据库实际保存数据的部分,从这里一直到文件末尾描述了文件系统中的目录及内容,存放格式是“目录+目录内容”,目录后面紧跟着这个目录下的所有内容。
1)目录
每个目录有一段描述信息,由结构体db_directory表示:
/* Directory header */
struct db_directory
{
uint64_t time_sec; /* st_[cm]time of the directory in big endian */
uint32_t time_nsec;
uint8_t pad[4]; /* 64-bit total alignment */
/* Followed by NUL-terminated absolute path of the directory */
};
uint64_t time_sec: 8byte,表示目录的时间(以秒为单位),取最后修改时间和创建时间中较大值(也就是st_ctime和st_mtime中的最大值);
uint32_t time_nsec: 4byte,目录时间(以ns为单位)。如果未知,则设为0,其值小于1000000000;
uint8_t pad[4]: 4type填充值;
目录后面紧跟着一个以NUL字符结尾的字符串记录这个目录的路径。
2)内容
目录内容是一系列的文件节点,紧跟在目录路径后面,
每一项目录内容前面有一个db_entry结构体表示这条内容的类型:
/* Directory entry */
struct db_entry
{
uint8_t type; /* See DBE_* below */
/* Followed by NUL-terminated name if tag != DBE_END */
};
enum
{
DBE_NORMAL = 0, /* A non-directory file */
DBE_DIRECTORY = 1, /* A directory */
DBE_END = 2 /* End of directory contents; contains no name */
};
uint8_t type: 表示类型,根据下面的枚举可以看出来,有3种情况,0 - 非目录文件;1 - 子目录; 2 - 标识当前目录的结尾。
mlocate.db文件中的内容基本上就是这些。
前面是整个文件的头部,有魔数、版本号等;
然后是配置块,其实就是把updatedb.conf配置文件中的内容放到了这里,以便updatedb在更新数据库时放弃可以过期的数据(这里要说了一下,mlocate与slocate一点重要的区别是,前者更新数据库的速度更快,因为mlocate中会有一个目录修改时间的判断,如果发现一个目录的时间没有更新,则不再遍历这个目录,直接将原数据库内容拷贝过来);
后面则是数据库的数据部分,由一个一个的目录及这个目录中的内容组成。locate进行搜索就是在这个部分进行查找并匹配,如果匹配成功则将这条内容打印出来。