0%

stat

可以用 stat 族系统调用来获取文件的属性。

utime 秒级时间戳更新

utime 族系统调用来设置文件的时间信息。修改时需要有相应的权限,以 utime 为例,其签名为:

int utime(const char *filename,
         const struct utimbuf *_Nullable times);

如果 times 参数为 NULL,则会将文件的 atime 和 mtime 修改为当前的时间。这需要进程的文件系统用户 ID 和文件匹配,且有写权限,或者是带有 CAP_FOWNER 或者 CAP_DAC_OVERRIDE 的特权级程序。

如果 times 参数非空,那么会用给定结构体的信息去更新 atime 和 mtime(ctime 信息是不能用系统调用任意修改的)。这需要进程的文件系统用户 ID 和文件匹配并有写权限,或者是带有 CAP_FOWNER 的特权级程序。

新建文件对应用户 ID 是进程(文件系统)有效用户 ID。

新建文件对应组 ID 在父目录 set-group-ID 未设置的情况下,是进程(文件系统)有效组 ID,否则是父目录的组 ID。除了显式设置父目录 set-group-ID 之外,使用 –o grpid 或者 –o bsdgroups 挂载也会将父目录的这一位置位。(挂载的默认行为是 –o nogrpid / –o sysvgroups,也就是 System V 行为。)

st_mode 的组成

st_mode 在 inode(7) 手册中的说明是 file type and mode,其组成是:文件类型 4 位 + 特殊 3 位 + 权限 9 位。

高 4 位是用来表示文件类型的(以下内容出自 man 7 inode),目前是有 7 种类型:

// 8 进制
S_IFMT     0170000   bit mask for the file type bit field

S_IFSOCK   0140000   socket
S_IFLNK    0120000   symbolic link
S_IFREG    0100000   regular file
S_IFBLK    0060000   block device
S_IFDIR    0040000   directory
S_IFCHR    0020000   character device
S_IFIFO    0010000   FIFO

stat 结构中 st_mode 字段的低 12 位定义了文件权限。其中的前 3 位为专用位,分别是 set-user-ID 位、set-group-ID 位和 sticky 位(粘着位)。后面的 9 位分别是用户、组、其他的 rwx 权限。

Set-user-ID 位

如果可执行文件具有此位,则在执行时将进程的有效用户 ID 设置为可执行文件对应的用户 ID。

访问文件时,需要拥有对路径名所列所有目录的执行权限。例如,想读取文件 /home/mtk/x,则需拥有对目录 /、/home 以及 /home/mtk 的执行权限(还要有对文件 x 自身的读权限)。若当前的工作目录为 /home/mtk/sub1 ,访问相对路径名 ../sub2/x 时,需握有 /home/mtk 和 /home/mtk/sub2 这两个目录的可执行权限(不必有对 / 或 /home 的执行权限)。

拥有对目录的读权限,用户只是能查看目录中的文件列表。要想访问目录内文件的内容或是这些文件的 i 节点信息,还需握有对目录的执行权限。

umask

用来限制当前进程(和它将来的子进程)创建文件的访问权限。大多数 shell 的初始化文件会将 umask 默认置为八进制值 022 (—-w–w-),也就是说即便给了组和其他的写权限,实际创建出来的文件也是没有这两项权限的。

access

检查实际用户 / 组是否有文件的访问权限,但是不做实际访问。也能仅仅检查文件是否存在。

变种 faccessat 有更多功能,比如有选项支持对有效用户 / 组进行权限检查。

但是,因为检查文件和实际访问文件之间有滞后性(不是原子的),所以尽量避免“先使用 access 检查文件再访问”。

chmod

当非特权级(不具备 CAP_FSETID 能力的)进程调用 chmod 时,若文件的组 ID 不等于进程的有效组 ID 或是任一辅助组 ID,内核则总是清除文件的 set-group-ID 权限,这也是出于安全考虑。书中的例子:

简要介绍

这是一种 Linux 相对于 UNIX 上扩展的非标准功能,只在部分文件系统上支持。不过现代 BSD 上也有 chflags 命令和系统调用,功能有类似之处。

在程序中可利用 ioctl() 系统调用来获取并修改 i 节点标志;在终端则可以使用 lsattrchattr 命令操作文件属性。

ioctl 更新标志的例子:

inode 标记的功能

设备

