Linux - 文件操作和系统接口

news2024/12/30 2:03:10

                                                             ​​​​​​​ 

                                               感谢各位 点赞 收藏 评论 三连支持

                                              本文章收录于专栏【Linux系统编程】   

                                                     ❀希望能对大家有所帮助❀

                                                       本文章由 风君子吖 原创

        ​​​​​​​        ​​​​​​​        ​​​​​​​               

前言

 对于文件操作,不知大家是否有过接触,例如(fopen,fclose...等),那么大家在刚接触使用这些文件接口的时候,是否有过一些难以言喻的懵懂感,不仅不太理解,而且难以刻于脑海。

本章内容不仅会讲解文件操作内容知识,还会讲授一个重要的概念,这个概念会影响你对于Linux系统编程的学习!

文件

什么是文件? 

对于我们日常使用来言,文件似乎就是我们每天运行的游戏,app(.exe)或者我们写的c语言代码(.c和.cpp等),它们都是在磁盘上存储的。  而这种观点知识狭义的,实际上,对于文件有着更广的范域。

文件不仅仅只是它存储的数据,还有它的属性(例如它的权限,它的拥有者、所有组,它的创建时间,它的类型)。

万物皆文件

对于操作系统而言 ,只要是能够被input读取或者output输出的设备就叫做文件!

而我们的键盘和显示器也有着具备读写的能力,这说明它们也可以被称之为文件,而我们日常使用的狭义上的文件是被存储在磁盘上的,我们如果要进行读写,也是对磁盘进行读写,那么也就说明磁盘也是文件。

而对于硬件的读写操作,只有操作系统才有权限进行读写!

文件的操作

对文件的操作,无非就是对文件的数据和文件的属性操作。

那么对于文件,我们知道了它具备读写的作用,那么这种读写的属性,是针对谁而言呢?

磁盘上的文件: 具备read(读)、write(写)的属性。

显示器:printf、cout  ->这是write(写)的属性。

键盘: scanf、cin -> 这是read(读)的属性

是不是对于键盘和显示器的属性有些诧异?,我们在显示器上难道不是读吗,不然怎么看到这篇文章?键盘不是我们一个一个打字才输进去的吗,可为什么却是读?

举一个例子,一个文件在磁盘上存储着,我们如果要访问一个文件,就需要写代码运行起来后才访问这个文件,而本质其实是进程在访问文件!

所以,我们不能站在自己的角度去理解它们的属性,我们应该站在进程的角度去理解,而进程是被加载到内存的。  对于一个进程,我们需要从键盘上读取到数据,而进程可以向显示器输出信息让我们看到。

回顾C语言的文件操作函数

打开一个文件,C语言提供的接口

 

写文件

 #include<stdio.h>
 #include<unistd.h>
 #include<string.h>
 int main()
 {
   FILE* fl = fopen("file.txt","w");                                                                                                                                           
   if(fl == NULL)
   {
     perror("Open");
     return 1;
   }
   //文件write
   const char* str1 = "hello fputs\n";
   fputs(str1,fl);
   const char* str2 = "hello fwrite\n";
   fwrite(str2,strlen(str2),1,fl);
   const char* str3 = "hello fprintf\n";
   fprintf(fl,"%s",str3);
   fclose(fl);
   return 0;
 }

fopen以w的形式打开,如果该名字文件不存在则会在当前目录下创建一个该名字的文件。

而这个文件是根据什么在哪里创建的呢? 当前目录?谁的当前目录?

答案是进程当前的工作目录!

 

 

 读文件

效果是不是跟cat指令一样?

那么我们这就来模拟一下cat

cat指令的模拟

系统接口

刚刚我们以上回顾的文件操作的接口函数都是C语言给我们提供的接口,但是通过我们之前已学的知识,如果要对硬件进行读写操作,只有操作系统有这个权限,用户是没有权限的。而我们使用的fopen之类的函数都是语言层面上的接口,所以我们就可以知道语言提供给我们的函数,在它实现的底层必然调用了系统接口。

所以这里就引出了一个我们为什么要学习Linux系统编程的概念,在我们之前学习进程的等待,替换中,我们也接触过了系统提供的接口,我们可以清晰的感受到,对于语言层面上提供的接口,我们虽然说在使用上会简洁,方便许多,但是对于它的实际理解却略有不足,单看我们现在使用的C语言提供文件操作函数,我们虽然知道如何去用,但是难以理解它到底做了什么。

