我们通过ls查到就是文件属性,只不过ls只显示了部分文件属性。
Table of Contents
10. link,unlink,remove,rename函数
1.涉及到的OS API
stat、fstat、lstat
umask
chmod、fchmod
chown,fchown,lchown
link,unlink,remove,ren
symlink和readlinkchdir、和getcwd
看起来很多,不过每一行都是一组,只要理解其中一个,其它的都很好理解。而且这些函数,都与我们常用的cd、ls、chmod、chown、pwd等命令息息相关
2.文件类型
2.1 文件的7种类型
Linux一切皆文件,文件一共分为7类分别是 - d c s p l b。
(1)普通文件(regular file:-
文本文件 纯二进制文件(机器码)
(2)目录文件(director file:d)
目录是一种特殊的文件,专门用于管理其它文件。
(3)字符设备文件(character special file:c)
字符设备文件,就是字符设备驱动程序,在上层的表现形式。
(4)块设备文件(block special file:b):对应块设备(如磁盘等)。
1)块设备文件,是块设备驱动程序在上层的表现形式。
2)字符设备与块设备有什么区别?
(5)FIFO(fifo:p)
管道文件,用于实现不同进程(程序)之间的通信
(6)套接字文件(socket:s)
专门用于网络通信的文件。
(7)符号连接(symbolic link:l):
其实就是一种快捷图标,背后指向了另外一个文件。
2.2 如何判断文件的类型
(1)ls查看- d c l b s p符号来区分
(2)可以使用file命令来查看
(a)如果查看的是文本文件
(b)如果你查看的是纯二进制文件(机器码)
3、获取文件属性的函数,stat、lstat、fstat
ls命令其实就是调用了这三个函数中的lstat来实现的,我们可以调用lstat函数来自己实现一个ls命令。
这三个是兄弟函数,实现的功能相同,只是略微有区别,我们只要先把stat函数搞清楚了,lstat、fstat非常容易理解。
3.1 stat
函数原型
#include <sys/types.h> #include <sys/stat.h> #include <unistd.h> int stat(const char *path, struct stat *buf);
3.2 struct stat结构体
7类文件都有的属性
专门给块设备文件用的。
专门给字符设备用的
ls的信息
(1)st_uid 和 st_gid 用户id 组的组id
(2)st_mode
(a)- :文件类型
(b)rwxrwxr-x:文件权限 (读写执行)
文件所属用户 所属组 其他人
(3)将数字形式的st_mode,打印为-rwxrwxr-x形式
文件类型
12~15 bit用于表示文件类型如何表示文件类型
![]()
· 如何取出12~15位的值,然后用于判断文件的类型
文件权限
(4)如何使用chmod命令修改文件权限(rwx)
(a)方法1:直接使用数字 chmod 777 file.txt
(a)方法2:直接使用rwx来设置
· 例子1:修改所有权限 - chmod a=rw- file.txt
· 例子2:只修改某一组的权限- chmod u=rwx,g=rw-,o=r-- file.txt
· 例子3:只修改组里面某一位的权限- chmod u+r,g+w,o+x file.txt
3.2 lstat
3.3.fstat
#include <sys/types.h> #include <sys/stat.h> #include <unistd.h> int fstat(int fd, struct stat *buf);
3.5 r w x的含义
1)x对于普通文件来说
如果普通文件存放的只是文字编码,因为文字编码无法被cpu执行,所以普通文件的x没有太大意义,所以一般的普通文件的x权限一般都是-。
2)x对于目录的意义?
我们发现目录都有x,显然目录里面放的并不是机器指令,是不能被执行的,那么x对于目录的意义何在呢?
对于目录的x来说,也被称为通过权限,也就是说,如果你的目录没有x权限,你是无法通过这个目录的。
3)x对于其它文件来说,意义不大
4. umask函数
4.0 open函数创建新文件时的一个问题
open函数创建新的文件时,如果指定的是0777满级权限的话,实际创建文件权限为0775(rwxrwxr-x)
(1)为什么不是满级权限
因为被文件权限掩码做了限制。
(2)怎么将文件权限掩码改为0
使用umask即可。
4.1 函数原型
#include <sys/types.h> #include <sys/stat.h> mode_t umask(mode_t mask);
4.2每一个进程都有一个文件权限掩码
我的程序,修改只是当前进程的文件权限掩码,对其它进程的文件权限掩码无影响。
6. 文件长度st_size
struct stat中的st_size被用来存放文件长度,但只对普通文件目录、以及符号连接文件有意义。
因为只有普通文件、目录、以及符号链接文件才有实际的数据,有数据才有文件长度
其它的文件在块设备上只存储了文件属性,它们只是挂了一个文件名,以文件的形式进行管理而已,没有实际的数据,所以对于 这些文件来说,文件大小是没有意义的。
符号链接文件的文件大小
符号链接文件就是一个快捷键,背后指向了某个文件。
符号链接文件的数据,就是所指向文件的文件名,所以它的文件大小指的就是这个名字的字符个数。
7. 文件截断函数truncate、 ftruncate
open可以指定了O_TRUNC后,文件里面有数据的话,会将打开的文件截短(清空)为0,
文件截短函数truncate,它不仅能够将文件截为0,还可以把文件截短为任意长度。
7.1 函数原型
#include <unistd.h> #include <sys/types.h> int truncate(const char *path, off_t length); int ftruncate(int fd, off_t length);
8. 空洞文件
我承诺给你一亩地,但是你又不是马上就要用满这一亩地,是一点一点来占用的,如果我现在一下子把一亩地全部你,
但是你要花费很久时间才会把地全用上,在你占满之前,一直有相当部分空间被闲置不用,显然非常浪费空间资源。
迅雷等下载文件
比如下载一个1M大小的文件,文件肯定是要花费相当长的时间才能下载完成,如果我直接就开辟一个实际占用1M空间
的普通文件来放数据的话,在实际下载完数据之前,未装满数据的空间都被闲置,会很浪费空间,怎么办呢?
解决办法就是开辟一个1M大小的空洞文件,空洞文件的理论大小是1M,但是并没有在块设备上实际给你分配1M的物理空间 而是在下载过程中,每下载一部分数据,再实际开辟一部分空间给你,直到整个文件下完位置。
如何制作空洞文件
truncate、ftrucate制作
文件截短长度 > 文件长度时,多余的部分就是空洞。
du命令:查看文件在块设备上,实际占用的物理空间。
ls查看到的只是文件的理论大小,但是空洞部分并不占用实际物理存储空间。
使用lseek制作
将文件读写位置调整到文件尾部之后,然后写点数据,中间空出的部分就是空洞。
9. 文件系统是如何管理文件的
文件系统管理文件的逻辑结构——树形结构
对于文件系统来说,目录是非常重要的文件组织节点。
文件在块设备上是如何存储的
(1)超级区
负责“块设备”空间的分配和回收。
(2)inode节点区
1)被划分为了一个个相连的,空间大小相同的inode节点空间。
2)每个节点空间被用于存放某个文件的属性信息,每个节点空间大小是固定的
3)每个节点都有一个节点编号,通过节点编号就可以索引找到inode节点空间。
(3)数据区
存储数据时,实际上并不是数据有多少个字节,就分配对应多少的字节空间给你,为了便于物理空间高效管理,往往都是按块 分配空间的,一块往往为4k字节(4*1024)
1)普通文件
2)目录文件
3)链接文件 : 存放的数据很简单,就是所指向文件的文件名。
文件系统是如何通过“文件路径名”索引找到文件
1)索引找到普通文件
fd = open("/new/xxx.txt", O_RDWR);
找到数据存放空间的起始地址后,read、write调用驱动读写数据时, 块设备驱动程序”通过这个地址,就能够实现读写。
stat("/new/xxx.txt", ...);
这个函数获取文件属性时,也是按照相同的原理来索引的,找到文件的inode节点空间后,就可以将inode节点中的文件属性读取出来。
10. link,unlink,remove,rename函数
这几个函数与ln、rm、mv命令息息相关,因为这几个命令就是调用这几个函数来实现的
10.1 link、unlink
10.1.1 硬链接
(1)ln命令创建硬链接: ln xxx.txt xxx1.txt
(2)创建硬链接,创建的是什么 创建硬链接,就是再为文件创建一个名字。
1)每创建一个硬链接,文件就多一个文件名,硬件链接数+1
从图中看出,创建硬链接后所得到的多个文件名,指向的同一个inode节点,
只有inode节点代表了文件的真实存在,
inode节点只有一个,因此多个文件名指向的是同一个文件,不管使用的是哪一个文件名,都能操作这个文件。
2)硬链接数
记录了有多少个文件名指向了inode节点,通过创建硬链接,每增加一个文件名,就多一个硬链接数。
3)删除文件后,文件数据还在吗?
还在,因为删除文件时,只是将文件的inode节点空间释放了,如果这个文件有数据的话,
那么这个文件的数据仍然还在,只要将文件的inode节点空间恢复,即可还原该文件。
4)有关目录的硬链接数
(a)为什么新建的目录一开始的文件链接数就是2
因为新创建的目录,一开始就有两个名字指向了目录的inode节点,分别是目录的本名new和.
(b)为什么在该目录下,每多创建一个目录,当前目录就会多一个硬链数
新创建目录的..名字,也指向了当前目录new。
(c)能不能使用ln命令,自己给目录创建硬链接
答:不能,Linux不允许用户自己给目录创建硬链接,只能由Linux系统自己给目录创建硬链接。
用户只能给目录以外的,其它类型的文件创建硬链接。
10.1.2 link函数
#include <unistd.h> int link(const char *oldpath, const char *newpath);
10.2 unlink函数
#include <unistd.h> int unlink(const char *pathname);
使用unlink创建临时文件
所谓临时文件就是,只在程序运行过程中有效,程序运行结束后就自动删除,这就是临时文件,
如何使用unlink创建临时文件?
- open创建一个文件后(新文件的硬链接数都是1),然后立即调用unlink将文件硬链接数减为0,将其删除。
- 虽然文件的硬链接数变成了0,但是在进程没有结束之前,这个文件仍然可以被使用,直到进程结束后,文件被删
10.3、remove函数
10.3.1函数原型
#include <stdio.h> int remove(const char *pathname);
10.3.2 为什么这个函数既能用于删除一般文件,也能用于删除目录
(1)remove是一个库函数 它封装了unlink和rmdir这两个系统函数。
(3)rm命令
这个命令既能用于删除目录,也能用于删除其它所有的文件,可以认为就是调用remove实现。
10.4、rename函数
修改文件的路径名,mv命令就是调用这个函数实现的。
函数原型
#include <stdio.h> int rename(const char *oldpath, const char *newpath);
1)修改路径名情况1:当只改路径,不改文件名字 : 这种情况其实就是移动。
如果文件移动起始位置和目标位置,在同一个分区里面的话
- 移动文件时,不会移动文件的数据,只是把文件的基本信息(名字、inode编号),
- 从这个目录记录到另一个目录下。
如果移动的起始位置和目标位置,不在同一个分区
- 既要移动数据,也要移动文件基本信息
在Linux下,分区是以目录的形式存在的,而在windows分区是以c:等盘符形式存在。
2)修改路径名情况2:不改路径,只改文件名 这种情况就是一般意义上的改名。
3)修改路径名情况3:既改路径,也改文件名
11. symlink、readlink
这两个函数与符号链接文件有关,(软链接文件)
11.1 软链接文件 (快捷图标)
11.1.1 使用ln -s就可以创建符号链接文件
11.1.2 什么是符号链接文件
符号链接文件就是一个快捷图标,它指向了另一个文件
11.1.3 符号链接 与 硬链接的对比
(1)创建硬连接
同一个文件有多个不同的名字,它们指向是同一个inode节点。
(2)创建符号链接文件
符号链接文件与它所指向的文件,是两个完全不同的独立的文件,拥有自己独立的inode节点。
符号链接文件的数据就是指向文件的文件名,文件大小就是名字的字符个数。
(3)不能给目录创建硬链接,但是可以给目录创建符号链接文
(4)可以给符号链接文件,创建硬链接吗 可以
11.2 symlink
#include <unistd.h> int symlink(const char *oldpath, const char *newpath);
11.3 readlink
#include <unistd.h> ssize_t readlink(const char *path, char *buf, size_t bufsiz);
11.4 符号跟随函数 与 符号不跟随函数
(1) 符号跟随函数
(2)符号不跟随函数
12. getcwd、chdir、mkdir、rmdir
12.1 getcwd
这是一个库函数,执行pwd命令就是调用这个函数实现的。
#include <unistd.h> char *getcwd(char *buf, size_t size);
pwd这个命令获取的是,当前终端这个进程的工作路径。
我自己的进程调用getcwd函数,获取的是我自己进程的当前工作路径,默认是你运行这个程序时所在的路径。
12.2 chdir
#include <unistd.h> int chdir(const char *path);
12.3 mkdir函数
mkdir命令调用的就是这个函数。
#include <sys/stat.h> #include <sys/types.h> int mkdir(const char *pathname, mode_t mode);
12.4 rmdir函数
rmdir和rm命令删除目录时,调用的都是rmdir这个函数。
- rmdir命令:只能删除空目录,rmdir命令用的很少
- rm:不管目录空不空,都能删除,rm用的最多
函数原型
#include <unistd.h> int rmdir(const char *pathname);
如果目录不为空,必须递归调用rmdir函数,实现递归删除。
什么是递归删除?
13. opendir、readdir
opendir:打开目录,以便调用readdir读取目录项
readdir:读取目录里面的目录项
什么是目录项?
13.1 opendir
#include <sys/types.h> #include <dirent.h> DIR *opendir(const char *name);
13.2 readdir
#include <dirent.h> struct dirent *readdir(DIR *dirp);
14. chmode、fchmod
#include <sys/stat.h> int chmod(const char *pathname, mode_t mode); int fchmod(int fd, mode_t mode);