Linux操作系统3-文件与IO操作1(从C语言IO操作到系统调用)
上篇文章:Linux操作系统2-进程控制3(进程替换,exec相关函数和系统调用)_execv系统调用-CSDN博客
本篇代码Gitee仓库:myLerningCode · 橘子真甜/linux学习 - 码云 - 开源中国 (gitee.com)
本篇重点:C语言基础IO与系统调用
目录
一. 文件相关知识
二. C语言的文件操作
2.1 fopen
2.2 fclose
2.3 fread
2.4 fwrite
2.5 fprintf
2.6 举例代码
三. 文件相关的IO系统调用
3.1 open
3.2 close
3.3 write
3.4 read
3.5 举例代码操作
四. OS是如何管理被打开的文件?
4.1 文件fd
五. 下篇重点: 文件fd, Linux下一切皆文件
一. 文件相关知识
在基础指令这篇文章 Linux基础1-基本指令2(你真的了解文件吗?)-CSDN博客 中,我们提到了文件的相关命令。总结一下
1 一个空文件也需要占用空间
2 文件 = 文件内容 + 文件属性
3 文件操作 = 操作文件内容 + 操作文件属性
4 我们使用文件路径+文件名标记一个文件
5 进程想要访问一个文件必须要先通过OS打开这个文件
C语言为用户提供了文件操作,C++也有相关的文件操作。像这些语言级别的库函数提供的文件操作,都是对OS提供的文件操作系统调用的封装。所以,学习系统调用提供的文件操作有利于我们掌握语言级的文件操作
二. C语言的文件操作
C语言中的库函数为我们提供了很多操作文件的函数:fopen,fclose,fwrite,fread,fprintf,fscanf...等。
2.1 fopen
fopen用于打开一个文件,其函数原型如下:
//所需头文件
#include <stdio.h>//函数原型
FILE* fopen(const char* filename, const char* mode);//filename,打开文件的路径。直接写名字默认在当前路径下查找//mode,打开的方式
"r" 只读方式打开
"w" 只写方式打开,默认会清空文件中的内容
"a" 只写,写的方式是追加
"b" 以二进制方式打开,一般配合r和w使用
"W+" 读写,没有文件会创建,写方式是清空文件从头开始写
"r+" 读写,从头开始写文件
"a+" 读写,追加写
2.2 fclose
用于关闭一个打开的文件
//函数原型
int fclose(FILE* stream);//关闭stream这个文件流(被fopen打开的文件流)
2.3 fread
用于读一个文件中的数据
//函数原型
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);ptr: 读取的文件存放在内存中的位置
size:读取文件中元素大小(以字节为单位)
nmemb:读取文件元素的数量
stream:读取文件的文件流(你要读取的文件)
2.4 fwrite
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);//参数和fread类似//只是功能是将ptr写入到stream这个文件流中
2.5 fprintf
//函数原型
int fprintf(FILE *stream, const char *format, ...);//用法和printf一样,不过是将数据写入到stream这个文件流中
其他文件操作都和上述文件操作类似。具体内容可以查找man手册
2.6 举例代码
用一段代码来举例这些操作的用法
test.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>#include <unistd.h>
#define MY_FILENAME "log.txt"int main()
{// 1.写入数据FILE *fp = fopen(MY_FILENAME, "w");if (fp == NULL){// 打开文件失败perror("fopen");}// 2.使用fwrite写数据,写入三行 Hello worldconst char *buffer = "Hello World!\n";fwrite(buffer, sizeof(char), strlen(buffer), fp);fwrite(buffer, sizeof(char), strlen(buffer), fp);fwrite(buffer, sizeof(char), strlen(buffer), fp);// 3. 关闭文件fclose(fp);fp = NULL;return 0;
}
Makefile
test:test.cgcc -o $@ $^ -std=c99.PHONY:clean
clean:rm -rf test log.txt
测试结果如下:
修改 log.txt 和 test.c 进行读取数据
log.txt
Hello World!
Hello World!
Hello World!
YZC yzc
abc 123
156 1sg 45qe1r 5h@#@ ^% 56 @# ^re8 5qh qer56h 16 32`7 tr314yt 9bm v891-3
test.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>#include <unistd.h>
#define MY_FILENAME "log.txt"int main()
{// 1.读取数据FILE *fp = fopen(MY_FILENAME, "r");if (fp == NULL){// 打开文件失败perror("fopen");}// 2.使用fread写数据,写入三行 Hello worldchar buffer[200];fread(buffer, sizeof(char), 200, fp);buffer[strlen(buffer) - 1] = '\0'; //将最后的'\n'变为'\0'printf("%s\n", buffer);// 3. 关闭文件fclose(fp);fp = NULL;return 0;
}
测试结果:
注意C语言的字符串默认在结尾有一个'0',而文本文件中末尾并没有'\0'。所以我们使用C语言接口读取文件后,如果是字符串,需要在末尾加上'\0'
三. 文件相关的IO系统调用
3.1 open
打开文件的系统调用
//所需头文件
#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 打开文件的名字
//flags 打开文件的方式
//mode 创建文件时候文件的权限//常见的flag
O_RDONLY 表示只读
O_WRONLY 只写
O_WRONLY 读写
O_APPEND 追加写
O_CREAT 没有这个文件要创建文件
O_TRUNC 打开文件的时候清空文件内容
3.2 close
关闭文件fd的系统调用
//文件关闭
#include <unistd.h>//函数原型
int close(int fildes);
3.3 write
向文件写入数据
//头文件
#include <unistd.h>//函数原型
ssize_t write(int fd, const void *buf, size_t count);//fd 写入的文件fd//buf 要写的数据缓冲区来源//count 写入的字节个数//返回值,成功写入,返回写入的字符数,失败返回-1
buf是void* 的原因:在系统看来,任何数据都是二进制
3.4 read
从文件中读取数据
//头文件
#include <unistd.h>//函数原型
ssize_t read(int fd, void *buf, size_t count)//将fd文件中的count字节数量的数据读取到buf中//返回0表示读取到文件结尾
3.5 举例代码操作
#include <stdio.h>
#include <string.h>
#include <stdlib.h>#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define MY_FILENAME "log.txt"int main()
{// 1.打开文件,方式是读写,没有文件创建,清空文件从头开始写。创建的文件权限是0666umask(0); // 清空系统的umaskint fd1 = open(MY_FILENAME, O_WRONLY | O_CREAT | O_TRUNC, 0666);// 如果写入失败if (fd1 < 0){perror("open");return -1;}// 2.写入数据char buffer[64];int cnt = 5;while (cnt){sprintf(buffer, "YZC Hello World [%d]\n", cnt--); // 将数据写入缓冲区write(fd1, buffer, strlen(buffer)); // 向文件写入数据不需要添加'\0'}// 3.关闭文件描述符fdclose(fd1);// 4.读取这些数据int fd2 = open(MY_FILENAME, O_RDONLY);// 如果文件打开错误if (fd2 < 0){perror("open");return -1;}// 读取文件的时候,buffer最多读取sizeof(buf)个数据,由于有'\0'。所以要-1char *buf[64];ssize_t num = read(fd2, buf, sizeof(buf) - 1);printf("%s", buf);// 关闭文件close(fd2);return 0;
}
测试结果:
语言级别的IO操作库函数都是对系统调用IO操作的封装
四. OS是如何管理被打开的文件?
我们知道,OS通过PCB来管理进程。在OS中有很多进程,这些进程也会打开很多的文件。那么OS是如何管理这些被打开的文件的?
OS为了管理被打开的文件,创建对应的内核数据结构 struct_file。这个结构体包含了文件的大量属性。
4.1 文件fd
文件fd是什么东西?我们打印出来看看
#include <stdio.h>
#include <string.h>
#include <stdlib.h>#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define MY_FILENAME "log.txt"int main()
{umask(0); //清楚umask码,仅仅修改该进程创建的文件int fd1 = open(MY_FILENAME, O_WRONLY | O_CREAT | O_APPEND, 0666);int fd2 = open(MY_FILENAME, O_WRONLY | O_CREAT | O_APPEND, 0666);int fd3 = open(MY_FILENAME, O_WRONLY | O_CREAT | O_APPEND, 0666);int fd4 = open(MY_FILENAME, O_WRONLY | O_CREAT | O_APPEND, 0666);int fd5 = open(MY_FILENAME, O_WRONLY | O_CREAT | O_APPEND, 0666);printf("fd1:%d\n",fd1); printf("fd2:%d\n",fd2); printf("fd3:%d\n",fd3); printf("fd4:%d\n",fd4); printf("fd5:%d\n",fd5); close(fd1);close(fd2);close(fd3);close(fd4);close(fd5);return 0;
}
fd为什么从3开始?
因为C语言会默认打开三个输入输出流,stdin, stdout, stderr。
即标准输入,标准输出,标准错误。它们占用了0 1 2
通过stdin这个文件的结构体中的 _fileno 即可获取fd
#include <stdio.h>
#include <string.h>
#include <stdlib.h>#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define MY_FILENAME "log.txt"int main()
{umask(0); //清楚umask码,仅仅修改该进程创建的文件int fd1 = open(MY_FILENAME, O_WRONLY | O_CREAT | O_APPEND, 0666);int fd2 = open(MY_FILENAME, O_WRONLY | O_CREAT | O_APPEND, 0666);int fd3 = open(MY_FILENAME, O_WRONLY | O_CREAT | O_APPEND, 0666);int fd4 = open(MY_FILENAME, O_WRONLY | O_CREAT | O_APPEND, 0666);int fd5 = open(MY_FILENAME, O_WRONLY | O_CREAT | O_APPEND, 0666);printf("stdin->fd [%d]\n",stdin->_fileno); printf("stdout->fd [%d]\n",stdout->_fileno); printf("stderr->fd [%d]\n",stderr->_fileno); printf("fd1:%d\n",fd1); printf("fd2:%d\n",fd2); printf("fd3:%d\n",fd3); printf("fd4:%d\n",fd4); printf("fd5:%d\n",fd5); close(fd1);close(fd2);close(fd3);close(fd4);close(fd5);return 0;
}
测试结果如下:
这些数字其实是一个数字的下标,在PCB中有一个指针数组 (称为文件描述符表)。这个指针数组存放的是指向struct_file这个文件管理的内核数据结构。
进程通过fd这个数组下标就能够访问文件结构体!
具体关系可见下图:
五. 下篇重点: 文件fd, Linux下一切皆文件
相关文章:

