unix高级编程系列之文件I/O

背景

作为linux 开发者,我们不可避免会接触到文件编程。比如通过文件记录程序配置参数,通过字符设备与外设进行通信。因此作为合格的linux开发者,一定要熟练掌握文件编程。在文件编程中,我们一般会有两类接口函数:标准I/O(带缓冲)和POXIS.1 I/O(不带缓冲)。本章节主要介绍不带缓冲的相关API及注意事项。

open 接口

open函数的作用是打开或创建一个文件。其声明如下:

int open(const char* path, int oflag, .../*mode_t mode*/);

参数解析:

  • path :是打开或需要创建的文件名称;
  • oflag : 设置打开文件的权限,该参数取值范围较广。并且需要区分。大致可以分为两类:
  1. 权限类型标识。需要关注的有O_RDONLY(只读权限)、O_WRONLY(只写权限)、O_RDWR(可读写权限)。这三个标识位必须指定一个且只能指定一个。(O_ECEC(只执行)和O_SEARCH(只搜索)已被移除)。
  2. 特性类标识。这些标识可多选,常见的有如下:
标识常量含义
O_APPEND每次写都追加到文件的尾端。即使你显式的调用lseek改变文件当前偏移量,但是在write时,依然会追加到文件末尾
O_CREAT若文件不存在则创建它。
O_EXCL如果同时指定了O_CREAT,而文件已经存在,则出错。经常用于判断文件是否存在,与access()函数功能类似。
O_NOBLOCK如果path引用的是一个FIFO、块特殊文件、字符特殊文件。那么本文件描述符后续的I/O操作,都设置为非阻塞方式。
O_SYNC每次write 都会等待物理I/O操作完成,包括文件属性更新。 在linux ext4 系统中,该标识可能不生效
O_TRUNC如果文件存在,且以只写或读写权限打开。则将长度截断为0。常见的业务场景就是更新配置文件。
  • mode 可选参数。只有当oflag参数中,具备O_CREAT属性时,才需要指定新创建的文件权限。

知识点open 函数返回的文件描述符一定时最小的未用描述符数值

基于上述知识点,经常会被用来重定向程序的标准输入、标准输出或标准错误输出。

场景如下:有一个封装库内部是通过采用的是printf进行日志打印,无法体现在我们日志系统中。我们如何观察其日志输出呢?常见做法如下:

#include<stdlib.h>
#include<stdio.h>
int main()
{ 
    printf("hello world\n");
    return 0;
}

默认情况下,日志输出到终端:

xieyihua@xieyihua:~/test$ gcc 1.c  -o 1
xieyihua@xieyihua:~/test$ ./1
hello world
xieyihua@xieyihua:~/test$

可以做以下修改:

#include<stdlib.h>
#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>
int main()
{
        close(1); /* 关闭 文件描述符1'*/

        open("./log",O_WRONLY|O_CREAT,0755);/* 此时文件描述符1 与 ./log文件绑定*/

        printf("hello world\n");
        return 0;
}

输出如下:

xieyihua@xieyihua:~/test$ gcc 1.c -o 1
xieyihua@xieyihua:~/test$ ./1
xieyihua@xieyihua:~/test$ cat log
hello world
xieyihua@xieyihua:~/test$

creat 接口

creat函数主要用于创建一个新文件。函数原型声明如下:

#include <fcntl.h>
int creat(const char* path, mode_t mode);

其等效于:open(path,O_WRONLY|O_CREAT|O_TRUNC,mode);但是由于creat只能以只写方式打开文件,使用场景便较少,渐渐被‘冷落’了。

close 接口

close函数关闭一个打开文件。其函数原型声明如下:

#include<unistd.h>
int close(int fd);

知识点当一个进程终止时,内核会自动关闭它所有的打开文件。很多程序都利用了这一功能而不显示调用close

lseek 接口

每个打开文件都有一个与其相关的“当前文件偏移量”。它通常是一个非负整数(/dev/kmem/支持负的偏移量),用于度量从文件开始处计算的字节数。通常读、写操作都是从当前文件偏移处开始的,并使偏移量增加所读写的字节数。

lseek接口就可以显式的为一个打开的文件设置偏移量。其函数原型声明如下:

