基础I/O--文件系统

news2025/1/12 20:57:07

在这里插入图片描述

文章目录

  • 回顾C文件接口
  • 初步理解文件
  • 理解文件
    • 使用和并认识系统调用
      • open
        • 概述
        • 标记位传参理解
        • 返回值
      • close
      • write
      • read
      • 总结
    • 文件描述符fd
      • 0&1&2
      • 理解

回顾C文件接口

C代码:

#include<stdio.h>    
    
int main()    
{    
  FILE *fp=fopen("log.txt","w");    
  if(NULL==fp)    
  {    
    perror("fopen");    
    return 1;    
  }    
    
  fclose(fp);                                                                                                                            
  return 0;    
}  

我们要进行文件操作,前提是程序运行起来了,所谓的文件的打开和关闭是CPU执行我们的代码才被打开或者关闭的。
fopen()函数:
在这里插入图片描述

初步理解文件

打开文件:本质上是进程打开文件

文件没有被打开的时候,在哪里?在磁盘上

一个进程能打开很多文件吗? 可以

系统中可不可以存在很多进程? 可以

因此,在很对情况下,操作系统内部一定存在大量的被打开的文件
操作系统要不要对这些被打开的文件进行管理? 要
如何管理?先描述,再组织

如果在磁盘新建一个文件,里面什么内容都没有,大小为0KB,那么这个文件占用空间吗?占,因为文件对应的创建时间、文件类型等对应的文件属性都存在。
因此,文件=内容+属性

理解文件

操作文件,本质上是进程在操作文件

文件没有被打开时,文件在磁盘上,磁盘属于外部设备,磁盘本质是一个硬件,向文件写入,本质是向硬件中写入,但是用户没有权利直接向硬件写入,硬件的管理者是操系统,因此用户不能绕过操作系统直接访问硬件,必须通过操作系统写入。操作系统给用户提供系统调用,我们用的C/C++/其他语言,都是对系统调用接口的封装。
访问文件除了可以使用已经封装好的函数,还可以使用系统调用。

在这里插入图片描述

使用和并认识系统调用

open

概述

在这里插入图片描述

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

int creat(const char *pathname, mode_t mode);

pathname: 要打开或创建的目标文件
flags: 打开文件时,可以传入多个参数选项,用下面的一个或者多个常量进行“或”运算,构成flags。
参数:
O_RDONLY: 只读打开
O_WRONLY: 只写打开
O_RDWR : 读,写打开
这三个常量,必须指定一个且只能指定一个
O_CREAT : 若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限
O_APPEND: 追加写
返回值:
成功:新打开的文件描述符
失败:-1

open 函数具体使用哪个,和具体应用场景相关,如目标文件不存在,需要open创建,则第三个参数表示创建文件的默认权限,否则,使用两个参数的open。

标记位传参理解
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>

int main()
{
  umask(0);
  //system call
  int fd=open("log.txt",O_WRONLY|O_CREAT,0666);
  if(fd<0)
  {
    perror("open");
    return 1;
  }
}

在这里插入图片描述

操作系统本身就有一个权限掩码,现在在代码中又写了一个权限掩码。这里使用的是代码中的掩码,就近原则。自己设置了权限掩码就用自己设置的,没有就用系统的。因此最终创建的权限掩码就是666


int flag:是32个比特位的,用比特位来进行标志位的传递。flag标志位传递本质上是一个位图。

我们来设计一个传递为图标记位的函数:

#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>

#define ONE 1            //1: 0000 0001
#define TWO (1<<1)       //2: 0000 0010
#define THREE (1<<2)     //3: 0000 0100
#define FOUR (1<<3)      //4: 0000 1000

void print(int flag)
{
  if(flag&ONE) printf("one\n");
  if(flag&TWO) printf("two\n");
  if(flag&THREE) printf("three\n");
  if(flag&FOUR) printf("four\n");
} 

int main()
{
  print(ONE);
  printf("\n");

  print(TWO);
  printf("\n");

  print(ONE|TWO);
  printf("\n");

  print(ONE|TWO|THREE);
  printf("\n");

  print(ONE|FOUR);
  printf("\n");

  return 0;
}

在这里插入图片描述

