Liunx系统编程:系统层面上的文件IO接口

news2024/10/6 8:21:28

目录

一. 如何在系统层面上理解文件

二. 语言层面上的文件IO函数

三. Linux操作系统提供的IO接口

3.1 open接口 -- 打开文件

3.2 close接口 -- 关闭文件

3.3 write接口 -- 向文件中写内容

3.4 read接口 -- 从文件中读取内容

四. 总结


一. 如何在系统层面上理解文件

在Linux操作系统层面,可以认为,只要能进行input写入或output读取的任何的任何设备,都可以被理解为文件,我们可以从狭义和广义两个方面认识文件的概念:

  • 狭义上的文件:磁盘文件
  • 广义上的文件:任何可以进行IO操作的设备,包括显示器、键盘、声卡、网卡、磁盘、光盘、音响、磁带、光盘等。

换句话说就是:Linux下一切皆文件

文件 = 内容 + 属性,对文件的操作无外乎就是对内容的操作和对属性的操作。因此,就算一个文件的内容为空,那么它的属性信息也要存储在磁盘中,也要占用存储空间

通过Linux操作系统的ls -l (可简写为ll) 指令,可以查看文件的属性信息,包括文件的类型、权限信息、链接数、拥有者、所属组、文件内容占用空间大小、最近一次修改的时间、文件名。

图1.1 ls -l指令查看文件信息

本质上,只有操作系统有权利对文件进行读写操作。用户对于文件的任何操作,其本质是都是调用操作系统的相关IO接口来完成的,只不过为了方便不同用户的使用,操作系统及各种编程语言,会通过图形化界面、库函数等方式,封装文件的IO接口。

如果我们希望通过代码来访问文件,那么操作流程依次为:写代码 -> 编译源代码 -> 载入内存运行,创建进程 -> 访问文件。因此,我们还可以认为,访问文件的操作本质是由进程来完成的

二. 语言层面上的文件IO函数

C/C++均提供了相应的库函数/类,来实现的文件得IO操作

  • C语言读文件函数:fgets、fgetc、fread、fscanf等。
  • C语言写文件函数:fputs、fputc、fwrite、fprintf等。
  • C++写文件类:std::ofstream,通过流插入运算符 << 来写文件。
  • C++读文件类:std::ifstream,通过流提取运算符 >> 来读文件。

这些语言层面的IO函数,在底层都封装了操作系统提供的IO接口。那么,既然操作系统已经提供了IO接口,为什么各种语言语言还要提供自己的IO函数呢?这可以从以下两个方面来理解:

  1. 语言层面的IO函数相对于系统接口使用难度较低。
  2. 语言具有跨平台的性能,而如果直接使用系统接口,那么代码就不具有跨平台性。

提问:库函数怎样针对不同的OS平台,实现不同版本的IO函数呢?答:一般采用条件编译#ifdef/#endif的方法来实现,在库函数中将全部平台下的IO接口都进行封装,实现不同底层原理的IO函数,再根据运行的平台,来对库函数代码选取一部分进行编译。

不同的语言,会提供不同的IO接口函数,但是,在同一操作系统平台下,系统IO的接口都是一样的,无论语言层面的IO接口再怎么变化,在底层都需要封装系统IO接口。

三. Linux操作系统提供的IO接口

3.1 open接口 -- 打开文件

用于打开文件的系统接口(可能创建文件),有下面两种格式: 

  • int open(const char* file, int flag)
  • int open(const char* file, int flag, int mode) 

使用open函数,要包含三个头文件:<sys/types.h>、<sys/stat.h>和<sys/fcntl.h> 

其中,file的意义为文件打开的路径,flag为打开方式的标识方法,mode为如果要创建文件的话文件的起始权限。

关于file,就是带有路径的文件的文件名。flag类似于C语言的open函数中"w"、"a"、"r"等打开方式控制的标识,主要的flag和对应的功能以下几种:

  • O_RDONLY -- 以只读方式打开文件。
  • O_WRONLY -- 以只写的方式打开文件。
  • O_RDWR -- 读写方式打开文件。
  • O_CREAT -- 如果指定的文件不存在,那就创建它。注意O_WRONLY与C语言的"w"不同的是,在open中只声明O_WRONLY并不会在文件不存在的时候创建文件。
  • O_APPEND -- 追加写入文件的内容,而不覆盖文件原有内容。
  • O_TRUNC -- 清空文件的内容后写文件。如果仅仅声明O_WRONLY,那么仅仅是在文件起始位置开始写数据,写多少内容覆盖文件原来的多少内容,不会情况原有内容。