所以,学习Linux系统编程,是为了更好的去理解底层原理,强化我们的知识,我们的视野,为什么? 因为不同的语言,它们对于各自的功能的实现方式是不同的,对于底层实现的系统接口的封装也是不同的,所以,学习了系统接口,是为了更好的理解语言层面的函数在做什么? 而且还有一个好处就是,再去学各种不同的语言提供给你的功能接口,都会显得更加得心应手。

语言的跨平台性

这里又在引出一个概念,语言的跨平台性,什么是跨平台?  比如说你在Linux上写了一个代码,不仅可以在Linux系统下运行,还可以在Windows下也能运行,这就是跨平台性。

那么Windows和Linux提供的系统接口会是一样的吗?  但是是不一样的。

那么语言是如何实现 不同平台提供了不同的系统接口 但却实现了语言的跨平台性的呢?

答案是 实现C语言库的那些高级程序员为了兼容不同的平台,必然每个平台都要实现一份语言层面的函数,再通过条件编译进行动态裁剪就能够实现在不同的平台使用对应的函数。

Linux系统提供的文件操作接口

open

man 2 open

先来看看第一个open

int open(const char *pathname, int flags);

先看返回值 是一个int类型,它返回的是如果文件打开成功,则返回该文件的文件描述符fd(对于文件描述符我们放在下一篇文章讲),而如果打开失败,则返回-1。 

第一个参数是pathname就是路径,这个很好理解。

第二个参数是flags(标识),这个是什么呢? 这个就很有意思了,这个涉及到位标识符的知识

先来看看man对于这个参数是干嘛用的?

 这些全是大写的单词可以理解为选项,而从命名风格来看就可以知道这些都是宏,对于这些选项,每个选项都代表不同的意义,有的是代表追加,有的是代表创建...(大家下来可以都看看)

那么如果有这么多选项,我们该用什么办法把它们都能传进去呢? 如果是一个功能代表一个参数的话,那么不仅对于函数来说会显得臃肿不好看,更是针对系统接口来讲是不可饶恕的,效率太低了。采用位标识符就很好的解决了这个问题。

位标识符(重点)

以一个int来作为参数的话,他有几个比特位?28个 

0000 0000 0000 0000

而如果每个比特位代表一个选项的话,是不是就可以通过一个int 类型来完成28个选项的判断?  是的 

这种方法效率高效且占用空间极低,堪称完美!

示例

 

 通过按位与来实现传入多个选项

现在我们来上手一下使用open

我们想对log.txt文件进行只写操作,所以传了O_WRONLY 

 

 而我们当前目录下是没有log.txt文件的

 运行程序后发现打印出了 没有找到该文件或者文件夹,这是为什么,按照我们使用C语言的fopen,如果没有这个文件难道不是应该会自动创建这个文件吗,为什么这里没有创建?

这就提到了我们刚才说讲的,在语言层面上给你提供的接口对系统接口进行了封装,是为了让我们能够更方便地使用。

所以如果我们想要 如果没有这个文件就自动创建这个文件的效果,那么系统接口函数给了我们O_CREAT这个宏选项。

 这个时候我们再运行一下程序

 虽然log.txt文件被创建出来了,但是它的文件权限有问题!

这个时候第二个open就有了作用了

int open(const char *pathname, int flags, mode_t mode);

它的第三个参数就是针对如果要创建文件,对该文件的权限进行设置

而对于文件的权限设置我们已经不陌生了。

这个时候我们输入0666(八进制) 代表 -rw-rw-rw

运行程序之后,新生成的文件的权限虽然比上次的正确多了,但是为什么是-rw-rw-r--?我们设置明明的是-rw-rw-rw- 

 这是因为有掩码的存在!

umask(用户创建文件掩码)

 使用umask指令可以查看当前的umask值

这里我的umask值是0002(八进制)转化为二进制就是0 000 000 010

那么掩码是怎么让我设置的0666权限编程0664的呢?

0666: 0 110 110 110                ->                          0 110 110 110 

0002: 0 000 000 010               -> 按位取反           1 111 111 101

                                                   ->按位与运算     & -------------------

结果:                                                                      0 110 110 100

这就是掩码的作用

那么有没有办法重新设置掩码呢? 系统提供了一个接口

mode_t umask(mode_t mask);

                                                                                     

 设置umask值为0

 

 这个时候创建的新文件的权限正确了!

close

 close没什么好说的,传入文件的文件描述符fd就可以关闭文件

write

 fd是要写入的文件的文件描述符,buf是要输入的字符串,count是期望写入的buf的字节数

返回值是实际写入的buf的字节数

先来看一下下面的代码

注意open的参数,想在打开的log.txt文件写入aa。

此时的log.txt已经有数据