Linux操作系统3-文件与IO操作1(从C语言IO操作到系统调用)
上篇文章:Linux操作系统2-进程控制3(进程替换,exec相关函数和系统调用)_execv系统调用-CSDN博客 本篇代码Gitee仓库:myLerningCode 橘子真甜/linux学习 - 码云 - 开源中国 (gitee.com) 本篇重点:C语言基础IO与系统调用 目录 一.…...

【Python网络爬虫笔记】8- (BeautifulSoup)抓取电影天堂2024年最新电影,并保存所有电影名称和链接
目录 一. BeautifulSoup的作用二. 核心方法介绍2.1 构造函数2.2 find()方法2.3 find_all()方法2.4 select()方法 三. 网络爬虫中使用BeautifulSoup四、案例爬取结果 一. BeautifulSoup的作用 解析HTML/XML文档:它可以将复杂的HTML或XML文本转换为易于操作的树形结构…...

Rancher V2.7.0安装教程
1、执行Docker命令 docker run -d --privileged --restartunless-stopped -p 80:80 -p 443:443 -v /home/rancher:/var/lib/rancher --name rancher registry.cn-hangzhou.aliyuncs.com/rancher/rancher:v2.7.0 注:如果容器启动失败,参考我另外一篇文章…...

STM32MX 配置CANFD收发通讯
一、环境 MCU:STM32G0B1CEU6 CAN收发器:JIA1042 二、MX配置 配置SYS 配置canfd并开启中断,我开了两个FDCAN,配置是一样的,这里贴一下波特率的计算公式: 也就是:CAN时钟频率/预分频器/&…...

