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

2024-02-07 01:52

本文主要是介绍【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      // 00014 #define Print2 (1<<1) // 00105 #define Print3 (1<<2) // 01006 #define Print4 (1<<3) // 10007 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下文件描述符的分享到这就结束了,希望大家读完后有很大的收获,也可以在评论区点评文章中的内容和分享自己的看法;个人主页还有很多精彩的内容。您三连的支持就是我前进的动力,感谢大家的支持!!! 

这篇关于【Linux系统化学习】文件描述符fd的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/686192

相关文章

Linux脚本(shell)的使用方式

《Linux脚本(shell)的使用方式》:本文主要介绍Linux脚本(shell)的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录概述语法详解数学运算表达式Shell变量变量分类环境变量Shell内部变量自定义变量:定义、赋值自定义变量:引用、修改、删

Linux链表操作方式

《Linux链表操作方式》:本文主要介绍Linux链表操作方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、链表基础概念与内核链表优势二、内核链表结构与宏解析三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势六、典型应用场景七、调试技巧与

Go学习记录之runtime包深入解析

《Go学习记录之runtime包深入解析》Go语言runtime包管理运行时环境,涵盖goroutine调度、内存分配、垃圾回收、类型信息等核心功能,:本文主要介绍Go学习记录之runtime包的... 目录前言:一、runtime包内容学习1、作用:① Goroutine和并发控制:② 垃圾回收:③ 栈和

详解Linux中常见环境变量的特点与设置

《详解Linux中常见环境变量的特点与设置》环境变量是操作系统和用户设置的一些动态键值对,为运行的程序提供配置信息,理解环境变量对于系统管理、软件开发都很重要,下面小编就为大家详细介绍一下吧... 目录前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变

Linux系统中的firewall-offline-cmd详解(收藏版)

《Linux系统中的firewall-offline-cmd详解(收藏版)》firewall-offline-cmd是firewalld的一个命令行工具,专门设计用于在没有运行firewalld服务的... 目录主要用途基本语法选项1. 状态管理2. 区域管理3. 服务管理4. 端口管理5. ICMP 阻断

Linux实现线程同步的多种方式汇总

《Linux实现线程同步的多种方式汇总》本文详细介绍了Linux下线程同步的多种方法,包括互斥锁、自旋锁、信号量以及它们的使用示例,通过这些同步机制,可以解决线程安全问题,防止资源竞争导致的错误,示例... 目录什么是线程同步?一、互斥锁(单人洗手间规则)适用场景:特点:二、条件变量(咖啡厅取餐系统)工作流

Linux中修改Apache HTTP Server(httpd)默认端口的完整指南

《Linux中修改ApacheHTTPServer(httpd)默认端口的完整指南》ApacheHTTPServer(简称httpd)是Linux系统中最常用的Web服务器之一,本文将详细介绍如何... 目录一、修改 httpd 默认端口的步骤1. 查找 httpd 配置文件路径2. 编辑配置文件3. 保存

Linux使用scp进行远程目录文件复制的详细步骤和示例

《Linux使用scp进行远程目录文件复制的详细步骤和示例》在Linux系统中,scp(安全复制协议)是一个使用SSH(安全外壳协议)进行文件和目录安全传输的命令,它允许在远程主机之间复制文件和目录,... 目录1. 什么是scp?2. 语法3. 示例示例 1: 复制本地目录到远程主机示例 2: 复制远程主

Android学习总结之Java和kotlin区别超详细分析

《Android学习总结之Java和kotlin区别超详细分析》Java和Kotlin都是用于Android开发的编程语言,它们各自具有独特的特点和优势,:本文主要介绍Android学习总结之Ja... 目录一、空安全机制真题 1:Kotlin 如何解决 Java 的 NullPointerExceptio

Linux基础命令@grep、wc、管道符的使用详解

《Linux基础命令@grep、wc、管道符的使用详解》:本文主要介绍Linux基础命令@grep、wc、管道符的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录grep概念语法作用演示一演示二演示三,带选项 -nwc概念语法作用wc,不带选项-c,统计字节数-