文章目录
文件在块设备上存储时,磁盘大致被分为三部分:
-
超级区
负责"块设备"空间的分配和回收,记录块设备空间哪里用了哪里没用。 -
inode节点区
- 被划分为了一个个相连的,空间大小相同的inode节点空间。
- 每个节点空间被用于存放某个文件的属性信息,每个节点空间大小是固定的比如一般的是512字节
- 每个节点都有一个节点编号,通过节点编号就可以索引找到inode节点空间。
使用stat读取文件属性时,struct stat结构体中的st_dev成员,就是用来存储inode节点编号的。
-
数据区
数据区专门用于存放文件的数据。存储数据时,实际上并不是数据有多少个字节,就分配对应多少的字节空间给你,为了便于助理空间高效管理,往往都是按块分配空间的,一块往往为4k字节(4*1024) 。
文件中的数据小于一块时,还是给你分配一块,当数据超过一块时,会给你再分配一块,当这一块又满了时,再给你分配一块。不同的数据块之间不一定是连续的,块之间使用地址进行相互链接,也即是说每一块都有存放前后块的地址,通过地址就可以找到前后块空间。
- 目录文件
目录文件的数据量小,所以一般就只有一块数据。目录文件里面放的内容,并不是目录所包含文件的数据,放的只是所包含文件的基本信息,其中两个信息最重要。文件名和文件的inode节点号 - 链接文件
存放的数据很简单,就是所指向文件的文件名。
- 目录文件
硬链接
每创建一个硬链接,文件就多一个文件名,硬件链接数+1
创建硬链接后所得到的多个文件名,指向的同一个inode节点,只有inode节点代表了文件的真实存在,inode节点只有一个,因此多个文件名指向的是同一个文件,不管使用的是哪一个文件名,都能操作这个文件。
硬链接数
记录了有多少个文件名指向这个inode节点,通过创建硬链接,每增加一个文件名,就多一个硬链接数。
同理,每删除一个一个硬链接,也就是删除一个文件名,就少一个硬链接数,当然硬链接数被减为0时,也就代表着文件名被删除完了,这个文件也就被删除了。
不过这里需要主要一点,当硬链接数被减为了0时,如果还有进程在操作这个文件的话,这个文件还会一直存在,直到进程结束后,这个文件才被删除。
删除文件后,文件数据还在,因为删除文件时,只是将文件的inode节点空间释放了,如果这个文件的数据没有被覆盖的话,只要将文件的inode节点空间恢复,即可还原该文件。
-
为什么新建的目录一开始的文件链接数就是2?
因为新创建的目录,一开始就有两个名字指向了目录的inode节点,分别是目录的本名和.
-
为什么在该目录下,每多创建一个目录,当前目录就会多一个硬链数?
新创建目录的..
名字,也指向了当前目录。 -
Linux不允许用户自己给目录创建硬链接,只能由Linux系统自己给目录创建硬链接。用户只能给目录以外的,其它类型的文件创建硬链接。
link与unlink
#include <unistd.h>
int link(const char *oldpath,const char *newplath);//原有路径名->新路径名
int unlink (const char *pathname) ;//pathname 删除硬链接的路径名
可以使用unlink
创建临时文件,先用open
创建一个文件,然后使用unlink
删除文件的硬链接,在进程结束前还可以使用这个文件,进程结束后文件就删除了。
remove
#include <stdio.h>
int remove (const char *pathname) ;
功能:可以用于删除任何文件(既可以删除目录文件,也可以删除非目录文件)。删除非目录文件时,功能与unlink一样。
remove是一个库函数。它封装了unlink和rmdir这两个系统函数。remove会自动检测文件类型,如果是目录就调用rmdir删除,如果其它文件,就调用unlink删除。
rename
#include <stdio.h>
int rename(const char *oldpath,const char *newpath);
功能:修改文件路径名,将旧的路径名oldpath,改为新的路径名newpath。
mv就是调用rename函数实现的,看起来功能有三个(改名、移动、移动+改名),其实功能就一个,修改文件的路径名。
符号链接
符号链接文件也被称为软链接文件。
使用ln -s就可以创建符号链接文件。
符号链接与硬链接的对比
- 创建硬连接
同一个文件有多个不同的名字,它们指向是同一个inode节点。 - 创建符号链接文件
符号链接文件与它所指向的文件,是两个完全不同的独立的文件,拥有自己独立的inode节点。符号链接文件的数据就是指向文件的文件名,文件大小就是名字的字符个数。 - 不能给目录创建硬链接,但是可以给目录创建符号链接
symlink
#include <unistd.h>
int symlink(const char *oldpath,const char *newpath);
功能:为oldpath,创建符号连接文件newpath。
使用ln创建硬链接时,调用的是link函数。
使用ln -s创建符号链接时,调用的是symlink,sym就是符号的意思。
readlink
#include <unistd.h>
ssize_t readlink(const char *path,char *buf,size_t bufsiz);
- 功能:读符号链接文件的数据(指向文件的名字)。readlink命令,就是调用这个函数实现的。
- 参数
const char *path
:符号连接文件的路径名。char *buf
:存放名字的缓存。size t bufsiz
:缓存的大小
- 返回值:调用成功,返回读到的字节数,失败返回-1,errno被设置
目录操作函数
getcwd
include <unistd.h>
char *getcwd(char *buf, size_t size) ;
char *getwd(char *buf);
- 功能:获取进程的当前工作目录
- 参数
buf
:存放获取到的当前路径的缓存size
:缓存的大小
- 返回值:成功返回指向字符串的指针,字符串存放当前工作目录,失败返回NULL,erno被设置。
chdir
#include <unistd.h>
int chdir(const char *path);
int fchdir(int fd) ;
切换进程当前工作目录到path
或文件描述符fd
指向的路径
mkdir
#include <sys/stat.h>
#include <sys/types.h>
int mkdir(const char *pathname,mode_t mode);
- 功能:创建新目录。
- 参数
pathname
:需创建目录的路径名mode
:指定目录的原始权限,一般给0775
给目录指定原始权限时,一定要有x权限,否者无法进入这个目录。
- 返回值:调用成功返回0,失败返回-1,errno被设置
rmdir
#include <unistd.h>
int rmdir(const char *pathname) ;
- 函数功能:删除路径名为pathname的这个目录,必须是空目录,如果目录不空,则需递归删除。
不管是我们讲那个函数,在指定路径名时,可以是相对路径,也可以是绝对路径。
删除时,Linux系统会调用相关函数,将目录硬链数全部减位0,然后目录就被删除了。 - 函数返回值:调用成功返回0,失败返回-1,errno被设置
opendir
#include <sys/types.h>
#include <dirent.h>
DIR *opendir(const char *name);
DIR *fdopendir(int fd);
-
功能:打开目录,不能使用open函数打开目录,只能使用opendir打开。
-
参数:
name:需打开目录的路径名
fd:文件描述符 -
返回值
调用成功:返回一个DIR *
的指针,这个指针指向了被打开的目录,readir通过这个指针就可以读取目录的目录项。
调用失败:返回NULL,errno被设置。
readdir
#include <dirent.h>
struct dirent *readdir(DIR *dirp) ;
-
功能:读取目录里的目录项。每调用一次,就读取出一条目录项。
-
参数
dirp
: opendir打开目录时,得到的指针。 -
返回值:调用成功,返回指针指向
struct dirent
结构体的指针。这个结构体其实是定义在了<dirent.h>
头文件中。这个结构体就是用来存放一条目录项的,调用readdir读取到目录项后,会自动开辟一个struct dirent变量来存放目录项,然后将变量的指针返回,应用程序通过这个指针,就可以访问结构体中的目录项信息(文件基本信息)。
struct dirent { ino_t d_ino;/*i节点编号*/ off_t d_off;/*地址偏移*/ unsigned short d_reclen;/*本条目录项的大小*/ unsigned chard_type;/*文件类型,不是所有系统都支持*/ char d_name [256]; /*文件名字*/ };
返回NULL的话有如下两种情况:
1)读到目录的末尾时,返回NULL。
2)函数调用失败时,也返回NULL,不过errno被设置。
怎么判断函数是否调用失败了呢,如果ernno==0,表示没有设置错误号,返回NULL是因为读到了文件的末尾。如果errno!=0,表示是因为函数调用出错而返回的NULL。
int main(void){
DIR *dirp = NULL;
dirp = opendir(".");
if(NULL == dirp)
{
perror("opendir fail");
exit(-1);
}
while(1){
struct dirent *direntp = NULL;
direntp = readdir(dirp);
if(direntp == NULL && errno!=0)//调用失败
{
perror( "readdir fail");
exit(-i);
}
printf("inodeID=%d,fnane=%s\n",direntp->d_ino,direntp->d_name);
if(direntp == NULL && errno == 0) break;//读到目录末尾
}
}
chmod
#include <sys/stat.h>
int chmod(const char *pathname, mode_t mode);
int fchmod(int fd, mode_t mode);
- 功能:修改文件权限,比如将权限指定为0664等。
chmod命令就是调用这两个函数来实现的。
chmod:使用路径名操作
fchmod:使用文件描述符操作