(12)时间序列预测之MICN(CNN)
文章目录 前言1. challenge 一、网络结构1. MHDecomp2. Trend-cyclical Prediction Block3. Seasonal Prediction BlockMIC LayerMerge 实验结果1.长时预测 总结参考 文章信息 模型: MICN (Multi-scale Isometric Convolution Network)关键词: 长时预测…...

嵌入式蓝桥杯学习3 外部中断实现按键
Cubemx配置 前面的配置依旧一样。 原文链接:https://blog.csdn.net/m0_74246768/article/details/144227188 1.打开cubemx,将PB0到PB1配置为GPIO_EXTI模式。 2.在System-Core中点击GPIO,选择PB0到PB2, GPIO_Mode(触…...

自由学习记录(29)
FileStream FileStream 是 .NET 中用于文件操作的重要类,位于 System.IO 命名空间中。它提供了对文件的同步和异步读写操作。以下是它的方法签名和重载的详细介绍: 构造函数签名和重载 FileStream 提供多个构造函数,允许在创建实例时指定文…...
使用YOLO系列txt目标检测标签的滑窗切割:批量处理图像和标签的实用工具
使用YOLO系列txt目标检测标签的滑窗切割:批量处理图像和标签的实用工具 使用YOLO的TXT目标检测标签的滑窗切割:批量处理图像和标签的实用工具背景1. 代码概述2. 滑窗切割算法原理滑窗切割步骤:示例: 3. **代码实现**1. **加载标签…...

架构10-可观测性
零、文章目录 架构10-可观测性 1、可观测性 (1)可观测性的背景 **历史沿革:**可观测性最初由匈牙利数学家鲁道夫卡尔曼提出,用于线性动态控制系统。后来,该概念被引入到计算机科学中。**现代意义:**在分…...

git管理Unity项目的正确方式
git管理Unity项目的正确打开方式 前言:对于刚开始git进行unity项目管理的时候,我采取的方式是全部文件上传,文件数量太多以及上传太大,我尝试过一下几个方法: 利用git的LFS大文件进行传方式,可行但比较麻…...

