【Linux课程学习】:文件第一弹---文件基础(文件描述符的底层设计)

🎁个人主页:我们的五年

🔍系列专栏:Linux课程学习

🌷追光的人,终会万丈光芒

🎉欢迎大家点赞👍评论📝收藏⭐文章

Linux学习笔记:

https://blog.csdn.net/djdjiejsn/category_12669243.html

前言:

这篇主要谈论的是关于文件的基础,让我们在接下来能更好的理解文件描述符,系统调用的相关的文件函数,和语言层封装的f系列的函数。对于这篇文章,会从C语言文件接口,C语言默认打开的三个输入输出流,认识系统文件调用接口,这几个方面介绍文件,打好基础。


目录

本节重点知识点:

一.预备知识

1.1文件=内容+属性:

1.2在访问文件之前,必须先打开它:

1.2.1但是为什么要打开文件呢?

1.3管理文件---先描述,在组织

二.C语言文件操作函数

2.1文件打开的方式:

2.2输出信息到显示器的方法:

三.进程默认打开的三个标准流

四.系统调用接口---系统文件I/O

4.1open系统调用

4.1.1函数原型:

4.1.2函数理解:

五.文件描述符fd

5.1见一见其他文件的文件描述符fd:

5.2stdin,stdout,stderror的文件描述符:

5.3文件描述符的底层设计:

5.3.1文件管理:

5.3.2进程中的文件描述指针的数组:

5.3.3每个进程都会有文件描述符表:


本节重点知识点:

1.fopen和fclose属于运行时操作。

2.深刻理解先描述,再组织。管理对象时,就要先进行描述。

3.理解一切皆文件,硬件设备对于进程来说也是文件。

4.文件描述符的底层设计--->进程与文件是怎么进行关联的。

一.预备知识

|-----------|--------|
| 文件的分类 | 位置 |
| 被打开的文件 | 内存 |
| 没有被打开的文件 | 磁盘 |

在文件中,没有被打开的文件比被打开的文件多的多。下面我们研究的被打开的文件。研究被打开的文件,是在研究文件与进程的关系,但是进程会描述文件,所以我们研究的是文件与file_struct的关系

1.1文件=内容+属性:

对于我们创建的空文件,也是有大小的。虽然文件没有内容,但是文件有属性,在文件中要保存这些属性,就会有大小。

1.2在访问文件之前,必须先打开它:

在C语言中,我们很明显的感受到,在进行文件操作之前,必须打开文件(fopen)。然后进行一系列的操作,最后关闭文件(fclose)。

FILE* fd=fopen("log.txt","w");
fclose(fd);

1.2.1但是为什么要打开文件呢?

因为文件一开始在磁盘中保存着,要打开文件,也就是把文件加载到内存中,才能对文件进行操作。fopen是运行时操作,当被运行到fopen这一行代码,才被进程所打开。

所以文件被打开时,是进程在进行访问。

但是进程在内存中,CPU要想访问文件,因为冯诺依曼体系,CPU不能直接去磁盘进行访问,所以只能把文件加载到内存,才能被CPU访问。

1.3管理文件---先描述,在组织

凡是要进行管理的,就先要对管理的对象进行描述(struct),描述文件的属性。然后才能对文件进行管理。文件描述的一些属性,可以由文件的属性获得,还有其他很多属性要OS进行描述。


二.C语言文件操作函数

从C语言的角度看文件,和文件接口。有一个预备的知识。也为后面语言层面的封装与系统接口进行区分和比较。

2.1文件打开的方式:

|--------|----|---------------------------|
| 文件打开方式 | 功能 | 效果 |
| w | 写 | 文件不存在,新建文件。文件存在,清空文件。 |
| w+ | 读写 | 文件不存在,新建文件。文件存在,清空文件。 |
| r | 读 | 读取存在的文件,文件不存在,报错。 |
| r+ | 读写 | 读写存在的文件,文件不存在,报错。 |
| a | 追加 | 在文件末尾追加,文件不存在,新建文件。 |
| a+ | 读写 | 在文件末尾读写,文件不存在,新建文件。 |

另外还可以增加b,比如wb,ab,rb。有b的,就是对二进制文件进行操作。其他的效果和上面类似,也是读(read),写(write),追加(append)。如果我们使用w方式,如果文件存在,会对文件进行清空(truncate)。

当我们输入重定向的时候,要以w方式打开文件,如果没有输入什么东西,文件就被我们清空了。

 > log.txt

2.2输出信息到显示器的方法:

int printf(const char *format, ...);

int fprintf(FILE *stream, const char *format, ...);

fprintf的用法和printf差不多,只需要在前面指明文件对象,就能向指定的文件进行打印。

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

size_t fwrite(const void *ptr, size_t size, ize_t nmemb, FILE *stream);

#include <stdio.h>    
#include <string.h>    
    
int main()    
{    
    FILE* fd=fopen("log.txt","w");    
    if(fd==NULL)    
    {    
        printf("open error!\n");    
    }    
    fprintf(stdout,"hello fprintf!\n");    
    
    const char* message="hello fwrite!\n";    
    fwrite(message,strlen(message),1,stdout);       
    fclose(fd);    
} 

三.进程默认打开的三个标准流

在下面我们会讲解如何理解键盘,显示器这些硬件也是文件。即一切皆文件

|----------|------|-----|
| stdin | 标准输入 | 键盘 |
| stdout | 标准输出 | 显示器 |
| stderror | 标准错误 | 显示器 |