#include <unistd.h>
off_t lseek(int fd,off_t offset,int whence);
  • fd, 文件描述符
  • offset,其含义与whence 的值相关。
  1. whenceSEEK_SET,则将该文件的偏移量设置为距文件开始处的offset个字节。
  2. whenceSEEK_CUR,则将该文件的偏移量设置为当前值加上offset个字节,offset可为正或负。
  3. whenceSEEK_END,则将该文件的偏移量设置为文件长度加上offset个字节,offset可为正或负。

知识点 系统默认情况下,当打开一个文件时,除非指定O_APPEND选项,否则该偏移量被设置为0

lseek 仅是将当前的文件偏移量记录在内核中,并不引起任何I/O操作,其目的是用于接下来的读写操作。

空洞文件

文件偏移量可以被设置为大于文件当前长度,在这种情况下,对该文件的下一次写将加长该文件,并在文件中构成一个空洞。位于文件中没有写过的字节都被读为0。并且文件中的空洞并不要求在磁盘上占用存储区。示例代码如下:

#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>

int main()
{
    int fd;
    char* buf1 = "123456789a";
    char* buf2 = "abcdefghij";

    if((fd = creat("file.hole",0755)) < 0)
    {
            printf("creat error\n");
            return -1;
    }
    printf("fd = %d\n",fd);

    if(write(fd,buf1,10) != 10)
    {
            printf("write buf1 error\n");
    }

    if(lseek(fd,16384,SEEK_SET) == -1)
    {
            printf("lseek error\n");
    }

    if(write(fd,buf2,10) != 10)
    {
            printf("write buf1 error\n");
    }

    return 0;
}

结果如下:

/*文件长度有16394 Byte*/
xieyihua@xieyihua:~/test$ ls -la file.hole
-rwxr-xr-x 1 xieyihua xieyihua 16394 Jul  4 01:58 file.hole

/*文本的实际内容也只有两个字符串*/
xieyihua@xieyihua:~/test$ od -c file.hole   
0000000   1   2   3   4   5   6   7   8   9   a  \0  \0  \0  \0  \0  \0
0000020  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
*
0010000   a   b   c   d   e   f   g   h   i   j
0010012
xieyihua@xieyihua:~/test$ cat file.hole
123456789aabcdefghijxieyihua@xieyihua:~/test$

/* 没有空洞的文件其占用了16个磁盘块*/
xieyihua@xieyihua:~/test$ ls -ls file.*
 8 -rwxr-xr-x 1 xieyihua xieyihua 16394 Jul  4 01:58 file.hole
16 -rwxr-xr-x 1 xieyihua xieyihua 16384 Jul  4 01:58 file.nohole

文件空洞的特性:分配了文件偏移量范围,但是实际却没有分配磁盘空间。我们一般可在两个方向应用:

  1. 多线程下载。当创建一个巨大的文件时,单个线程逐步构建文件会耗费大量时间。一种优化思路是将文件划分为多个段,利用多线程同时操作,每个线程负责写入其中一段数据。这类似于现实生活中修路的场景,如修建高速公路时,单个施工队的进度可能较慢,但通过安排多个施工队,每个队负责修建一段,最终将它们连接起来,大大提高了效率。
  2. 共享内存。当不同进程需要共享内存时,并不清楚实际需要多大的文件,可以先开辟一个大文件。比如:在创建虚拟机时,如果一开始就分配了100GB的磁盘空间,而实际上系统安装完成后可能只使用了3、4GB的空间,这就是空洞文件的应用。通过空洞文件,可以避免一开始就分配过多的资源,节约了存储空间的浪费。

read 接口

read接口用于向打开文件中读数据。其原型声明如下:

#include<unistd.h>
ssize_t read(int fd, void* bff, size_t nbyte);

read成功,则返回读到的字节数,如果已经达到文件的尾端,则返回0。

知识点 大多数文件系统为改善性能都会采用某种预读技术,即即使你每次仅读取100Byte内容,但是实际上会从磁盘中读取一页数据,保存在内存中。从而减少磁盘I/O操作,提高系统性能。但是也会增加内存使用压力。

write 接口

write接口用于向打开文件写数据。其接口声明如下:

#include <unistd.h>
ssize_t write(int fd, const void* buf,size_t nbytes);

其返回值通常与参数nbytes的值相同,否则标识出错。其出错原因:

  1. 磁盘已写满
  2. 超过文件限定长度

