Linux系统编程:基础IO

目录

1.C语言文件回顾        

2.系统文件I/O

2.1 系统接口介绍

2.2 文件描述符fd

 2.3 重定向

2.4 理解缓冲区

2.5 理解文件系统


1.C语言文件回顾        

在学习系统文件的操作之前,还记得C语言是如何进行对文件的操作的吗?下面看C语言接口:

FILE *fopen(const char *path, const char *mode);  
fopen()的功能为打开文件,该文件的名称是path指向的字符串,并将流与之关联。

参数:mode:

返回值:成功,则返回一个FILE指针,失败返回NULL

读文件:函数fread()从stream指向的流中读取nmemb个数据元素,每个元素size字节长,并将它们存储在ptr给定的位置

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); 
写文件:函数fwrite()将从ptr给定的位置获得的nmemb个数据元素(每个大小字节长)写入stream所指向的流。

size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
返回值:
成功后,fread()和fwrite()返回读取或写入的items数。此数字等于仅当size为1时传输的字节数。如果发生错误,或者到达文件末尾,则返回值为短项目计数(或零)。

  • C默认会打开三个输入输出流,分别是stdin, stdout, stderr
  • 仔细观察发现,这三个流的类型都是FILE*, fopen返回值类型,文件指针

2.系统文件I/O

        除了上述C 接口( C++ 也有接口),我们还可以采用系统接口来进行文件访问:

2.1 系统接口介绍

#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);
参数:pathname: 要打开或创建的目标文件
           flag:要打开或创建的目标文件             
                   O_RDONLY: 只读打开
                   O_WRONLY: 只写打开
                   O_RDWR : 读,写打开
                   这三个常量,必须指定一个且只能指定一个
                   O_CREAT : 若文件不存在,则创建它。需要使用mode 选项,来指明新文件的访问权限
                   O_APPEND: 追加写
                   O_TRUNC:覆盖式的写文件
           mode:设置文件的权限,否则文件权限为乱码  权限:mode&~umsk
返回值:成功返回一个文件描述符 失败返回-1
int close(int fd);  关闭一个文件描述符fd
返回值:成功返回0,失败返回-1

#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count); 从文件描述符fd中读取最多count字节的数据到buf中

返回值:成功返回读取到的字节数,如果该数字少于申请的字节数,不存在错误;失败返回-1。

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

描述:从指向的缓冲区buf向文件描述符fd引用的文件写入最多count个字节。

返回值:成功返回写入的字节数,如果count为零并且fd引用的是一个常规文件,可能会检测到错误,write()可能会返回失败状态。如果没有检测到错误,将返回0,而不会造成任何其他影响。失败返回-1。

2.2 文件描述符fd

        文件描述符就是一个小整数 

  • Linux进程默认情况下会有3个缺省打开的文件描述符,分别是标准输入0, 标准输出1, 标准错误2
  • 0,1,2对应的物理设备一般是:键盘,显示器,显示器。这三个文件描述符也是可以用close关闭的
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main()
{
 char buf[1024];
 ssize_t s = read(0, buf, sizeof(buf));
 if(s > 0){
 buf[s] = 0;
 write(1, buf, strlen(buf));
 write(2, buf, strlen(buf));
 }
 return 0;
}

        上述代码是从文件描述符0中也就是键盘读入数据,再分别写到文件描述符1,2中。 

那么我们如何理解文件呢?

        被打开的文件要被OS管理,采用先描述再组织的方式,OS为文件创建对应的内核数据结构标识文件struct file{},每个文件对应一个file,链式结构存储;这个结构体中包含文件的大部分属性。而进程执行open系统调用,所以必须让进程和文件关联起来。每个进程都有一个指针*files, 指向一张表files_struct,该表最重要的部分就是包涵一个指针数组,每个元素都是一个指向打开文件的指针!所以,本质上,文件描述符就是该数组的下标。所以,只要拿着文件 描述符,就可以找到对应的文件。一般我们自己打开的文件描述符是从3开始的。

 2.3 重定向

        若我们关闭文件描述符1,再代开一个新的文件时,文件描述符1会被分配给新的文件,那么此时,本应该输出到屏幕上的内容,被输出到我们新打开的文件中,这种现象就叫做输出重定向。

重定向的本质:上层用的fd不变,在内核中更改fd对应的structural file*的地址

#include <unistd.h>

int dup2(int oldfd, int newfd);

        使newfd成为oldfd的副本,必要时可以先关闭newfd

        返回值:成功返回新的文件描述符,失败返回-1.

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

#define FILENAME "log.txt"

