Linux - 文件描述符 | 文件系统 | 软硬链接

文章目录


一、文件描述符

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>

参数:

  1. pathname:要打开或创建的文件的路径名。这个参数是一个字符串,指定了文件的绝对路径或相对路径。
  2. flags:文件的打开方式,这是一个整数,由一系列标志位组成,如果需要选择多个选项可以使用位运算符或上即可。常用的标志位包括:
    O_RDONLY:只读方式打开文件。
    O_WRONLY:只写方式打开文件。 O_RDWR:读写方式打开文件。
    O_CREAT:如果文件不存在,则创建新文件。此时需要同时指定第三个参数mode来设置新文件的权限。
    O_TRUNC:如果文件已存在且为只写或读写方式打开,则将其长度截断为0。
    O_APPEND:以追加方式打开文件。写入的数据会被追加到文件末尾,而不是覆盖原有数据。
    其他标志位还包括O_NONBLOCK、O_SYNC等,用于控制文件的打开行为和I/O操作的同步性。
  3. 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> 

参数:

  1. fd:文件描述符,标识了要写入数据的文件或设备。
  2. buf:指向包含要写入数据的缓冲区的指针。
  3. count:要写入的数据字节数。

返回值:

成功时返回实际写入的字节数,失败时返回 -1 并设置 errno。

(3)read

功能:从文件中读取数据。

函数原型:

bash 复制代码
ssize_t read(int fd, void *buf, size_t count);

头文件:

bash 复制代码
<unistd.h> 

参数:

  1. fd:文件描述符,标识了要从中读取数据的文件或设备。
  2. buf:指向用于存储读取数据的缓冲区的指针。
  3. 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)补充:

  1. c语言的处理文件的库函数封装了系统调用(fopen对open进行了封装)。
  2. 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)进程替换为什么不会影响重定向?

  1. 文件描述符的独立性:在Linux系统中,每个进程都有一个独立的文件描述符表,用于管理该进程打开的文件和相关的输入输出操作。当进程进行替换时,虽然进程的代码和数据被新程序替换,但文件描述符表(除了被明确关闭的文件描述符)通常会被保留下来。因此,之前设置的重定向操作仍然有效。
  2. 进程属性的保留:尽管exec函数族改变了进程的内容,但进程ID和大多数属性(如文件描述符和信号处理程序)保持不变。这种特性使得exec成为实现各种高级进程控制技巧的基础,同时也保证了重定向等文件操作在进程替换后的连续性。
  3. 重定向的实现方式:重定向是通过修改文件描述符表来实现的,而不是通过修改进程的代码或数据。因此,无论进程的内容如何变化(包括进程替换),只要文件描述符表保持不变或相应的文件描述符未被关闭,重定向就会继续生效。

二、文件系统

  1. Block Group:ext2文件系统会根据分区的大小划分为数个Block Group。而每个Block Group都有着相同的结构组成。
  2. 超级块(Super Block):存放文件系统本身的结构信息。记录的信息主要有:bolck 和 inode的总量, 未使用的block和inode的数量,一个block和inode的大小,最近一次挂载的时间,最近一次写入数据的 时间,最近一次检验磁盘的时间等其他文件系统的相关信息。Super Block的信息被破坏,可以说整个 文件系统结构就被破坏了
  3. GDT,Group Descriptor Table:块组描述符,描述块组属性信息。
  4. 块位图(BlockBitmap):Block Bitmap中记录着Data Block中哪个数据块已经被占用,哪个数据块没 有被占用
  5. inode位图(inode Bitmap):每个bit表示一个inode是否空闲可用。
  6. i节点表:存放文件属性 如文件大小,所有者(但是不存在文件名),最近修改时间等数据区:存放文件内容

目录文件

目录文件 = inode + datablock = 属性 + 内容,而内容储存的是文件名和inode的映射关系,所以通过路径找到文件名就能找到相应的inode了。

文件描述符和进程的关系

创建新文件过程

  1. 存储属性 内核先找到一个空闲的i节点(这里是263466)。内核把文件信息记录到其中。
  2. 存储数据 该文件需要存储在三个磁盘块,内核找到了三个空闲块:300,500,800。将内核缓冲区的第一块数据 复制到300,下一块复制到500,以此类推。
  3. 记录分配情况 文件内容按顺序300,500,800存放。内核在inode上的磁盘分布区记录了上述块列表。
  4. 添加文件名到目录 新的文件名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)目录

软链接可以链接目录文件,硬链接一般不行。

相关推荐
wangjing_05222 小时前
C语言练习.if.else语句.strstr
c语言·开发语言
南暮思鸢2 小时前
应急响应靶机——linux1
linux·运维·网络安全·centos·write up·应急响应靶机·蓝队溯源
weixin_414321982 小时前
Linux 编译Ubuntu24内核
linux·运维·服务器
xiaozhiwise4 小时前
Makefile 之 join
linux
maknul4 小时前
【学习笔记】AD智能PDF导出(装配文件)
笔记·学习·pdf
时光の尘5 小时前
C语言菜鸟入门·关键字·int的用法
c语言·开发语言·数据结构·c++·单片机·链表·c
pq113_65 小时前
ftdi_sio应用学习笔记 4 - I2C
笔记·学习·linux驱动·ftdi_sio
Edward-tan5 小时前
c语言数据结构与算法--简单实现线性表(顺序表+链表)的插入与删除
c语言·开发语言·链表
武昌库里写JAVA5 小时前
一文读懂Redis6的--bigkeys选项源码以及redis-bigkey-online项目介绍
c语言·开发语言·数据结构·算法·二维数组
儿时可乖了5 小时前
Linux 定时任务全解析
linux·oracle