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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/374790.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

JAVASE进阶:高级写法——方法引用(Mybatis-Plus必学前置知识)

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位大四、研0学生&#xff0c;正在努力准备大四暑假的实习 &#x1f30c;上期文章&#xff1a;JAVASE进阶&#xff1a;一文精通Stream流函数式编程 &#x1f4da;订阅专栏&#xff1a;JAVASE进阶 希望文章对你们有所帮助 相信…

SpringCloud--Eureka注册中心服务搭建注册以及服务发现

注意springboot以及springcloud版本&#xff0c;可能有莫名其妙的错误&#xff0c;这里使用的是springboot-2.6.13&#xff0c;springcloud-2021.0.5 一&#xff0c;Eureka-Server搭建&#xff1a; 1.创建项目&#xff1a;引入依赖 <dependency><groupId>org.sp…

MyBatis 分页插件 PageHelper-Dazer007

文章目录 MyBatis 分页插件 PageHelper-Dazer0071、使用方式1.1 原始PageHelper1.2 ruoyi框架封装PageHelper1.3 MyBaits-Plus自带分页&#xff0c;无需PageHeler 2、作用原理3、数据方言实现3.1、MySqlDialect3.2、OracleDialect3.3、SqlServer2012Dialect 3、数据方言实现 My…

【Spring】Spring事务和事务传播机制

文章目录 什么是事务事务的操作Spring 中事务的实现Spring编程式事务Spring 声明式事务 TransactionalTransactional作用Transactional 详解rollbackFor事务隔离级别Spring 事务隔离级别Spring 事务传播机制 什么是事务 事务&#xff08;Transaction&#xff09;是一个程序中一…

嵌入式软件的设计模式与方法

思想有多远&#xff0c;我们就能走多远 4、状态与工作流类设计模式 4.1 状态与事件 行为随条件变化而改变&#xff0c;这里状态切换的模式也称为状态机。有限状态机 (Finite State Machine&#xff0c;FSM) 是由3 个主要元素组成的有向图: 状态、转换和动作。 状态是系统或者…

jsp教材管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 JSP 教材管理系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql5.0&…

python常用的深度学习框架

目录 一&#xff1a;介绍 二&#xff1a;使用 Python中有几个非常受欢迎的深度学习框架&#xff0c;它们提供了构建和训练神经网络所需的各种工具和库。以下是一些最常用的Python深度学习框架&#xff1a; 一&#xff1a;介绍 TensorFlow&#xff1a;由Google开发的TensorF…

LeetCode-第171题-Excel表的序列号

1.题目描述 给你一个字符串 columnTitle &#xff0c;表示 Excel 表格中的列名称。返回 该列名称对应的列序号 。 例如&#xff1a; A -> 1 B -> 2 C -> 3 ... Z -> 26 AA -> 27 AB -> 28 ... 2.样例描述 3.思路描述 遍历时将每个字母与 A 做减法&…

python 动态数据 展示 ,数据是由51单片机发送过来的,温度传感器。

import tkinter as tk import randomimport seriallis[] for i in range(50):lis.append(i1) # 打开串行端口 ser serial.Serial(COM3, 9600) # 9600为波特率&#xff0c;根据实际情况进行调整# 初始化数据 lis [random.randint(15, 35) for _ in range(50)]def update_data…

jenkins 发布远程服务器并部署项目

安装参考另一个文章 配置maven 和 jdk 和 git 注意jdk的安装目录&#xff0c;是jenkins 安装所在服务器的jdk目录 注意maven的目录 是jenkins 安装所在服务器的maven目录 注意git的目录 是jenkins 安装所在服务器的 git 目录 安装 Publish Over SSH 插件 配置远程服务器 创…

信号系统之线性系统详解

1 线性系统 信号描述了一个参数如何随另一个参数变化。例如&#xff0c;电子电路中的电压随时间变化&#xff0c;或图像中随距离变化的亮度。系统是响应输入信号而产生输出信号的任何过程。如图中的框图所示。 有几个规则用于命名信号&#xff1a; 连续信号使用圆括号&#x…

Python tkinter (15) —— PhotoImage

本文主要介绍Python tkinter PhotoImage图像应用及示例。 系列文章 python tkinter窗口简单实现 Python tkinter (1) —— Label标签 Python tkinter (2) —— Button标签 Python tkinter (3) —— Entry标签 Python tkinter (4) —— Text控件 Python tkinter (5) 选项按…

22.HarmonyOS App(JAVA)位置布局PositionLayout使用方法

不常用 在PositionLayout中&#xff0c;子组件通过指定准确的x/y坐标值在屏幕上显示。(0, 0)为左上角&#xff1b;当向下或向右移动时&#xff0c;坐标值变大&#xff1b;允许组件之间互相重叠 布局方式 PositionLayout以坐标的形式控制组件的显示位置&#xff0c;允许组件相…

JVM 性能调优 - Java 虚拟机内存体系(1)

Java 虚拟机我们简称为 JVM&#xff08;Java Virtual Machine&#xff09;。 Java 虚拟机在执行 Java 程序的过程中&#xff0c;会管理几个不同的数据区域。如下图所示&#xff1a; 下面我会介绍这几个数据区的特点。 堆 堆区的几个特点&#xff1a; 线程共享。启动时创建堆…

【MATLAB源码-第131期】基于matlab的淘金优化算法(GRO)机器人栅格路径规划,输出做短路径图和适应度曲线。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 淘金优化算法&#xff08;GoldRush Optimizer&#xff0c;简称GRO&#xff09;是一种启发式优化算法&#xff0c;它受到淘金过程的启发。在淘金过程中&#xff0c;淘金者在河流或矿区中寻找金矿&#xff0c;通过筛选沙砾来寻…

Django通过Json配置文件分配多个定时任务

def load_config():with open("rule.json", rb)as f:config json.load(f)return configdef job(task_name, config, time_interval):# ... 通过task_name判断进行操作if task_name get_data_times:passdef main():config load_config()for task_name, task_value…

SpringBoot多模块项目proguard混淆

SpringBoot多模块项目proguard混淆 前言整活项目目录混淆后的效果图混淆配置混淆配置规则keep相关通配符和关键字keep说明常见问题解决办法效果前言 proguard 是压缩、优化和混淆Java字节码文件的免费的工具。 它可以删除无用的类、字段、方法和属性。可以删除没用的注释,最大…

网络故障的排错思路

一、网络排错必备知识 1、网络通信的基础设备和其对应的OSI层次 在网络通信中&#xff0c;了解基础设备如交换机、三层交换机、路由器和防火墙以及它们在OSI七层模型中 的作用至关重要。对于网络管理员和工程师来说&#xff0c;深入了解这些设备在OSI模型中的位置和功能可 …

探索Gin框架:Golang Gin框架请求参数的获取

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站https://www.captainbed.cn/kitie。 前言 我们在专栏的前面几篇文章内讲解了Gin框架的路由配置&#xff0c;服务启动等内容。 专栏地址&…

为什么有的人渐渐不点外卖了?

​随着互联网的发展和普及&#xff0c;外卖行业也在近几年内得到了迅猛的发展&#xff0c;它方便快捷、节省时间的特点使得外卖成为了很多人生活的一部分。但是&#xff0c;随着时间的推移&#xff0c;越来越多的人开始减少点外卖的频率&#xff0c;这是为什么呢&#xff1f; 首…