linux文件系统之获取文件信息总结

本文详细解读Linux文件系统的stat结构,探讨了dev_t、ino_t、mode_t、nlink_t、uid_t、gid_t等关键字段,以及硬链接、软链接的区别,同时介绍了st_mode的宏详解和获取文件信息的函数,如stat、fstat和lstat。
摘要由CSDN通过智能技术生成

List

获取文件信息

获取文件信息相对而言是一个比较简单的过程,但是其中的设计其实比较的复杂,这涉及到了Linux的文件系统的设计
我想通过对这些信息的整理强化我对于文件系统的理解

文件信息结构体

在进行函数调用之前,先得对文件信息结构体进行一个了解,以便在函数的使用时可以知道我们可获取的文件信息
文件结构体如下所示

           struct stat {
               dev_t     st_dev;     /* ID of device containing file */
               ino_t     st_ino;     /* inode number */
               mode_t    st_mode;    /* protection */
               nlink_t   st_nlink;   /* number of hard links */
               uid_t     st_uid;     /* user ID of owner */
               gid_t     st_gid;     /* group ID of owner */
               dev_t     st_rdev;    /* device ID (if special file) */
               off_t     st_size;    /* total size, in bytes */
               blksize_t st_blksize; /* blocksize for filesystem I/O */
               blkcnt_t  st_blocks;  /* number of 512B blocks allocated */
               time_t    st_atime;   /* time of last access */
               time_t    st_mtime;   /* time of last modification */
               time_t    st_ctime;   /* time of last status change */
           };

我们一个个解释其含义

dev_t st_dev/* ID of device containing file */

  • dev_t st_dev; /* ID of device containing file */

st_dev 存的是文件本身存储设备的设备号,也就是硬盘的设备号

我们可以通过df查看我们的设备信息

Blankspace@ubuntu:~/blankspace_Gnu/blankspace_bin$ df -h
Filesystem      Size  Used Avail Use% Mounted on
udev            2.0G  4.0K  2.0G   1% /dev
tmpfs           394M  1.5M  392M   1% /run
/dev/sda1        36G  4.7G   29G  14% /
none            4.0K     0  4.0K   0% /sys/fs/cgroup
none            5.0M     0  5.0M   0% /run/lock
none            2.0G  152K  2.0G   1% /run/shm
none            100M   84K  100M   1% /run/user

  • Filesystem:代表该文件系统时哪个分区,所以列出的是设备名称。
  • 1K-blocks:说明下面的数字单位是1KB,可利用-h或-m来改变单位大小,也可以用-B来设置。
  • Used:已经使用的空间大小。
  • Avail(Available):剩余的空间大小。
  • Use%:磁盘使用率。如果使用率在90%以上时,就需要注意了,避免磁盘容量不足出现系统问题,尤其是对于文件内容增加较快的情况(如/home、/var/spool/mail等)。
  • Mounted on:磁盘挂载的目录,即该磁盘挂载到了哪个目录下面。

这个和Linux文件系统有关
《鸟哥私房菜》一书中有详细描述,有需要的可以进行一个阅读,在这就没有进行一个纪录

ino_t st_ino /* inode number */

  • ino_t st_ino; /* inode number */

文件的INode号
文件数据存储在块中,那么还必须找到一个地方存储文件的元信息,比如文件的创建者、文件的创建日期、文件的大小等等。这种存储文件元信息的区域就叫做inode

谈到INode就不得不和block一起提一提

inode和block概述

我们知道,文件是存储在硬盘中的,硬盘最小的存储单位是扇区(sector),每个扇区存储 512字节
操作系统在读取硬盘是会采取一次性读入多个扇区,这多个扇区我们称之为一个块 (block)
块的大小一般是4KB,也就是连续八个扇区(sector)组成一个块(block)。

文件数据存储在块(block)中,当然不能只存储而不能读取,如果不告诉操作系统文件的存储位置等信息,操作系统是无法进行读取的
举个例子大致就是,如果一个朋友想来找你,如果你只是回答:我住在房子里,这样的回答没有任何意义,而且可能会被朋友骂一顿,得需要告诉朋友你所住地址的详细信息,这里也类似,硬盘也需要告诉操作系统文件存储的位置,硬盘中会有一块记录着文件的元信息的区域,也就是我们所说的INode,中文译名索引节点

