文件属性与目录
1、七种文件类型
普通文件(regular file)
-
普通文件中的数据存在系统磁盘中,可以访问文件中的内容,文件中的内容以字节为单位进行存储于访问。
-
文本文件
-
ASCII 码字符
-
常见的.c、.h、.sh、.txt 等
这些都是文本文件
-
-
二进制文件
-
.o 文件、.bin 文件等都是二进制文件
-
数字
-
-
-
通过 stat 命令或者 ls 命令来查看文件类型
-
对于 ls 命令,其中第一个字符(’ - ')就用于表
示文件的类型 -
⚫ ’ - ':普通文件
-
⚫ ’ d ':目录文件
-
⚫ ’ c ':字符设备文件
-
⚫ ’ b ':块设备文件
-
⚫ ’ l ':符号链接文件
-
⚫ ’ s ':套接字文件
-
⚫ ’ p ':管道文件
-
目录(directory)
-
就是文件夹,文件夹在 Linux 系统中也是一种文件,是一种特殊文件
-
可以用vi编辑器来查看
-
文件夹中记录了该文件夹本省的路径以及该文件夹下所存放的文件
-
字符设备(character)和块设备(block)
-
设备文件(字符设备文件、块设备文件)对应的是硬件设备
-
Linux 系统中,可将硬件设备分为字符设备和块设备
-
字符设备文件一般存放在 Linux 系统/dev/目录下,所以/dev 也称为虚拟文件系统 devfs
-
设备文件并不对应磁盘上的一个文件,也就是说设备文件并不存在于磁盘中,而是由文件系统虚拟出来的,一般是由内存来维护,当系统关机时,设备文件都会消失
符号链接(link)
- 类似于 Windows 系统中的快捷方式文件,是一种特殊文件,它的内容指向的是另一个文件路径
管道(pipe)
- 主要用于进程间通信
套接字(socket)
- 用于网络通信,它们可以在不同主机上的进程间通信
2、获取文件属性:stat函数
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *pathname, struct stat *buf);
-
pathname:用于指定一个需要查看属性的文件路径
-
buf:struct stat 类型指针,用于指向一个 struct stat 结构体变量。获取到的文件属性信息就记录在 struct stat 结构体中
-
返回值:成功返回 0;失败返回-1,并设置 error
struct stat结构体
-
struct stat 是内核定义的一个结构体,在<sys/stat.h>头文件中申明,所以可以在应用层使用
-
这个结构体中的所有元素加起来构成了文件的属性信息
-
struct stat
{
dev_t st_dev; /* 文件所在设备的 ID /
ino_t st_ino; / 文件对应 inode 节点编号 /
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; / 文件内容存储的块大小 /
blkcnt_t st_blocks; / 文件内容所占块数 */
struct timespec st_atim; /* 文件最后被访问的时间 /
struct timespec st_mtim; / 文件内容最后被修改的时间 /
struct timespec st_ctim; / 文件状态最后被改变的时间 */
}
- st_dev:该字段用于描述此文件所在的设备。不常用,可以不用理会。
- st_ino:文件的 inode 编号
- st_mode:该字段用于描述文件的模式,譬如文件类型、文件权限都记录在该变量中
- 用于记录文件的硬链接数,也就是为该文件创建了多少个硬链接文件。链接文件可以分为软链接(符号链接)文件和硬链接文件
- st_uid、st_gid:此两个字段分别用于描述文件所有者的用户 ID 以及文件所有者的组 ID
- st_rdev:该字段记录了设备号,设备号只针对于设备文件
- st_size:该字段记录了文件的大小(逻辑大小),以字节为单位
st_mode变量
-
struct stat 结构体中的一个成员变量
-
32 位无符号整形数据
-
记录了文件的类型、文件的权限
-
O 对应的 3 个 bit 位用于描述其它用户的权限
-
G 对应的 3 个 bit 位用于描述同组用户的权限
-
U 对应的 3 个 bit 位用于描述文件所有者的权限
-
S 对应的 3 个 bit 位用于描述文件的特殊权限
-
struct timespec结构体
-
定义在<time.h>头文件中
-
struct timespec
{
time_t tv_sec; /* 秒 /
syscall_slong_t tv_nsec; / 纳秒 */
};-
time_t 其实指的就是 long int 类型
-
time_t 时间指的是一个时间段,从某一个时间点到某一个时间点所经过的秒数
-
time_t 时间在 Linux下被称为日历时间
-
-
时间可以精确到纳秒
-
通过 localtime()/localtime_r()或者 strftime()来得到更利于我们查看的时间表达方式
- 2020-10-10 18:30:30
3、fstat 和 lstat 函数
除了 stat 函数之外,fstat 和 lstat 两个系统调用也可以获取文件属性信息。fstat、lstat 与 stat 的作用一样
fstat 函数
-
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int fstat(int fd, struct stat *buf);-
fd 表示文件描述符
-
buf:struct stat 类型指针,用于指向一个 struct stat 结构体变量。获取到的文件属性信息就记录在 struct stat 结构体中
-
返回值:成功返回 0;失败返回-1,并设置 error
-
lstat 函数
-
对于符号链接文件,stat、fstat 查阅的是符号链接文件所指向的文件对应的文件属性信息,而 lstat 查阅的是符号链接文件本身的属性信息
-
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int lstat(const char *pathname, struct stat *buf);-
pathname:用于指定一个需要查看属性的文件路径
-
buf:struct stat 类型指针,用于指向一个 struct stat 结构体变量。获取到的文件属性信息就记录在 struct stat 结构体中
-
返回值:成功返回 0;失败返回-1,并设置 error
-
4、文件属主
用户和用户组
-
文件所有者表示该文件属于“谁”,也就是属于哪个用户
-
文件所属组则表示该文件属于哪一个用户组
-
系统通过ID来识别不同的用户和用户组
-
使用 ls 命令或 stat 命令便可以查看到文件的所有者和所属组
用户 ID(UID)组 ID(GID)
-
文件的用户 ID 和组 ID 分别由 struct stat 结构体中的 st_uid 和 st_gid 所指定
-
与一个进程相关联的 ID 有 5 个或更多
-
实际用户ID
-
实际组ID
-
绝大部分情况下,进程的有效用户等于实际用户(有效用户 ID 等于实际用户 ID),有效组等于实际组(有效组 ID 等于实际组 ID)
-
有效用户ID
-
有效组ID
-
附属组 ID
-
chown()函数
-
改变文件的所有者(用户 ID)和所属组(组 ID)
-
系统调用函数
- 可通过"man 2 chown"命令查看
-
Linux 系统下也有一个 chown 命令,该命令的作用也是用于改变文件的所有者和所属组
- 这个命令内部其实就是调用了 chown 函数来实现功能的
-
#include <unistd.h>
int chown(const char *pathname, uid_t owner, gid_t group);-
pathname:用于指定一个需要修改所有者和所属组的文件路径。
-
owner:将文件的所有者修改为该参数指定的用户(以用户 ID 的形式描述)
-
group:将文件的所属组修改为该参数指定的用户组(以用户组 ID 的形式描述)
-
返回值:成功返回 0;失败将返回-1,并且会设置 errno
-
-
两个限制条件
-
只有超级用户进程能更改文件的用户 ID
-
普通用户进程可以将文件的组 ID 修改为其所从属的任意附属组 ID,前提条件是该进程的有效用户 ID 等于文件的用户 ID;而超级用户进程可以将文件的组 ID 修改为任意值
-
fchown()和lchown()函数
-
系统调用
-
作用与 chown 函数相同,只是参数、细节方面有些许不同
-
int fchown(int fd, uid_t owner, gid_t group);
-
int lchown(const char *pathname, uid_t owner, gid_t group);
5、文件访问权限
普通权限和特殊权限(也可称为附加权限)
-
每个文件都有 9 个普通的访问权限位
-
使用 ls 命令或 stat 命令可以查看到文件的这 9 个访问权限
-
-
st_mode 字段中除了记录文件的 9 个普通权限之外,还记录了文件的 3 个特殊权限
-
S_ISUID 04000 set-user-ID bit
S_ISGID 02000 set-group-ID bit (see below)
S_ISVTX 01000 sticky bit (see below) -
例如当进程对文件进行操作的时候、将进行权限检查,如果文件的 set-user-ID 位权限被设置,内核会将进程的有效 ID 设置为该文件的用户 ID(文件所有者 ID),意味着该进程直接获取了文件所有者的权限、以文件所有者的身份操作该文件。
-
Linux 系统下绝大部分的文件都没有设置 set-user-ID 位权限和 set-group-ID 位权限,所以通常情况下, 进程的有效用户等于实际用户(有效用户 ID 等于实际用户 ID),有效组等于实际组(有效组 ID 等于实际组 ID)
-
目录权限
-
删除文件、创建文件这些操作也是需要相应权限
-
目录的读权限:可列出(譬如:通过 ls 命令)目录之下的内容(即目录下有哪些文件)
-
目录的写权限:可以在目录下创建文件、删除文件
-
目录的执行权限:可访问目录下的文件,譬如对目录下的文件进行读、写、执行等操作
权限检查:access()函数
-
文件的权限检查不单单只讨论文件本身的权限,还需要涉及到文件所在目录的权限,只有同时都满足了,才能通过操作系统的权限检查,进而才可以对文件进行相关操作。那么程序当中对文件进行相关操作之前,需要使用 access 系统调用先检查执行进程的用户是否对该文件拥有相应的权限
-
#include <unistd.h>
int access(const char *pathname, int mode);-
pathname:需要进行权限检查的文件路径
-
mode
-
F_OK:检查文件是否存在
-
R_OK:检查是否拥有读权限
-
W_OK:检查是否拥有写权限
-
X_OK:检查是否拥有执行权限
-
除了可以单独使用之外,还可以通过按位或运算符" | "组合在一起
-
-
返回值:检查项通过则返回 0,表示拥有相应的权限并且文件存在;否则返回-1,如果多个检查项组合在一起,只要其中任何一项不通过都会返回-1。
-
权限修改:chmod()函数
-
在 Linux 系统下,可以使用 chmod 命令修改文件权限,该命令内部实现方法其实是调用了 chmod 函数
-
chmod 函数是一个系统调用
-
#include <sys/stat.h>
int chmod(const char *pathname, mode_t mode);-
pathname:需要进行权限修改的文件路径,若该参数所指为符号链接,实际改变权限的文件是符号链接所指向的文件,而不是符号链接文件本身
-
mode:该参数用于描述文件权限,与 open 函数的第三个参数一样
-
O—这 3 个 bit 位用于表示其他用户的权限
-
G—这 3 个 bit 位用于表示同组用户(group)的权限,即与文件所有者有相同组 ID 的所有用户
-
U—这 3 个 bit 位用于表示文件所属用户的权限,即文件或目录的所属者
-
S—这 3 个 bit 位用于表示文件的特殊权限
-
-
返回值:成功返回 0;失败返回-1,并设置 errno
-
-
文件权限对于文件来说是非常重要的属性,是不能随随便便被任何用户所修改的,要想更改文件权限,要么是超级用户(root)进程、要么进程有效用户 ID 与文件的用户 ID(文件所有者)相匹配
-
fchmod 函数
-
功能与 chmod 一样,参数略有不同。fchmod()与 chmod()的区别在于使用了文件描述符来代替文件路径,就像是 fstat 与 stat 的区别
-
#include <sys/stat.h>
int fchmod(int fd, mode_t mode);- 使用了文件描述符 fd 代替了文件路径 pathname,其它功能都是一样的
-
umask
-
在 Linux 下有一个 umask 命令,用于查看/设置权限掩码
-
umask 权限掩码是进程的一种属性,用于指明该进程新建文件或目录时,应屏蔽哪些权限位。进程的umask 通常继承至其父进程,譬如在 Ubuntu shell
终端下执行的应用程序,它的 umask 继承至该 shell 进程 -
umask 不能对特殊权限位进行屏蔽
-
文件实际的权限并不等于我们所设置的权限
- mode & ~umask
-
该函数是一个系统调用
-
#include <sys/types.h>
#include <sys/stat.h>
mode_t umask(mode_t mask);-
mask:需要设置的权限掩码值,可以发现 make 参数的类型与 open 函数、chmod 函数中的 mode 参数对应的类型一样,所以其表示方式也是一样的
-
O—这 3 个 bit 位用于表示其他用户的权限
-
G—这 3 个 bit 位用于表示同组用户(group)的权限,即与文件所有者有相同组 ID 的所有用户
-
U—这 3 个 bit 位用于表示文件所属用户的权限,即文件或目录的所属者
-
S—这 3 个 bit 位用于表示文件的特殊权限
-
-
返回值:返回设置之前的 umask 值,也就是旧的 umask
-
6、文件的时间戳
三个时间属性:st_atim、st_mtim 以及 st_ctim
- 状态更改指的是该文件的 inode 节点最后一次被修改的时间
- 有些操作并不仅仅只会影响文件本身的时间属性,还会影响到其父目录的相关时间属性
-
修改时间属性:utime()和utimes()
-
系统调用
-
显式的修改文件的时间属性
- 只能显式修改文件的最后一次访问时间和文件内容最后被修改的时间,不能显式修改文件状态最后被改变的时间
-
#include <sys/types.h>
#include <utime.h>
int utime(const char *filename, const struct utimbuf *times);-
filename:需要修改时间属性的文件路径
-
times:将时间属性修改为该参数所指定的时间值,times 是一个 struct utimbuf 结构体类型的指针,稍后给大家介绍,如果将 times 参数设置为 NULL,则会将文件的访问时间和修改时间设置为系统当前时间
-
struct utimbuf {
time_t actime; /* 访问时间 /
time_t modtime; / 内容修改时间 */
};- time_t 类型其实就是 long int 类型,所以这两个时间是以秒为单位的
-
-
返回值:成功返回值 0;失败将返回-1,并会设置 errno
-
-
#include <sys/time.h>
int utimes(const char *filename, const struct timeval times[2]);-
utimes()与 utime()最大的区别在于前者可以以微秒级精度来指定时间值
-
filename:需要修改时间属性的文件路径
-
times 是一个 struct timeval 结构体类型的数组
- struct timeval {
long tv_sec; /* 秒 /
long tv_usec; / 微秒 */
}
- struct timeval {
-
返回值:成功返回 0;失败返回-1,并且会设置 errno
-
修改时间属性:futimens()和utimensat()
-
系统调用
-
显式修改文件时间戳
- 只能显式修改文件的最后一次访问时间和文件内容最后被修改的时间,不能显式修改文件状态最后被改变的时间
-
相对于 utime 和 utimes 函数有以下三个优点
-
可按纳秒级精度设置时间戳
-
可单独设置某一时间戳。譬如,只设置访问时间、而修改时间保持不变
-
可独立将任一时间戳设置为当前时间
-
-
使用 futimens()函数 , 需 要 先 将 文 件 打 开 , 通 过 文 件 描 述 符 进 行 操 作 , utimensat()可 以 直 接 使 用 文 件 路 径 方 式 进 行 操 作
-
#include <fcntl.h>
#include <sys/stat.h>
int futimens(int fd, const struct timespec times[2]);-
fd:文件描述符
- 使用 futimens()设置文件时间戳,需要先打开文件获取到文件描述符
-
times:将时间属性修改为该参数所指定的时间值,times 指向拥有 2 个 struct timespec 结构体类型变量的数组
-
返回值:成功返回 0;失败将返回-1,并设置 errno
-
该函数的时间戳可以按下列 4 种方式之一进行指定
-
如果 times 参数是一个空指针,也就是 NULL,则表示将访问时间和修改时间都设置为当前时间
-
如果 times 参数指向两个 struct timespec 结构体类型变量的数组,任一数组元素的 tv_nsec 字段的值设置为 UTIME_NOW,则表示相应的时间戳设置为当前时间,此时忽略相应的 tv_sec 字段
-
如果 times 参数指向两个 struct timespec 结构体类型变量的数组,任一数组元素的 tv_nsec 字段的值设置为 UTIME_OMIT,则表示相应的时间戳保持不变,此时忽略 tv_sec 字段
-
如果 times 参 数 指 向 两个 struct timespec 结 构 体类 型 变 量 的 数 组 ,且 tv_nsec 字 段 的 值 既 不是
UTIME_NOW 也不是 UTIME_OMIT,在这种情况下,相应的时间戳设置为相应的 tv_sec 和 tv_nsec
字段指定的值
-
-
使用 futimens()函数只有以下进程,可对文件时间戳进行修改
-
超级用户进程
-
在参数 times 等于 NULL 的情况下,对文件拥有写权限的进程
-
有效用户 ID 与该文件用户 ID(文件所有者)相匹配的进程
-
-
-
#include <fcntl.h>
#include <sys/stat.h>
int utimensat(int dirfd, const char *pathname, const struct timespec times[2], int flags);-
dirfd:该参数可以是一个目录的文件描述符,也可以是特殊值 AT_FDCWD;如果 pathname 参数指定的是文件的绝对路径,则此参数会被忽略
-
pathname:指定文件路径。如果 pathname 参数指定的是一个相对路径
-
dirfd 参数不等于特殊值
AT_FDCWD- 实际操作的文件路径是相对于文件描述符 dirfd 指向的目录进行解析
-
dirfd 参数等于特殊值 AT_FDCWD
- 实际操作的文件路径是相对于调用进程的当前工作目录进行解析
-
-
times:将时间属性修改为该参数所指定的时间值,times 指向拥有 2 个 struct timespec 结构体类型变量的数组
-
flags : 此参数可以为 0 , 也可以设置为 AT_SYMLINK_NOFOLLOW
- 如 果 设 置 为
AT_SYMLINK_NOFOLLOW,当 pathname 参数指定的文件是符号链接,则修改的是该符号链接的时间戳,而不是它所指向的文件
- 如 果 设 置 为
-
返回值:成功返回 0;失败返回-1、并会设置时间戳
-
7、符号链接
软链接与硬链接
-
软链接文件也是 Linux 系统下的七种文件类型之一,其作用类似于 Windows 下的快捷方式
-
软链接文件与源文件有着不同的 inode 号,所以也就是意味着它们之间有着不同的数据块
-
软链接文件的数据块中存储的是源文件的路径名,链接文件可以通过这个路径找到被链接的源文件
- 它们之间类似于一种“主从”关系,当源文件被删除之后,软链接文件依然存在,但此时它指向的是一个无效的文件路径,这种链接文件被称为悬空链接
-
inode 节点中记录的链接数并未将软链接计算在内
-
-
从使用角度来讲,软链接与硬链接没有任何区别,都与正常的文件访问方式一样,支持读、写以及执行
-
使用 ln 命令可以为一个文件创建软链接文件或硬链接文件,用法如下:
硬链接:ln 源文件 链接文件
软链接:ln -s 源文件 链接文件
关于该命令其它用法,可以查看 man 手册-
使用 ln 命令创建的两个硬链接文件与源文件都拥有相同的 inode 号
-
如果删除了硬链接文件或源文件其中之一,那文件所对应的 inode 以及文件内容在磁盘中的数据块不会被文件系统回收
-
因为inode 数据结结构中会记录文件的硬链接数,struct stat 结构体中的st_nlink 成员变量就记录了文件的链接数
-
只有当硬链接文件和源文件都被删除,inode 节点和对应的数据块才会被文件系统回收
-
-
struct stat 结构体中的st_nlink 成员变量就记录了文件的链接数,使用stat命令也可以查看
-
-
硬链接的限制
-
不能对目录创建硬链接(超级用户可以创建,但必须在底层文件系统支持的情况下)
-
硬链接通常要求链接文件和源文件位于同一文件系统中
-
-
软链接的优势
-
可以对目录创建软链接
-
可以跨越不同文件系统
-
可以对不存在的文件创建软链接
-
创建硬链接:link()
-
系统调用函数
-
#include <unistd.h>
int link(const char *oldpath, const char *newpath);-
oldpath:用于指定被链接的源文件路径,应避免 oldpath 参数指定为软链接文件,为软链接文件创建硬链接没有意义,虽然并不会报错
-
newpath:用于指定硬链接文件路径,如果 newpath 指定的文件路径已存在,则会产生错误
-
返回值:成功返回 0;失败将返回-1,并且会设置 errno
-
创建软链接:symlink()
-
系统调用函数
-
#include <unistd.h>
int symlink(const char *target, const char *linkpath);-
target:用于指定被链接的源文件路径,target 参数指定的也可以是一个软链接文件
- 如果文件不存在,那么创建的软链接将成为“悬空链接”
-
linkpath:用于指定硬链接文件路径,如果 linkpath 指定的文件路径已存在,则会产生错误
-
返回值:成功返回 0;失败将返回-1,并会设置 errno
-
读取软链接:readlink()
-
使用 read 函数之前,需要先 open 打开该文件得到文件描述符,但是调用 open 打开一个链接文件本身是不会成功的,因为打开的并不是链接文件本身、而是其指向的文件,所以不能使用 read 来读取
-
使用系统调用 readlink
-
#include <unistd.h>
ssize_t readlink(const char *pathname, char *buf, size_t bufsiz);-
pathname:需要读取的软链接文件路径。只能是软链接文件路径,不能是其它类型文件,否则调用函数将报错
-
buf:用于存放路径信息的缓冲区
-
bufsiz:读取大小,一般读取的大小需要大于链接文件数据块中存储的文件路径信息字节大小
-
返回值:失败将返回-1,并会设置 errno;成功将返回读取到的字节数
-
8、目录
目录的存储形式
-
由 inode 节点和目录块所构成,目录块当中记录了有哪些文件组织在这个目录下,记录它们的文件名以及对应的 inode 编号
-
普通文件与目录
-
普通文件由 inode 节点和数据块构成
-
目录由 inode 节点和目录块构成
-
创建和删除目录:mkdir()、rmdir()
-
#include <sys/stat.h>
#include <sys/types.h>
int mkdir(const char *pathname, mode_t mode);-
pathname:需要创建的目录路径
- 该路径名可以是相对路径,也可以是绝对路径,若指定的路径名已经存在,则调用 mkdir()将会失败
-
mode:新建目录的权限设置,设置方式与 open 函数的 mode 参数一样,最终权限为(mode & ~umask)
-
目录拥有与普通文件相同的权限位,但是其表示的含义与普通文件却有不同,权限的含义另有所指
-
目录的读权限:可列出(譬如:通过 ls 命令)目录之下的内容(即目录下有哪些文件)
-
目录的写权限:可以在目录下创建文件、删除文件
-
目录的执行权限:可访问目录下的文件,譬如对目录下的文件进行读、写、执行等操作
-
-
-
返回值:成功返回 0;失败将返回-1,并会设置 errno
-
-
#include <unistd.h>
int rmdir(const char *pathname);-
pathname
-
删除的目录对应的路径名
-
该目录必须是一个空目录,也就是该目录下只有.(当前目录)和…(上一级目录)这两个目录项
-
指定的路径名不能是软链接文件,即使该链接文件指向了一个空目录
-
-
返回值
- 成功返回 0;失败将返回-1,并会设置 errno
-
打开、读取以及关闭目录:opendir()、readdir()、closedir()
-
C 库函数
-
#include <sys/types.h>
#include <dirent.h>
DIR *opendir(const char *name);-
用于打开一个目录,并返回指向该目录的句柄,供后续操作使用
-
name:指定需要打开的目录路径名,可以是绝对路径,也可以是相对路径
-
返回值:成功将返回指向该目录的句柄,一个 DIR 指针(其实质是一个结构体指针),其作用类似于open 函数返回的文件描述符 fd,后续对该目录的操作需要使用该 DIR 指针变量;若调用失败,则返回 NULL
-
-
#include <dirent.h>
struct dirent *readdir(DIR *dirp);-
用于读取目录,获取目录下所有文件的名称以及对应 inode 号,(事实上 Linux 系统还提供了一个 readdir 系统调用)
-
dirp:目录句柄 DIR 指针
-
返回值:返回一个指向 struct dirent 结构体的指针,该结构体表示 dirp 指向的目录流中的下一个目录条目。在到达目录流的末尾或发生错误时,它返回 NULL
-
对于目录这种特殊文件来说,这里将目录块中存储的数据称为目录流,存储了一个一个的目录项(目录条目)
-
struct dirent {
ino_t d_ino; /* inode 编号 /
off_t d_off; / not an offset; see NOTES /
unsigned short d_reclen; / not an offset; see NOTES /
unsigned char d_type; / type of file; not supported by all filesystem types */
-
-
char d_name[256]; /* 文件名 */
}
- 我们需要关注 d_ino 和 d_name 两个字段即可,分别记录了文件的 inode 编号和文件名,其余字段并不是所有系统都支持
- 每次调用readdir()函数,都会从dirp所指向的目录流中读取下一条目录项(目录条目),并返回一个指向经静态分配而得的struct dirent结构体的指针。每次调用readdir()都会覆盖这个结构体。当遇到目录结尾或者出现错误时,readdir()会返回NULL。对于出错的情况,还会设置errno以表示具体的错误信息
- 使用readdir()函数返回的文件名并未按照任何特定顺序进行排序,而是按照文件在目录中出现的天然次序。这个天然次序取决于文件系统向目录添加文件时所遵循的顺序,以及在删除文件后对目录列表中空隙的填补方式。换句话说,readdir()返回的文件名顺序是由文件系统内部的管理方式决定的,而不是按照特定的排序规则。
-
#include <sys/types.h>
#include <dirent.h>
void rewinddir(DIR *dirp);-
将目录流重置为目录起点,以便对 readdir()的下一次调用将从目录列表中的
第一个文件开始 -
dirp:目录句柄
-
返回值:无返回值
-
-
#include <sys/types.h>
#include <dirent.h>
int closedir(DIR *dirp);-
关闭处于打开状态的目录,同时释放它所使用的资源
-
dirp:目录句柄
-
返回值:成功返回 0;失败将返回-1,并设置 errno
-
进程的工作目录
-
Linux 下的每一个进程都有自己的当前工作目录(current working directory),当前工作目录是该进程解析、搜索相对路径名的起点(不是以" / "斜杆开头的绝对路径)
-
运行一个进程时、其父进程的当前工作目录将被该进程所继承,成为该进程的当前工作目录
-
通过 getcwd 函数来获取进程的当前工作目录
-
系统调用
-
#include <unistd.h>
char *getcwd(char *buf, size_t size);-
buf:getcwd()将内含当前工作目录绝对路径的字符串存放在 buf 缓冲区中
-
size:缓冲区的大小,分配的缓冲区大小必须要大于字符串长度,否则调用将会失败
-
返回值:如果调用成功将返回指向 buf 的指针,失败将返回 NULL,并设置 errno
-
-
若传入的 buf 为 NULL,且 size 为 0,则 getcwd()内部会按需分配一个缓冲区,并将指向该缓冲区的指针作为函数的返回值,为了避免内存泄漏,调用者使用完之后必须调用 free()来释放这一缓冲区所占内存空间
-
-
改变当前工作目录
-
系统调用 chdir()和 fchdir()可以用于更改进程的当前工作目录
-
#include <unistd.h>
int chdir(const char *path);
int fchdir(int fd);-
path:将进程的当前工作目录更改为 path 参数指定的目录,可以是绝对路径、也可以是相对路径,指定的目录必须要存在,否则会报错
-
fd:将进程的当前工作目录更改为 fd 文件描述符所指定的目录(譬如使用 open 函数打开一个目录)
-
返回值:成功均返回 0;失败均返回-1,并设置 errno
-
-
9、删除文件与文件重命名操作
删除文件:unlink()、remove()
-
通过系统调用 unlink()或使用 C 库函数 remove()删除一个普通文件
-
#include <unistd.h>
int unlink(const char *pathname);-
用于移除/删除一个硬链接(从其父级目录下删除该目录条目)
- 删除硬链接后,硬链接数就会减少,只有当硬链接数变为0的时候文件才会被删除
-
pathname:需要删除的文件路径,可使用相对路径、也可使用绝对路径,如果 pathname 参数指定的文件不存在,则调用 unlink()失败
- 若 pathname 指定的文件为软链接文件,则删除软链接文件本身,而非软链接所指定的文件
-
返回值:成功返回 0;失败将返回-1,并设置 errno
-
-
#include <stdio.h>
int remove(const char *pathname);-
移除一个文件或空目录
-
pathname:需要删除的文件或目录路径,可以是相对路径、也可是决定路径
-
pathname 参数指定的是一个非目录文件,那么 remove()去调用 unlink(),如果 pathname 参数指定的是一个目录,那么 remove()去调用 rmdir()
-
pathname 参数指定的是一个软链接文件,则 remove()会删除链接文件本身、而非所指向的文件
-
-
返回值:成功返回 0;失败将返回-1,并设置 errno
-
文件重命名:rename()
-
rename()既可以对文件进行重命名,又可以将文件移至同一文件系统中的另一个目录下
-
调用rename()函数会将一个现有的路径名oldpath重命名为newpath参数所指定的路径名
-
rename()函数只操作目录条目,不会移动文件数据(不改变文件的inode编号,也不会移动文件数据块中存储的内容)
-
-
#include <stdio.h>
int rename(const char *oldpath, const char *newpath);-
oldpath:原文件路径
-
newpath:新文件路径
-
返回值:成功返回 0;失败将返回-1,并设置 errno
-