在 linux 中,经常需要获取文件的属性,比如修改时间,文件大小等等。stat 函数将会帮助我们得到这些信息。
1 stat 函数
1.1 stat 函数的作用
linux 中,可以使用 stat 函数来获取文件相关的信息,就比如说文件的大小,文件的类型等等。
1.2 struct stat 结构体
stat 函数将获取到的结果保存到一个名为 struct stat 的结构体中。它的样子如下:
struct stat { dev_t st_dev; /* 包含这个文件的设备 ID */ ino_t st_ino; /* inode 编号 */ mode_t st_mode; /* 访问权限 */ nlink_t st_nlink; /* 硬链接数量 */ uid_t st_uid; /* 用户ID */ gid_t st_gid; /* 组ID */ dev_t st_rdev; /* 设备ID */ off_t st_size; /* 文件占用的字节数 */ blksize_t st_blksize; /* 文件系统块大小 */ blkcnt_t st_blocks; /* 文件占用了几个512字节 */ time_t st_atime; /* 最后访问时间 */ time_t st_mtime; /* 最后更改时间 */ time_t st_ctime; /* 最后状态更改时间 */ };
1.3 stat 函数原型
int stat(const char *pathname, struct stat *buf);
stat 函数的第一个参数是目标文件的路径。第二个参数是输出参数,用来保存返回的文件信息的结果。
stat 函数的返回值如果是 0,表示函数执行成功,否则失败。失败后会改写 errno 这个全局变量。我们可以使用 perror 这个函数打印失败的原因。
2 实验
- 代码
// filename: statdemo.c #include
#include
#include
#include
int main() { struct stat st; int r = stat("test.txt", &st); if (r) { perror("stat"); return -1; } printf("st_dev = %lld\n", st.st_dev); printf("st_ino = %ld\n", st.st_ino); printf("st_mode = %d\n", st.st_mode); printf("st_nlink = %d\n", st.st_nlink); printf("st_uid = %d\n", st.st_uid); printf("st_gid = %d\n", st.st_gid); printf("st_size = %ld\n", st.st_size); printf("st_atime = %ld\n", st.st_atime); printf("st_mtime = %ld\n", st.st_mtime); printf("st_ctime = %ld\n", st.st_ctime); return 0; }
- 编译
$ gcc statdemo.c -o st
- 运行结果
如果 test.txt 文件不存在,打印如下结果:
stat: No such file or directory
如果 test.txt 文件存在,其内容只包含 hello 5 个字符,打印结果如下:
st_dev = 2049 st_ino = st_mode = 33204 st_nlink = 1 st_uid = 1000 st_gid = 1000 st_size = 6 // 这个结果是 6 是因为 vim 编辑器会自动在最后一个字符后面插入一个 '\n' 字符 st_atime = st_mtime = st_ctime =
从以上结果可以看到文件信息各个字段的的内容。比如最近访问时间,最近更改时间,最近状态更改时间等等。还有访问权限位(st_mode)等等。另外需要注意的是,文件的类型也可以从 st_mode 中得出,这些我们将在后面介绍。
3 更加深入
掌握了 stat 函数的用法,相信你不应该止步于此。我们提出的问题是,stat 函数是从哪儿获取到文件信息的?
倘若你阅读了前面几篇有关文件系统的章节,相信你还回忆得起 目录项、inode 节点 这些很重要的名词。
3.1 再理一遍文件查找过程
cur_inode = 2 for name in path: // 分析路径中的每一层次的名称 if isLeaf(name): // 如果是最后一级目录,退出 break for dir_entry in cur_inode.dir_entries: // 遍历当前 inode 节点中的每一个目录项 if dir_entry.name == name: // 根据路径中某一层次的名称找到目录项 if dir_entry.file_type == EXT2_FT_DIR: // 判断是是否是目录 cur_inode = dir_entry.inode // 更新当前 inode 节点 continue else: break return cur_inode // 到这里就已经找到了 当前文件的 inode 编号,即 cur_inode。
3.2 相关结构体
- dir_entry 结构体如下:
struct ext2_dir_entry_2 { __u32 inode; /* Inode number */ __u16 rec_len; /* Directory entry length */ __u8 name_len; /* Name length */ __u8 file_type; char name[EXT2_NAME_LEN]; /* File name */ };
- inode 节点结构体如下:
struct ext2_inode { __u16 i_mode; /* File mode */ __u16 i_uid; /* Low 16 bits of Owner Uid */ __u32 i_size; /* Size in bytes */ __u32 i_atime; /* Access time */ __u32 i_ctime; /* Creation time */ __u32 i_mtime; /* Modification time */ __u32 i_dtime; /* Deletion Time */ __u16 i_gid; /* Low 16 bits of Group Id */ __u16 i_links_count; /* Links count */ __u32 i_blocks; /* Blocks count */ __u32 i_flags; /* File flags */ union { struct { __u32 l_i_reserved1; } linux1; struct { __u32 h_i_translator; } hurd1; struct { __u32 m_i_reserved1; } masix1; } osd1; /* OS dependent 1 */ __u32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */ __u32 i_generation; /* File version (for NFS) */ __u32 i_file_acl; /* File ACL */ __u32 i_dir_acl; /* Directory ACL */ __u32 i_faddr; /* Fragment address */ union { struct { __u8 l_i_frag; /* Fragment number */ __u8 l_i_fsize; /* Fragment size */ __u16 i_pad1; __u16 l_i_uid_high; /* these 2 fields */ __u16 l_i_gid_high; /* were reserved2[0] */ __u32 l_i_reserved2; } linux2; struct { __u8 h_i_frag; /* Fragment number */ __u8 h_i_fsize; /* Fragment size */ __u16 h_i_mode_high; __u16 h_i_uid_high; __u16 h_i_gid_high; __u32 h_i_author; } hurd2; struct { __u8 m_i_frag; /* Fragment number */ __u8 m_i_fsize; /* Fragment size */ __u16 m_pad1; __u32 m_i_reserved2[2]; } masix2; } osd2; /* OS dependent 2 */ };
3.3 stat 是如何找到文件信息的?
参考 3.1 中的文件查找流程,stat 可以很容易的从 inode 结构体中获取。比如 st_mode 字段来源于 inode 的 i_mode 字段等等。
需要注意的是,作为用户态程序,你是无法直接读取磁盘的,这也导致了我们没有办法自己去解析磁盘数据。这是出于操作系统安全角度考虑的,如果你不小心破坏了文件系统,这将直接导致操作系统无法启动。
linux 并不直接提供你操作磁盘的方法,除非你自己写 linux 驱动程序。但是为了让你获取一些必要的信息,linux 提供了一些接口给你使用,比如这里的 stat 函数。
4 小结
本节你需要掌握 stat 函数,并打印出这些整数值。关于这些整数如何转换成人类可读的,后面将一一介绍。
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/228437.html原文链接:https://javaforall.net
