【Linux系统化学习】文件描述符fd

目录

基础IO预备知识

C语言文件接口

"w"的方式打开,fputs写入

以"a"的方式打开,fputs写入

使用位图传参

系统调用操作文件

open的使用

 第一种形式

第二种形式

write()

文件描述符

文件描述符和进程的关系

默认的三个IO流和文件描述符的关系


基础IO预备知识

在之前的文章中我们提到过:文件 = 内容 + 属性

我们直接给出下面几条结论:

  • 对所有的文件的操作:a.对内容操作 b.对属性操作
  • 内容是数据,属性其实也是数据;因此储存文件必须储存内容和属性。
  • 我们想要访问一个文件,就必须先将这个文件打开。
  • 通过进程打开文件且一个进程可以打开多个文件。
  • 未被打开的文件存储在磁盘中,打开的文件存储在内存中。

我们知道文件默认是在磁盘中的,我们想要打开文件就必须访问磁盘;用户是没有权限访问磁盘的必须使用系统调用,通过操作系统将文件从磁盘加载到内存中进行操作。使用系统调用就要创建进程,因此用户是通过进程打开文件的,一个进程又可以打开很多个文件;打开的文件像内存中的进程一样,进程需要被管理,打开加载在内存中的文件也需要被管理。操作系统管理的方式——先描述在组织;像进程一样,一个文件被打开一定要现在内核中形成被打开的文件对象,然后再将这些文件对象使用某种数据结构串联起来即可。

在本篇文章中我们研究文件操作的本质是:进程和文件的关系


C语言文件接口

在之前C语言的文件操作中我们学习了一些相关C语言的文件操作包括以各种方式打开文件,关闭文件等等。这里我们只简单复习下以”w”和“a"的方式打开文件及使用fputs向文件中写入。

"w"的方式打开,fputs写入

  1 #include<stdio.h>
  2 int main()
  3 {
  4     FILE* fp = fopen("log.txt","w");
  5     if(fp)
  6     {
  7         perror("fopen");
  8     }
  9     fputs("aaaaaaaaaaaaaaaaaa",fp);                                                     
 10     fclose(fp);               
 11     return 0;                 
 12 }  

 

总结:以读("w")的方式打开文件,当文件不存在时候会在当前工作目录下创建新文件;但是以这个方式写入是从头开始覆盖式写入,相当于将之前文件的内容清空再写入。相当于Linux中的输入重定向。

以"a"的方式打开,fputs写入

  1 #include<stdio.h>
  2 int main()
  3 {
  4     FILE* fp = fopen("log.txt","a");
  5     if(fp)
  6     {
  7         perror("fopen");
  8     }
  9     fputs("ccccccccccccc",fp);                                                          
 12     fclose(fp);
 13     return 0;
 14 }

总结:以追加("a")的方式打开文件,会在文件末尾进行写入。相当于Linux下的追加重定符。

 


使用位图传参

在学习Linux的系统调用之前我们还要学习下使用位图传参。

对于我们的自定义函数来说,定义了几个参数在使用时候就要传几个实参;但是我们传递的实参非常多时,又对应函数参数的一个参数该怎么办?

将我们的参数按位与(|)形成一个新的参数,这个参数的每个比特位为代表一个参数; 对传入的参数进行位操作即可。

  1 #include <stdio.h>
  2 
  3 #define Print1 1      // 0001
  4 #define Print2 (1<<1) // 0010
  5 #define Print3 (1<<2) // 0100
  6 #define Print4 (1<<3) // 1000
  7 
  8 void Print(int flags)
  9 {
 10     if(flags&Print1) printf("hello 1\n");
 11     if(flags&Print2) printf("hello 2\n");
 12     if(flags&Print3) printf("hello 3\n");
 13     if(flags&Print4) printf("hello 4\n");
 14 }
 15 
 16 
 17 int main()
 18 {
 19     Print(Print1);
 20     Print(Print1|Print2);
 21     Print(Print1|Print2|Print3);
 22     Print(Print3|Print4);
 23     Print(Print4);
 24     return 0;
 25 } 

 

如上面的代码那样使用位图的思想解决多个实参对应一个实参进行传参操作。 


系统调用操作文件

上面我们提到操作系统是不相信我们用户的,用户直接对操作系统的文件操作不了;需要使用操作系统提供的系统接口——open、close。open系统调用代表打开文件,close代表关闭文件。

open的使用

open系统调用含有两种形式,第一种形式适用于打开存在的文件,当打开不存在时会造成文件的权限乱码;第二种形式适用于打开不存在的文件。此函数的返回值大于0代表打开成功打开文件,小于0代表打开失败。

参数说明

pathname:打开的文件名

flags:对文件进行各种操作对应数字的按位与(|),下面的几个参数为常用的操作对应数字的宏定义。

        O_WRONLY:只读

        O_CREAT:创建新文件

        O_TRUNC:覆盖式写入

        O_APPEND:追加式写入

mode:形成新文件的权限

 第一种形式

  1 #include<stdio.h>
  2 #include<sys/types.h>
  3 #include<sys/stat.h>
  4 #include<fcntl.h>
  5 #include<unistd.h>                                                                      
  6 
  7 int main()
  8 {
  9     int fb = open("vlog.txt",O_WRONLY|O_CREAT|O_TRUNC);
 10     if(fb<0)
 11     {
 12         perror("open");
 13         return 0;
 14     }
 15     close(fb);
 16 
     }