四.系统调用接口---系统文件I/O

我们使用的C语言文件操作函数,底层是封装了系统类的文件调用。比如C语言的w方式打开,可能是在封装open,并且使用O_WRONLY | O_CREAT | O_TRUNC。mode设置为0666。

4.1open系统调用

4.1.1函数原型:

头文件:

#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);

4.1.2函数理解:

pathname:

和C语言的的fopen一样,也是文件的名称,是要打开或者创建的文件,名称。

flags:

是标记位,是int类型,表示要以什么方式进行打开,其本质是位图的思想。某个比特位上有1就表示一个功能。后面我们要使用多个功能时,是需要把代表不同功能的整数,进行按位或就可以达到目的。

flags分类有:

|----------|--------|
| O_RDONLY | 只读方式打开 |
| O_WRONLY | 只写方式打开 |
| O_WDWR | 读写方式打开 |

上面的这三个参数,必须要有一个,并且只能有一个,要不就是只读,要不就是只写,要不就是读写。

|----------|--------------------------|
| O_CREAT | 如果文件不存在,创建文件。需要使用mode函数。 |
| O_APPEND | 以追加的方式进行写。 |
| O_TRUNC | 对文件进行清空。 |

返回值:

如果打开成功,就返回新打开的文件描述符。

如果失败就返回-1。

mode选项的函数,表示新创建文件的起始权限。文件的起始权限一般是0666,目录的起始权限一般是0777。所以我们在有O_CREAT时,应该加上对应的权限。但是最终的权限会&(~umask)。也可以设置文件的umask,最终可以让权限是0666。系统有umask,进程也有umask,最终使用的umaks是就近原则

int ft=open("log.txt",O_WRONLY|O_TRUNC);

五.文件描述符fd

5.1见一见其他文件的文件描述符fd:

文件描述符是整数。让我们看看打开其他文件时的文件描述符是多少?

#include <stdio.h>    
#include <string.h>    
#include <sys/types.h>    
#include <sys/stat.h>    
#include <fcntl.h>    
#include <unistd.h>        
    
int main()    
{    
    
    int fd1=open("log1.txt",O_WRONLY|O_TRUNC|O_CREAT);                                                                                                                                                             
    int fd2=open("log2.txt",O_WRONLY|O_TRUNC|O_CREAT);    
    printf("fd1:%d\n",fd1);    
    printf("fd2:%d\n",fd2);    

    close(fd1);    
    close(fd2);    
    return 0;
}

我们发现文件描述符fd不是从0开始的,而是从3开始的,为什么呢?

5.2stdin,stdout,stderror的文件描述符:

|----------|-------|-----|
| 名称 | 文件描述符 | 硬件 |
| stdin | 0 | 键盘 |
| stdout | 1 | 显示器 |
| stderror | 2 | 显示器 |

在打开我们的文件时,进程已经帮我们打开这个三个文件。这三个文件的文件描述符是0,1,2。所以我们打开其他文件的时候,就只能从3开始了。在后面的文件描述符的分配规则是占据最小空的元素。

5.3文件描述符的底层设计:

我们观察文件描述符是从0开始的,这很像数组。就可能存在一个表结构,让进程与文件管理起来。

5.3.1文件管理:

被打开的文件,会加载到内存中,加载之前就会对文件进行先描述,然后再进行组织。最后在内存中就会有很多的file_struct,他们之间的联系用某种数据结构进行关联起来。最后形成了一张file list的表。

5.3.2进程中的文件描述指针的数组:

在进程中,会存在一个文件描述指针数组的指针。这个指针指向是一个文件描述指针数组。这个数组从0开始。这个数组的第一个元素,也就是0号下标,指向的是stdin文件。

第二个元素,指向的是stdout文件。第三个元素就是指向就是stderror。

其他被打开的文件被串在file list中。如果被该进程打开,就会把这个文件的描述信息的指针放在文件描述符表中。

关闭文件本质就是在文件描述符表中删除某个文件的指针。

打开一个文件本质就是把文件指针放在文件描述符表中。

这样就可以让进程与文件关联起来。

5.3.3每个进程都会有文件描述符表:

每个进程中的文件描述符表中的元素都是指向文件的。应该文件可以被多个进程指向。


相关推荐
Lbs_gemini06031 小时前
C++研发笔记14——C语言程序设计初阶学习笔记12
c语言·开发语言·c++·笔记·学习
MC何失眠1 小时前
vulnhub靶场【哈利波特】三部曲之Fawkes
网络·python·学习·网络安全
Curtis09801 小时前
RHCE——Ubuntu Linux操作系统
linux·服务器·ubuntu
MarisolHu2 小时前
前端学习笔记-Vue篇-02
前端·vue.js·笔记·学习
青瓷看世界2 小时前
华为HarmonyOS 让应用快速拥有账号能力 -- 1 华为账号一键登录
服务器·华为·harmonyos·应用账号能力
我的老子姓彭3 小时前
C++学习笔记
c++·笔记·学习
YRr YRr4 小时前
在Ubuntu 20.04和ROS中使用RViz进行数据可视化:详解Fixed Frame参数的选择与应用
linux·ubuntu·信息可视化
Hansel_Zhang4 小时前
CentOS 7 docker部署jar包
linux·docker·centos
搬砖的果果5 小时前
HTTP代理有那些常见的安全协议?
服务器·python·网络协议·tcp/ip
炸毛的飞鼠5 小时前
PWN的简单了解
笔记·学习