运行代码之后

 

 发现log.txt在写入aa之前并没有清空原先log.txt中的数据,这是因为我们在open少传了个选项

 

 这时就实现了fopen("log.txt","w")的完整功能。

而如果要实现fopen("log.txt","a")的功能,则需要把O_TRUNC替换为O_APPEND

多运行几次程序 

 这就实现了fopen("log.txt","a")的完整功能!

read

 参数和返回值域write差不多,只不过这里的buf是需要从文件中读取内容

 

 读取成功!

初识文件描述符fd

 对于文件描述符的内容,我会再重新写一篇文章专门针对文件再写一章,里面会详细讲解文件描述符和文件的密切关系,现在我们就来简单认识一下

首先对于我们上面写的代码,我们总能看到我们输出的fd为3

 

 能从结果发现文件描述符的似乎是一串连续的数字

而如果是一串连续的数字,那么为什么又是从3开始的呢?

这是因为 在我们打开c语言程序的时候,会默认打开stdin(标准输入) ,stdout(标准输出),stderr(标准错误),这三个文件,它们分别对应我们的键盘,显示器,显示器。

而它们的文件描述符分别是stdin(0),stdout(1),stderr(2)。

如何证明?

 既然1代表标准输出,那么这个程序是不是也能将hello world输出到屏幕上?

 正确的!

FILE是什么

先说结论,它是一个结构体,里面存储着一个被打开的文件的几乎所有内容(包括属性)

如何证明?

 _fileno代表的就是文件描述符fd

 对于FILE我也会在下一篇文章详细讲解!

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

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

相关文章

永恒之黑漏洞复现

一、实验环境搭建 系统镜像&#xff1a; ed2k://|file|cn_windows_10_consumer_editions_version_1903_x64_dvd_8f05241d.iso|4905476096|F28FDC23DA34D55BA466BFD6E91DD311|/ 建议使用迅雷下载,安装版本选win10专业版 安装完后记得一定要关闭defender&#xff0c;防火墙&…

配置主机加入已有 tinc 集群简明过程

文章目录 Cent OS服务器安装tinc配置文件过程中使用到的一些Linux命令小记 启动tinc开放端口 Windows主机参考资料 本文的主要内容是如何将主机加入已有的 tinc 集群。 Cent OS服务器 安装tinc yum install tinc如果不先 su 到 root 账户的话&#xff0c;可能会无法安装。 因…

Python模块os 操作系统

目录 1. 系统类 --------------------- 解释器 --------------------- system 执行系统命令 wait 等待任意子进程 waitpid 等待指定的子进程 kill 指定杀死进程 abort 立即中止解释器 pipe 管道操作 --------------------- 随机字符 --------------------- urandom …

KMeans+DBSCAN密度聚类+层次聚类的使用(附案例实战)

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

数据结构之栈、队列——算法与数据结构入门笔记(四)

本文是算法与数据结构的学习笔记第四篇&#xff0c;将持续更新&#xff0c;欢迎小伙伴们阅读学习 。有不懂的或错误的地方&#xff0c;欢迎交流 栈 栈是一种线性数据结构&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶 (Top&…

虚幻5-编辑器扩展开发Editor-Slate的TabManager结构如下

目录 Editor-Slate WorkSpaceMenu(Slate相关类) Editor-Tab-界面刷新 Editor-Slate 基本上&#xff0c;地球人都知道&#xff08;我不是地球人&#xff09;虚幻引擎的Editor界面&#xff08;自定义&#xff09;通过Slate管理 Slate的入口是方法&#xff1a;&#xff1a;Co…

检测到“_CRT_STDIO_ISO_WIDE_SPECIFIERS”的不匹配项

libboost_thread-vc142-mt-x64-1_82.lib(thread.obj) : error LNK2038: 检测到“_CRT_STDIO_ISO_WIDE_SPECIFIERS”的不匹配项: 值“0”不匹配值“1”(AcadStr.obj 中) 1> 正在创建库 x64\Release\ArxDbg.lib 和对象 x64\Release\ArxDbg.exp : fatal error LNK1319: 检测到 …

这AI二维码也太酷炫了!谷歌生成式AI学习路径;媒体的AI炒作套路报告;使用GPT-4自动化制作短视频 | ShowMeAI日报

&#x1f440;日报&周刊合集 | &#x1f3a1;生产力工具与行业应用大全 | &#x1f9e1; 点赞关注评论拜托啦&#xff01; &#x1f916; 新鲜出炉&#xff01;2023人工智能10大分类排行榜 这是根据2023年6月德本咨询、eNet研究院和互联网周刊联调的人工智能排行榜&#xf…