如上图所示:权限只有rwx,这里蹦出来一个T,显然乱码了。 

第二种形式

1 #include<stdio.h>
  2 #include<sys/types.h>
  3 #include<sys/stat.h>
  4 #include<fcntl.h>
  5 #include<unistd.h>
  6 
  7 int main()
  8 {
  9     int fb = open("vlog.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);                            
 10     if(fb<0)
 11     {
 12         perror("open");
 13         return 0;
 14     }
 15     close(fb);
 16 
    }

总结:通过第二种函数我们将创建的新文件的权限进行设置,但是系统中含有umask。设置的权限和文件的权限不一样,需要我们手动修改umask之后便可以相同。

write()

这个系统调用是用来对文件进行写入操作的;返回值我们会在以后的文章中介绍此处不用过多了解。

参数说明

  • fb:需要进行操作的文件描述符
  • buf:需要写入的字符串(这个字符串不用以’\0‘结尾,因为这个规定是C语言的不是文件系统的
  • count:写入字符串的长度的

写入的方式和打开时传入的参数有关。


文件描述符

使用系统调用open会返回一个整形,这个整形叫做文件描述符。并且使用系统调用write以这个整形为参数时会对文件写入,感觉好神秘,通过一个整数对系统文件操作。

一个进程可以打开多个文件,我们不妨打开好几个文件将每个文件的文件描述符打印出来。

 #include<stdio.h>
  2 #include<sys/types.h>
  3 #include<sys/stat.h>
  4 #include<fcntl.h>
  5 #include<unistd.h>
  6 #include<string.h>
  7 int main()
  8 {
  9     int fba = open("fba.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);
 10     int fbb = open("fbb.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);
 11     int fbc = open("fbc.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);
 12     int fbd = open("fbd.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);
 13     printf("%d\n",fba);
 14     printf("%d\n",fbb);
 15     printf("%d\n",fbc);
 16     printf("%d\n",fbd);                                                                 
 17     return 0;
     }

是从3开始的连续小整数,其实这三个整数是数组的下标

文件描述符和进程的关系

上面我们提到一个进程可以打开很多文件,操作系统为了方便管理这些文件会对每个文件编写相应的结构体即 struct file,但是操作系统不想让进程和每个文件对应的结构体耦合过大,因此对此进程打开的所有文件又组织起来创建一个结构体 struct files _struct,在此进程的PCB中含有一个 struct files_struct * files指向这个结构体;其中这个结构体中还有一个指针数组,从下标3开始数组中的每个元素为struct file * fd ,一一指向这个进程打开的文件。

结论:操作系统访问文件只认文件描述符。

默认的三个IO流和文件描述符的关系

刚开始学习Linux的时候就说过一个特别重要的结论:Linux下一切皆文件;当我们使用键盘、显示器编写C语言(包含其他编程语言)的时候,其实就是对键盘文件、显示器文件进行文件读写;当我们执行C语言的时候系统默认给我们打开三个流:stdin、stdout、stderr。这三个流分别对应我们的键盘、显示器、显示器;用于我们进行输入、输出,像scanf就是对键盘文件的读取、printf就是对显示器的写入。(stderr流会在后面的文章中介绍到)

这三个流对应的返回值是FILE*,和C语言对系统文件的各种函数的返回值一样。C语言可以对系统中的文件进行操作,必定封装了系统调用open、close。FILE用来保存打开文件的信息的,里面肯定封装了文件描述符(fd);因此我们可以使用C语言对系统文件进行操作。

  1 #include<stdio.h>
  2 #include<sys/types.h>
  3 #include<sys/stat.h>
  4 #include<fcntl.h>
  5 #include<unistd.h>
  6 #include<string.h>
  7 int main()
  8 {
  9     printf("stdin->fd:%d\n ",stdin->_fileno );
 10     printf("stdout->fd:%d\n ",stdout->_fileno );
 11     printf("stderr->fd:%d\n ",stderr->_fileno );
 12     
 13     FILE* fp =fopen("vlog.txt","w");
 14     printf("fp->fd:%d \n",fp->_fileno);
 15     fclose(fp);                                                                         
 16           
    }


今天对Linux下文件描述符的分享到这就结束了,希望大家读完后有很大的收获,也可以在评论区点评文章中的内容和分享自己的看法;个人主页还有很多精彩的内容。您三连的支持就是我前进的动力,感谢大家的支持!!! 

相关推荐

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-02-13 18:46:01       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-02-13 18:46:01       101 阅读
  3. 在Django里面运行非项目文件

    2024-02-13 18:46:01       82 阅读
  4. Python语言-面向对象

    2024-02-13 18:46:01       91 阅读

热门阅读

  1. mysql索引的概念以及数据结构

    2024-02-13 18:46:01       53 阅读
  2. 时间序列预测——BiGRU模型

    2024-02-13 18:46:01       50 阅读
  3. [AIGC] 上传文件:后端处理还是直接阿里云OSS?

    2024-02-13 18:46:01       47 阅读
  4. 再说开源软件

    2024-02-13 18:46:01       55 阅读
  5. 数据库常用数据类型

    2024-02-13 18:46:01       57 阅读
  6. 从一道板子题了解LIS(最长上升子序列)

    2024-02-13 18:46:01       67 阅读
  7. rtt设备io框架面向对象学习-adc设备

    2024-02-13 18:46:01       55 阅读