<Linux>基础IO_文件描述符

目录

一、预备知识

为什么没有听系统过文件接口?

Linux认为一切皆文件

现在我们谈谈什么叫文件?

二、复习C接口

什么叫做当前路径?

直接使用系统接口

int open(const char *pathname, int flags, mode_t mode);

int close(int fd);

ssize_t read(int fd, void *buf, size_t count);

 ssize_t write(int fd, const void *buf, size_t count);

分析系统接口的细节,引入fd(文件描述符)

周边文件(fd的理解,fd和FILE关系,fd分配规则,fd和重定向,缓冲区?.....)

那么fd到底是什么呢?


一、预备知识

  • 文件 = 内容 + 属性
  • 对文件的操作类别:对内容,对属性
  • 文件存放在磁盘中,如果我们想要访问文件需要经过:写代码->编译->生成.exe文件->运行->访问文件,这些过程,那么有一个问题,本质到底是谁在访问文件呢?

答案:是进程在访问文件 

<Linux>基础IO_文件描述符_第1张图片

如果我们想要访问文件,那么本质就是需要我们去操作硬件,去实现硬件的IO操作,但是我们用户是没有权限去直接操作硬件的,我们只能通过操作系统提供的系统接口,通过这些接口去告诉操作系统我们想要做什么,从而间接的访问硬件进行文件的写入等操作。

那么用户去请求是通过什么方式去向操作系统沟通的呢?是程序吗?显然不是的,程序在我们写的时候,是写在磁盘上的,在程序没有运行起来时,就是一堆没有用的数据,只有我们将程序生成的可执行程序运行起来的时候,OS会在内存中创建一个进程,这个进程是动起来的,而我们写的程序是在进程创建成功后才有效,所以本质是进程通过去调用系统接口去访问的文件

为什么没有听系统过文件接口?

上面我们会涉及文件类的系统调用接口,但是我们之前学习语言的时候,是没有听过这些接口的?为什么呢?

  1. 系统调用接口比较难
  2. 跨平台性

比较:语言对这些系统接口进行了封装,为了让接口更好用,但同时也导致了不同的语言,有不同语言级别的文件访问方式,但是封装的都是系统接口,所以我们为什么要学习操作系统层面的文件系统接口?因为这样的OS接口只有一套!!

跨平台性:如果语言不提供对文件的系统接口封装,那么所有的文件操作就都必须只能使用系统接口,但是这样的接口只适用于一种操作系统,一旦使用操作系统编写代码,那么这个进程就无法在其他平台正常运行了!语言是如何实现跨平台性的呢?很简单粗暴,直接将所有平台的代码都实现了一遍,再用条件编译,动态裁剪;

显示器是硬件吗?

printf向显示器打印的时候,为什么没有觉得奇怪呢?其实向显示器打印的过程也是一种写入,和磁盘写入到文件没有本质的区别


Linux认为一切皆文件

我们可以感性的认识一下:

曾经的文件:read、write

显示器:printf、cout -> 本质是一种write

键盘:scanf、cin -> 本质一种read

现在我们谈谈什么叫文件?

站在系统的角度,能够input读取,或者能够被output写入的设备就叫做文件!

狭义文件:普通的磁盘文件;

广义文件:磁盘、显示器、网卡、显卡、....几乎所有的外设,都可以称之为文件!    

二、复习C接口

  • 什么叫做当前路径?

进程运行时,会记录当前的工作目录!

  • 直接使用系统接口

C库函数:fopen、fread、fwrite、fclose

系统调用:open、read、write、close

C文件函数:(54条消息) 【C语言】文件操作详解_绅士·永的博客-CSDN博客

系统调用:

int open(const char *pathname, int flags, mode_t mode);

当 open() 调用 成功, 它会 返回 一个 新的 文件描述符 (永远取未用 描述符的 最小值).  这个调用 创建 一个 新的 打开文件, 即 分配 一个 新的 独一无 二的 文件描述符, 不会与 运行中的 任何 其他程序 共享 (但 可以 通过 fork (2) 系统调用 实现  共享)

flags标志位:

如何给函数传递标志位!如果学过STM32固件库的GPIO_Pin_x的初始化操作,其实就是那个参数传递的过程;

O_WRONLY:文件只写

O_RDONLY:文件只读

O_TRUNC: 清空文件内容        

O_APPEND:文件  以  追加 模式 打开

