读取文件元数据
应用程序能通过调用stat
和fstat
函数,检索到关于文件的信息(又称文件的元数据 File Metadata
)
#include <unistd.h>
#include <sys/stat.h>
int stat(const char* filename, struct stat *buf);
int fstat(int fd, struct stat *buf);
若成功则返回0,出错则返回-1
其中,struct stat *buf
中返回查询到的文件元数据。
对于stat
函数,第一个参数是文件名;
对于fstat
函数,第一个参数是文件描述符。
struct stat {
dev_t st_dev; 文件的设备编号
ino_t st_ino; 索引结点编号
mode_t st_mode; 文件类型和权限
nlink_t st_nlink; 硬链接数
uid_t st_uid; 用户ID
gid_t st_gid; 组ID
dev_t st_rdev; 设备类型(若此文件为设备文件,则为设备编号
off_t st_size; 文件大小
blksize_t st_blksize; 文件系统的I/O缓冲区大小
blkcnt_t st_blocks; 块数
time_t st_atime; 访问时间
time_t st_mtime; 修改时间
time_t st_ctime; 更改时间
};
可以根据stat
函数来实现一个Linux
的stat
命令,类似于下图.
Linux
上确实有stat
这个命令:
读取目录内容
应用程序可以用readdir系列函数读取目录内容。如果出错,readdir返回NULL,并设置errno。可惜的是唯一能区分错误和流结束情况的方法是检查自调用readdir以来errno是否被修改过。
#inlcude<sys/types.h>
#include<dirent.h>
//以路径名为参数,返回指向目录流的指针,流是针对条目有序列表的抽像。
DIR *opendir(const char *name);
//返回的是指向流dirp在下一个目录项的指针,如果没有更多的目录项返回NULL,每个目录项都是一个结构体
struct dirent *readdir(DIR *dirp);
//虽然有的linux版本包含了其他数据成员,但这两个对所有系统来说都是标准的。d_name是文件名,
//d_ino是文件位置
struct dirent{
ino_t d_ino;
char d_name[256];
}
函数closedir关闭并释放所有资源。
共享文件
-
Descriptor table描述符表
:每个进程都有它独立的描述符表,以文件描述符为索引,每个打开的描述符表项指向打开文件表
中的一个表项。 -
Open file table打开文件表
:打开文件的集合是由一张文件表来表示的,所偶的进程共享这张表。每个文件表的表项组成包括当前的文件位置、引用计数(reference count)(即档期按指向该表项的描述符表项数),以及一个指向v-node
表中对应表项的指针。关闭一个描述符会减少相应的文件表表项中的引用计数。内核不会删除这个文件表表项,直到它的引用计数为零。 -
v-node table
:同文件表一样,所有进程共享这张v-node表。每个表项包含stat
中的大多数信息,包括st_mode
和st_size
。
-
若同一个进程打开2次相同文件,会生成两个不同的打开文件表,与之对应有两个彼此独立的文件描述符,所以可以完全独立地对2个打开文件进行读写。(但是显然一般情况不建议两个同时write,容易把文件变得很乱)
-
fork
会为子进程创建一个与父进程相同的描述符表的副本,并且打开文件表的refcnt会变成2(可见fork函数内部的设计也不是那么简单的完全复制的工作,也有增加refcnt这样的操作),父子进程会共享这同一个打开文件表,因此,文件位置File pos
是共享的,这样,当一个进程读入时,另一个进程读时会在新的文件位置去读。(当然父子进程可以自行打开和关闭文件,但这样会变得非常混乱,所以必须要用到引用计数)
-
** 打开文件表的关闭 **:
close
某一文件描述符,所做的是对该文件描述符对应的那个打开文件表的refcnt进行-1
,如果refcnt变成0了,才会删除这个打开文件表。如果fork,所有的进程都需要去close,才能从操作系统的角度真正关闭它。
I/O重定向
include <unistd.h>
int dup2(int oldfd, int newfd);
若成功则返回非负的描述符,出错返回-1.
dup2函数
:复制描述符表表项oldfd到描述符表表项newfd,覆盖描述符表表项new-fd以前的内容。