这里需要注意的是
一个文件必须占用一个iNode,iNode也至少会占用一个block

大致的关系是

  • 元信息存储的区域 -------> iNode
  • 数据存储的区域 ---------->block
iNode的内容

iNode包括很多的内容
大致如下

  • 文件类型: 普通文件,目录,管道等等
  • 权限:可读,可写,可执行
  • 链接数:链接到该inode的硬链接数
  • User ID:文件所有者
  • Group ID:所有者组ID
  • 文件大小
  • 时间信息
  • 属性:比如,不可改变位
  • 访问控制列表
  • 文件数据存储的实际位置
  • 其他元数据

注意iNode并不包含文件名,因为Linux系统内部不使用文件名来查找文件,而是使用iNode号码来识别文件
对于文件系统来说,文件名只是iNode号便于识别的别称而已

并且硬盘分区的INode总数在格式化后就已经固定,而每个文件必须有一个iNode,所以如果 iNode耗尽,即使硬盘中还剩余存储空间,也无法新建文件

mode_t st_mode /* protection */

st_mode 用来判断文件类型和存储权限

st_mode内容详解

先来讲讲mode_t,其实就是一个无符号整型 unsigned int

但是整个st_mode只使用了后16位
在这里插入图片描述

st_mode主要包括三个部分

  • 15bit ~ 12bit 保存文件类型

- : 普通文件(regular file)
d : 目录(directory)
c : 字符设备(character device)
b : 块设备(block device)
p : 管道(FIFO)
l : 符号链接文件(symbolic link)
s : 套接口文件(socket)

  • 11bit ~ 9bit 保存执行文件时设置的信息

bit11:set-user-ID位,执行时设置用户ID
bit10:set-group-ID位,执行时设置组ID
bit9:sticky位,仅对目录有效。设置后所有用户都可在这个目录下创建文件,但该目录下的文件只能被owner和root删除

  • 8bit ~ 0bit 保存文件权限信息

st_mode字段的最低9位,代表文件的许可权限
标识了文件所有者(owner)、组用户(group)、其他用户(other)的读(r)、写(w)、执行(x)权限。

st_mode 宏详解

虽说我们已经了解了整个st_mode的设计
但是如果去手动分析st_mode的话,效率太低,且容易出错,
可使用 st_mode & 掩码来得到 st_mode 中特定的部分。比如:

st_mode & 0170000 : 得到文件类型
st_mode & 0007000 : 得到执行文件时设置信息
st_mode & 0000777 : 得到权限位
st_mode & 00100: 判断所有者是否可执行

这样还是比较麻烦于是设计了一系列的宏来对其进行判断
宏设计如下

#include <sys/stat.h>
//bit15 ~ bit12 , 文件类型属性区域
#define  S_IFMT      0170000     文件类型的位遮罩(掩码)
#define  S_IFSOCK    0140000     socket
#define  S_IFLNK     0120000     符号链接(symbolic link)
#define  S_IFREG     0100000     一般文件
#define  S_IFBLK     0060000     区块装置(block device)
#define  S_IFDIR     0040000     目录
#define  S_IFCHR     0020000     字符装置(character device)
#define  S_IFIFO     0010000     先进先出(fifo)

//提供了一些宏来帮助用户执行&操作,是则返回1
#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)  
#define S_ISLNK(m)  (((m) & S_IFMT) == S_IFLNK)  
#define S_ISREG(m)  (((m) & S_IFMT) == S_IFREG)
#define S_ISBLK(m)  (((m) & S_IFMT) == S_IFBLK)
#define S_ISDIR(m)  (((m) & S_IFMT) == S_IFDIR)
#define S_ISCHR(m)  (((m) & S_IFMT) == S_IFCHR)
#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)

//bit11 ~ bit9,权限的特殊属性区域
#define  S_ISUID      0004000     文件的(set user-id on execution)位
#define  S_ISGID      0002000     文件的(set group-id on execution)位
#define  S_ISVTX      0001000     文件的sticky位

//bit8 ~ bit0,权限属性区域
//文件所有者(owner)
#define S_IRWXU 00700    /* mask for file owner permissions */
#define S_IRUSR 00400    /* owner has read permission */
#define S_IWUSR 00200    /* owner has write permission */
#define S_IXUSR 00100    /* owner has execute permission */
 //组用户(group)