int main()
{
  
    int fd = open("out.txt", O_CREAT | O_TRUNC | O_RDWR, 0666);
    if(fd  < 0)
    {
        perror("open");
        return 1;
    }
    close(1);
    dup2(fd, 1);
    while(1)
    {
    char buf[1024];
        ssize_t s = read(0, buf, sizeof(buf)-1);
        if(s < 0)
        {
            perror("read");
            break;
        }
        else{
            printf("%s", buf);
            fflush(stdout);
        }
    }

    return 0;
}

       上述这段代码是,向打开一个文件,并关闭标准输出文件描述符1。将其重定向到fd中,循环的从键盘读入数据,本该输出到屏幕的数据,经过重定向后,被输出到文件描述符1所链接的文件out.txt中。结果如下:

常见的重定向有:>, >>, <

> : 输入重定向    >> : 追加重定向    < : 输出重定向
        执行程序替换时,并不会影响曾经进程打开的重定向文件,只是将代码和数据替换,不会影响内核数据结构。

2.4 理解缓冲区

        因为IO相关函数与系统调用接口对应,并且库函数封装系统调用,所以本质上,访问文件都是通过fd访问的。所以C库当中的FILE结构体内部,必定封装了fd.

        缓冲区:本质就是一块内存 意义:节省进程进行数据IO的时间

        拷贝:fwrite、write,可以理解为将数据从进程拷贝到"缓冲区"或者外设中。

缓冲区刷新策略的问题:

  • 立即刷新  -- 无缓冲
  • 行刷新      -- 行缓冲  显示器
  • 缓冲区满   -- 全缓冲  磁盘文件
  • 特殊情况:用户强制刷新 -- fflush();  进程退出,一般都会刷新缓冲区
#include <stdio.h>
#include <string.h>
int main()
{
    const char *msg0="hello printf\n";
    const char *msg1="hello fwrite\n";
    const char *msg2="hello write\n";
    printf("%s", msg0);
    fwrite(msg1, strlen(msg0), 1, stdout);
    write(1, msg2, strlen(msg2));
    fork();
    return 0;
}

结果如下:

我们将结果从定向到文件file.txt中时,会发现:库函数都输出了两次,而write只输出了一次,为什么呢?结果肯定是与fork()有关的! 

 

出现上述现象的原因:

  • 没有重定向">"前,看到三条消息,是因为stdout默认使用行刷新。由于每一条数据的结尾都为"\n",在fork()之前,三条C函数已经将数据刷新到显示器(外设)上,FILE结构体内部已经不存在对应的数据了。
  • 进行重定向">"后,写入文件不再是显示器,而是普通文件,采用的刷新策略为全缓冲(缓冲区满/进程退出/手动刷新时才会进行缓冲区刷新),三条C显示函数虽然带了"\n",但是缓冲区并未满,数据不会进行刷新。执行fork时,stdout属于父进程,创建子进程后,紧接着就是进程退出,无论谁先退出,都会刷新缓冲区。刷新缓冲区就是修改,会发生写时拷贝,复制一份一摸一样的缓冲区给子进程,父子进程退出时都会刷新缓冲区,出现两份打印数据。
  • write没有打印两次是因为:上面的过程与write无关,write没有FILE“,而是fd,没有C语言提供的缓冲区。

2.5 理解文件系统

        磁盘上的大量文件,也是需要被静态管理起来的,方便我们随时打开。在堆磁盘文件进行管理时,要将其分区再进一步分组,每个分组有自己的BlockGroup。如下图:

        Linux ext2文件系统,上图为磁盘文件系统图(内核内存映像肯定有所不同),磁盘是典型的块设备,硬盘分区被 划分为一个个的block 。一个 block 的大小是由格式化的时候确定的,并且不可以更改。例如 mke2fs -b 选项可以设 定block 大小为 1024 2048 4096 字节。而上图中启动块( Boot Block)的大小是确定的。 Block Group:ext2文件系统会根据分区的大小划分为数个Block Group。而每个Block Group都有着相 同的结构组成。
Linux的文件内容和文件属性是分开存储的。文件内容在datablock中存储,随应用类型的变化而变化。文件属性在inode中存储,inode是固定大小的,一个文件一个inode,文件名不在inode中存储,每个inode有自己的id。
  • Super Block:保存整个文件系统的信息。记录的信息主要有:bolck 和 inode的总量, 未使用的block和inode的数量,一个block和inode的大小,最近一次挂载的时间,最近一次写入数据的 时间,最近一次检验磁盘的时间等其他文件系统的相关信息。Super Block的信息被破坏,可以说整个 文件系统结构就被破坏了。
  • inode table:保存了分组内部所有的可用(已使用+未使用)的inode,存放文件属性 如 文件大                         小,所有者,最近修改时间等。每个bit表示一个inode是否空闲可用。
  • data block:保存分组内部所以普文件的数据块
  • inode Bitmap:inode对应的位图结构,位图中比特位的位置和当前文件对应的inode的位置是                           一一对应的。
  • block Bitmap:数据块对应的数据结构,位图中比特位的位置和当前dataBlcok对应的数据块                            位置是一一对应的。
  • GDT:块组描述表:对应分组的宏观的属性信息

        可以通过命令  ls -i 文件名  来查看文件的inode编号。

       inode中存在一个block[]数组,元素为data block的编号。通过这种映射关系来通过inode编号找到所对应的文件内容数据。