如果我们同时需要两种flag的功能,那么应该使用按位或操作符,来同时对两个位置进行标记。如:我们希望open以只写的方式打开某个文件的同时在文件不存在的时候创建它,那么flag的传参方式为O_WRONLY|O_CREAT。

mode为文件创建的起始权限,以8进制的方式传参,如果我们希望创建文件的起始权限为拥有者、所属组和其它人都是rw-,那么在调用open时就应该给mode传0666。

但是,实际创建文件的起始权限,要经过系统的文件权限掩码过滤相应权限。Linux系统默认的文件掩码一般为0002,即:其它人不能拥有写文件的权限。那么,我们就可以在创建的进程的文件中,使用umask(0000),将该进程中的文件掩码暂时改为0000,不对任何权限进行过滤,这样就可以按照希望的方式给定文件的起始权限。

open接口的返回值:a.如果文件打开成功,就返回被打开的文件的文件描述符。b.如果文件打开失败,那么就返回-1。

再使用open之后,应当通过if判断open函数的返回值,再确保文件打开成功之后,再使用文件。

代码3.1:文件打开

#include<stdio.h>    
#include<unistd.h>    
#include<sys/types.h>    
#include<sys/stat.h>    
#include<sys/fcntl.h>    
    
int main()    
{    
    int fd1 = open("log1.txt", O_WRONLY|O_CREAT);   //以只写的方式打开log1.txt文件,如果文件不存在就创建    
    if(fd1 < 0)  //判断打开是否成功    
    {    
        perror("open fd1");    
        return 1;    
    }    
    
    //下面省略打开文件是否成功的判断    
    int fd2 = open("log2.txt", O_RDONLY);   //以只读方式打开log2.txt文件    
    int fd3 = open("log3.txt", O_WRONLY|O_APPEND);   //以追加写的方式打开log3.txt文件    
    int fd4 = open("log4.txt", O_WRONLY|O_TRUNC);    //清空文件原有内容再写入    
    
    umask(0000);   //在此之后,权限掩码为0000    
    int fd5 = open("log5.txt", O_WRONLY|O_CREAT, 0666);   //给定权限0666创建文件    
    
    close(fd1);    
    close(fd2);    
    close(fd3);    
    close(fd4);    
    close(fd5);                                                                                                                                                                                                                               
    
    return 0;    
} 

3.2 close接口 -- 关闭文件

close用于关闭某个被open接口打开的文件,其函数原型为:

  • int close(int fd) -- 其中fd为文件描述符,关闭文件成功返回0,失败返回-1。

使用close函数需要包含的头文件:<unistd.h> 

close一般配合open的返回值使用,如果传入错误的文件描述符,那么close就会失败。

3.3 write接口 -- 向文件中写内容

  • 函数原型:ssize_t wirte(int fd, const void* ptr, size_t count)
  • 函数功能:将从ptr位置处开始的count个字节的内容,写入到fd所对应的文件中去。
  • 函数返回值:返回实际写入到文件中的bytes数。

使用write函数需要包含的头文件:<unistd.h>

代码3.2:写文件

#include<stdio.h>    
#include<string.h>    
#include<unistd.h>    
#include<sys/types.h>    
#include<sys/stat.h>    
#include<sys/fcntl.h>    
    
int main()    
{    
    int fd1 = open("log1.txt", O_WRONLY|O_CREAT|O_TRUNC);    
    if(fd1 < 0)    
    {    
        perror("open fd1");    
        return 1;    
    }    
    
    const char* s1 = "hello world\n";    
    //这里不需要strlen(s) + 1
    //字符串末尾以\0结尾是语言层面的规则,与文件无关
    write(fd1, s1, strlen(s1));
    write(fd1, s1, strlen(s1));
    write(fd1, s1, strlen(s1));

   int fd2 = open("log2.txt", O_WRONLY|O_CREAT|O_APPEND|O_TRUNC); 
   if(fd2 < 0)
   {
       perror("open fd2");
       return 2;
   }

   const char* s2 = "hello Linux\n";
   write(fd2, s2, strlen(s2));
   write(fd2, s2, strlen(s2));
   write(fd2, s2, strlen(s2));

   close(fd1);
   close(fd2);                                                                                                                                                                                                                                
                                                                                                                              
   return 0;                                                                                                                  
}

3.4 read接口 -- 从文件中读取内容

  • ssize_t read(int fd, void *buff, size_t count)
  • 从fd确定的文件后缀读取count个字符,到buff所指向的内存空间中去。
  • 函数返回值:a.如果成功从文件中读取到内容,就返回实际读取到的内容的bytes数  b.如果读到了文件的末尾,就返回0。