#define S_IRWXG 00070    /* mask for group permissions */
#define S_IRGRP 00040    /* group has read permission */
#define S_IWGRP 00020    /* group has write permission */
#define S_IXGRP 00010    /* group has execute permission */
 //其他用户(other)
#define S_IRWXO 00007    /* mask for permissions for others (not in group) */
#define S_IROTH 00004    /* others have read permission */
#define S_IWOTH 00002    /* others have write permission */
#define S_IXOTH 00001    /* others have execute permission */

nlink_t st_nlink; /* number of hard links */

连到该文件的硬链接数目, 刚建立的文件 值为1.

硬链接和软链接之间的区别
  • 硬链接

通过文件系统的inode链接来产生的新的文件名,而不是产生新的文件,称为硬链接。

一般情况下,每个inode号码对应一个文件名,但是Linux允许多个文件名指向同一个inode号码。意味着可以使用不同的文件名访问相同的内容。

ln 源文件 目标

运行该命令以后,源文件与目标文件的inode号码相同,都指向同一个inode。inode信息中的链接数这时就会增加1。

当一个文件拥有多个硬链接时,对文件内容修改,会影响到所有文件名;但是删除一个文件名,不影响另一个文件名的访问。删除一个文件名,只会使得inode中的链接数减1。

需要注意的是不能对目录做硬链接

通过mkdir命令创建一个新目录,其硬链接数应该有2个,因为常见的目录本身为1个硬链接,而目录下面的隐藏目录.(点号)是该目录的又一个硬链接,也算是1个连接数。

  • 软链接

类似于Windows的快捷方式功能的文件,可以快速连接到目标文件或目录,称为软链接。

ln -s 源文件或目录 目标文件或目录

软链接就是再创建一个独立的文件,而这个文件会让数据的读取指向它连接的那个文件的文件名。例如,文件A和文件B的inode号码虽然不一样,但是文件A的内容是文件B的路径。读取文件A时,系统会自动将访问者导向文件B。这时,文件A就称为文件B的软链接soft link或者符号链接symbolic link。

这意味着,文件A依赖于文件B而存在,如果删除了文件B,打开文件A就会报错。这是软链接与硬链接最大的不同:文件A指向文件B的文件名,而不是文件B的inode号码,文件B的inode链接数不会因此发生变化。

uid_t st_uid; /* user ID of owner */

文件所有者的用户识别码

表示文件由谁创建,他与用户名唯一对应
我们可以使用

id

来查看我的用户名,UID,组名,GID等
在这里插入图片描述

与之对应的就是setuid,setuid可以改变这种设置
如果是一个可执行文件, 那么在执行时, 一般该文件只拥有调用该文件的用户(当前用户)具有的权限.

setuid: 设置使文件在执行阶段具有文件所有者的权限. 典型的文件是 /usr/bin/passwd. 如果一般用户执行该文件, 则在执行过程中, 该文件可以获得root权限, 从而可以更改用户的密码.
这个对应的命令为

chmod u+s temp -- 为temp文件加上setuid标志. (setuid 只对文件有效)  

一般文件的权限通过三组八进制数字来标识,如

chmod 777 [filename]
chmod 666 [filename]

等等
而如果设置了特殊的标志位,则会在这组数字之外另加一组八进制数字,对应上述

bit11:set-user-ID位,执行时设置用户ID
bit10:set-group-ID位,执行时设置组ID
bit9:sticky位,仅对目录有效。设置后所有用户都可在这个目录下创建文件,**但该目录下的文件只能被owner和root删除**。

我们设置完这些标志后,可以使用ls-l来查看,如果有这些标志,则会在执行标志位置上显示
如:

rwsrw-r-- 表示有setuid标志  
rwxrwsrw- 表示有setgid标志  
rwxrw-rwt 表示有sticky标志  

至于原有的执行标志X,他其实以另一种形式表示出来
系统是这样规定的, 如果本来在该位上有x, 则这些特殊标志显示为小写字母 (s, s, t). 否则, 显示为大写字母 (S, S, T)

相关函数

gid_t st_gid; /* group ID of owner */

文件所有者的组识别码

chmod g+s tempdir -- 为tempdir目录加上setgid标志 (setgid 只对目录有效)  

