文件的读写(标准库函数与系统调用函数),文件描述符的复制

文件描述符

  •  为了解决内核对象在可访问性与安全”性之间的矛盾,Unix系统通过所谓的文件描述符,将位于内核空间中的文件表项间接地提供给运行于用户空间中的程序代码。
  • 为了便于管理在系统中运行的各个进程,内核会维护一张存有各进程信息的列表,谓之进程表。系统中的每个进程在进程表中都占有一个表项。每个进程表项都包含了针对特定进程的描述信息,如进程ID、用户ID、组ID等,其中也包含了一个被称为文件描述符表的数据结构
  • 文件描述符表的每个表项都至少包含两个数据项--文件描述符标志和文件表项指针,而所谓文件描述符,其实就是文件描述符表项在文件描述符表中从0开始的下标
  • 作为文件描述符表项在文件描述符表中的下标,合法的文件描述符一定是个大于等于0的整数
  • 每次产生新的文件描述符表项,系统总是从下标0开始在文件描述符表中寻找最小的未使用项
  • 每关闭一个文件描述符,无论被其索引的文件表项和v节点是否被删除,与之对应的文件描述符表项一定会被标记为未使用,并在后续操作中为新的文件描述符所占用                             
  •  系统内核缺省为每个进程打开三个文件描述符,它们在unistd.h头文件中被定义为三个宏                            

                                                       #define STDIN_FILENO 0 //标准输入                                                                                               #define STDOUT_FILENO 1 // 标准输出        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​                               ​​​​​​​#define STDERR_FILENO 2 // 标准错误

 

文件的读写 

#include <unistd.h>
ssize_t write(int fd, void const* buf size t count);

->功能:向指定的文件写入数据
->参数:fd 文件描述符

            buf内存缓冲区,即要写入的效据
            count 期望写入的字节数
->返回值:成功返回实际写入的字节数,失败返回-1。

#include <unistd.h>
ssize_t read(int fd, void* buf, size t count);

->功能:从指定的文件中读取数据
->参数:fd        文件描述符
           buf       内存缓冲区,存读取到的数据
           count    期望读取的字节数

->返回值:成功返回实际读取的字节数,失败返回-1。 

代码实现 

write函数

//向文件写入数据
#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>int main(){//打开文件int fd = open("./shared.txt",O_WRONLY | O_CREAT | O_TRUNC,0664);if(fd == -1){perror("open");return -1;}//读取存储区内容写入文件char* buf = "hello world";ssize_t size = write(fd,buf,strlen(buf));if(size == -1){perror("write");return -1;}printf("实际写入文件%ld个字节\n",size);//关闭文件close(fd);
}

read函数 

//从文件读数据
#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>int main(){//打开文件int fd = open("./shared.txt",O_RDONLY);if(fd == -1){perror("open");return -1;}//读数据char* buf = malloc(sizeof(char)*100);printf("buf = %zu\n",sizeof(buf));ssize_t size = read(fd,buf,99);if(size == -1){perror("read");return -1;}//打印数据printf("实际读取%ld个字节\n",size);printf("读取到的数据是:%s\n",buf);//关闭文件close(fd);return 0;}

输出重定向 

代码实现

//输出重定向
#include<stdio.h>
#include<unistd.h>//close
#include<fcntl.h>//open
int main(){//修改1的指向 1---->out.txtclose(STDOUT_FILENO);//关闭1 1空闲int fd = open("./out.txt",O_WRONLY | O_CREAT | O_TRUNC,0664);printf("fd = %d\n",fd);return 0;
}

        内容最后写入out.txt 文件中。

