坚持 成长 每日一篇
stat结构体介绍
在unix系统中我们可以通过了解stat 结构体的成员了解文件的所有属性。结构体的定义可能随实现有所不同,但其基本形式是:下面是mac下的stat格式
struct stat {
dev_t st_dev; /* 拥有该文件的设备ID[XSI] ID of device containing file */
ino_t st_ino; /* 文件结点号[XSI] File serial number */
mode_t st_mode; /* 文件的类型 Mode of file (see below) */
nlink_t st_nlink; /* 硬连接数 Number of hard links */
uid_t st_uid; /* 文件用户标识 User ID of the file */
gid_t st_gid; /* 文件用户组标识 Group ID of the file */
dev_t st_rdev; /* 文件所表示的特殊设备文件的设备标识 Device ID for sepecial files */
#if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE)
struct timespec st_atimespec; /* time of last access */
struct timespec st_mtimespec; /* time of last data modification */
struct timespec st_ctimespec; /* time of last status change */
#else
time_t st_atime; /* 最后的访问时间 Time of last access */
long st_atimensec; /* nsec of last access */
time_t st_mtime; /* 最后修改时间 Last data modification time */
long st_mtimensec; /* last data modification nsec */
time_t st_ctime; /* 最后状态改变时间 Time of last status change */
long st_ctimensec; /* nsec of last status change */
#endif
off_t st_size; /* 总大小,字节为单位 file size, in bytes */
blkcnt_t st_blocks; /* 允许分配给文件的块的数量,512字节为单元 blocks allocated for file */
blksize_t st_blksize; /* 文件系统的块大小 optimal blocksize for I/O */
__uint32_t st_flags; /* 用户自定义文件描述符给该文件 user defined flags for file */
__uint32_t st_gen; /* file generation number */
__int32_t st_lspare; /* RESERVED: DO NOT USE! */
__int64_t st_qspare[2]; /* RESERVED: DO NOT USE! */
}
文件类型
Unix中大多数的文件类型是普通文件或目录。下面是Unix文件类型的介绍
- 普通文件:包含了某种形式的数据。对于Unix内核而言,不管是文本还是二进制数据都没有区别。对于普通文件内容的解释是由处理该文件的应用程序进行的。
- 目录文件:包含其他文件的名字以及执行这些文件有关信息的指针。对目录文件有读权限的任一进程都可以读该目录内容,但只有内核可以直接写目录。进程需要通过一些函数才能修改目录。
- 特殊文件:提供对设备(如磁盘)带缓冲的访问,每次访问以固定长度为单位进行。
- 字符特殊文件:提供对设备不带缓冲的访问,每次访问长度可变。(FreeBSD所有设备都是通过字符特殊文件访问,没有特殊文件)
- FIFO:用于进程间通信,也叫管道。
- 套接字:用于进程间的网络通信。
- 符号连接:该文件会指向另一个文件
上面的7种文件类型包含在stat结构体的st_mode成员中。通过使用下面宏的参数可以确定文件类型。
例如判断文件是否是目录文件类型:
#define S_ISDIR(mode) (((mode)&S_IFMT)== S_IFDIR);
各种类型文件对应的宏如下
S_ISREG() 普通文件
S_ISDIR() 目录文件
S_ISCHR() 字符特殊文件
S_ISBLK() 块特殊文件
S_ISFIFO() 管道活着FIFO
S_ISLNK() 符号连接
S_ISSOCK() 套接字
POSIX.1允许实现将进程通信对象当作文件,但是坚持宏参数不是stat的st_mode而是stat的指针
S_TYPEISMQ(buf):消息队列
S_TYPEISSEM(buf):信号量
S_TYPEISSHM(buf):共享存储对象
函数stat,fstat,fstatat,lstat获取文件的结构体stat
我们可以通过下面的四个函数获取文件的stat。下面是从mac上截取的四个函数定义
#include <sys/stat.h>
int fstat(int fd, struct stat *buf) __DARWIN_INODE64(fstat);
int lstat(const char *pathname, struct stat *buf) __DARWIN_INODE64(lstat);
int stat(const char *pathname, struct stat *buf) __DARWIN_INODE64(stat);
int fstatat(int fd, const char *pathname, struct stat *buf, int flag);
fstat 根据打开的文件描述符fd上的文件返回stat;
lstat 返回符号连接的有关信息,而不是符号连接引用的文件信息。
stat 返回此命名文件有关的信息,如果是符号连接返回连接引用的文件信息。
fstatat 如果flag设置为:AT_SYMLINK_NOFOLLOW,则类似lstat。否则默认类似于stat
fd参数为AT_FDCWD 表示pathname支持相对路径。如果pathname是绝对路径fd传什么都无所谓
测试源码
#include <stdio.h>
#include <sys/stat.h>
#include <time.h>
//时间转换,参数word修饰这是什么时间
void printfTime(long time,char word[])
{
static char str_time[100];
struct tm *local_time = NULL;
time_t utc_time;
utc_time = time;
local_time = localtime(&utc_time);
strftime(str_time, sizeof(str_time), "%Y-%m-%d,%H:%M:%S", local_time);
printf("%s:%s\n",word,str_time);
}
int main(int argc, const char * argv[])
{
int i;
struct stat buf;
char *ptr;
//遍历判断文件的类型
for (i = 1; i<argc; i++) {
printf("%s: ",argv[i]);
if (lstat(argv[i], &buf)<0) {
printf("error:");
perror("lstat error");
continue;
}
//通过检查stat结构体buf的st_mode来检查文件类型
if (S_ISREG(buf.st_mode)) {
ptr = "regular";//普通文件
}else if(S_ISDIR(buf.st_mode)){
ptr = "S_ISDIR";//目录文件
}else if(S_ISCHR(buf.st_mode)){
ptr = "S_ISCHR";//字符特殊文件
}else if(S_ISBLK(buf.st_mode)){
ptr = "S_ISBLK";//块特殊文件
}else if(S_ISFIFO(buf.st_mode)){
ptr = "S_ISFIFO";//管道或FIFO
}else if(S_ISLNK(buf.st_mode)){
ptr = "S_ISLNK";//符号链接
}else if(S_ISSOCK(buf.st_mode)){
ptr = "S_ISSOCK";//套接字
}else{
ptr = "** _unknow mode **";
}
printf(" %s\n",ptr);
printf("time = %ld\n",buf.st_atime);
printfTime(buf.st_atime,"访问时间");
printfTime(buf.st_ctime,"修改时间");
printfTime(buf.st_birthtime,"创建时间");
printf("DeviceID = %d\n",buf.st_dev);
printf("UserID = %d\n",buf.st_uid);
printf("文件大小:%lld字节\n",buf.st_size);
//**可以判断该Stat是不是信号量
if(S_TYPEISMQ(buf)){printf("是一个消息队列\n");}else printf("NO Queue\n");
if(S_TYPEISSEM(buf)){printf("是一个信号量\n");}else printf("NO SEM\n");
if(S_TYPEISSHM(buf)){printf("是一个共享存储对象\n");}else printf("NO SHM\n");
}
return 0;
}