linux 内核标识打开文件的方式

linux 内核通过三个数据结构表示打开的文件。记录项、文件表项、V节点。其三者关系大致如下:

  • 进程表项中,记录中文件描述符文件表项的关系
  • 文件表项中,记录文件状态标志位当前文件偏移量V节点指针
  • V节点中,记录文件类型各种操作函数指针指向i节点。而i节点包含文件的详细信息,比如:文件的所有者、文件长度、指向文件实际数据块再磁盘上所在位置的指针等。

注意:每一个文件只有一个唯一的V节点表;多个文件表项可以指向同一个V节点表,每调用一次open,则创建一个新的文件表项;不同fd可以指向同一个文件表项;即可能存在以下场景:

正是这样的机制原理,linux 让我们可以多任务同时访问同一文件。但是在写文件时,我们需要关注写入时序以及数据错乱问题。

  • 在完成每一个readwrite操作后,文件表项中的当前文件偏移量增加所读写的字节数。
  • 如果使用O_APPEND标志打开一个文件,则响应标志也被设置到文件表项的文件状态标志中。每次进行写操作时,文件表项中的当前文件偏移量首先会被设置为表项中的文件长度。这就确保每次写入的数据追加到当前尾端处。
  • lseek 函数只是修改文件表项中的当前文件偏移量,不进行任何I/O操作。
  • 每一个进程都有它自己的文件表项和进程表项。

dup和dup2

这两个接口用于复制一个现有的文件描述符。其接口声明如下:

#include<unistd.h>

int dup(int fd);
int dup2(int fd, int fd2);
/*两函数的返回值:若成功,返回新的文件描述符;若出错,返回-1*/

dup返回新的文件描述符一定是当前可用文件描述符中的最小数值。其效果就是多个fd指向同一个文件表项。其关系与上图中多线程访问文件一致。

sync、fsync、fdatasync接口

传统的linux 系统中设备缓冲区高速缓存或页高速缓存,大多数磁盘I/O都通过缓冲区进行。当我们向文件写入数据时,内核通常先将数据复制到缓冲区中,然后排入队列,晚些时候在写入磁盘。这种方式称为“延迟写”。

“延迟写”虽然提高了write的响应速度(不需要等待数据经过IO,写入磁盘)。但是也带来了风险:当应用层认为已经将数据写入文件了,但实际数据还并没有落入磁盘。若此时系统出现异常,则会将这部分数据丢失。为了避免这种情况,linux 系统提供了syncfsyncfdatasync接口,应用层主动要求内核将缓冲区中的数据进行落盘。原型声明如下:

#include<unistd.h>
int fsync(int fd);
int fdatasync(int fd);

void sync(void);
  • sync 只是将所有修改过的块缓冲区排入写队列,然后就返回。它并不等待实际写磁盘操作结束
  • fsync 函数只对文件描述符fd指定的文件起作用,并等待写磁盘操作结束才返回
  • fdatasync 函数类似于 fsync,但只影响文件的数据部分。

注: open 接口中有一个标识 O_SYNC含义标识同步写,但经过验证,似乎并不起作用,与预期不一致。建议为了保险起见,还是调用fsync接口。

总结

文件编程是Linux开发者必须掌握的技能。本文介绍了Linux文件编程中常用的API及其注意事项,包括open、creat、close、lseek、read、write、dup和dup2等。还介绍了sync、fsync和fdatasync等接口,用于确保数据安全。此外,文章还解释了Linux内核如何标识打开的文件,以及文件表项、V节点和进程表项之间的关系。希望能给您带来帮助。

若我的内容对您有所帮助,还请关注我的公众号。不定期分享干活,剖析案例,也可以一起讨论分享。
我的宗旨:
踩完您工作中的所有坑并分享给您,让你的工作无bug,人生尽是坦途

在这里插入图片描述

参考文章:https://applink.feishu.cn/client/message/link/open?token=AmX27V1AQAADZjdT9KRAgAQ%3D

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

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

相关文章

Vue异步操作发送AJAX请求

5. Vue异步操作 1 axios介绍 在Vue中发送异步请求&#xff0c;本质上还是AJAX。我们可以使用axios这个插件来简化操作&#xff01; 使用步骤 1.引入axios核心js文件。 2.调用axios对象的方法来发起异步请求。 3.调用axios对象的方法来处理响应的数据。 axios常用方法 代码…