如果我们将打印替换成其他功能,那么就可以写出一个向指定函数传递标记位的方法。

返回值
RETURN VALUE
open() and creat() return the new file descriptor, or -1 if an error occurred (in which case, errno is set appropriately).

在这里插入图片描述
返回成功,就帮我们创建一个新的文件描述符,就是一个整数,失败返回-1

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>

int main()
{
  int fda=open("loga.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);
  printf("fda:%d\n",fda);

  int fdb=open("logb.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);
  printf("fdb:%d\n",fdb);

  int fdc=open("logc.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);
  printf("fdc:%d\n",fdc);

  int fdd=open("logd.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);
  printf("fdd:%d\n",fdd);

  return 0;
  
}

运行结果:

在这里插入图片描述

返回值是3 4 5 6,怎么不见0 1 2呢?
因为 0 1 2分别对应标准输入(键盘)、标准输出(显示器)、标准错误(显示器)

下文有具体介绍

close

在这里插入图片描述

#include <unistd.h>

int close(int fd);

write

man 2 write可查看write函数
在这里插入图片描述

第一个参数 fd:表示待写入文件的文件描述符。

第二个参数 buf:指向待写入的文件内容。

第三个参数 count:待写入内容的大小,单位是字节。

返回值:实际上写入的字节数。

#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
int main()
{
  umask(0);
  //system call
  int fd=open("log.txt",O_WRONLY|O_CREAT,0666);
  if(fd<0)
  {
    perror("open");
    return 1;
  }

  const char *message="hello linux file!\n";
  write(fd,message,strlen(message));

  close(fd);
  return 0;
}

在这里插入图片描述

会发现确实是写入了hello linux file!

将上述代码中,写入hello linux file!,修改成aaa

  const char *message="aaa";
  write(fd,message,strlen(message));

运行结果:

在这里插入图片描述

可见,默认不清空文件

那么,如何再次写入文件时,之前文件会被清空呢?
需要添加一个O_TRUNC

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
int main()
{
  umask(0);
  //system call
  int fd=open("log.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);
  if(fd<0)
  {
    perror("open");
    return 1;
  }

  const char *message="aaa";
  write(fd,message,strlen(message));

  close(fd);
  return 0;
}

在这里插入图片描述

运行结果:

在这里插入图片描述


追加写入:

int fd=open("log.txt",O_WRONLY|O_CREAT|O_APPEND,0666);

在这里插入图片描述

read

#include <unistd.h>

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

第一个参数 fd:要读取文件的文件描述符。

第二个参数 buf:指向一段空间,该空间用来存储读取到的内容。

第三个参数 count:参数二指向空间的大小。

总结

intfd=open("log.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);

写方式打开,不存在就创建,存在就先清空


int fd=open("log.txt",O_WRONLY|O_CREAT|O_APPEND,0666);

追加形式写入

文件描述符fd

0&1&2

  • Linux进程默认情况下会有3个缺省打开的文件描述符,分别是标准输入0, 标准输出1, 标准错误2.
  • 0,1,2对应的物理设备一般是:键盘,显示器,显示器

理解

在一个操作系统中有很多文件,操作系统需要对被打开文件进行管理,也就是需要创建对应的数据结构(struct_file),对文件的管理转换成对链表的增删查改。
任何一个文件=内容+属性,磁盘中文件的属性来初始化struct_file,将文件内容写入文件内核缓存里。

一个进程可以打开多个文件,对于进程来说怎么知道被打开文件与自己有关系,因此对应的task_struct存在一个属性struct files_struct *files,指向该类型的一个对象,该类型对象记录了当前进程所打开的所有文件新信息,操作系统中就存在一个结构体:struct file_struct,里面存在一个指针数组,数组的内容指向当前进程所打开的文件结构对象,也就是指向当前进程打开的文件。我们称这个数组为文件描述符表,数组下标称为文件描述符

文件描述符fd的本质是什么?
内核进程:文件映射关系的数组下标。

在这里插入图片描述

无论读写,都必须在合适的时候,让操作系统把文件内容读到文件缓冲区中。

open在干什么??

  1. 创建file
  2. 开辟文件缓冲区的空间,加载文件数据(延后)
  3. 查进程的文件描述符表
  4. file地址填入对应的下标中
  5. 返回下标

读写函数本质是拷贝函数

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于Pytorch深度学习——GPU安装/使用

本文章来源于对李沐动手深度学习代码以及原理的理解&#xff0c;并且由于李沐老师的代码能力很强&#xff0c;以及视频中讲解代码的部分较少&#xff0c;所以这里将代码进行尽量逐行详细解释 并且由于pytorch的语法有些小伙伴可能并不熟悉&#xff0c;所以我们会采用逐行解释小…

用git上传本地文件到github

两种方式&#xff1a;都需要git软件&#xff08;1&#xff09;VScode上传 &#xff08;2&#xff09;直接命令行&#xff0c;后者不需要VScode软件 &#xff08;1&#xff09;vscode 上传非常方便&#xff0c;前提是下载好了vscode和git软件 1 在项目空白处右击&#xff0c;弹…

字符函数与字符串函数(2)

遇见她如春水映莲花 字符函数与字符串函数&#xff08;2&#xff09; 前言一、strcatstrncat 二、strcmpstrncmp在这里插入图片描述 三、strstr四、strtok五、strerror总结 前言 根据上期字符函数与字符串函数我们可以了解到字符函数与个别字符串函数的用法&#xff0c; 那么接…

手写一个uart协议——rs232

先了解一下关于uart和rs232的基础知识 文章目录 一、RS232的回环测试1.1模块整体架构1.2 rx模块设计1.2.1 波形设计1.2.2代码实现与tb1.2.4 仿真 1.3 tx模块设计1.3.1 波形设计1.3.2 代码实现与tb1.3.4 顶层设计1.3.3 仿真 本篇内容&#xff1a; 一、RS232的回环测试 上位机…

改变视觉创造力:图像合成中基于样式的生成架构的影响和创新

原文地址&#xff1a;revolutionizing-visual-creativity-the-impact-and-innovations-of-style-based-generative 2024 年 4 月 30 日 介绍 基于风格的生成架构已经开辟了一个利基市场&#xff0c;它将机器学习的技术严谨性与类人创造力的微妙表现力融为一体。这一发展的核…

4.3 JavaScript变量

4.3.1 变量的声明 JavaScript是一种弱类型的脚本语言&#xff0c;无论是数字、文本还是其他内容&#xff0c;统一使用关键词var加上变量名称进行声明&#xff0c;其中关键词var来源于英文单词variable&#xff08;变量&#xff09;的前三个字母。 可以在声明变量的同时对其指定…

汇川AM400PLC通过EtherCAT总线控制禾川X3E伺服使能和点动控制

进行通信之前需要安装禾川X3E的XML文件&#xff0c;具体方法如下&#xff1a; 1、汇川AM400PLC和X3E通信配置 汇川AM400PLC和禾川X3E伺服EtherCAT通信-CSDN博客文章浏览阅读29次。1、汇川H5UPLC和X3E伺服EtherCAT总线控制汇川H5U PLC通过EtherCAT总线控制SV660N和X3E伺服_伺服…

转速和频率转换功能块(CODESYS ST源代码)

1、转速/频率常用转换关系 转速/频率/线速度/角速度计算FC_200 plc计算角速度-CSDN博客文章浏览阅读3.2k次。里工业控制张力控制无处不在,也衍生出很多张力控制专用控制器,磁粉制动器等,本篇博客主要讨论PLC的张力控制相关应用和算法,关于绕线机的绕线算法,大家可以参看专…

并行魔法:揭秘scikit-learn中的Bagging并行训练技术

并行魔法&#xff1a;揭秘scikit-learn中的Bagging并行训练技术 随着大数据时代的到来&#xff0c;机器学习模型的训练时间越来越长&#xff0c;尤其是在处理大规模数据集时。如何高效地训练模型成为了一个亟待解决的问题。幸运的是&#xff0c;scikit-learn库为我们提供了一种…

如何访问远程MySQL数据库?

访问远程MySQL数据库是在进行数据库开发和运维工作中经常遇到的需求。本文将介绍如何使用合适的工具和方法进行远程访问MySQL数据库。 什么是天联组网 天联组网是一款异地组网内网穿透产品&#xff0c;由北京金万维科技有限公司自主研发。天联能够在任何网络环境下实现不同地区…

Vitis HLS 学习笔记--HLS眼中的完美循环嵌套

目录 1. 简介 2. 示例 2.1 不完美循环 2.2 完美循环 2.3 HLS 眼中的循环 3. 总结 1. 简介 在处理嵌套循环时&#xff08;HDL或HLS工具中&#xff09;&#xff0c;优化循环结构对于实现最佳性能至关重要。嵌套循环的性能优化直接影响着计算的时延和资源利用率。创建完美嵌…

Redis 实战2

系列文章目录 本文将从字典的实现、哈希算法、解决键冲突、rehash、渐进式rehash几方面来阐述 Redis 实战Ⅱ 系列文章目录字典的实现哈希算法解决键冲突rehash渐进式 rehash渐进式 rehash 执行期间的哈希表操作 字典 API总结 字典的实现 Redis 的字典使用哈希表作为底层实现&…

什么是数据工程?

目录 一、什么是数据工程&#xff1f; 二、数据工程在医疗行业中的应用 三、数据工程在精准医疗方面的应用有哪些&#xff1f; 一、什么是数据工程&#xff1f; 数据工程是一个综合性的技术学科&#xff0c;涵盖了从数据的收集、存储、处理、分析到应用的整个数据处理过程。…

深入了解 Arthas:Java 应用程序诊断利器

序言 在 Java 应用程序的开发和运维过程中&#xff0c;诊断和解决性能问题是一项非常重要的任务。而 Arthas 作为一款由阿里巴巴开发的 Java 应用程序诊断工具&#xff0c;提供了一系列强大的功能&#xff0c;帮助开发人员实时监控、诊断和调优Java 应用程序。本文将深入介绍 …

光伏光热一体化技术PVT

1、PVT集热器简介 太阳能光伏光热一体化组件主要由光伏与光热两个部分组成。光伏部分采用技术成熟的太阳能光伏面板&#xff0c;通过控制系统为建筑提供所需电能&#xff0c;主要包括光伏电池、蓄电池、逆变器和控制器等构件。光热部分主要为集热器&#xff0c;将太阳能转换为热…

禅道项目管理系统 身份验证漏洞分析QVD-2024-15263

前言 最近不怎么更新了&#xff01;向小伙伴说明下 我不是什么组织 更不什么经销号&#xff08;尽管csdn有很多经销广告号&#xff09; 一确实是下岗了&#xff01;忙着为找工作而发愁。简历都投出去如同石沉大海能不愁吗!.哎...... 二是忙着论文及材料的事...…

领域驱动设计(DDD)笔记(一)基本概念

文章链接 领域驱动设计&#xff08;DDD&#xff09;笔记&#xff08;一&#xff09;基本概念-CSDN博客领域驱动设计&#xff08;DDD&#xff09;笔记&#xff08;二&#xff09;代码组织原则-CSDN博客领域驱动设计&#xff08;DDD&#xff09;笔记&#xff08;三&#xff09;后…

嵌入式开发四:STM32 基础知识入门

为方便更好的学习STM32单片机&#xff0c;本篇博客主要总结STM32的入门基础知识&#xff0c;重点在于理解寄存器以及存储器映射和寄存器映射&#xff0c;深刻体会STM32是如何组织和管理庞大的寄存器&#xff0c;从而提高开发效率的&#xff0c;为后面的基于标准库的开发做好铺垫…

Elasticsearch:对 Java 对象的 ES|QL 查询

作者&#xff1a;Laura Trotta ES|QL 是 Elasticsearch 引入的一种新的查询语言&#xff0c;它将简化的语法与管道操作符结合起来&#xff0c;使用户能够直观地推断和操作数据。官方 Java 客户端的新版本 8.13.0 引入了对 ES|QL 查询的支持&#xff0c;提供了一个新的 API&…

手撸Mybatis(四)——连接数据库进行简单查询

本专栏的源码&#xff1a;https://gitee.com/dhi-chen-xiaoyang/yang-mybatis。 添加数据库操作模板 对于JDBC操作&#xff0c;一般包括以下几个步骤&#xff1a; 1&#xff09;注册驱动 2&#xff09;建立连接 3&#xff09;执行sql语句 4&#xff09;处理结果 5&#xff09…