文章目录
一、文件描述符
1、补充知识
(1)文件 = 属性 + 文件内容
(2)想要访问文件就需要先打开文件,而文件本身在磁盘中,如果想要打开文件就需要将文件加载到内存上(冯诺依曼体系结构决定的。
(3)管理文件:先描述,再组织( 就会像进程那样被管理起来),所以文件在内存中:文件内核数据结构+文件内容。
(4)linux下一切皆文件。
2、文件描述符是什么?
在进程中每打开一个文件,都会创建有相应的文件描述信息struct file,这个描述信息被添加在pcb的struct files_struct中,以数组的形式进行管理,随即向用户返回数组的下标作为文件描述符,用于操作文件,其中默认打开的三个文件标准输入(stdin)、标准输出(stdout)和标准错误(stderr)分别分配为文件描述符0、1和2 。
3、处理文件的系统调用接口
(1)open
功能:打开文件。
函数原型:
bash
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
头文件:
bash
#include <fcntl.h>
参数:
- pathname:要打开或创建的文件的路径名。这个参数是一个字符串,指定了文件的绝对路径或相对路径。
- flags:文件的打开方式,这是一个整数,由一系列标志位组成,如果需要选择多个选项可以使用位运算符或上即可。常用的标志位包括:
O_RDONLY:只读方式打开文件。
O_WRONLY:只写方式打开文件。 O_RDWR:读写方式打开文件。
O_CREAT:如果文件不存在,则创建新文件。此时需要同时指定第三个参数mode来设置新文件的权限。
O_TRUNC:如果文件已存在且为只写或读写方式打开,则将其长度截断为0。
O_APPEND:以追加方式打开文件。写入的数据会被追加到文件末尾,而不是覆盖原有数据。
其他标志位还包括O_NONBLOCK、O_SYNC等,用于控制文件的打开行为和I/O操作的同步性。- mode(可选):当flags包含O_CREAT标志时,需要指定此参数来设置新文件的权限。mode是一个整数,表示文件的访问权限和文件类型。通常使用八进制数来表示权限,如0644表示rw-r--r--。
返回值:
成功时,open接口返回一个非负整数的文件描述符。
失败时,返回-1,并设置全局变量errno以指示错误类型。
(2)write
功能:向文件写入数据。
函数原型:
bash
ssize_t write(int fd, const void *buf, size_t count);
头文件:
bash
<unistd.h>
参数:
- fd:文件描述符,标识了要写入数据的文件或设备。
- buf:指向包含要写入数据的缓冲区的指针。
- count:要写入的数据字节数。
返回值:
成功时返回实际写入的字节数,失败时返回 -1 并设置 errno。
(3)read
功能:从文件中读取数据。
函数原型:
bash
ssize_t read(int fd, void *buf, size_t count);
头文件:
bash
<unistd.h>
参数:
- fd:文件描述符,标识了要从中读取数据的文件或设备。
- buf:指向用于存储读取数据的缓冲区的指针。
- count:希望读取的数据字节数。
返回值:
成功时返回实际读取的字节数(可能小于请求的字节数,表示已到达文件末尾或读取过程中发生错误),失败时返回 -1 并设置 errno。
(4)close
功能:关闭文件。
函数原型:
bash
int close(int fd);
头文件:
c
<unistd.h>
参数:
fd:文件描述符。
返回值:
成功时返回0。
失败时返回-1,并设置errno以指示错误类型。
(5)综合使用
写文件:
c
1 #include <stdio.h>
2 #include <fcntl.h>
3 #include <unistd.h>
4 #include <string.h>
5
6 int main()
7 {
8 int fd = open("bite.txt",O_WRONLY|O_CREAT,0666); //打开
9
10 if(fd < 0)
11 return -1;
12
13 const char *s = "i like linux!";
14
15 write(fd,s,strlen(s)); //写入
16
17 close(fd); //关闭文件
18 return 0;
19 }
读文件:
c
1 #include <stdio.h>
2 #include <fcntl.h>
3 #include <unistd.h>
4 #include <string.h>
5
6 int main()
7 {
8 int fd = open("bite.txt",O_RDONLY,0666); //打开
9
10 if(fd < 0)
11 return -1;
12
13 char s[1024];
14 read(fd,s,1024); //读文件
15
16 write(1,s,strlen(s)); //向显示器输入
17
18 close(fd); //关闭文件
19 return 0;
20 }
4、文件描述符的分配规则
文件描述符分配:指针数组从开始查找第一个空缺的位置的下标。
如:关闭输入流,再打开一个文件。
c
1 #include <stdio.h>
2 #include <fcntl.h>
3 #include <unistd.h>
4 #include <string.h>
5
6 int main()
7 {
8 //关闭输出流
9 close(0);
10
11 //打开文件
12 int fd = open("test1.txt",O_WRONLY|O_CREAT,0666);
13 if(fd < 0)
14 return -1;
15
16 const char *s = "linux!";
17
18 printf("fd : %d\n%s\n",fd,s);
19
20 close(fd);//关闭文件
21 return 0;
22 }
5、简单理解Linux下一切皆文件
6、FILE
(1)补充:
- c语言的处理文件的库函数封装了系统调用(fopen对open进行了封装)。
- FILE是结构体,并且封装了文件描述符。
(2)缓冲区
文件内核缓冲区:
使用系统调用接口如write时,会将输入的内容输入到该缓冲区,等到一定时机操作系统会自动刷新如关闭文件。
语言级缓冲区(用户级缓冲区):
存在哪里?
存在FILE结构体中(使用字符数组储存),如当使用fwrite接口时,先将输入的内容存到该数组中,等到一定数组满的时候就会调用系统接口write输入到文件内核缓冲区中(全缓冲),但是如果时写入显示器文件时是行缓冲。
为什么存在缓冲区?
减少调用,提高I/O效率。
(3)刷新缓冲区接口
fflush
功能:
对于C语言中的标准I/O操作,fflush 函数可以用来刷新缓冲区。它适用于文件指针(FILE *)类型。
头文件:
c
#include <stdio.h>
函数原型:
bash
int fflush(FILE *stream);
参数:
fflush函数的参数是一个文件指针(FILE *stream),用于指定需要刷新的。
文件流。
返回值:
fflush函数的返回值是一个整数,表示函数调用的结果状态。如果成功刷新,fflush返回0;如果指定的流没有缓冲区或者只读打开,也返回0值;如果发生错误,返回EOF。
fsync
功能:
fsync 函数用于刷新文件描述符(int fd)的缓冲区,确保数据从内核缓冲区写入到磁盘。
头文件:
bash
unistd.h
函数原型:
bash
int fsync(int fd);
参数:
fsync函数的参数是一个文件描述符(int fd),表示需要同步的文件。文件描述符是一个整数,用于在程序中唯一标识一个打开的文件。
返回值:
fsync函数的返回值是一个整数,表示函数调用的结果状态。如果同步成功,fsync函数返回0;如果同步失败,返回-1,并设置errno为错误码,以便进一步诊断问题。
7、重定向
(1)向看下面例子:
c
1 #include <stdio.h>
2 #include <fcntl.h>
3 #include <unistd.h>
4 #include <string.h>
5
6 int main()
7 {
8 //关闭输出流
9 close(1);
10
11 //打开文件
12 int fd = open("test.txt",O_WRONLY|O_CREAT,0666);
13 if(fd < 0)
14 return -1;
15
16 const char *s = "linux!";
17
18 printf("fd : %d\n%s\n",fd,s); //此时不向显示器输入,向test.txt
19 fflush(stdout);//刷新缓冲区
20
21 close(fd);//关闭文件
22 return 0;
23 }
上述操作从输入到显示器文件转到输入test.txt文件中,实现了重定向,重定向操作:< 、> 、 >>。
(2)本质
原本printf想向文件描述符为1指向的file(输出流)输入的,但是现在被
test.txt替换了,从而向test.txt文件输入了。
(3) dup2系统调用
函数原型:
c
int dup2(int oldfd, int newfd);
参数
oldfd:要复制的文件描述符。
newfd:目标文件描述符。
返回值:
成功时,返回 newfd 的值。
失败时,返回 -1 并设置 errno 以指示错误。可能的错误包括: EBADF:oldfd 或newfd 不是有效的文件描述符。 EINTR:调用被信号中断。
功能:
dup2 会将 oldfd 复制到 newfd 上。 如果 newfd 已经被打开,dup2 会首先关闭它,这样可以确保没有资源泄漏。 然后,newfd 将会变成 oldfd 的一个副本,也就是说,newfd 和 oldfd 指向相同的文件表项(file table entry)。 如果 oldfd 和 newfd 是相同的,那么 dup2 什么都不做,直接返回 newfd。
使用:
c
1 #include <stdio.h>
2 #include <fcntl.h>
3 #include <unistd.h>
4 #include <string.h>
5
6 int main()
7 {
8 //打开文件
9 int fd = open("test2.txt",O_WRONLY|O_CREAT,0666);
10 if(fd < 0)
11 return -1;
12
13 dup2(fd,1); //1是输出显示器的文件描述符的值
14 const char *s = "linux!";
15
16 printf("fd : %d\n%s\n",fd,s); //此时不向显示器输入,向test2.txt
17 fflush(stdout);//刷新语言缓冲区
18
19 close(fd);//关闭文件
20 return 0;
21 }
(4)进程替换为什么不会影响重定向?
- 文件描述符的独立性:在Linux系统中,每个进程都有一个独立的文件描述符表,用于管理该进程打开的文件和相关的输入输出操作。当进程进行替换时,虽然进程的代码和数据被新程序替换,但文件描述符表(除了被明确关闭的文件描述符)通常会被保留下来。因此,之前设置的重定向操作仍然有效。
- 进程属性的保留:尽管exec函数族改变了进程的内容,但进程ID和大多数属性(如文件描述符和信号处理程序)保持不变。这种特性使得exec成为实现各种高级进程控制技巧的基础,同时也保证了重定向等文件操作在进程替换后的连续性。
- 重定向的实现方式:重定向是通过修改文件描述符表来实现的,而不是通过修改进程的代码或数据。因此,无论进程的内容如何变化(包括进程替换),只要文件描述符表保持不变或相应的文件描述符未被关闭,重定向就会继续生效。
二、文件系统
- Block Group:ext2文件系统会根据分区的大小划分为数个Block Group。而每个Block Group都有着相同的结构组成。
- 超级块(Super Block):存放文件系统本身的结构信息。记录的信息主要有:bolck 和 inode的总量, 未使用的block和inode的数量,一个block和inode的大小,最近一次挂载的时间,最近一次写入数据的 时间,最近一次检验磁盘的时间等其他文件系统的相关信息。Super Block的信息被破坏,可以说整个 文件系统结构就被破坏了
- GDT,Group Descriptor Table:块组描述符,描述块组属性信息。
- 块位图(BlockBitmap):Block Bitmap中记录着Data Block中哪个数据块已经被占用,哪个数据块没 有被占用
- inode位图(inode Bitmap):每个bit表示一个inode是否空闲可用。
- i节点表:存放文件属性 如文件大小,所有者(但是不存在文件名),最近修改时间等数据区:存放文件内容
目录文件
目录文件 = inode + datablock = 属性 + 内容,而内容储存的是文件名和inode的映射关系,所以通过路径找到文件名就能找到相应的inode了。
文件描述符和进程的关系
创建新文件过程
- 存储属性 内核先找到一个空闲的i节点(这里是263466)。内核把文件信息记录到其中。
- 存储数据 该文件需要存储在三个磁盘块,内核找到了三个空闲块:300,500,800。将内核缓冲区的第一块数据 复制到300,下一块复制到500,以此类推。
- 记录分配情况 文件内容按顺序300,500,800存放。内核在inode上的磁盘分布区记录了上述块列表。
- 添加文件名到目录 新的文件名abc。linux如何在当前的目录中记录这个文件?内核将入口(263466,abc)添加到目录文 件。文件名和inode之间的对应关系将文件名和文件的内容及属性连接起来。
三、软硬链接
1、软连接
(1)什么是软链接
软链接(也称为符号链接或symlink)是一种特殊的文件类型,它指向另一个文件或目录。软连接类似于Windows中的快捷方式。通过使用软连接,你可以在不复制文件内容的情况下,为文件或目录创建多个访问路径,软连接有独立的inode,内容保存的是目标文件的路径。
(2)建立软链接
bash
ln -s [目标文件或目录] [软链接名称]
如:
(3)删除
使用rm命令。
(4)应用场景
快捷方式。
2、硬链接
(1)什么是硬链接
硬链接(Hard Link)是一种文件链接方式,它允许你为文件系统中的同一个文件创建多个目录项(也称为文件名或入口)。这些硬连接共享相同的inode(索引节点)和数据块(不同文件名映射同一inode),因此它们实际上是同一个文件的多个访问点。
(2)建立硬链接
bash
ln [源文件] [硬连接名称]
(3)删除
使用rm命令。
(4)应用场景
备份。
3、软硬链接对比
(1)inode
软链接有独立的inode,硬链接与源文件一样。
(2)删除源文件后
软链接不能使用,硬链接可以。
(3)文件的权限
软链接的权限和多个方面有关,一般来说设置为:777,硬链接和源文件的权限一样。
(4)跨文件系统
软连接可以跨文件系统进行连接(因为保存的是文件的路径,通过路径找到源文件的inode),硬链接不可以(使用同一个inode,只能在同一个文件系统中)。
(5)目录
软链接可以链接目录文件,硬链接一般不行。