泽州县和美环保科技有限公司——绿色环保的践行者

在环保产业蓬勃发展的今天&#xff0c;泽州县和美环保科技有限公司以其卓越的技术和强大的实力&#xff0c;成为山西省危废综合处置领域的翘楚。作为雅居乐环保集团的全资子公司&#xff0c;和美环保科技有限公司紧跟集团发展战略&#xff0c;致力于为社会提供全方位的环境服务…

阿里云安装rabbitMQ

1、首先看linux 版本 uname -a如果时centos 7 可以参考其他文档。我这里是centos 8 这个很重要 。网上全是按centos7 按照。导致我前面一直安装不上 各种问题。 2、查看rabbitmq 对应 erl 的版本下载 https://www.rabbitmq.com/docs/which-erlang 选择rabbitmq 3.11.19 选择…

https 自签证书相关生成csr文件、p12文件、crt文件、jks文件、key文件、pem文件

文章目录 前言https 自签证书相关生成csr文件、p12文件、crt文件、jks文件、key文件、pem文件1, 检查openssl的版本2. 生成私钥和证书签署请求 (CSR)3. 生成自签名证书4. 将证书和私钥转换为 PKCS12 格式的密钥库5. 创建信任库 (Truststore)6. 将 PKCS12 文件转换为 JKS 文件7.…

java对接海康摄像头

现在制造业很多都是用的海康的摄像头&#xff0c;作为程序员有时候需要对接海康摄像头&#xff0c;实现门禁访问控制&#xff0c;监控预览&#xff0c;录像文件下载等功能。 一、开发环境准备 在海康官网下载SDK开发文档及库文件&#xff1a; https://open.hikvision.com/dow…

关于如何做好淘汰 IT 资产数据安全销毁工作的思考 文件销毁 硬盘销毁 数据销毁 物料销毁 文件粉碎

在当今数字化时代&#xff0c;企业的 IT 资产不断更新换代&#xff0c;淘汰的 IT 资产中往往存储着大量的敏感数据。如何确保这些数据在资产淘汰过程中被安全销毁&#xff0c;成为了企业面临的重要挑战。以下是对如何做好淘汰 IT 资产数据安全销毁工作的一些思考。 一、明确数…

WACV2023论文速览域迁移Domain相关

Paper1 CellTranspose: Few-Shot Domain Adaptation for Cellular Instance Segmentation 摘要原文: Automated cellular instance segmentation is a process utilized for accelerating biological research for the past two decades, and recent advancements have produc…

控制下属绝不是拍桌子、甩脸子、摆架子,一流领导都用这3招!

控制下属绝不是拍桌子、甩脸子、摆架子&#xff0c;一流领导都用这3招&#xff01; 第一招&#xff1a;规矩严 国有国法&#xff0c;家有家规&#xff0c;公司也不例外。 下属能不能和领导齐心&#xff0c;愿不愿意共同发力&#xff0c;全看管理者如何操控。 毫无疑问的是&a…

Leetcode - 周赛403

目录 一&#xff0c;3200. 三角形的最大高度 二&#xff0c;3195. 包含所有 1 的最小矩形面积 I 三&#xff0c;3196. 最大化子数组的总成本 四&#xff0c;3197. 包含所有 1 的最小矩形面积 II 一&#xff0c;3200. 三角形的最大高度 本题是一道模拟题&#xff0c;可以先排…

【不容错过】可灵AI重磅更新:画质升级,运镜控制,首尾帧自定义,还有30万创作激励奖金!

还记得最近在各大平台肆虐的老照片变成视频吗&#xff0c;就是用快手的可灵AI做的&#xff0c;今天可灵又迎来了一次重大更新。 「电脑端上线了」 之前一直用其他工具生的图片还需要保存到手机上&#xff0c;再用可灵来生成视频&#xff0c;很多人都能感受到手机操作不太方便&…

七人互助拼团模式:共创共赢的电商新篇章

在当今电商行业的繁荣浪潮中&#xff0c;七人互助拼团模式犹如一股清流&#xff0c;凭借其独特的激励机制与深厚的互助合作文化&#xff0c;赢得了消费者与商家的广泛赞誉。这一模式不仅重新定义了团购体验&#xff0c;更在无形中强化了社群间的联系与协作&#xff0c;共同绘制…