smardaten简直是无代码软件开发的天花板

目录 前言 一、smardaten简单介绍 二、基于smardaten创建应用 1、创建一个炫酷的大屏 2、创建一个web端和移动端共存的应用 三、smardaten功能特性和优势 1、操作简单&#xff0c;快速上手 2、圆桌开发&#xff0c;效率倍升 3、图形编排&#xff0c;拖拽生效 4、低无代…

ARM---驱动开发

目录 1.驱动大纲&#xff1a; 2.单片机开发属于嵌入式开发吗&#xff1f; 3.RAM裸机代码和驱动有什么区别&#xff1f; 4.Linux系统的组成 5.宏内核、微内核 6.驱动移植 1.驱动大纲&#xff1a; &#xff08;1&#xff09;内核模块 &#xff08;2&#xff09;字符设备驱…

docker创建ubuntu 22.04

1、拉取镜像 sudo docker pull ubuntu:22.04 2、启动ubuntu22.04&#xff0c;这里映射物理机23端口对应docker22端口用于远程连接 sudo docker run -it -p 23:22 1f6ddc1b2547 /bin/bash 3、进入容器后配置远程&#xff1a; apt update apt upgrade apt install vim ap…

springboot+vue+java在线教育课程教学辅助系统

本文介绍了在线教育系统的开发全过程。通过分析在线教育系统管理的不足&#xff0c;创建了一个计算机管理在线教育系统的方案。文章介绍了在线教育系统的系统分析部分&#xff0c;包括可行性分析等&#xff0c;系统设计部分主要介绍了系统功能设计和数据库设计。课程辅助教学&a…

智慧PG集成开发平台pgting-cli发布了

介绍 两周前我们发布了智能页面搭建平台 —— 智慧PG(pgting)&#xff0c;深受用户青睐&#xff0c;很多用户尝试了在线开发组件。为了方便用户定制开发组件和组件共享&#xff0c;智慧PG设计之初就考虑了组件定制开发问题&#xff0c;为此&#xff0c;我们设计和研发了智慧PG…

Spring Catch

一、Spring Cache整合服务 1.pom.xml <!--spring catch--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId></dependency>2.application.properties #开启缓存空值&am…

canvas图片旋转,图片base64编码,保存图片

在一些业务场景中&#xff0c;常常需要前端对图片进行操作&#xff0c;这样可以将部分的性能压力转移到前端设备&#xff0c;有利于减小服务器压力&#xff0c;下面讲解前端怎么操作图片。 首先&#xff0c;对图片的操作都是依赖于canvas画布&#xff0c;这里对canvas标签不再赘…

华为OD机试题【字符统计】【2023 B卷 100分】

文章目录 &#x1f3af; 前言&#x1f3af; 题目描述&#x1f3af; 解题思路&#x1f4d9; Python代码实现&#x1f4d7; Java代码实现&#x1f4d8; C语言代码实现 &#x1f3af; 前言 &#x1f3c6; 《华为机试真题》专栏含2023年牛客网面经、华为面经试题、华为OD机试真题最…

LVGL学习(3):页面切换原理和页面管理实现

在LVGL中&#xff0c;大多情况下是有多个页面的&#xff0c;一般来说页面的切换有两种情况&#xff1a; 删除当前的页面&#xff0c;创建新页面加载保留当前的页面&#xff0c;创建新页面加载 我们来分析一下这两种情况&#xff0c;比如页面1有一个列表框&#xff0c;有三个选…

VESC操作入门(三)——PPM输入控制和ADC输入控制

目录 一、PPM输入控制1.1、硬件准备1.2、PPM信号1.3、校准电机1.4、输入设置 二、ADC输入控制2.1、硬件准备2.2、更改固件2.3、电压信号2.4、校准电机2.5、输入设置 三、电动车转把控制3.1、转把说明3.2、转把测试 四、ADC的其它模式4.1、Current No Reverse Brake ADC24.2、Cu…

Windows 安装Redis教程(图文详解)_下载使用redis_Redis可视化_配置Redis环境变量

下载、安装和配置 给出自己的百度网盘链接&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/14uO7jSm0DuoBWBaFO-obTw 提取码&#xff1a;1234 1. Redis下载 由于 Redis 官网没有提供 windows 版本的&#xff0c;只能去 github 上下载。 1、下载地址&#xff1a;github链…

【C/C++】基础知识之bool布尔类型

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; &#x1f525;c系列专栏&#xff1a;C/C零基础到精通 &#x1f525; 给大…