O_CREAT:若文件 不存在 将 创建 一个 新 文件

mode:权限设置,普通文件设置为0666;

//以读的方式打开文件,并且清空文件
int fd = open("log.txt", O_WRONLY | O_TRUNC | O_CREAT, 0666);
 15     if (fd < 0)
 16     {
 17         perror("open");
 18         return 1;
 19     }

int close(int fd);

关闭  一个  文件  描述符 , 使它 不在 指向 任何 文件 和 可以 在 新的 文件 操作 中 被 再次 使用.  

   int fd = open("log.txt", O_WRONLY | O_TRUNC | O_CREAT, 0666);
 15     if (fd < 0)
 16     {
 17         perror("open");
 18         return 1;
 19     }
 20 
 21     dup2(fd, 1);//这里做了一个重定向的操作,可以不用管它
 22     fprintf(stdout, "%s\n", argv[1]); //stdout -> 1 -> 显示器
 23 
 24     close(fd);//关闭文件

ssize_t read(int fd, void *buf, size_t count);

从文件描述符 fd 中读取 count 字节的数据并放入从 buf 开始的缓冲区中.

如果 count 为零,read()返回0,不执行其他任何操作.  如果 count 大于SSIZE_MAX,那么结果将不可预料.

  
 33     char buffer[64];
 34     memset(buffer, '\0', sizeof(buffer));
 35     read(fd, buffer, sizeof(buffer));//将文件的内容读取到buffer中去

 ssize_t write(int fd, const void *buf, size_t count);

向文件描述符    fd    所引用的文件中写入   从   buf   开始的缓冲区中   count   字节的数据

总结:我们在应用层看到的一个简单的动作,在系统接口层面甚至可能OS层面,可能做了非常多的事情

  • 分析系统接口的细节,引入fd(文件描述符)

        int fd1 = open("log.txt", O_RDONLY, 0666);// rw-rw-rw-
 16     printf("open success, fd1:%d\n", fd1);
 17     int fd2 = open("log.txt", O_RDONLY, 0666);// rw-rw-rw-
 18     printf("open success, fd2:%d\n", fd2);
 19     int fd3 = open("log.txt", O_RDONLY, 0666);// rw-rw-rw-
 20     printf("open success, fd3:%d\n", fd3);
 21     int fd4 = open("log.txt", O_RDONLY, 0666);// rw-rw-rw-
 22     printf("open success, fd4:%d\n", fd4);

上面的代码如何理解?0,1,2去哪了呢?

<Linux>基础IO_文件描述符_第2张图片

程序在运行时,会默认打开3个流

stdin(标准输入流):0

stdout(标准输出流):1

stderr(标准错误流:2

那么FILE又是什么鬼呢?FILE是一个结构体!是由C语言提供封装的;并且在FILE结构体种一定是封装了fd的,即站在系统的角度,是只认识fd的!

<Linux>基础IO_文件描述符_第3张图片

  • 周边文件(fd的理解,fd和FILE关系,fd分配规则,fd和重定向,缓冲区?.....)

文件:

1.被进程打开的文件

2.没有被进程打开的文件(磁盘上,文件 = 内容 + 属性)

那么fd到底是什么呢?

进程要访问文件,必须先打开文件,一个进程是可以打开多个文件的,并且文件要被访问,前提是加载到内存中才能被直接访问!!而且如果多个进程都要打开自己的文件,那么OS就需要将这些大量的打开文件管理起来,那么是如何管理的呢?模式是先描述,在组织!!

那么在内核中,如何看待被打开的文件?OS内部为了管理每一个被打开的文件,构建了了一个struct file结构体:文件对象很多是用双链表组织起来的;

<Linux>基础IO_文件描述符_第4张图片

 文件对象里包含了文件的所有内容;

过程:可执行程序运行加载到内存时,OS创建一个该进程对应的PCB结构体,该结构体中有一个struct file结构体的指针,其指向了一个struct file的数组,每打开一个文件,这个数组就会增加1,fd就是struct file文件对象对应的在这个数组的下标,通过这个fd下标就可以访问该fd对应的文件。

我们可以解释一下程序运行时打开的三个流

struct file[0]:对应的是stdin

struct file[1]:对应的是stdout

struct file[2]:对应的是stderr

<Linux>基础IO_文件描述符_第5张图片

 总结:fd在内核中,本质就是一个数组下标!!

你可能感兴趣的:(Linux,c++,开发语言)