openssl使用哈希算法生成随机密钥
文章目录 一、openssl中随机数函数**OpenSSL 随机数函数概览**1. **核心随机数函数** **常用函数详解**1. RAND_bytes2. RAND_priv_bytes3. RAND_seed 和 RAND_add4. RAND_status **随机数生成器的熵池****常见用例****注意事项** 二、使用哈希算法生成随机的密钥 一、openssl中…...

将word里自带公式编辑器编辑的公式转换成用mathtype编辑的格式
文章目录 将word里自带公式编辑器编辑的公式转换成用mathtype编辑的格式MathType安装问题MathType30天试用延期MathPage.wll文件找不到问题 将word里自带公式编辑器编辑的公式转换成用mathtype编辑的格式 word自带公式编辑器编辑的公式格式: MathType编辑的格式&a…...

校园失物招领系统基于 SpringBoot:点亮校园归还遗失物之光
2系统开发环境 2.1vue技术 Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式JavaScript框架。 [5] 与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第…...

dhcpd服务器的配置与管理(超详细!!!)
前提条件: (1)虚拟机能够联网(如果nat模式不能联网的看另一期) CentOS7 NAT模式不能联网-CSDN博客 (2)系统是Centos8,因为下载的dhcp-server软件包版本和Centos7不匹配,如果你能成…...
Qml之基本控件
一.Qml常用控件 1.Text(显示普通文本和富文本) 1.1显示普通文本: Window { visible: true width: 320 height: 240 title: qsTr("Hello World") Text { text: "Hello World!" font.family: "Helvetica" font.pointSize: 24 color:…...
【Java从入门到放弃 之 Stream API】
Java Stream API Stream API行为参数化传递代码Lambda表达式Lambda 表达式的语法方法引用 Lambda 表达式的实际应用集合操作并发编程 Lambda 表达式的注意事项总结 Stream API Java8提供了一个全新的API - Stream。引入这个Stream的主要目的,一个是可以支持更好的并…...
Ruby On Rails 笔记1——Rails 入门
突然想跟着官方文档把Ruby On Rails过一遍,把一些有用的记下来就可以一直看了,do它! https://guides.rubyonrails.org/v7.2/ 注:官网是英文文档,我自己翻译了一下,不确保完全准确,只供自己学习开发使用。 …...
高效开发 Python Web 应用:FastAPI 数据验证与响应体设计
高效开发 Python Web 应用:FastAPI 数据验证与响应体设计 目录 🧑💻 FastAPI 的数据验证系统与 Pydantic 模型📦 响应体与模型:定义响应数据的最佳实践🔄 响应模型与查询参数的结合:增强灵活…...

基于“开源 2+1 链动 O2O 商城小程序”的门店拉新策略与流程设计
摘要:在数字化商业浪潮席卷之下,实体门店面临着激烈的市场竞争,如何高效拉新成为关乎门店生存与发展的关键问题。本文聚焦于“开源 21 链动 O2O 商城小程序”,深入探讨结合多种手段的门店拉新策略及详细流程设计。通过剖析到店扫码…...
33.5 remote实战项目之设计prometheus数据源的结构
本节重点介绍 : 项目要求 通过remote read读取prometheus中的数据通过remote write向prometheus中写入数据 准备工作 新建项目 prome_remote_read_write设计prometheus 数据源的结构初始化 项目要求 通过remote read读取prometheus中的数据通过remote write向prometheus中写…...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:
一、属性动画概述NETX 作用:实现组件通用属性的渐变过渡效果,提升用户体验。支持属性:width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项: 布局类属性(如宽高)变化时&#…...
【算法训练营Day07】字符串part1
文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接:344. 反转字符串 双指针法,两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...

从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)
设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile,新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...

ios苹果系统,js 滑动屏幕、锚定无效
现象:window.addEventListener监听touch无效,划不动屏幕,但是代码逻辑都有执行到。 scrollIntoView也无效。 原因:这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作,从而会影响…...
C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...
python打卡第47天
昨天代码中注意力热图的部分顺移至今天 知识点回顾: 热力图 作业:对比不同卷积层热图可视化的结果 def visualize_attention_map(model, test_loader, device, class_names, num_samples3):"""可视化模型的注意力热力图,展示模…...
【java】【服务器】线程上下文丢失 是指什么
目录 ■前言 ■正文开始 线程上下文的核心组成部分 为什么会出现上下文丢失? 直观示例说明 为什么上下文如此重要? 解决上下文丢失的关键 总结 ■如果我想在servlet中使用线程,代码应该如何实现 推荐方案:使用 ManagedE…...