顺序与随机读写

  • 每个打开的文件都有一个与其相关的文件读写位置保存在文件表项中,用以记录从文件头开始计算的字节偏移文件读写位置通常是一个非负的整数,用off_t类型表示,在32位系统上被定义为long int,而在64位系统上则被定义为long long int
  • 打开一个文件时,除非指定了O_APPEND标志,否则文件读写位置一律被设为0,即文件首字节的位置
  • 每一次读写操作都从当前的文件读写位置开始,并根据所读写的字节数,同步增加文件读写位置,为下一次读写做好准备

  • 因为文件读写位置是保存在文件表项而不是v节点中的,因此通过多次打开同一个文件得到多个文件描述符,各自拥有自己的文件读写位置

修改文件读写位置

#include <unistd.h>
off_t lseek(int fd, off t offset, int whence)

->功能:人为调整文件读写位置
-> 参数:fd:文件描述符。
             offset: 文件读写位置偏移字节数
             whence:offset参数的偏移起点,可如下取值:
                                      SEEK_SET         -从文件头(首字节)开始
                                      SEEK_CUR        -从当前位置(最后被读写字节的下一个字节)开始                                            SEEK_END         -从文件尾(最后一个字节的下一个字节)开始

->返回值:成功返回调整后的文件读写位置,失败返回-1

  • lseek函数的功能仅仅是修改保存在文件表项中的文件读写位置,并不实际引发任何I/O动作
  • ->lseek (fd,-7,SEEK_CUR); // 从当前位置向文件头偏移7字节
  • ->lseek (fd, 0, SEEK_CUR); // 返回当前文件读写位置
  • ->lseek (fd,0,SEEK_END); //返回文件总字节数
  • 可以通过lseek函数将文件读写位置设到文件尾之后,,在超过文件尾的位置上写入数据,将在文件中形成空洞,位于文件中但没有被写过的字节都被设为0,文件空洞不占用磁盘空间,但被计算在文件大小之内

 代码实现

//修改文件读写位置
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include<string.h>int main(){//打开文件int fd = open("./lseek.txt",O_WRONLY|O_CREAT|O_TRUNC,0664);if(fd == -1){perror("open");return -1;}//向文件写入hello world! 12char *str = "hello world!";if(write(fd,str,strlen(str)) == -1){perror("write");return -1;}//修改文件读写位置if(lseek(fd,6,SEEK_SET) == -1){perror("lseek");return -1;  }//再次向文件写入数据linux!char *str1 = "linux!";if(write(fd,str1,strlen(str1)) == -1){perror("write");return -1;}// 再次修改文件读写位置if(lseek(fd,8,SEEK_END) == -1){perror("lseek");return -1;}// 再次向文件写入数据char *str2 = "地三鲜";if(write(fd,str2,strlen(str2)) == -1){perror("write");return -1;  }//再次修改文件读写位置if(lseek(fd,-4,SEEK_SET) == -1){perror("lseek3");//error:Invalid argumentreturn -1;}//再次向文件写入数据char *str3 = "123456";if(write(fd,str3,strlen(str3) == -1)){perror("write");return -1;  }//关闭文件close(fd);return 0;
}

系统IO与标准IO

当系统调用函数被执行时,需要在用户态和内核态之间来回切换,因此频繁执行系统调用函数会严重影响性能
标准库做了必要的优化,内部维护一个缓冲区,只在满足特定条件时才将缓冲区与系统内核同步,借此降低执行系统调用的频率,减少进程在用户态和内核态之间来回切换的次数,提高运行性能。

标准库函数与系统调用函数性能比较

标准库函数使用(执行100W次写入)

#include<stdio.h>int main(){FILE *fp = fopen("./std.txt","w");if(fp == NULL){perror("fopen");return -1;}for(int i = 0;i < 1000000;i++){fwrite(&i,sizeof(int),1,fp);}fclose(fp);return 0;
}

系统调用函数使用(执行100W次写入)

#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>int main(){//打开文件int fd = open("./sys.txt", O_WRONLY | O_CREAT |O_TRUNC, 0644);if(fd == -1){perror("open");return -1;}//写文件for(int i = 0; i < 1000000; i++){write(fd,&i,sizeof(int));}//关闭文件close(fd);return 0;
}

执行情况

