Linux:文件、fd
- 前言
- 一、C语言中常见打开文件的函数接口
- 二、打开文件的系统调用接口
- 三、文件描述符fd
- 四、为何Linux下一切皆文件
前言
文件 = 内容 + 属性 |
所有对文件的操作本质上就分为:对内容的修改
和对属性的修改
。
内容是数据,属性也是数据。所以存储文件,必须同时存储文件相关的数据信息和属性信息
。默认情况下文件存储在磁盘中,但由于冯诺依曼体系,CPU只能从内存中获取文件信息,对文件进行操作。所以当进程打开文件
时,OS需要先将文件信息加载到内存中,在被CPU调度执行对文件进行操作!!
一个进程可以打开多个文件,多个进程也可以打开同一个文件。所以当文件被加载到内存时,被打开的文件可能存在多个。操作系统需要对这些文件进行管理。先组织在描述!!
OS会在内核数据结构中,为所有被打开的文件形成一个结构体对象。最后将所有文件结构体对象通过链表的形式链接起来,从而将对文件的管理转化为对该链表的增删查改!!
文件按照是否被打开分为:被打开的文件(在内存中)、没有被打开的文件(磁盘中)。所以文件操作的本质就是:进程和打开文件的关系!!
一、C语言中常见打开文件的函数接口
C语言中,打开文件的函数接口如下:
FILE *fopen(const char *path, const char *mode);
文件打开方式:
r Open text file for reading.
The stream is positioned at the beginning of the file.
r+ Open for reading and writing.
The stream is positioned at the beginning of the file.
w Truncate(缩短) file to zero length or create text file for writing.
The stream is positioned at the beginning of the file.
w+ Open for reading and writing.
The file is created if it does not exist, otherwise it is truncated.
The stream is positioned at the beginning of the file.
a Open for appending (writing at end of file).
The file is created if it does not exist.
The stream is positioned at the end of the file.
a+ Open for reading and appending (writing at end of file).
The file is created if it does not exist. The initial file position
for reading is at the beginning of the file,
but output is always appended to the end of the file.
w
方式打开,会先清理文件内容。a
方式打开,不会清理文件内容,直接在文件内容后追加新内容。
二、打开文件的系统调用接口
进程打开文件,只能通过OS去打开文件。所以操作系统会为我们提供一些打开文件的系统调用接口!!
(也从侧面说明C库函数一定封装了这些系统调用接口)
下面我们来看看操作系统都提供了哪些系统调用接口吧!
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
int creat(const char *pathname, mode_t mode);//不介绍, 和open类似,但只能以w方式打开
-
返回值int是文件描述符fd,类似于C语言中的FILE(真实情况是FILE中封装了fd)。创建失败时,返回-1。
-
flags:打开文件操作时选择的标记位。正常情况下,标记位为两态的数据,即0、1。所以我们可以通过flag整型的不同比特位表示不同的标记位。最后通过按位或的方式,将所有选择的标记位结合起来。从而通过一个变量传递多个标记位!!
常见标记位:
O_RDONLY//只读方式打开
O_WRONLY//写的方式打开
O_RDWR//读写方式打开
O_CREAT//文件不存在,创建文件
O_TRUNC//截断清空,主要用于只读方式打开时,需要先将文件内容清空
O_APPEND//追加的方式打开
- mode:新创建的文件默认要受Linux权限控制。如果没有指定权限,新创建的文件权限发生乱码!
int fd = open("log.txt", O_CREAT | O_WRONLY | O_TRUNC);
if(fd < 0)
{
perror("open");
return 1;
}
close(fd);
【创建文件权限乱码】:
【正确创建方式】:
int fd = open("log.txt", O_CREAT | O_WRONLY | O_TRUNC, 0666);
if(fd < 0)
{
perror("open");
return 1;
}
close(fd);
- 文件以666权限创建,最终结果为664。是由于文件掩码的存在,也可通过
umask
系统调用接口更改文件创建的权限掩码。但不推荐,通常和系统默认掩码保持一致!!
三、文件描述符fd
普通情况下,文件描述符是从3开始依次递增
的。Linux进程默认情况下会有3个缺省打开的文件描述符,分别是标准输入0, 标准输出1, 标准错误2。0,1,2对应的物理设备一般是:键盘,显示器,显示器。
那文件描述符fd这的数字都表示什么?
操作系统需要对文件进行管理,在Linux中,OS会为每一个被打开的文件形成对应的描述结构体对象(Linux中被称为struct file)并且操作系统会将所有被打开文件的描述结构体对象通过链表形式链接起来。
但每一个进程可能打开多个文件,多个进程可能打开同一个文件。并且操作系统不希望每个管理模块间耦合度过大。所以操作系统为了让进程和对应的打开文件关联起来,操作系统会在进程的PCB中维护一个结构体指针(struct files_struct*
),该指针指向一张进程文件描述符表(files_strict),该表中包含一个指针数组,每个元素都是指向被打开文件的函数指针。因此,文件描述符表本质上就是该数组中指向对应文件的元素下标!!!
四、为何Linux下一切皆文件
计算机所有的底层硬件的读写方式都是不一样的,但是操作系统为了管理这些硬件也会和文件一样,生成对应的描述结构体对象(struct file)。而在所有的文件结构体中,都包含文件的读指针(read*)、写指针(write*)。对不同硬件的读写,都可以通过结构体对象中的读指针和写指针指向底层不同的硬件各自读写方式。
&emap;尽管不同硬件的读写方式不同,但操作系统为了管理会为我们抽象一层软件层。对于上层用户来说,不管是对硬件还是文件的读写操作,本质上都和控制文件一样,调用struct file
结构体对象中的读写指针。所以Linux下一切皆文件!!!
我们也将该软件层称为文件虚拟系统(VFS)。这是是多态技术的来源之一!!