提升用户体验之requestAnimationFrame实现前端动画

1)requestAnimationFrame是什么? 1.MDN官方解释 2.解析这段话&#xff1a; 1、那么浏览器重绘是指什么呢&#xff1f; ——大多数电脑的显示器刷新频率是60Hz&#xff0c;1000ms/6016.66666667ms的时间刷新一次 2、重绘之前调用指定的回调函数更新动画&#xff1f; ——requ…

多特征线性回归

目录 一、多特征符号意义说明&#xff1a;二、多特征模型表示&#xff1a;三、Numpy向量表示、内积计算&#xff1a;1.向量表示&#xff1a;2.内积计算&#xff1a; 四、多元线性回归梯度下降算法&#xff1a; 一、多特征符号意义说明&#xff1a; x下标j&#xff1a;表示第j个…

Docker:一、安装与卸载、配置阿里云加速器(Ubuntu)

目录 &#x1f341;安装docker&#x1f332;1、环境准备&#x1f332;2、安装docker Engine&#x1f9ca;1、卸载旧版、任何冲突的包&#x1f9ca;2、使用存储库安装&#x1f9ca;3、安装 Docker 包。&#x1f9ca;4、查询是否安装成功&#x1f9ca;5、运行hello-world镜像&…

医疗器械FDA | FDA如何对医疗器械网络安全认证进行审查?

FDA医械网络安全文件出具​https://link.zhihu.com/?targethttps%3A//www.wanyun.cn/Support%3Fshare%3D24315_ea8a0e47-b38d-4cd6-8ed1-9e7711a8ad5e FDA对医疗器械的网络安全认证进行审查时&#xff0c;主要关注以下几个方面&#xff0c;以确保医疗器械在网络环境中的安全性…

MAX98357、MAX98357A、MAX98357B小巧、低成本、PCM D类IIS放大器,具有AB类性能中文说明规格书

前言&#xff1a; MAX98357A支持标准I2S数据&#xff0c;MAX98357B支持左对齐数字音频数据。两个版本均支持8通道TDM音频数据。 IIS数字功放MAX98357开发板/评估系统 MAX98357 WLP-9(1.347x1.437mm)封装的外观和丝印AKM MAX98357 TQFN-16-EP(3x3mm)封装的外观和丝印AKK 引脚说…

Rust: polars行遍历,从dataframe到struct及Bar设计比较

pandas提供了iterrows()、itertuples()、apply等行遍历的方式&#xff0c;还是比较方便的。 polars的列操作功能非常强大&#xff0c;这个在其官网上有详细的介绍。由于polars底层的arrow是列存储模式&#xff0c;行操作效率低下&#xff0c;官方也不推荐以行方式进行数据操作。…

谷粒商城 - 树形菜单递归流查询、三级分类数据查询性能优化、Jmter 性能压测

目录 树形分类菜单&#xff08;递归查询&#xff0c;强扩展&#xff09; 1&#xff09;需求 2&#xff09;数据库表设计 3&#xff09;实现 4&#xff09;关于 asSequence 优化 性能压测 1&#xff09;Jmeter 安装使用说明 2&#xff09;中间件对性能的影响 三级分类数…

昇思25天学习打卡营第5天|GAN图像生成

文章目录 昇思MindSpore应用实践基于MindSpore的生成对抗网络图像生成1、生成对抗网络简介零和博弈 vs 极大极小博弈GAN的生成对抗损失 2、基于MindSpore的 Vanilla GAN3、基于MindSpore的手写数字图像生成导入数据数据可视化模型训练 Reference 昇思MindSpore应用实践 本系列…

01 企业网站架构部署于优化之Web基础与HTTP协议

目录 1.1 Web基础 1.1.1 域名和DNS 1. 域名的概念 2. Hosts文件 3. DNS 4. 域名注册 1.1.2 网页与HTML 1. 网页概述 2. HTML概述 3. HTML基本标签 4. 网站和主页 5. Web1.0与Web2.0 1.1.3 静态网页与动态网页 1. 静态网页 2. 动态网页 3. 动态网页语言 1.2 HTTP协议 1…