本章主要描述文件系统的其他特征和文件的性质。从stat函数开始,逐个说明stat结构的每一个成员,并详细说明UNIX文件系统的结构和符号链接。
1.四个stat函数
#include <sys/stat.h>
int stat(const char *restrict pathname, struct stat *restrict buf);
int fstat(int fd, struct stat *buf);
int lstat(const char *restrict pathname, struct stat *restrict buf);
int fstatat(int fd, const char *restrict pathname, struct stat *restrict buf, int flag);
stat类型的实际定义可能随具体实现有所不同,但基本形式是:
struct stat {
mode_t st_mode; /* file type & mode (permissions) */
ino_t st_ino; /* inode number (serial number) */
dev_t st_dev; /* device number (file system) */
dev_t st_rdev; /* device number for special file) */
nlink_t st_nlink; /* number of hard links */
uid_t st_uid; /* user ID of owner */
gid_t st_gid; /* group ID of owner */
off_t st_size; /* size in bytes for regular files */
struct timespec st_atime; /* time of last access */
struct timespec st_mtime; /* time of last modification */
struct timespec st_ctime; /* time of last file status change */
blksize_t st_blksize; /* best I/O blocksize */
blkcnt_t st_blocks; /* number of disk blocks allocated */
};
timespec结构类型按照秒和纳秒定义了时间,至少包括两个字段:
time_t tv_sec;
long tv_nsec;
Linux 系统中的文件共分为 7 种类型:-dcbpls, 分别对应于以上七种文件类型。
文件类型信息包含在st_mode成员中。以下宏可以确定文件类型,参数为st_mode:
POSIX.1允许将进程间通信(IPC)对象说明为文件。对应的确定其类型的宏的参数是指向stat结构的指针,而非st_mode:
3.设置用户ID和组ID
与一个进程相关联的ID有6个或者更多:
4.文件访问权限
每个文件都有访问权限。每个文件有9个访问权限位,分为三类:
st_mode屏蔽 | 含义 |
---|---|
S_IRUSR | 用户读 |
S_IWUSR | 用户写 |
S_IXUSR | 用户执行 |
S_IRGRP | 组读 |
S_IWGRP | 组写 |
S_IXGRP | 组执行 |
S_IROTH | 其他读 |
S_IWOTH | 其他写 |
S_IXOTH | 其他执行 |
文件访问权限规则:
5.函数access和faccessat
按照实际用户ID和实际用户组进行访问权限测试。
#include <unistd.h>
int access(const char *pathname, int mode);
int faccessat(int fd, const char *pathname, int mode, int flag);
6.函数umask
umask函数为进程设置文件模式创建屏蔽字,并返回之前的值。
#include <sys/stat.h>
mode_t umask(mode_t cmask);
7.函数chmod、fchmod、fchmodat
这三个函数可以改变现有文件的访问权限
#include <sys/stat.h>
int chmod(const char *pathname, mode_t mode);
int fchmod(int fd, mode_t mode);
int fchmodeat(int fd, const char *pathname, mode_t mode, int flag);
8.粘着位
在UNIX的早期版本中,有一位被称为粘住位(sticky bit)。如果一个可执行程序文件的这一位被设置了,那么在该程序第一次执行并结束时,该程序正文的一个文本被保存在交换区(程序的正文部分是机器指令部分)。这使得下次执行该程序时能较快地将其装入内存区。其原因是:在交换区,该文件是被连续存放的,而在一般的UNIX文件系统,文件的各数据块很可能是随机存放的。对于常用的应用程序,例如文本编辑程序和编译程序的各部分常常设置它们所在文件的粘住位。
9.函数chown、fchown、fchownat和lchown
#include <unistd.h>
int chown(const char* pathname,uid_t owner,gid_t group);
int fchown(int fd,uid_t owner,gid_t group);
int fchownat(int fd, const char *pathname, uid_t owner, gid_t group, int flag);
int lchown(const char* pathname,uid_t owner,gid_t group);
这四个函数用于更改文件的用户ID和组ID。
10.文件长度
文件空洞是由所设置的偏移量超过文件尾端,并写入了某些数据后造成的。
11.文件截断
truncate和ftruncate函数用于截断文件。
int truncate(const char* pathname,off_t length);
int ftruncate(int fd,off_t length);
12.文件系统
UNIX文件系统的基本结构。传统的基于BSD的UNIX文件系统称为UFS,UFS是以Berkeley快速文件系统为基础的。
一个磁盘被分为一个和多个分区。每个分区可以包含一个文件系统。i节点是固定长度的记录项,它包含有关文件的大部分信息。
每个分区(一个文件系统)包括:自举块、超级块、n个柱面组
每个柱面组包括:超级块副本、配置信息、i节点图、块位图、n个i节点、数据块
int link(const char *existingpath, const char *newpath);
int linkat(int efd, const char *existingpath, int nfd, const char *newpath, int flag);
int unlink(const char *pathname);
int unlinkat(int fd, const char *pathname, int flag);
int remove(const char *pathname);
int rename(const char *oldname, const char *newname);
int renameat(int oldfd, const char *oldname, int newfd, const char *newname);
不能对.和..重命名。
15.符号链接
符号链接是对一个文件的间接指针,它与上一节所述的硬链接有所不同,硬链接直接指向文件的i节点。
当使用以名字引用一个文件的函数时,应当了解该函数是否处理符号链接功能。也就是是否跟随符号链接到达它所链接的文件。如若该函数处理符号链接功能,则该函数的路径名参数引用由符号链接指向的文件。否则,一个路径名参数引用链接本身,而不是由该链接指向的文件。
符号链接可能在文件系统中引入循环,大多数查找路径名的函数在这种情况发生时都返回出错。
16.创建和读取符号链接
int symlink(const char *actualpath, const char *sympath);
int symlinkat(const char *actualpath, int fd, const char *sympath);
ssize_t readlink(const char* restrict pathname, char *restrict buf, size_t bufsize);
ssize_t readlinkat(int fd, const char* restrict pathname, char *restrict buf, size_t bufsize);
17.文件的时间
对每个文件维护三个时间:
字段 | 说明 | 例子 | ls(1)选项 |
---|---|---|---|
st_atim | 文件数据最后访问时间 | read | -u |
st_mtim | 文件数据最后修改时间 | write | 默认 |
st_ctim | i节点状态最后更改时间 | chmod、chown | -c |
18.函数futimens、utimensat、和utimes
一个文件的访问和修改时间可以用以下几个函数更改。futimens和utimensat可以指定纳秒级精度的时间戳。utimes函数对路径名
int futimens(int fd, const struct timespec times[2]);
int utimensat(int fd, const char *path, const struct timespec times[2], int flag);
int utimes(const char *pathname, const struct timeval times[2]);
timeval定义如下:
struct timeval {
time_t tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
int mkdir(const char *pathname, mode_t mode);
int mkdirat(int fd, const char *pathname, mode_t mode);
int rmdir(const char *pathname);
DIR *opendir(const char *pathname);
DIR *fdopendir(int fd);
struct dirent *readdir(DIR *dp);
void rewinddir(DIR *dp);
int closedir(DIR *dp);
long telldir(DIR *dp);
void seekdir(DIR *dp, long loc);
21.函数chdir、fchdir和getcwd
int chdir(const char *pathname);
int fchdir(int fd);
char *getcwd(char *buf, size_t size);
22.设备特殊文件
stdev和strdev这两个字段经常引起混淆,讨论ttyname函数时,需要使用这两个字段。有关规则很简单: