9 官方库
约 2399 字大约 8 分钟
2025-06-22
<sys/stat.h
获取文件的属性
- stat函数根据文件名获取文件的属性
- fstat函数获取文件描述符fields上打开文件的有关信息。
- lstat返回该符号链接的有关信息,而不是由该符号链接引用文件的信息。
- pathname: 文件路径+文件名
- buf: 传出参数
- fields: 对应文件的文件描述符
- 返回值:若成功则返回0,失败则返回-1,错误代码存于errno。
int stat(const char *restrict pathname,struct stat *restrict buf);
int fstat(int fields,struct stat *buf);
int lstat(const char *restrict pathname,struct stat *restrict buf);
错误代码 | 含义 |
---|---|
ENOENT | 参数file_name 指定的文件不存在 |
ENOTDIR | 路径中的目录存在但却非真正的目录 |
ELOOP | 欲打开的文件有过多符号连接问题, 上限为16 符号连接 |
EFAULT | 参数buf 为无效指针, 指向无法存在的内存空间 |
EACCESS | 存取文件时被拒绝 |
ENOMEM | 核心内存不足 |
ENAMETOOLONG | 参数file_name 的路径名称太长 |
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 file system 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 */ 最后一次改变该文件状态的时间
};
st_mode 主要包含了 3 部分信息:
- 15-12 位保存文件类型
- 11-9 位保存执行文件时设置的信息
- 8-0 位保存文件访问权限
S_IFMT 0170000 文件类型的位遮罩
S_IFSOCK 0140000 套接字
S_IFLNK 0120000 符号连接
S_IFREG 0100000 一般文件
S_IFBLK 0060000 区块装置
S_IFDIR 0040000 目录
S_IFCHR 0020000 字符装置
S_IFIFO 0010000 先进先出
S_ISUID 04000 文件的(set user-id on execution)位
S_ISGID 02000 文件的(set group-id on execution)位
S_ISVTX 01000 文件的sticky位
S_IRUSR(S_IREAD) 00400 文件所有者具可读取权限
S_IWUSR(S_IWRITE)00200 文件所有者具可写入权限
S_IXUSR(S_IEXEC) 00100 文件所有者具可执行权限
S_IRGRP 00040 用户组具可读取权限
S_IWGRP 00020 用户组具可写入权限
S_IXGRP 00010 用户组具可执行权限
S_IROTH 00004 其他用户具可读取权限
S_IWOTH 00002 其他用户具可写入权限
S_IXOTH 00001 其他用户具可执行权限
上述的文件类型在POSIX中定义了检查这些类型的宏定义:
S_ISLNK (st_mode) 判断是否为符号连接
S_ISREG (st_mode) 是否为一般文件
S_ISDIR (st_mode) 是否为目录
S_ISCHR (st_mode) 是否为字符装置文件
S_ISBLK (s3e) 是否为先进先出
S_ISSOCK (st_mode) 是否为socket
若一目录具有sticky位(S_ISVTX),则表示在此目录下的文件只能被该文件所有者、此目录所有者或root来删除或改名,在linux中,最典型的就是这个/tmp目录啦。
dirent
struct dirent {
long d_ino; //目录项的 inode 节点号。
unsigned short d_reclen; //目录项的长度(以字节为单位)。
unsigned short d_namlen; //表示目录项名称的长度(不包括终止 null 字符)。
char d_name[260]; //目录项的名称(以 null 结尾的字符串)。
}
DIR结构体用于描述这个目录本身 dirent结构体用于描述目录这个中的某一项(文件或目录)
opendir
打开一个目录流。
- pathname: 目录路径 (如: 当前目录为".")
- return: 返回一个指向目录流的 DIR 指针。如果无法打开目录,则返回 NULL。
DIR opendir(const char* pathname);
readdir
从目录流中读取下一个目录项。readdir() 函数以一个目录流指针作为参数,并返回一个指向 dirent 结构体的指针。如果目录流中没有更多目录项,则返回 NULL。
dirent* readdir(DIR* dir);
使用reddir读取目录对象时其中有一个指针指向当前的读取位置,第一次读取时指向的是目录中的第一项,每读取一次指针就会向后偏移一次,下次读取时指向的位置就会向后移动
closedir
关闭一个目录流。closedir() 函数以一个目录流指针作为参数,并释放与该目录流关联的系统资源。
void closedir(DIR* dir);
rewinddir
将目录流中的指针重置到开头。rewinddir() 函数以一个目录流指针作为参数,并将目录流重置到其开头。
dirent* rewinddir(DIR* dir);
telldir
返回目录流的当前位置。telldir() 函数以一个目录流指针作为参数,并返回目录流的当前位置。
int telldir(DIR* dir);
seekdir
将目录流移动到指定位置。seekdir() 函数以一个目录流指针和一个位置作为参数,并将目录流移动到指定位置。
types
每一个已打开的文件都有一个读写位置, 当打开文件时通常其读写位置是指向文件开头, 若是以附加的方式打开文件(如O_APPEND), 则读写位置会指向文件尾. 当read()或write()时, 读写位置会随之增加 lseek()便是用来控制该文件的读写位置.
lseek
移动文件描述符所描述文件的读写位置
- fildes: 已打开的文件描述符
- offset: 根据参数whence来移动读写位置的位移数.
- whence: 要移动的方式,为以下三种: SEEK_SET: 文件头.0 SEEK_CUR: 当前的读写位置.1 SEEK_END: 文件尾. 2 当whence 值为SEEK_CUR 或SEEK_END 时, 参数offet 允许负值的出现.
- return:当调用成功时则返回目前的读写位置, 也就是距离文件开头多少个字节. 若有错误则返回-1, 并将错误代码存放到errno中
off_t lseek(int fildes, off_t offset,int whence);
fseek
移动文件流的读写位置
- stream: 已打开的文件指针
- offset: 根据参数whence 来移动读写位置的位移数。和lseek一样
- return:当调用成功时则返回0, 若有错误则返回-1, errno 会存放错误代码.
int fseek( FILE* stream,long offset,int whence);
例如:
fseek(fp,100L,SEEK_SET); //把 stream 指针移动到离文件开头 100 字节处;
fseek(fp,100L,SEEK_CUR); //把 stream 指针移动到离文件当前位置 100 字节处;
fseek(fp,-100L,SEEK_END); //把 stream 指针退回到离文件结尾 100 字节处。
fseek()不像lseek()会返回读写位置, 因此必须使用ftell()来取得目前读写的位置.
sendfile
sendfile()作用于数据拷贝在两个文件描述符之间的操作函数.这个拷贝操作是内核中操作的,所以称为"零拷贝".sendfile函数比起read和write函数高效得多,因为read和write是要把数据拷贝到用户应用层操作.
- 传统的read/write方式进行socket的传输。:
- 调用read函数,文件数据copy到内核缓冲区
- read函数返回,文件数据从内核缓冲区copy到用户缓冲区
- write函数调用,将文件数据从用户缓冲区copy到内核与socket相关的缓冲区
- 数据从socket缓冲区copy到相关协议引擎。 在这个过程中发生了四次copy操作: 硬盘->内核->用户->socket缓冲区(内核)->协议引擎。
- sendfile的工作原理
- 系统调用 sendfile() 通过 DMA 把硬盘数据拷贝到 kernel buffer,然后数据被 kernel 直接拷贝到另外一个与 socket 相关的 kernel buffer。这里没有 用户态和核心态 之间的切换,在内核中直接完成了从一个 buffer 到另一个 buffer 的拷贝。
- DMA 把数据从 kernel buffer 直接拷贝给协议栈,没有切换,也不需要数据从用户态和核心态,因为数据就在 kernel 里。
- in_fd: 待读出内容的文件描述符
- out_fd: 待写入内容的文件描述符。
- offset: 指定从读入文件流的哪个位置开始读,如果为空,则使用读入文件流默认的起始位置。
- count: 指定文件描述符in_fd和out_fd之间传输的字节数。
- return: 如果成功的拷贝,返回写操作到out_fd的字节数,错误返回-1,并相应的设置error信息.
ssize_t senfile(int out_fd,int in_fd,off_t* offset,size_t count);
in_fd必须是支持类似mmap函数的文件描述符,即它必须指向真实的文件,不能是socket和管道. 而在Linux内核2.6.33以前,out_fd必须是一个socket fd;在2.6.33之后,out_fd可以是任意文件。如果out_fd是普通文件,sendfile()将适当地改变file的offset。
errorno | 含义 |
---|---|
EAGAIN | 无阻塞I/O设置O_NONBLOCK时,写操作(write)阻塞了 |
EBADF | 输出或者输入的文件描述符没有打开 |
EFAULT | 错误的地址 |
EINVAL | 描述符不可用或者锁定了,或者用mmap()函数操作的in_fd不可用 |
EIO | 当读取(read)in_fd时发生未知错误 |
ENOMEM | 读(read)in_fd时内存不足 |
贡献者
版权所有
版权归属:PinkDopeyBug