1. stat函数
stat函数是用于获取文件信息,比如文件权限,文件类型信息等等。
函数原型:
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *pathname, struct stat *buf);
参数说明:
pathname:表示文件路径名
buf:用于保存获取到的文件属性信息(这是一个传出参数)
返回值说明:成功返回0,失败返回-1并设置errno
2. struct stat结构体
stat系统调用会将获取到的文件信息保存到一个struct stat *buf的结构体中,其格式如下:
struct stat {
dev_t st_dev; /* 块设备号(ID) */
ino_t st_ino; /* inode结点号,文件属性信息所存inode节点的编号 */
mode_t st_mode; /* 文件类型和文件权限*/ ls
nlink_t st_nlink; /* 链接数 */ ls
uid_t st_uid; /* 文件所属用户ID*/ ls
gid_t st_gid; /* 文件所属组ID */ ls
dev_t st_rdev; /* 字符设备ID */
off_t st_size; /* 文件大小 */
blksize_t st_blksize; /* 系统每次按块Io操作时,块的大小(一般是512或1024) */
blkcnt_t st_blocks; /* 块的索引号 */
time_t st_atime; /* 最后一次访问时间,read*/ ls
time_t st_mtime; /* 最后一次修改时间,write */
time_t st_ctime; /* 最后一次属性修改的时间,如权限被修改,文件所有者(属主)被修改 */
};
通过这个结构体可知,stat函数获取的文件信息大多是从inode中获取的。
3. 获取文件信息实验
使用stat函数查看文件属性实验
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
int main(void) {
struct stat buf; //stat函数获取到的文件信息最终保存在struct stat结构体中
int ret = stat("test.txt", &buf);
//ret返回-1表示出错
if(ret == -1){
perror("stat error");
exit(1);
}
printf("st_ino = %ld\n", buf.st_ino); //inode号
printf("st_size = %ld\n", buf.st_size); //文件大小
printf("st_nlink = %d\n", buf.st_nlink); //硬链接数
printf("st_uid = %d\n", buf.st_uid); //用户id
printf("st_gid = %d\n", buf.st_gid); //组id
printf("st_mode = %x\n", buf.st_mode); //文件属性权限
return 0;
}
运行结果:
stat 是如何找到文件信息的?从文件查找过程中可知,结合dentry结构体和inode结构体分析,stat函数是从inode结构体中获取文件信息的,比如struct stat结构体中的st_mode就是inode结构体中的i_mode,包括其他的一些属性信息,如果有看过ext2文件系统实验中的9-dentry结构体和inode结构体之间的关系,那么相信你非常明白stat函数是如何获取文件属性的。
4. 使用stat函数获取文件类型
ext2文件类型:
EXT2_FT_UNKNOWN, /*未知*/
EXT2_FT_REG_FILE, /*常规文件*/
EXT2_FT_DIR, /*目录文件*/
EXT2_FT_CHRDEV, /*字符设备文件*/
EXT2_FT_BLKDEV, /*块设备文件*/
EXT2_FT_FIFO, /*命名管道文件*/
EXT2_FT_SOCK, /*套接字文件*/
EXT2_FT_SYMLINK, /*符号连文件*/
EXT2_FT_MAX /*文件类型的最大个数*/
在前面的学习中我们已经知道了目录文件和常规文件,一般来说,linux下常用的文件有以下这些:
1. 常规文件,这是最常用的文件类型,用于保存某种形式的数据(文本或者二进制数据)
2. 目录文件,包含了其他文件的文件名和指向这些文件有关信息的指针
3. 块特殊文件,比如磁盘就是块设备,并且块设备还提供缓冲区,每次以固定的字节数访问,比如512字节,1024字节等。也就是说系统中能够随机(不需要按顺序)访问固定大小数据片(chunks)的设备被称作块设备
4. 字符文件,比如键盘就是字符设备,按照字节流的方式被有序访问
5. 管道(fifo和pipe),这种文件类型一般用于进程间通信,FIFO为有名管道,PIPE无名管道
6. 套接字(socket),用于进程间网络通信
7. 符号链接,是一种快捷图标,通过图标文件指向了另外一个文件
文件类型信息包含在stat结构体的st_mode成员中,这些宏的参数都是stat结构体中的st_mode成员,也就是说通过st_mode成员可以判断文件类型:
S_ISREG(m) 是否为普通文件
S_ISDIR(m) 是否为目录
S_ISCHR(m) 是否为字符设备
S_ISBLK(m) 是否为块设备
S_ISFIFO(m) 是否为FIFO(命名管道文件,用于进程通信)
S_ISLNK(m) 是否为符号链接
S_ISSOCK(m) 是否为套接字
通过stat结构体中的st_mode成员判断文件类型:
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
int main(int argc, char *argv[])
{
char *p;
struct stat statbuf;
int ret = lstat(argv[1], &statbuf);
if(S_ISREG(statbuf.st_mode))
p = "regular file";
else if(S_ISDIR(statbuf.st_mode))
p = "directory";
else if(S_ISLNK(statbuf.st_mode))
p = "symbolic link";
else if(S_ISCHR(statbuf.st_mode))
p = "character device";
else if(S_ISBLK(statbuf.st_mode))
p = "block device";
else if(S_ISFIFO(statbuf.st_mode))
p = "named pipe";
else if(S_ISSOCK(statbuf.st_mode))
p = "socket";
else
p = "unknown";
printf("\"%s\" is %s\n", argv[1], p);
return 0;
}
5. st_mode结构体
st_mode 主要包含了 3 部分信息:
15-12 位保存文件类型
11-9 位保存执行文件时设置的信息
8-0 位保存文件访问权限
这里的文件指的是所有文件类型(目录,普通文件,字符设备文件)都有访问权限。每个文件有9个访问权限位,分为三类(即读,写,执行权限),如图:
6. 文件权限位
st_mode结构体的每个位如图:
st_mode每个位都对应一个权限,0即无该权限,1有该权限,0-8位表示的文件权限位,u表示用户,g表示用户组,o表示其他。
12-15位表示文件的类型,d开头表示目录文件,后面的都是表示文件的权限位(r代表读权限,w代表写权限,x代表执行权限)。
前三个权限位表示用户对code文件目录的访问权限
中间三个权限位表示用户组对code文件目录的访问权限
后三个权限位表示其他对code文件目录的访问权限
比如对于chmod 777 test命令来说,777对应着test文件的用户,用户组,其他用户的读写执行权限。
st_mode结构体中的三类文件权限分别配合不同函数使用,需要注意几点:
1. 当打开一个文件时,对包含该文件的每一个目录,都应该具有执行权限。
比如打开/testcode/c_test/hello.c文件时,对/,/testcode和/testcode/code等目录都应该具有执行权限,然后需要具备对hello.c文件本身有合适的权限。另外,目录的读权限和执行权限的意义是不同的,读权限允许获取该目录中的所有文件,执行权限是当我们需要通过该目录查找一个特定文件需要用到的权限,说白了就是如果有该目录的执行权限,那么就允许在该目录中搜索要查找的文件,没有就不允许在该目录中搜索。
2. 如果要删除一个文件,必须对包含该文件的目录有写权限和执行权限,对文件本身可以不需要读,写权限。
3. 进程每打开,删除,创建一个文件,内核就会对该文件进行访问权限测试。