day03$time ./stdreal	0m0.021s
user	0m0.019s
sys	0m0.000s
day03$time ./sysreal	0m0.702s
user	0m0.161s
sys	0m0.539s
day03$
  • real:表示从开始到结束的实际时间(也称为“墙钟时间”)。这个时间包含了所有程序执行过程中消耗的时间,包括等待 I/O 操作、操作系统调度等。
  • user:表示程序在用户模式下花费的 CPU 时间。这是程序实际执行其指令所消耗的时间。
  • sys:表示程序在内核模式下花费的 CPU 时间。这部分时间主要用于处理系统调用,如文件 I/O、内存分配等。

        系统调用函数在执行大量I/O操时,由于频繁在内核态与用户态切换,降低了执行效率

 文件描述符的复制

#include <unistd.h>
int dup(int oldfd);

->功能:复制文件描述符表的特定条目到最小可用项
->参数:oldfd:源文件描述符
->返回值:成功返回目标文件描述符,失败返回-1
        dup函数将oldfd参数所对应的文作描述符表项复制到文件描述符表第一个空闲项中,同时返回该表项对应的文作描述符。dup函数返回的文件描述符一是调用进程当前未使用的最小文件描述符

        dup函数只复制文件描述符表项,不复制文件表项和v节点,因此该函数所返回的文件描述符可以看做是参数文件描述符oldfd的副本,它们标识同一个文件表项

  •  注意,当关闭文件时,即使是由dup函数产生的文件描述符副本,也应该通过close函数关闭,因为只有当关联于一个文件表项的所有文件描述符都被关闭了,该文件表项才会被销毁,类似地,也只有当关联于一个v节点的所有文件表项都被销毁了,v节点才会被从内存中删除,因此从资源合理利用的角度讲,凡是明确不再继续使用的文件描述符,都应该尽可能及时地用close函数关闭
  • dup函数究竟会把oldfd参数所对应的文件述符表项,复制到文件描述符表的什么位置,程序员是无法控制的,这完全由调用该函数时文件描述符表的使用情况决定,因此对该函数的返回值做任何约束性设都是不严谨的。由dup函数返回的文件描述符与作为参数传递给该函数的文件描述符标识的是同一个文件表项而文件读写位置是保存在文件表项而非文件描述符表项中的,因此通过这些文件描述符中的任何一个,对文件进行读写或随机访问,都会影响通过其它文件描述符操作的文件读写位置。这与多次通过open函数打开同一个文件不同。

代码实现 

//文件描述符的复制
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>int main(){//打开文件,得到文件描述符oldfdint oldfd = open("./dup.txt", O_WRONLY | O_CREAT | O_TRUNC, 0664);if(oldfd == -1){perror("open");return -1;}printf("oldfd = %d\n", oldfd);//通过复制文件描述符oldfd,得到新的文件描述符newfdint newfd = dup(oldfd);if(newfd == -1){perror("dup");return -1;  }printf("newfd = %d\n", newfd);//通过oldfd向文件写入数据,hello world!if(write(oldfd, "hello world!", 12) == -1){perror("write");return -1;}//通过newfd修改文件读写位置lseekif(lseek(newfd, 6, SEEK_SET) == -1){perror("lseek");return -1;}//通过oldfd向文件写入数据,hello linux!char* buf = "linux!";if(write(oldfd, buf, 6) == -1){perror("write");return -1;}//关闭文件close(oldfd);close(newfd);return 0;
}

执行结果

oldfd = 3
newfd = 4
[1] + Done                       "/usr/bin/gdb" --interpreter=mi --tty=${DbgTerm} 0<"/tmp/Microsoft-MIEngine-In-kyfxvhcx.pyn" 1>"/tmp/Microsoft-MIEngine-Out-fcceg4ba.gyc"
day03$cat dup.txt
hello linux!day03$

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

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

相关文章

C++智能指针配合STL模板类