创建一个新文件主要有一下4个操作:

  • 1. 存储属性--内核先找到一个空闲的i节点(这里是263466)。内核把文件信息记录到其中。
  • 2. 存储数据--该文件需要存储在三个磁盘块,内核找到了三个空闲块:300,500800。将内核缓冲区的第一块数据复制到300,下一块复制到500,以此类推。
  • 3. 记录分配情况--文件内容按顺序300,500,800存放。内核在inode上的磁盘分布区记录了上述块列表。
  • 4. 添加文件名到目录

       目录文件也是像上面这种方式设计的。

       删除文件则只需要设置inode bitmap即可。

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

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

相关文章

浪潮信息打造业界首款50℃进液温度服务器 PUE逼近理论极限1.0!

在科技飞速发展的今天&#xff0c;浪潮信息以其前瞻性的技术创新思维&#xff0c;再次突破行业极限&#xff0c;推出业界首个支持50℃进液温度的浸没式液冷服务器NF5180G7。这一创新成果不仅展现了浪潮信息在液冷技术领域的深厚实力&#xff0c;更标志着服务器冷却技术的一次重…

【2024亲测无坑】在Centos.7虚拟机上安装Oracle 19C

目录 一、安装环境准备 1、linux虚拟机安装 2、虚拟机快照 3、空间检查&软件上传 二、Oracle软件安装 1.preinstall安装及其他配置准备 2.oracle安装 三、数据库实例的安装 1.netca——网络配置助手 2.dbca——数据库配置助手 四、ORACLE 19C 在linux centos 7上…

基于PPO的强化学习超级马里奥自动通关

目录 一、环境准备 二、训练思路 1.训练初期&#xff1a; 2.思路整理及改进&#xff1a; 思路一&#xff1a; 思路二&#xff1a; 思路三&#xff1a; 思路四&#xff1a; 3.训练效果&#xff1a; 三、结果分析 四、完整代码 训练代码&#xff1a; 测试代码&#…

MySQL 日志(二)

本篇将继续介绍MySQL日志的相关内容 目录 一、二进制日志 简介 注意事项 删除二进制日志 查看二进制日志 二进制日志的格式 二、服务器日志维护 一、二进制日志 简介 二进制日志中主要记录了MySQL的更改事件&#xff08;不包含SELECT和SHOW),例如&#xff1a;表的…

Base64编码的工作原理与实际应用

目录 前言 一、什么是Base64编码&#xff1f; 二、Base64编码的原理 三、Base64编码的应用场景 四、为什么要使用Base 64 五、Base64加密解密的实现 前言 当你需要将二进制数据转换为可传输和存储的文本格式时&#xff0c;Base64编码是一个常用的选择。在这篇博客中&#…

C++ 51 之 继承中的构造和析构

对象构造和析构的调用原则 继承中的构造和析构 子类对象在创建时会首先调用父类的构造函数父类构造函数执行完毕后&#xff0c;才会调用子类的构造函数当父类构造函数有参数时&#xff0c;需要在子类初始化列表(参数列表)中显示调用父类构造函数析构函数调用顺序和构造函数相…

可以用来制作硬模空心耳机壳的胶粘剂有哪些种类?

可以用来制作硬模空心耳机壳的胶粘剂有哪些种类&#xff1f; 制作耳机壳的胶粘剂有很多种类&#xff0c;常见的有环氧树脂胶水、UV树脂胶、快干胶、热熔胶等。 这些胶粘剂都有不同的特点和适用场景&#xff0c;可以根据自己的需求选择合适的类型。 例如&#xff1a; 环氧树脂…

Adobe设计替代软件精选列表

Adobe软件的替代列表&#xff0c;最初由 XdanielArt 收集&#xff0c;并由社区改进。您可以随意打开问题或拉出请求&#xff0c;或从数据中创建图像(以便于共享)。列表总是按照免费和开源选项的顺序排列&#xff0c;但根据您的用例&#xff0c;它可能不是最佳选择 替代因素 &am…

Python 潮流周刊#56:NumPy 2.0 里更快速的字符串函数

△△请给“Python猫”加星标 &#xff0c;以免错过文章推送 本周刊由 Python猫 出品&#xff0c;精心筛选国内外的 250 信息源&#xff0c;为你挑选最值得分享的文章、教程、开源项目、软件工具、播客和视频、热门话题等内容。愿景&#xff1a;帮助所有读者精进 Python 技术&am…

【linux】认识“文件”的本质,理解“文件系统”的设计逻辑,体会linux优雅的设计理念

