Files and Directories
1. stat, fstat, fstatat, lstat
/** |
参数buf
是为指向stat结构体的指针, stat的定义如下:
struct stat { |
timespec的定义如下:
struct timespec { |
2. File Types
以下是UNIX的文件类型:
- Regular File(
S_IFREG
): 最常见的文件类型, 该文件类型负责保存数据, 无论二进制还是文本数据, 应用程序读取或更新该文件. - Directory File(
S_IFDIR
): 该文件可包含一些文件的名字或指向文件的指针, 可读取directory file, 但程序只能调用kernel function通过kernel向directory file写入文件 - Special File: 也称为device file, 其意义在于将device呈现为文件系统中的一个file, 开发者可以像操作传统文件系统中的文件一样操作hardware device, 读写该类文件时, 请求会转交给device driver, 而不是由文件系统处理. 根据操作模式两种special file:
- Block Special File(
S_IFBLK
): 该设备上的数据会被持久化, 反复读取, 且每次访问取出一块连续数据的block, 因此称为block device, 如/dev/disk0
- Character Special File(
S_IFCHR
): 该设备上的数据只被读取一次, 顺序执行, 且每次访问只取1 byte, 因此称为character device, 如/dev/stdin
- Block Special File(
- FIFO(
S_IFIFO
): 也称为pipe. UNIX的一大优势为IPC(进程间通信), 当一个进程与其他进程通信时, 可创建pipe向其他进程发送数据 - Socket(
S_IFSOCK
): 可用于IPC, 但也可以通过网络传输数据 - Symbolic Link(
S_IFLNK
): 该文件类型是对另一个文件的引用, 存储了引用文件的路径
stat结构体的st_mode表示文件类型, 可用以下macro判断文件是什么类型(m表示stat.st_mode
):
S_ISREG(m)
: 是否为regular fileS_ISDIR(m)
: 是否为directory fileS_ISCHR(m)
: 是否为character special fileS_ISBLK(m)
: 是否为block special fileS_ISFIFO(m)
: 是否为FIFOS_ISLNK(m)
: 是否为symbolic linkS_ISSOCK(m)
: 是否为socket
3. User ID and Group ID
当用户使用计算机时, 需登录一个username, 通常来说, 一个username对应一个user ID. User通过group分类, 每个user可属于多个group, 同一group内的user可共享资源. 每个group有一个group name和group ID.
用户无法直接访问资源, 而是通过process(进程), POSIX standard为process规定了三种user ID:
- Real User ID(RUID): 创建当前process的用户的user id, 表示当前进程属于哪个用户
- Effective User ID(EUID): 系统用EUID判断当前process是否有权限访问资源, 创建进程时为RUID, 可通过
seteuid
将EUID改为执行文件所有者的user id. - Saved User ID(SUID): 当privileged process(EUID为root)降低自身权限时, 会先将EUID保存在SUID, 执行完毕后, 将EUID改回SUID.
POSIX Standard还为process规定了三种group ID:
- Real Group ID(RGID): 创建当前process的用户所在的group的group id
- Effective Group ID(EGID): 系统用EGID判断当前process是否有权限访问资源, 创建进程时维RGID, 可通过
setegid
将EGID改为执行文件所有者的group id - Saved Group ID(SGID): 当privileged process(EGID为root)降低自身权限时, 会先将EGID保存在SGID, 执行完毕后, 将EGID改回SGID
4. File Access Permissions
UNIX中的每一个文件都有权限设置, 由9个bit组成, 如rwxrwxrwx
, 并分为三类:
- owner: 第一组
rwx
, 文件所有者的权限 - group: 第二组
rwx
, 文件所属组的权限 - other: 第三组
rwx
, 其他人的权限
每组权限包含三种权限:
- r: 查看文件内容, 或查看文件夹内的文件列表
- w: 修改或删除文件, 或从文件夹中添加或删除文件
- x: 运行可执行文件, 或搜索文件夹内文件, 但不能读取文件夹内的文件列表
有时文件的拥有者并不想其他进程修改或读取文件内容, 但需要其他进程执行该文件. 因此UNIX还增加三种额外的权限模式:
- setuid(SUID): 进程执行该文件期间, 当前进程的EUID改为文件所有者的user id, 如
rwsrwxrwx
- setgid(SGID): 进程执行该文件期间, 当前进程的EGID改为文件所有者的group id, 如
rwxrwsrwx
- sticky(SBIT): 对于可执行文件, 即使进程运行结束, kernel也会将text segment保留在内存中, 但目前只有极少数UNIX系统应用; 对于文件夹, 即使进程拥有写入权限, 如果不是文件夹拥有者或root, 则不能重命名, 移动或删除里面的文件.
调用open
或chmod
时, 可通过以下mode设置文件权限, 多个mode可通过OR(或运算)相连:
mode | mask | class | permission |
---|---|---|---|
S_IRWXU | 00700 | user | read, write, execute |
S_IRUSR | 00400 | user | read |
S_IWUSR | 00200 | user | write |
S_IXUSR | 00100 | user | execute |
S_IRWXG | 00070 | group | read, write, execute |
S_IRGRP | 00040 | read | read |
S_IWGRP | 00020 | group | write |
S_IXGRP | 00010 | group | execute |
S_IRWXO | 00007 | other | read, write, execute |
S_IROTH | 00004 | other | read |
S_IWOTH | 00002 | other | write |
S_IXOTH | 00001 | other | execute |
S_ISUID | 0004000 | user | set-user-ID |
S_ISGID | 0002000 | group | set-group-ID |
S_ISVTX | 0001000 | other | sticky bit |
Kernel以一套规则来判断当前进程是否有权限来对文件进行操作:
- 若EUID为0, 则当前进程为root, 允许操作
- 若当前进程的EUID与文件所有者的user ID相同, 则使用文件owner的权限模式
- 若当前进程的EGID与文件所有者的group ID相同, 则使用文件group的权限模式
- 使用other的权限模式
5. access and faccessat
/* |
6. umask
/** |
7. chmod, fchmod, fchmodat
/** |
chmod
不会更改symbolic link的权限, 因为从不使用symbolic link的权限设置, 但chmod
会更改symbolic link指向的文件的权限, 也会无视循环指向的symbolic link.- 只有当前进程的EUID与文件所有者的UID相同, 或为root权限时, 才能修改目标文件的mode.
- 当前进程没有root权限, 且EGID不匹配文件的group id, 则无视
S_ISGID
并返回错误 - 一些UNIX系统只允许拥有root权限的用户在文件上设置sticky bit, 且拥有特殊意义
8. chown, fchown, fchownat, lchown
/** |
- 只有privileged process才能更改文件的owner; 文件的owner和privileged process都更改文件所属的group
- 若
chown
的参数owner
或group
为-1, 则不作任何更改 - 若目标文件为可执行文件, 且当前进程为privileged process, 则目标文件的
S_ISUID
和S_ISGID
会被清除
9. File Size
若文件为regular file, directory或symbolic link, stat的st_size
表示文件占多少byte. 以下是一些特殊情况:
- 若文件为regular file,
st_size
可为0, 文件的唯一字符为EOF. - 若文件为directory, 则
st_size
无法告知文件夹内的文件总大小, 只能通过递归不断查看每个文件的大小 - 若文件为symbolic link,
st_size
表示pathname的长度
10. File Truction
/** |
11. File System
File System(FS)负责数据存储和检索, 并通过device file提供对外部资源的访问(如打印机, 鼠标等). UNIX File System(UFS)受Berkley Fast File System启发, 是操作系统的核心组件,
UFS中, 每个device类型有一个固定的major device number, 每个device类型下的每个device都有一个minor device number. 以RAM为例, 其major device number为1, device类别为block, 第一个RAM的minor device number为0, 第二个为1, 其次递归.
Kernel通过block device switch table和character device switch table与device driver沟通, 每个device类型占一项, 里面包含系统调用(如open)对应的device driver接口(如软盘driver的open). 调用device driver时, kernel需将minor device number作为参数传给driver, 保证driver使用正确的device.
UFS中, 物理磁盘被拆分为多个逻辑磁盘, 称为partition, 每个partition都是一个独立的FS, 因此当我们讨论FS时, 指代的是单个partition.
讨论文件存储前, 需先了解磁盘: 磁盘的最小存储单元为sector(扇区, 通常为512kb). 由于磁盘读取操作是一个费时操作, 因此UNIX不会一次只读取一个sector, 而是读取多个sector, 称为block, 通常block包含8个sector, 也就是4KB. 但UNIX需要一种方法来跟踪每个文件对应的block, 也就是inode. 以下是UFS中文件存储结构图:
- Boot Block: FS的第一个block, 其中包含bootstrap程序, 负责系统启动
- Super Block:
- inode数量, block总数, 空闲block数量, 空闲inode数量
- 第一个block, block大小
- cylinder group的data block数量
- mount时长, write时长
- 兼容性, volume信息
- FS状态
- cylinder group: 一个disk slice(磁盘片)拆为多个cylinder group, 一个cylinder group包含一个或多个连续disk cylinder(磁盘柱面). 一个cylinder group由以下组成:
- superblock的copy
- inode map: 指向每个inode的位置表
- 可用block的bitmap
- inode列表
- data block略表: 包含文件的具体内容
- inode: 包含一个文件的所有信息, 除了文件名(包含在文件夹内, 文件夹内的filename其实是指向inode的hard link), 每个inode占128字节.
- 文件类型, 文件权限
- UID, GID
- 文件大小(byte)
- 访问时间, 创建时间, 修改时间, 删除时间
- Hard Link数量: 当该值为0, 文件将被删除
- Flags
inode包含15个block pointer, 分别指向不同的data block. 前12个block为direct block pointer, 也就是说, 地址直接指向包含文件数据的block, 总共48KB; 若文件大于48KB, 则使用indirect block pointer:
- 第13个block pointer: indirect block pointer, 该pointer指向的block只有direct block pointer, 总共
4KB/4B*4KB = 4MB
数据. - 第14个block pointer: double indirect block pointer, 该pointer指向的block只有indirect block pointer, 二级pointer指向含有数据的block, 总共
(4KB/4B)*(4KB/4B)*4KB = 4GB
数据. - 第15个block pointer: triple indirect block pointer, 该pointer指向的block只有double indirect block pointer, 三级pointer指向含有数据的block, 总共
(4KB/4B)*(4KB/4B)*(4KB/4B)*4KB = 4TB
数据
Data block: inode指向data block, 分为以下三种:
- Plain data block: 包含文件的数据
- Symbolic-link data block: 包含路径名的symbolic link
- Directory data block: 包含一组文件名和对应的inode
12. link, linkat, unlink, unlinkat, remove
/** |
- 若系统允许创建hard link, 只有root权限用户才能创建, 因为hard link可能导致循环访问.
- 有些系统不支持对directory使用
unlink
, 需使用rmdir
13. rename, renameat
/** |
14. Symbolic Links
Symbolic link也称为soft link, 以下是其与hard link的不同之处:
- hard link指向inode, 删除文件, 重命名文件, 或移动文件都不会影响inode, 因此不会影响hard link
- symbolic link只包含pathname
/** |
- 系统解析symbolic link时, 会将symbolic link里的内容当做path, 用于搜索文件或路径. 若symbolic link包含
..
, 则该path为symbolic link所在的文件夹的相对路径. readlink()
不会在buf
中追加空字符(\0
), 若pathname
的内容长度超出bufsize
, 会自动截断.
15. File Times
Field | Description | Example | ls option |
---|---|---|---|
st_atim | last-access time of file data | read | -u |
st_mtim | last-modification time of file data | write | default |
st_ctim | last-change time of i-node status | chmod, chown | -c |
st_ctim
: 修改权限, UID, 添加或删除Hard Link等操作都会更新st_ctim
access()
和stat()
只访问inode, 不访问文件内的数据, 因此不更新st_atim
16. futimens, utimenset and utimes
struct timespec { |
17. mkdir, mkdirat, rmdir
/** |
新建文件夹的UID为当前进程的EUID. 若mode
设置了set-group-ID
, 则新建文件夹的GID为父文件夹的owner, 否则为当前进程的EGID.
18. Reading Directories
/** |
19. chdir, fchdir, getcwd
每个进程都有一个当前操作的文件夹. 可通过以下函数来查看或更改当前进程所在的文件夹.
/** |