注意
要删除一个文件,你不一定要有这个文件的写权限,但你一定要有这个文件的上级目录的写权限。也就是说,你即使没有一个文件的写权限,但你有这个文件的上级目录的写权限,你也可以把这个文件给删除,而如果没有一个目录的写权限,也就不能在这个目录下创建文件。
如何才能使一个目录既可以让任何用户写入文件,又不让用户删除这个目录下他人的文件,sticky就是能起到这个作用。stciky一般只用在目录上,用在文件上起不到什么作用。
在一个目录上设了sticky位后,(如/tmp,权限为1777)所有的用户都可以在这个目录下创建文件,但只能删除自己创建的文件,这就对所有用户 能写的目录下的用户文件启到了保护的作用。(/tmp没有设sticky位,而在文件上设了,这也就是为什么设了sticky位,还能删除自己创建的文件的原因了)

dev_t st_rdev; /* device ID (if special file) */

若此文件为装置设备文件, 则为其设备编号

文件大小三个属性

off_t st_size; /* total size, in bytes */

文件的大小

blksize_t st_blksize; /* blocksize for filesystem I/O */

文件所占块大小

blkcnt_t st_blocks; /* number of 512B blocks allocated */

文件所占块的数量

由上面的文件系统的知识可知道:
st_blocks * 512 = st_blksize

三个时间戳

ctime:change time

是最后一次改变文件或目录(属性)的时间,例如执行chmod,chown等命令。

atime:access time

是最后一次访问文件或目录的时间。

mtime:modify time

是最后一次修改文件或目录(内容)的时间。

这三个是由一个结构体 struct timespec定义的

##获取文件信息系列函数
了解完上面的结构体信息后,我们便可以对文件信息获取函数进行一个讲解了,相信会非常快地进行一个了解,因为获取文件信息系列函数,其实就是对文件结构体赋值的过程,Linux中获取文件信息的函数有三个

头文件
       #include <sys/types.h>
       #include <sys/stat.h>
       #include <unistd.h>
函数原型
       int stat(const char *path, struct stat *buf);
       int fstat(int fd, struct stat *buf);
       int lstat(const char *path, struct stat *buf);
返回值

成功返回0,失败返回-1,并且将详细错误信息赋值给errno全局变量。

区别

他们大致的功能差不多,但是本身还是一些的区别

  • fstat和stat功能一致,只是第一个形参是文件描述符,也就是说需要先打开文件句柄
struct stat file_stat;
FILE * file_ret = open(filepath, O_RDONLY);
fstat(file_ret, &file_stat);
  • lstat函数的形参跟stat函数的形参一样。其功能也跟stat函数功能一样,仅有一点不同:stat函数是穿透(追踪)函数,即对软链接文件进行操作时,操作的是链接到的那一个文件,不是软链接文件本身;而lstat函数是不穿透(不追踪)函数,对软链接文件进行操作时,操作的是软链接文件本身。
    下面是一个lstat函数使用的实例
int main(int argc, char* argv[])
{

    struct stat file_stat;
    lstat(argv[1], &file_stat);
    printf("st_dev:%lld\n", file_stat.st_dev);       //unsigned long long int
    printf("st_ino:%ld\n", file_stat.st_ino);        //unsigned long int
    printf("st_mode:%o\n", file_stat.st_mode);       //unsigned int
    printf("st_nlink:%d\n", file_stat.st_nlink);     //unsigned int
    printf("st_uid:%d\n", file_stat.st_uid);         //unsigned int
    printf("st_gid:%d\n", file_stat.st_gid);         //unsigned int
    printf("st_rdev:%lld\n", file_stat.st_rdev);     //unsigned long long int
    printf("st_size:%ld\n", file_stat.st_size);      //long int
    printf("st_blksize:%ld\n", file_stat.st_blksize);//long int
    printf("st_blocks:%ld\n", file_stat.st_blocks);  //long int
    printf("Last status change:       %s\n", ctime(&file_stat.st_ctime));// struct timespec
    printf("Last file access:         %s\n", ctime(&file_stat.st_atime));// struct timespec
    printf("Last file modification:   %s\n", ctime(&file_stat.st_mtime));// struct timespec

    return 0;
}

输出的结果如下所示:
在这里插入图片描述
这也是实现 ls-l的基础

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值