⭐⭐⭐个人主页⭐⭐⭐ ~~~~~~~~~~~~~~~~~~ C站最❤❤❤萌❤❤❤博主 ~~~~~~~~~~~~~~~~~~~ ​♥东洛的克莱斯韦克-CSDN博客♥ ~~~~~~~~~~~~~~~~~~~~ 嗷呜~ ✌✌✌✌ 萌妹统治世界~ &#x1f389;&#x1f389;&#x1f389;&#x1f389; ✈✈✈✈相关文章✈✈✈✈ &#x1f4a…

2023年的Top20 AI应用在近一年表现怎么样?

AI应用现在进入寒武纪大爆发时代&#xff0c;百花争艳。如果倒回到2023年初&#xff0c;那时候排名靠前的AI应用在一年多时间&#xff0c;发生了哪些变化&#xff1f;能带给我们什么启示&#xff1f; 在2023年1月&#xff0c;排名靠前20的AI应用是&#xff1a; DeepL&#xff…

MATLAB中与直方图有关函数的关系

histogram Histogram plot画直方图 histcounts 直方图 bin 计数 histcounts是histogram的主要计算函数。 discretize 将数据划分为 bin 或类别 histogram2 画二元直方图 histcounts2 二元直方图 bin 计数 hist和histc过时了。替换不建议使用的 hist 和 histc 实例 hist → \r…

Day54 JDBC

Day54 JDBC JDBC&#xff1a;SUN公司提供的一套操作数据库的标准规范&#xff0c;就是使用Java语言操作关系型数据库的一套API JDBC与数据库驱动的关系&#xff1a;接口与实现的关系 给大家画一个jdbc的工作模式图 1.JDBC的四大金刚 1.DriverManager&#xff1a;用于注册驱动 2…

【Quartus 13.0】NIOS II 部署UART 和 PWM

打算在 EP1C3T144I7 芯片上部署 nios ii 做 uart & pwm控制 这个芯片或许不够做 QT 部署 这个芯片好老啊&#xff0c;但是做控制足够了&#xff0c;我只是想装13写 leader给的接口代码是用VHDL写的&#xff0c;我不会 当然verilog我也不太会 就这样&#xff0c;随便写吧 co…

SUSTAINABILITY,SCIESSCI双检期刊还能投吗?

本期&#xff0c;小编给大家介绍的是一本MDPI出版社旗下SCIE&SSCI双检“毕业神刊”——SUSTAINABILITY。据悉&#xff0c;早在2024年1月&#xff0c;ElSEVIER旗下的Scopus数据库已暂停收录检索期刊SUSTAINABILITY所发表文章&#xff0c;同时重新评估是否继续收录该期刊。随…

Qwen2——阿里巴巴最新的多语言模型挑战 Llama 3 等 SOTA

引言 经过几个月的期待&#xff0c; 阿里巴巴 Qwen 团队终于发布了 Qwen2 – 他们强大的语言模型系列的下一代发展。 Qwen2 代表了一次重大飞跃&#xff0c;拥有尖端的进步&#xff0c;有可能将其定位为 Meta 著名的最佳替代品 骆驼3 模型。在本次技术深入探讨中&#xff0c;我…

零基础入门学用Arduino 第三部分(三)

重要的内容写在前面&#xff1a; 该系列是以up主太极创客的零基础入门学用Arduino教程为基础制作的学习笔记。个人把这个教程学完之后&#xff0c;整体感觉是很好的&#xff0c;如果有条件的可以先学习一些相关课程&#xff0c;学起来会更加轻松&#xff0c;相关课程有数字电路…

python-基础篇-类与对象/面向对象程序设计-是什么

文章目录 定义一&#xff1a;面对对象是一种编程思想定义一&#xff1a;面向对象是一种抽象1、面向对象的两个基本概念2、面向对象的三大特性 定义一&#xff1a;你是土豪&#xff0c;全家都是土豪面向对象编程基础类和对象定义类创建和使用对象访问可见性问题面向对象的支柱 定…

C++初学者指南第一步---4.基本类型

C初学者指南第一步—4.基本类型 文章目录 C初学者指南第一步---4.基本类型1.变量声明2.快速概览Booleans 布尔型Characters 字符型Signed Integers 有符号整数Unsigned Integers 无符号整数Floating Point Types 浮点数类型 3.Common Number Representations 常用的数字表示常用…

用Copilot画漫画,Luma AI生成视频:解锁创意新玩法

近年来&#xff0c;随着人工智能技术的不断发展&#xff0c;各种创意工具也层出不穷。今天&#xff0c;我们就来介绍一种全新的创作方式&#xff1a;使用Copilot画漫画&#xff0c;再将漫画放入Luma AI生成视频。 Copilot&#xff1a;你的AI绘画助手 Copilot是一款基于人工智…