代码3.3:read读取文件内容

  #include<stdio.h>    
  #include<string.h>    
  #include<unistd.h>    
  #include<sys/types.h>    
  #include<sys/stat.h>    
  #include<sys/fcntl.h>    
      
  int main()    
  {    
      int fd = open("log1.txt", O_RDONLY);   //只读方式打开文件    
      if(fd < 0)  //检查文件是否打开成功    
      {    
          perror("open");    
          return 1;    
      }    
      
      char buff[100];   //记录存储从文件中读取到的内容    
      memset(buff, '\0', 100);  //空间内容初始化                                                                                                                                                                                              
      
      ssize_t ret = read(fd, buff, 100);   //读取100字节的内容到buff中        
      printf("ret = %d\n", ret);    
      printf("buff:%s\n", buff);    
      
      close(fd);    
      return 0;    
  }  

四. 总结

  • 在操作系统层面,一切可以进行IO操作的设备都被视为文件。从狭义上讲文件就是磁盘文件,从广义上讲文件包含磁盘、显示器、键盘、鼠标、网卡、声卡等诸多设备。
  • 每一种编程语言都会提高特定的IO接口,对系统IO接口进行封装,从而方便用户的使用、实现语言的跨平台性能。
  • Linux操作系统提供了open、write、read、close等系统调用接口,用于文件IO操作。

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

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

相关文章

axios文件上传和 Content-Type类型介绍

Content-Type的作用是什么&#xff1f; Content-Type: 用于在请求头部指定资源的类型和字符编码。 请求头中的content-type&#xff0c;就是 B端发给S端的数据类型描述 。 即告诉服务器端&#xff0c;我给你传的数据是某种类型的。 服务器端针对不同类型的数据&#xff0c;做法…

分布式监控Zabbix的部署

zabbix 6.0 一、zabbinxzabbix的简介Zabbix 6.0 功能组件zabbix的监控原理zabbix 6.0的特性 二、zabbix 6.0 部署部署 Nginx PHP 环境并测试部署数据库&#xff0c;要求 MySQL 5.7 或 Mariadb 10.5 及以上版本编译安装 zabbix Server 服务端部署 Web 前端&#xff0c;进行访问…

HTTP的一些概念

文章目录 HTTP定义URI&URL HTTP 常见的状态码http 首部请求首部字段响应首部字段为cookie服务的首部字段get请求和post请求及区别GET 和 POST 方法是否安全和幂等 HTTP特性HTTP&#xff08;1.1&#xff09; 的优点有哪些HTTP/1.1 的缺点HTTP/1.1 的性能HTTP/1.1 相比 HTTP/…

[MySQL]表的操作

[MySQL]表的操作 文章目录 [MySQL]表的操作1. 创建表2. 创建表的示例3. 查看表4. 修改表5. 删除表6. 关于表操作 1. 创建表 语法&#xff1a; CREATE TABLE [IF NOT EXISTS] table_name(field1 datatype1 [COMMENT 注释信息],field2 datatype2 [COMMENT 注释信息],field3 dat…

伦敦金实时报价技术分析

技术分析是从伦敦金的价格行为&#xff0c;来分析未来走势变化的方法。伦敦金市场的变化可以有多种表现形式&#xff0c;其中根据市场价格、成交量等的变化&#xff0c;可以探索出一些规律&#xff0c;技术分析者通过对市场过去和现在的行为&#xff0c;应用数学原理去理解价格…

【Python】基于Python的机器学习分类的模型选择:交叉验证和模型质量评估

目录 1 简介2 思路分解与说明3 完整代码 1 简介 最近完成一个工作&#xff0c;就基于一些表格化的数据进行机器学习分类。 由于分类是研究中的关键步骤&#xff0c;所以首先要选择到底哪个模型适合我们的分类任务。 比较传统且经典的选择方法就是用交叉验证。 交叉验证是什么可…

openwrt使用记录

背景&#xff1a; 平时在vmware中做实验时候&#xff0c;经常需要在不同的机器上下载一些github上的项目进行调试&#xff0c;之前解决方案是在路由器层小米ac2100上装openwrt&#xff0c;试用一番发现太卡了。放弃&#xff0c;这次在vmware中安装作为小米ac2100的旁路由 规划…

推荐五款优秀,可替代商业软件的开源软件

​ 在日常的使用中&#xff0c;我们需要使用各种软件来提高我们的工作效率或者进行创意的表达。然而&#xff0c;商业软件价格昂贵&#xff0c;某些国产软件又充斥着广告。因此&#xff0c;开源软件成为了一个不错的选择&#xff0c;以下是我推荐的五款优秀的开源软件。 图片浏…

一文get,最容易碰上的接口自动化测试问题汇总