设备分为两种:字符设备和块设备。每个设备都有主 ID 和辅 ID。主 ID 表示设备的一般等级,辅 ID 在主 ID 相同的设备中标识自己。每个设备驱动程序都是按照自己和主设备号的关联关系向内核注册的

在 Linux 的早期版本中,/dev 包含了系统中所有可能设备的条目,即使某些设备实际并未与系统连接。这意味着 /dev 会包含数以千计的未用设备项,从而导致了两个缺点:其一,对于需要扫描该目录内容的应用而言,降低了程序的执行速度;其二,根据该目录下的内容无法发现系统中实际存在哪些设备。Linux 2.6 运用 udev 程序解决了上述问题。该程序所依赖的 sysfs 文件系统,是装载于 /sys 下的伪文件系统,将设备和其他内核对象的相关信息导出至用户空间。

一些命令

创建文件系统组分的系统调用/命令基本上都是用 mk 开头。比如:

  1. mknod 可以创建 inode 结点,具体来说包括 socket、fifo、普通文件、字符设备、块设备。除了创建设备之外,一般都使用专门的系统调用不是直接用 mknod。有些 UNIX 实现用 mknod 来创建目录,但是 Linux 不这样做。
  2. mkdir 表示创建目录。
  3. mkfifo
  4. mkswap。这是用户空间命令,而不是一个系统调用。

文件系统

文件系统分配空间的基本单位是逻辑块,这个块大小可能和物理块不同。文件系统由引导块(第 1 块,不管这个磁盘是不是用来引导操作系统的)、超级块(第 2 块,包含和文件系统相关的诸多参数,比如 i 节点表的容量、文件系统逻辑块大小和文件系统大小)、i 节点表、数据块组成。

挂载基础

挂载首先是需要管理员权限的,而且必须是目标已经存在才能挂载并覆盖它。然后 mount 命令是挂载,umount 是解除挂载。

Linux 中有三个文件包含了挂载信息:

  1. /proc/mounts 包含了文件系统的挂载信息。因为 Linux 现在支持了每个进程有不同于系统的文件系统,所以 /proc/mounts 被实现为指向 /proc/self/mounts 的一个符号链接。
  2. /etc/mtab 文件由 mount(8) 和 umount(8) 来维护。
  3. /etc/fstab 由系统管理员来维护,其中指定的挂载项目会在 systemd 启动其他服务前执行。

创建 tmpfs 文件系统

tmpfs 并不只是把数据写到内存那么简单,它也是一个虚拟文件系统,因而在内存不足时,依然可以将文件写到交换中。tmpfs 除了用来把内存作为硬盘加速计算之外,也被用于 System V 共享内存和共享匿名内存映射、POSIX 共享内存和 POSIX 信号量。

以下代码是创建 tmpfs 文件系统并挂载到 /tmp。这里的 mytmpfs 是挂载源,但是因为是 tmpfs,所以这里只充当名称的作用,并不是从这个路径挂载。

总览

700

和时间相关的很多函数都是不可重入的。它们修改同一块静态分配的内存区域并返回,因此不是线程安全的。在新版本已经有 *_r 这样的可重入函数来替代它们。

timeval 精确时间,微秒级

struct timeval 是带有秒和微秒的结构体,可以用 gettimeofday 获取,gettimeofday 的第二个参数 struct timezone_t * 建议填 NULL

#include <sys/time.h>

struct timeval {
   time_t       tv_sec;   /* Seconds */
   suseconds_t  tv_usec;  /* Microseconds */
};

time_t 日历时间,秒级

C 标准库 time 函数获得的秒就是 timeval 结构体的一个分量。尽管 timeval 信息更全面,在分解和格式化时间的时候都只支持了秒级别(time_t)。

首先,更新系统时间要求程序具有相应的权限,即 CAP_SYS_TIME

settimeofday 允许我们以 timeval 结构体表示的时间设置系统时间,不过,虽然它的精度很高,但是我们的计时器不一定能够达到这么高的精度,因此在微秒级设定的时间也可能是不准确的。

stime 类似,但是只能设置秒级时间。

adjtime 函数则是可以减慢或者加快始终速度,从而温和地完成时间调整。不过 adjtime 在执行的时间里可能无法完成所有的时间调整,这个时候剩余未修改的时间将保存在第二个参数中。(settimeofday 可能会突然改变系统时间,从而使得依赖绝对时间戳工作的程序不能正常运行,比如数据库或者 make。)