代码 #include <unordered_map> #include <set> #include <memory> class ResID { public:using SP std::shared_ptr<ResID>;ResID() default;ResID(const std::string& id, const std::string& type): m_id(id), m_type(type){}public:~Re…

HoloLens 坐标系统 Coordinate systems

Hololens 和 Unity 空间坐标系统-CSDN博客文章浏览阅读79次。这意味着&#xff0c;在 X、Y 或 Z 轴上相距 2 个单位的物体&#xff0c;在混合现实中的渲染效果是相距 2 米。虽然左手坐标和右手坐标是最常见的系统&#xff0c;但 3D 软件中也会使用其他坐标系。例如&#xff0c;…

Veritas NBU8.3.0.2 安装部署环境备份实施介绍(篇一)

1、本次有两台NBU服务器&#xff0c;都是Windows Server 2016 Standard 2、一台作为Master Server角色&#xff0c;另外一台则作为Media Server角色 3、两台服务器均已加入域并关闭防火墙 4、后期备份客户端会有Linux和Windows系统&#xff0c;也会对接VCenter无代理备份虚拟…

xss靶场详解

目录 1.第一题 2.第二题 3.第三题 4.第四题 5.第五题 6.第六题 7.第七题 8.第八题 1.第一题 在源码script标签里边&#xff0c;innerhtml是用于访问或修改 HTML 元素内的 HTML 内容的&#xff0c;这里是访问spaghet这个元素的&#xff0c;并通过括号里面的东西搜索当前…

图搜v1.1.3 - 图库几千张图片再也不怕了

图搜是一款使用先进的自然语言处理技术的相册搜索助手&#xff0c;支持通过简单的描述来搜索相册中的图片&#xff0c;如“草丛中的猫”或“公园里的狗”。这款应用完全免费、无需内购&#xff0c;且完全离线使用&#xff0c;保证了用户数据的安全与私密性。它的高效索引系统确…

法律知识有奖竞答

关于开展线上法律知识竞赛活动的通知 根据字【2024】008号 《关于进一步推动普法贯穿于各行业各领域的倡议》的相关要求&#xff0c;特举办本次线上答题活动。 一、活动时间 2024年08月19日08:00至08月25日24:00 二、活动对象 公司全体成员 三、竞赛规则 1、每人每天拥有1次…

Jenkins UI与接口自动化测试持续集成实战

篇幅较长&#xff0c;要耐心阅读哦~ 基础知识简要回顾 持续集成、持续交付的好处与产生的必然性Jenkins服务的搭建方法Jenkins节点管理与用户权限Jenkins插件Jenkins父子多任务关联运行Jenkins报警机制 目录 SeleniumUI自动化测试持续集成演练接口自动化测试持续集成演练 …

vue+echarts:echarts地图页面跳转

在setOption的平级写点击事件&#xff0c;给chart添加click监听 getmapChart.setOption({......})//和数据对应即可 //点击区域实现页面跳转getmapChart.on(click,function(params){// console.log(params);switch(params.name){case "xxxx":top.location.href"…

JUC阻塞队列(四):DelayQueue

1、DelayQueue介绍 DelayQueue 是一个延迟队列&#xff0c;生产者写入一个数据&#xff0c;这个数据具有被直接消费的延迟时间&#xff0c; 让数据具有延迟的特性。 DelayQueue底层也是基于二叉堆来实现的&#xff0c;DelayQueue本就是基于PriorityBQueue 实现的。 二叉堆结构每…

Kubernetes Pod入门

在 Kubernetes 中&#xff0c;一个重要的概念就是 Pod(豆英)&#xff0c;Kubernetes 并不是直接管理容器他的最小管理单元叫做 Pod。的&#xff0c; 在 Docker 的应用中&#xff0c;我们把一个应用程序封装在一个镜像中&#xff0c;之后启动这个镜像并映射一个宿主机端…

x-cmd pkg | dua - 一个可以方便地了解给定目录的磁盘空间使用情况的工具