本篇文章分享几个接口自动化用例编写过程遇到的问题总结&#xff0c;希望能对初次探索接口自动化测试的小伙伴们解决问题上提供一小部分思路。 sql语句内容出现错误 空格&#xff1a;由于有些字段判断是变量&#xff0c;需要将sql拼接起来&#xff0c;但是在拼接字符串时没有…

手写操作系统--完善内核加载器之内存检测

这一篇我们来完善内核加载器的功能&#xff0c;我们知道内存是很重要的区域&#xff0c;我们需要对内存有个大致的描述&#xff0c;哪些可用&#xff0c;那些不可用&#xff0c;内存有多大。因此在内核加载器中我们需要对内存进行检测。内存检测的方法翻译文档如下&#xff1a;…

DAY39:贪心算法(八)无重叠区间+划分字母区间+合并区间

文章目录 435.无重叠区间思路完整版注意点 右区间排序 763.划分字母区间思路完整版如何确定区间分界线debug测试时间复杂度 总结 56.合并区间思路最开始的写法&#xff1a;直接在原数组上修改debug测试 修改版时间复杂度总结 435.无重叠区间 给定一个区间的集合 intervals &am…

“钓鱼”网站也有https?如何一招识破?

作为企业网站安全建设的基础设施&#xff0c; SSL证书可以对数据进行加密传输&#xff0c;保护数据在传输过程中不被监听、截取和篡改&#xff0c;因此部署了SSL证书的网站会比传统的http协议更加安全&#xff0c;也更受主流操作系统和浏览器的信任。 然而随着SSL证书的普及&a…

AI做PPT,五分钟搞定别人一天的量,最喜欢卷PPT了

用AI做PPT 主题生成大纲制作PPT 主题生成大纲 如何使用人工智能工具&#xff0c;如ChatGPT和mindshow&#xff0c;快速生成PPT。 gpt国内版 制作PPT&#xff0c;你可能只有一个主题&#xff0c;但没有明确的提纲或思路。 问gpt&#xff1a;计算机视觉的周工作汇报。我这周学…

MyBatis 与 Hibernate 有哪些不同?

ORM框架的选择与适用场景 MyBatis和Hibernate都是Java领域中流行的面向关系型数据库的ORM&#xff08;对象关系映射&#xff09;框架。它们的共同目标是简化开发人员操作数据库的工作&#xff0c;提供便捷的持久化操作。然而&#xff0c;两者在设计理念和适用场景上有所不同。…

Zabbix6.0 的部署

目录 一、概述 二、 zabbix 1.zabbix简介 2.zabbix监控原理 3. Zabbix 6.0 新特性 3.1Zabbix server高可用防止硬件故障或计划维护期的停机 3.2 Zabbix 6.0 LTS新增Kubernetes监控功能&#xff0c;可以在Kubernetes系统从多个维度采集指标 4. Zabbix 6.0 功能组件 4.1Z…

浏览器内核的介绍

文章目录 1、什么是浏览器内核2、常用浏览器内核3、浏览器内核分类3. 1、Trident3.2、Gecko3.3、Webkit3.4、Chromium3.5、Presto3.6、国内主流浏览器 4、五大主流浏览器&#xff08;诞生顺序&#xff09;4.1、IE&#xff08;Internet Explorer&#xff09;浏览器4.2、Opera浏览…

解决vue3中使用个别form表单校验失灵

当我点击校验时 其他都有触发校验 唯独radio没有触发&#xff0c;绑定都没有问题 看一下代码 const data reactive({form: {},rules: {serverStatus: [{ required: true, message: "服务状态不能为空", trigger: change }],tenantName: [{ required: true, messag…

hypef 五、请求及响应

文档地址 Hyperf https://hyperf.wiki/2.0/#/zh-cn/response 一、请求 1.1 安装 composer require hyperf/http-message 框架自带不用手动安装。 1.2 请求对象 在 onRequest 生命周期内可获得Hyperf\HttpServer\Request对象。 可以通过以来注入和路由对应参数获取。 de…

我的创作纪念日 --- 简单记录

理想并不是一种空虚的东西&#xff0c;也并不玄奇&#xff1b;它既非幻想&#xff0c;更非野心&#xff0c;而是一种追求善美的意识。 机缘 大家好&#xff0c;今天是我成为创作者的第256天&#xff0c;也是我在CSDN上发布的第81篇文章。在这里&#xff0c;我想和大家简单记…

C#(五十五)之线程死锁

死锁是指多个线程共享资源是&#xff0c;都占用同意部分资源&#xff0c;而且都在等待对方师范另一部分资源&#xff0c;从而导致程序停滞不前的情况 示例&#xff1a; /// <summary>/// 定义一个刀/// </summary>public static object knife new object();/// …