locate 工作原理

locate是Linux下实现快速查找文件的工具。

它的搜索速度要比find快很多,因为它在搜索时并没有去遍历文件系统查找,而是在一个索引数据库中进行查找。这个数据库文件在/var/lib/mlocate/mlocatedb(版本不同,会有所不同。有的版本位置是/var/lib/slocate/slocate.db,还有的是/var/cache/locate/locatedb)。此数据库中存储了系统的全部文件名和目录,locate命令就是在这个数据库内查找,所以速度非常快。但locate的查找并不是实时的,而是以数据库的更新为准。所以在查找前,可先运行”sudo updatadb”命令来更新数据库为最新。

整个locate工作其实是由四部分组成的:

  • /usr/bin/updatedb
  • /usr/bin/locate(mlocate)
  • /etc/updatedb.conf
  • /var/lib/mlocate/mlocate.db

updatedb主要用来更新数据库,这个工作可以通过crontab自动完成的;
mlocate/locate是完成查询功能的程序;
updatedb.conf用来配置数据库中要放入哪些目录和文件,排除哪些文件等;
mlocate.db则是存放文件信息的文件;

locate数据库格式

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

我已经将关键信息加粗显示,结合下面的介绍看的时候会更清楚。

1. 文件头

整个文件头包括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"

2. 数据部分

文件头后面接下来就是数据库实际保存数据的部分,从这里一直到文件末尾描述了文件系统中的目录及内容,存放格式是“目录+目录内容”,目录后面紧跟着这个目录下的所有内容。
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进行搜索就是在这个部分进行查找并匹配,如果匹配成功则将这条内容打印出来。

你可能感兴趣的:(Everything,for,linux)