目录 简介用户首次快速实验指南技术特点竞品和相关项目进一步阅读 简介 dua 是 Disk Usage Analyzer 的简写&#xff0c;该工具可以快速查看给定目录的磁盘空间使用情况。 对于想要深入了解磁盘空间使用情况并有效管理存储的用户来说&#xff0c;Dua 是一个很有价值的工具。通…

华硕飞行堡垒键盘全部失灵【除电源键】

华硕飞行堡垒FX53VD键盘全部失灵【除电源键】 前言一、故障排查二、发现问题三、使用方法总结 前言 版本型号&#xff1a; 型号 ASUS FX53VD&#xff08;华硕-飞行堡垒&#xff09; 板号&#xff1a;GL553VD 故障情况描述&#xff1a; 键盘无法使用&#xff0c;键盘除开机键外…

轮式自主移动机器人的研究发展与ROS环境搭建

前言&#xff1a; 在轮式自主移动机器人的研发过程中&#xff0c;编程技术的重要性不言而喻。编程不仅涉及到机器人各种功能模块的协调运作&#xff0c;还决定了机器人能否实现复杂的行为决策和控制。首先&#xff0c;编程技术为机器人提供了“大脑”&#xff0c;从而使其能够感…

暑假请停止躺平!0门槛赚22万奖金!

在这个数字化时代&#xff0c;数据成为推动社会进步的重要力量。您是否想运用手中的数据&#xff0c;为治理与环保领域贡献力量&#xff1f;现在&#xff0c;机会就在眼前&#xff01; 2024年厦门市大数据创新应用大赛正在火热进行中&#xff0c;我们诚邀全国高等院校在校学生…

工厂现场多功能帮手,三防平板改善管理体验

随着制造业的智能化变革&#xff0c;信息化、自动化和智能化逐渐成为工厂管理的新常态。在这一波技术浪潮中&#xff0c;三防平板作为一种多功能的工作工具&#xff0c;正在逐步改善工厂现场的管理体验。 一、三防平板的定义与特点 三防平板&#xff0c;顾名思义&#xff0c;是…

『 Linux 』利用UDP套接字实现简单群聊

文章目录 服务端通过传入命令处理实现远程命令执行使用Windows编辑UDP客户端实现Windows远程控制Linux接收套接字的其他信息UDP套接字简单群聊服务端UDP套接字简单群聊客户端运行测试及分离输入输出 参考代码 服务端通过传入命令处理实现远程命令执行 『 Linux 』利用UDP套接字…

springboot医疗远程管理系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图详细视频演示技术栈系统测试为什么选择我官方认证玩家&#xff0c;服务很多代码文档&#xff0c;百分百好评&#xff0c;战绩可查&#xff01;&#xff01;入职于互联网大厂&#xff0c;可以交流&#xff0c;共同进步。有保障的售后 代码参考数据库参…

el-table实现动态添加行,并且有父子级联动下拉框

<template><div><el-button click"addRow">添加行</el-button><el-table :data"tableData" style"width: 100%"><el-table-column label"序号"type"index"width"100"align"…

【ARM+Codesys 客户案例 】基于RK3568/A40i/STM32+CODESYS开发的控制器在自动输送分拣系统上的应用,支持定制

2021年“京东618” 累计下单金额超3438亿元,再次刷新纪录! 从下单到收货&#xff0c;各种货品均可在短短几天内通过四通八达的物流网络送达全国任何一个家庭。电子商务和快递物流的迅猛发展对仓储、分拣、配送效率和准确性均提出了更高的要求&#xff0c;加速了智能物流的发展。…

如何免费获取乡镇级边界数据geoJson数据

如何免费获取乡镇级边界数据geoJson数据 我们可以通过 阿里云数据可视化平台 &#xff0c;可以获取到中国各个省份/区级/县级的json数据&#xff0c;但是区级和县级&#xff0c;并没有包含街道和乡镇的数据 获取乡镇级边界数据 1.下载bigemap全能版 安装好后选择你要导出的…