本章将描述文件系统的其他特征和文件的性质。
4个stat
函数:
#include
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);
// 所有4个函数的返回值:若成功,返回0;若出错,返回-1
stat
函数返回与pathname命名文件有关的信息结构;fstat
函数获得已在描述符fd上打开文件的有关信息;lstat
函数类似于stat
函数,但在当命名文件是一个符号链接时,返回该符号链接的有关信息,而不是由该符号链接引用的文件的信息;fstatat
函数返回一个相当于当前打开目录(fd参数指向)的路径名返回文件统计信息。flag参数控制着是否跟随着一个符号链接,当AT_SYMLINK_NOFOLLOW标志被设置时,fstatat
不会跟随符号链接,而是返回符号链接本身的信息,否则,在默认情况下,返回符号链接所指向的实际文件的信息;UNIX系统的文件类型包括:
普通文件
:最常用的类型,包含了某种形式的数据;目录文件
:包含了其他文件的名字以及指向与这些文件有关信息的指针;块特殊文件
:提供对设备(如磁盘)带缓冲的访问,每次访问以固定长度为单位进行;字符特殊文件
:提供对设备不带缓冲的访问,每次访问长度可变;FIFO
:用于进程间通信,亦称管道
;套接字
:用于进程间的网络通信,也可用于在一台宿主机上进程之间的非网络通信;符号链接
:指向另一个文件。文件类型信息包含在stat结构的st_mode成员中,用下表中的宏确定,这些宏的参数为stat成员st_mode。
宏 | 文件类型 |
---|---|
S_ISREG() | 普通文件 |
S_ISDIR() | 目录文件 |
S_ISCHR() | 字符特殊文件 |
S_ISBLK() | 块特殊文件 |
S_ISFIFO() | 管道或FIFO |
S_ISLNK() | 符号链接 |
S_ISSOCK() | 套接字文件 |
POSIX.1允许实现将进程间通信(IPC)对象说明为文件,下表中的宏可用来从stat结构中确定IPC对象的类型,这些宏的参数为stat结构指针。
宏 | 对象的类型 |
---|---|
S_TYPEISMQ() | 消息队列 |
S_TYPEISSEM() | 信号量 |
S_TYPEISSHM() | 共享存储对象 |
与每个进程相关联的用户ID和组ID | |
---|---|
实际用户ID | 标识我们实际上是谁; 在登录时取自口令文件中的登录项; 在登录会话期间不改变,但超级用户可以修改。 |
实际组ID | |
有效用户ID | 用于文件访问权限检查 |
有效组ID | |
附属组ID | |
保存的设置用户ID | 由exec函数保存; 在执行程序时包含了有效用户ID和有效组ID的副本。 |
保存的设置组ID |
st_mode值也包含了对文件的访问权限位,每个文件有9个访问权限位,可分3类:
9个访问权限位 | |
---|---|
st_mode屏蔽 | 含义 |
S_IRUSR | 用户读 用户写 用户执行 |
S_IWUSR | |
S_IXUSR | |
S_IRGRP | 组读 组写 组执行 |
S_IWGRP | |
S_IXGRP | |
S_IROTH | 其他读 其他写 其他执行 |
S_IWOTH | |
S_IXOTH |
access
和faccessat
函数是按实际用户ID和实际组ID进行访问权限测试:
#include
int access(const char *pathname, int mode);
int faccessat(int fd, const char *pathname, int mode, int flag);
// 两个函数的返回值:若成功,返回0;若出错,返回-1
mode | 说明 |
---|---|
R_OK | 测试读权限 |
W_OK | 测试写权限 |
X_OK | 测试执行权限 |
如果flag设置为AT_EACCESS,访问检查用的是调用进程的有效用户ID和有效组ID,而不是实际用户ID和实际组ID。
faccessat
函数计算相对于打开目录(由fd参数指向)的pathname,faccessat
函数与access
函数在下面两种情况下是相同的:
(1)pathname参数为绝对路径;
(2)fd参数取值为AT_FDCWD而pathname参数为相对路径时。
umask
函数为进程设置文件模式创建屏蔽字,并返回之前的值:
#include
mode_t umask(mode_t cmask);
// 返回值:之前的文件创建屏蔽字
或
”构成的;chmod
、fchmod
和fchmodat
这3个函数可以更改现有文件的访问权限:
#include
int chmod(const char *pathname, mode_t mode);
int fchmod(int fd, mode_t mode);
int fchmodat(int fd, const char *pathname, mode_t mode, int flag);
// 3个函数返回值:若成功,返回0;若出错,返回-1
chmod
函数在指定的文件上进行操作;fchmod
函数对已打开的文件进行操作;fchmodat
计算相对于打开目录(由fd参数指向)的pathname,flag参数用于改变fchmodat
的行为,当设置了AT_SYMLINK_NOFOLLOW标志时,fchmodat
并不会跟随符号链接。参数mode是下表中所有常量的按位或:
chmod函数的mode常量 | |
---|---|
mode | 说明 |
S_ISUID S_ISGID S_ISVTX |
执行时设置用户ID 执行时设置组ID 保存正文(粘着位) |
S_IRWXU S_IRUSR S_IWUSR S_IXUSR |
用户(所有者)读、写和执行 用户(所有者)读 用户(所有者)写 用户(所有者)执行 |
S_IRWXG S_IRGRP S_IWGRP S_IXGRP |
组读、写和执行 组读 组写 组执行 |
S_IRWXO S_IROTH S_IWOTH S_IXOTH |
其他读、写和执行 其他读 其他写 其他执行 |
S_ISVTX
被称为粘着位:如果一个可执行程序文件的这一位被设置了,那么当该程序第一次被执行,在其终止时,程序正文部分的一个副本仍被保存在交换区(程序的正文部分是机器指令);交换区被作为一个连续文件来处理,这使得下次执行该程序时能较快地将其载入内存。
下面几个chown
函数可用于改变文件的用户ID和组ID,如果两个参数owner或group中的任意一个是**-1**,则对应的ID不变:
#include
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);
// 4个函数的返回值:若成功,返回0;若出错,返回-1
lchown
和fchownat
(设置了AT_SYMLINK_NOFOLLOW标志)更改符号链接本身的所有者,而不是该符号链接所指向的文件的所有者;fchown
函数改变fd参数指向的打开文件的所有者,因为它在一个已打开的文件上操作,所以不能用于改变符号链接的所有者;fchownat
函数与chown
或者lchown
函数在下面两种情况下是相同的:fchownat
与lchown
行为相同,如果flag参数中清除了AT_SYMLINK_NOFOLLOW标志,则fchownat
与chown
行为相同。如果fd参数设置为打开目录的文件描述符,并且pathname参数是一个相对路径名,fchownat
函数计算相对于打开目录的pathname。stat结构成员st_size表示以字节为单位的文件的长度,此字段只对普通文件、目录文件和符号链接有意义:
为了截断文件可以调用函数truncate
和ftruncate
:
#include
int truncate(const char *pathname, off_t length);
int ftruncate(int fd, off_t length);
// 两个函数的返回值:若成功,返回0;若出错,返回-1
可以把一个磁盘分成一个或多个分区,每个分区可以包含一个文件系统,i节点
是固定长度的记录项,它包含有关文件的大部分信息:
创建一个指向现有文件的硬链接
的方法是使用link
函数或linkat
函数:
#include
int link(const char *existingpath, const char *newpath);
int linkat(int efd, const char *existingpath, int nfd, const char *newpath, int flag);
// 两个函数的返回值:若成功,返回0;若出错,返回-1
mkdir
函数要求一样;linkat
函数,现有文件是通过efd和existingpath参数指定的,新的路径名是通过nfd和newpath参数指定的。默认情况下,如果两个路径名中的任一个是相对路径,那么它需要通过相对于对应的文件描述符进行计算;如果两个文件描述符中的任一个设置为AT_FDCWD,那么相应的路径名(如果它是相对路径)就通过相对于当前目录计算;如果任一路径名是绝对路径,相应的文件描述符参数就会被忽略;为了删除一个现有的目录项,可以调用unlink
函数:
#include
int unlink(const char *pathname);
int unlinkat(int fd, const char *pathname, int flag);
// 两个函数的返回值:若成功,返回0;若出错,返回-1
unlinkat
函数类似于rmdir
一样删除目录,如果这个标志被删除,unlinkat
与unlink
执行同样操作;unlink
删除该符号链接,而不是删除该链接所引用的文件。可以使用remove
函数解除对一个文件或目录的链接:
#include
int remove(const char *pathname);
// 返回值:若成功,返回0;若出错,返回-1
remove
的功能与unlink
相同;remove
的功能与rmdir
相同。文件或目录可以用rename
函数或者renameat
函数进行重命名:
#include
int rename(const char *oldname, const char *newname);
int renameat(int oldfd, const char *oldname, int newfd, const char *newname);
// 两个函数的返回值:若成功,返回0;若出错,返回-1
符号链接是对一个文件的间接指针,它与硬链接不同,硬链接直接指向文件的i节点,引入符号链接的原因是为了避开硬链接的一些限制:
对符号链接以及它指向何种对象并无任何文件系统限制,任何用户都可以创建指向目录的符号链接;符号链接一般用于将一个文件或整个目录结构移到系统中的另一个位置。
可以用symlink
或symlinkat
函数创建一个符号链接:
#include
int symlink(const char *actualpath, const char *sympath);
int symlinkat(const char *actualpath, int fd, const char *sympath);
// 两个函数的返回值:若成功,返回0;若出错,返回-1
symlinkat
函数的sympath参数根据相对于打开文件描述符引用的目录(由fd参数指定)进行计算;如果sympath参数指定的是绝对路径或者fd参数设置了AT_FDCWD值,那么symlinkat
就等同于symlink
函数。可以用readlink
和readlinkat
函数来打开符号链接本身:
#include
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);
// 两个函数的返回值:若成功,返回读取的字节数;若出错,返回-1
readlinkat
函数的行为与readlink
相同;readlinkat
计算相对于由fd代表的打开目录的路径名。每个文件维护3个时间字段,保存在stat结构中:
一个文件的访问时间和修改时间可以用futimens
和utimensat
函数修改,它们使用纳秒级精度的时间戳:
#include
int futimens(int fd, const struct timespec times[2]);
int utimensat(int fd, const char *pathname, const struct timespec times[2], int flag);
// 两个函数的返回值:若成功,返回0;若出错,返回-1
futimens
函数需要打开文件来更改它的时间;utimensat
函数的pathname参数是相对于fd参数进行计算的,fd要么是打开目录的文件描述符,要么设置为特殊值AT_FDCWD;如果pathname指定了绝对路径,那么fd参数被忽略。/* POSIX.1b structure for a time value. This is like a `struct timeval' but
has nanoseconds instead of microseconds. */
struct timespec
{
__time_t tv_sec; /* Seconds. */
__syscall_slong_t tv_nsec; /* Nanoseconds. */
};
#include
int utimes(const char *pathname, const struct timeval times[2]);
// 函数返回值:若成功,返回0;若出错,返回-1
utimes
函数对路径名进行操作;/* A time value that is accurate to the nearest
microsecond but also has a range of years. */
struct timeval
{
__time_t tv_sec; /* Seconds. */
__suseconds_t tv_usec; /* Microseconds. */
};
用mkdir
和mkdirat
函数创建目录:
#include
int mkdir(const char *pathname, mode_t mode);
int mkdirat(int fd, const char *pathname, mode_t mode);
// 两个函数返回值:若成功,返回0;若出错,返回-1
mkdirat
函数的fd参数具有特殊值AT_FDCWD或者pathname参数指定了绝对路径名时,mkdirat
和mkdir
完全一样,否则,当fd参数是一个打开目录,相对路径名根据此打开目录来计算。用rmdir
函数删除目录:
#include
int rmdir(const char *pathname);
// 返回值:若成功,返回0;若出错,返回-1
对某个目录具有访问权限的任一用户都可以读该目录,但为了防止文件系统产生混乱,只有内核才能写目录。
#include
DIR *opendir(const char *pathname);
DIR *fdopendir(int fd);
// 两个函数返回值:若成功,返回指针;若出错,返回NULL
struct dirent *readdir(DIR *dp);
// 返回值:若成功,返回指针;若在目录尾或出错,返回NULL
void rewinddir(DIR *dp);
int closedir(DIR *dp);
// 返回值:若成功,返回0;若出错,返回-1
long telldir(DIR *dp);
// 返回值:与dp关联的目录中的当前位置
void seekdir(DIR *dp, long loc);
进程使用chdir
或fchdir
函数可以更改当前工作目录:
#include
int chdir(const char *pathname);
int fchdir(int fd);
// 两个函数的返回值:若成功,返回0;若出错,返回-1
chdir
的进程本身,而不影响其他进程。可以用getcwd
函数获取当前工作目录完整的绝对路径名:
#include
char *getcwd(char *buf, size_t size);
// 返回值:若成功,返回buf;若出错,返回NULL
major
和minor
来访问主、次设备号;chapter4