当前位置: 首页 > news >正文

【Linux】第二十一站:文件(一)

文章目录

  • 一、共识原理
  • 二、C系列文件接口
  • 三、从C过渡到系统:文件系统调用
  • 四、访问文件的本质

一、共识原理

  1. 文件 = 内容 + 属性

  2. 文件分为打开的文件没打开的文件

  3. 打开的文件:是谁打开的?是进程!----所以研究打开的文件本质是研究进程和文件的关系!

  4. 没打开的文件:在哪里放着呢?在磁盘上放着。我们最关注什么问题?没有被打开的文件非常多。文件如何被分门别类的放置好—即我们要快速的进行增删查改—即快速的找到文件

所以上面的问题总结下来就是如何存储?

  1. 一个文件被打开,必须先被加载到内存中!

进程 : 打开的文件 = 1:n(即一个进程可以打开任意个文件)

由以上两点我们可以得到:操作系统内部,一定存在大量的被打开的文件! ----OS要不要管理这些被打开的文件呢? —肯定是要的,那么怎么管理呢?----先描述,在组织 ---- 所以在内核中,一个被打开的文件都必须有自己的文件打开对象,包含文件的很多属性。struct XXX {文件属性; struct XXX* next};

二、C系列文件接口

如下所示,在下面这个函数中

第一个参数是路径,第二个参数是打开方式。返回值是FILE*即文件指针

image-20231125151450730

如下所示,当我们使用如下代码的时候

#include <stdio.h>int main()
{FILE* fp = fopen("log.txt","w");if(fp == NULL){perror("fopen");return 1;}fclose(fp);return 0;
}

因为我们是以写的方式打开文件,所以如果不存在这个文件,他会自动创建一个这样的文件

image-20231125151902300

注意这里的打开文件的路径和文件名,默认在当前路径下新建一个文件。

那么这里当前路径是什么呢?其实是进程的当前路径cwd

比如下面的代码中

#include <stdio.h>
#include <unistd.h>int main()
{printf("PID:%d\n",getpid());FILE* fp = fopen("log.txt","w");if(fp == NULL){perror("fopen");return 1;}fclose(fp);sleep(1000);return 0;
}

我们可以去查看一下它的当前目录

image-20231125152707828

在这里,如果我们更改了当前进程的cwd,就可以把文件新建到其他目录了

而我们在一个进程中更改当前目录,可以使用chdir

image-20231125152950457

所以,我们可以使用如下代码

#include <stdio.h>
#include <unistd.h>int main()
{chdir("/home/jby_1");printf("PID:%d\n",getpid());FILE* fp = fopen("log.txt","w");if(fp == NULL){perror("fopen");return 1;}fclose(fp);sleep(1000);return 0;
}

我们可以观察一下运行结果。可以发现这个文件去对应的路径创建了。

image-20231125153527696

我们再看一下当前的目录

image-20231125153633149

以上是文件的打开,我们现在重点来看一下文件写入的操作

如下是文件写入的函数

它的作用是将nmemb个size大小的ptr处的数据写入到一个文件中

image-20231125155557844

如下代码所示

#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main()
{printf("PID:%d\n",getpid());FILE* fp = fopen("log.txt","w");if(fp == NULL){perror("fopen");return 1;}const char* message = "hello linux message";fwrite(message,strlen(message),1,fp);fclose(fp);return 0;
}

运行结果如下所示

image-20231125160850651

image-20231125160903359

但是如果我们将上面代码的message修改了之后

image-20231125161038623

我们在运行一下,里面的内容也随之改变了

image-20231125161321371

这里我们会发现,原来文件的内容全部不见了。所以说w方法写入之前,都会对文件进行清空处理

这就类似于之前的重定向,就是相当于用w的方法打开了文件。然后写入内容

image-20231125161744002

所以,如果我们使用重定向的时候,前面什么也没有,就相当于清空了这个文件。当我们用w的方法打开一个文件以后,里面什么也都不会有了。

那么现在我们再来看一下上面这个代码

我们前面在这里没有+1,不过c语言中,默认会添加上\0,那么这里需要加1吗

image-20231125162847096

我们先运行一下,然后我们就会发现,这个文件里面的内容就变成了这样了,出现了一个乱码

image-20231125162953414

所以说,这里是不需要+1的,因为字符串后加上\0是C语言的规定,与文件有什么关系呢?

不过在打开文件的方式中,有一个方式是a方式,它是在文件的结尾写。如果文件不存在,则创建一个文件

我们试一下下面这个代码

image-20231125163441508

运行结果为

image-20231125163522699

所以说像我们之前的>>追加重定向,其实就是a方式的打开文件

所以w/a都是写入,w清空并从头写,a在文件结尾,追加写!

我们知道Linux下一切皆文件。在C语言中默认会打开三个流,stdin,stdout,stderr。如下图所示,这三个流的类型就是文件指针。

image-20231125164227819

其实类似的,C++中也会默认打开三个流:cin && cout && cerr

如果我们想向显示器打印也是可以的

image-20231125165627108

我们先看下面的代码。使用fprintf,我们也可以实现前面的在文件中打印的操作

image-20231125165424217

image-20231125165539739

对于fprintf,我们也可以将它的第一个流改为stdout

image-20231125165729504

这样的话,运行结果为,就成功的向显示器打印了

image-20231125165747352

如果我们想在stderr流中去写的话

image-20231125165900674

但从运行结果来看,似乎好像没有什么太大的区别

image-20231125165922307

其实

image-20231125170050877

三、从C过渡到系统:文件系统调用

我们知道,文件其实是在磁盘上的,磁盘是外部设备。所以访问文件其实是访问硬件!

我们知道计算机中是分层的

用户

程序 < - std lib / c / c++

系统调用

操作系统

硬件驱动

硬件

我们知道我们是不可直接访问硬件的,必须要自顶向下贯穿访问。而操作系统不相信任何人,所以就需要提供系统调用!

所以几乎所有的库只要是访问硬件设备,必定要封装系统调用。即printf/fprintf/fscanf/fwrite/fread/fgets/gets/…这些都是库函数,他们必定要封装系统调用接口

如下所示,这些就是文件系统调用接口

image-20231125173109123

我们先只考虑这两个open函数。

这两个open系统调用接口,一个有两个参数,一个有三个参数

上面的这个是下面的子集

所以我们先只谈三个参数的open

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

在这里,第一个参数是对应文件的路径:可是是绝对/相对都可以。也可以直接是文件名,那么默认当前目录

而第二个参数中,我们可以看到如下所示

image-20231125174502790

即flag就是一个打开的模式。必须包含O_RDONLY,O_WRONLY或者O_RDWR。

这些其实就是比特位的传参方式。

对于它的返回值,如果失败返回-1

我们先看如下代码

image-20231125175648807

运行结果为,打开失败了

image-20231125175754441

这是因为,我们刚刚用到的这个O_WRONLY选项它并不会新建文件。我们得告诉操作系统,如果文件不存在,我们需要新建它。所以我们还得加上O_CREAT选项

image-20231125180012284

运行结果为

image-20231125180029891

但是我们发现这里新创建的文件的权限是完全不对的

这是因为在linux中,要创建一个文件必须得告诉权限是什么。所以就需要第三个参数了。设置好权限

image-20231125180218285

此时新建的文件的权限已经不是刚刚那种乱码的样子了

image-20231125180326962

不过这里我们发现创建的文件它的权限也不是666,而是664,这是因为我们之前所说的,linux创建一个文件有默认的umask。这是由于这个umask是0002,所以最后一个才出现了一些问题

image-20231125180445133

但是如果我们非要创建一个666的文件。我们就需要用这个umask系统调用了

image-20231125180857722

它可以将代码里面的umask给修改掉。这里只影响该进程,不影响系统的

由于就近原则, open就会听进程的umask。

image-20231125181140151

运行结果为。可以看到,权限确实被改为了666

image-20231125181127957

对于这个open函数,它的返回值为一个int,这个整数我们称为file descriptor,即文件描述符,如果打开失败,则为-1。

如果我们想要关闭一个文件,可以用close

image-20231125182748486

它的参数正好就是文件描述符,所以我们可以传入一个文件描述符,就可以关闭对应的文件了

image-20231125182902897

还有一个系统调用是write

image-20231125183108619

它的功能是向fd文件中写入buf的count字节

image-20231125183401275

运行结果为

image-20231125183448003

如果我们紧接着将字符串改短一些

image-20231125184026136

那么最终的结果为

image-20231125184054054

现象就是,原来的内容都保留着,但是会从文件开始覆盖式的写入,但是并不会清空。

那么如果我们也想做到清空操作呢?

在我们打开文件的时候,即open函数中的第二个参数,我们可以使用O_TRUNC,即清空

image-20231125184707096

image-20231125184411874

此时我们就可以看到,原来的就被清空了

image-20231125184500466

那么如果我们想要实现追加写的功能呢?我们可以使用O_APPEND

image-20231125185213226

image-20231125185300323

运行结果为

image-20231125185350859

这样就实现了追加的功能

所以我们得到的结论是

FILE* fp = fopen("log.txt", "a");
//上面的代码下层一定封装了下面的系统调用接口
int fd = open("log.txt", O_WRONLY|O_CREAT|O_APPEND, 0666);
FILE* fp = fopen("log.txt", "w");
//上面的代码下层一定封装了下面的系统调用接口
int fd = open("log.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666);

所以说,无论是什么语言,最终底层一定会采用同样的接口。底层都是open,只是他们的上层有所区别罢了。

不过我们会好奇的是,上层的函数返回值是指针,而下层的系统调用接口的返回值是int,它们是如何联系起来的呢?

四、访问文件的本质

如下图所示,我们知道,文件都是在磁盘中存储着的,并且文件需要由一个进程来打开,那么进程要打开这个文件。就需要为这个文件创建对应的内核数据结构,即struct file。这个结构体里面存储着一个被打开文件的各种信息。当打开了多个文件的时候,这些结构体就会用一个双向链表连接起来。

也就是先描述在组织,此时对文件的管理就变为了对这个链表的增删查改

image-20231125202346970

可是我们的系统存在多个进程。那么哪一个文件是哪一个进程的呢?所以我们需要建立对应关系

如下图所示,我们的进程PCB结构体里面就有一个指针它指向struct files_struct这样的一个结构体,这个结构体里面,有一个struct file* fd_array[]数组,数组里面存储着很多struct file*指针,然后每当这个进程打开一个文件时,要创建一个struct file结构体,然后将这个结构体的地址放入一个没有被使用的下标中。

image-20231125203953104

而这个表就是文件描述符表。而前面的open系统调用中,这个返回值,就是对应文件描述符表中的下标。

image-20231125204956050

所以这个fd,本质就是一个数组的下标。我们使用write这些接口的时候就需要使用文件描述符来进行辨认文件

image-20231125205218613

所以最终,文件管理和进程管理就通过这个下标产生了关联

image-20231125205533973

我们也许会思考,既然都已经让进程管理起来了文件,为什么要让文件用双链表呢?因为进程也可能会崩掉。

现在我们已经了解了访问文件的本质了,open的返回值其实就是文件描述符表的下标,那么既然如此。我们来验证一下

image-20231125210152268

运行结果为

image-20231125210220783

我们可以在多验证一些

image-20231125210537992

运行结果为

image-20231125210555886

这里返回连续的下标我们也能理解,我们也知道失败会返回-1。那么0,1,2这些下标在哪呢?

我们会注意到,0,1,2刚好是3个。在C语言中刚好要打开三个流

image-20231125210804945

C语言认为要打开三个,linux下一切皆文件。

所以每一个被打开的文件,它在底层根本就不存在这个FILE*流,在操作系统中只认fd。

所以我们现在可以验证一下,这三个流就是0,1,2这三个文件

image-20231125211320279

运行结果为

image-20231125211349480

所以这两个分别为stdout,stderr

我们现在在验证一下stdin

对于这个我们可以用read系统调用接口

image-20231125211457674

image-20231125212234495

运行结果为

image-20231125212300357

注意在这里,由于操作系统并不知道我们读取的是字符串,它最后也不会加上\0,所以我们需要自己加上

这样一来也就验证了0是stdin

所以当一个C语言程序启动的时候,会打开三个标准输入输出流,这个是C语言的特性吗?

答案显然不是,是操作系统的特性,进程默认会打开键盘,显示器,显示器

那么为什么操作系统要这么做?

因为我们电脑刚打开,显示器,键盘早就被操作系统打开了。我们在编程的时候,必须得用显示器和键盘输入和查看结果,所以语言默认都能打开。

那么在C语言中这个FILE是什么呢?

这个FILE是C库自己封装的一个结构体,这个结构体里面必须包含文件描述符。因为操作系统只认文件描述符。

我们可以来证明一下

image-20231125213516775

运行结果为

image-20231125213542822

所以现在我们就知道了这里有两种的封装了。

一种是库函数封装了系统调用接口,一种是FILE封装了文件描述符

如果我们直接将1号文件给关了

image-20231125213830203

我们会发现什么也没有了

image-20231125213845164

因为一号就是显示器文件。而printf里面必然调用了这个1号文件描述符。

如果我们将代码改为下面的

image-20231125214442540

那么结果为

image-20231125214457736

因为我们用的是2号文件去写的。我们关的只是一号文件

还有一点是,在struct file结构体里面,其实还有一个信息是引用计数count。因为可能多个文件描述符指向同一个文件。一个文件描述符指向就是1,两个指向就是2.

image-20231125214924406

所以我们关闭文件去调用close的时候,它的工作其实很简单,只需要引用计数减减,然后将这个指针位置置空。然后判断这个引用计数是否为0,如果不为空则什么也不用做到,如果为空,那么就在去回收这个struct file对象。

所以这就是我们刚刚关闭了1号文件,2号文件还能继续打印的原因。因为仅仅只是引用计数减减了。

而C++中的那些fstream中也是一样的包含fd的。

相关文章:

【Linux】第二十一站:文件(一)

文章目录 一、共识原理二、C系列文件接口三、从C过渡到系统&#xff1a;文件系统调用四、访问文件的本质 一、共识原理 文件 内容 属性 文件分为打开的文件 和 没打开的文件 打开的文件&#xff1a;是谁打开的&#xff1f;是进程&#xff01;----所以研究打开的文件本质是研…...

centos7 docker开启认证的远程端口2376配置

docker开启2375会存在安全漏洞 暴露了2375端口的Docker主机。因为没有任何加密和认证过程&#xff0c;知道了主机IP以后&#xff0c;&#xff0c;任何人都可以管理这台主机上的容器和镜像&#xff0c;以前贪图方便&#xff0c;只开启了没有认证的docker2375端口&#xff0c;后…...

Java王者荣耀小游戏

Background类 package LX;import java.awt.*; //背景类 public class Background extends GameObject{public Background(GameFrame gameFrame) {super(gameFrame);}Image bg Toolkit.getDefaultToolkit().getImage("C:\\Users\\ASUS\\Desktop\\王者荣耀图片\\Map.jpg&…...

谈谈Redis的几种经典集群模式

目录 前言 主从复制 哨兵模式 分片集群 前言 Redis集群是一种通过将多个Redis节点连接在一起以实现高可用性、数据分片和负载均衡的技术。它允许Redis在不同节点上同时提供服务&#xff0c;提高整体性能和可靠性。在Redis中提供集群方案总共有三种&#xff1a;主从复制、…...

【腾讯云 HAI域探秘】基于高性能应用服务器HAI部署的 ChatGLM2-6B模型,我开发了AI办公助手,公司行政小姐姐用了都说好!

目录 前言 一、腾讯云HAI介绍&#xff1a; 1、即插即用 轻松上手 2、横向对比 青出于蓝 3、多种高性能应用部署场景 二、腾讯云HAI一键部署并使用ChatGLM2-6B快速实现开发者所需的相关API服务 1、登录 高性能应用服务 HAI 控制台 2、点击 新建 选择 AI模型&#xff0c;…...

服务器tar压缩解压文件

文章目录 一、前言二、命令2.1、解压2.2、压缩 三、最后 一、前言 前端上传dist代码到服务器上后&#xff0c;是在linux上操作&#xff0c;所以和window有所不同。一般是打好dist&#xff0c;然后压缩成gz传输到服务器&#xff0c;此时在服务器上可能涉及到解压和压缩的操作&a…...

博物馆线上导览系统的设计与实现-计算机毕业设计源码64574

摘 要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们所认识&#xff0c;科学化的管理&#xff0c;使信息存…...

vue升级题

不熟悉的&#xff1a; 2&#xff0c; 3.你用过befcoreDetory 吗&#xff1f;清除定时器&#xff0c;第一个和第二个再看一下 实例加载完成是在哪个生命周期--beforecreate 7.父子组件生命周期执行顺序&#xff1f;为什么这么渲染&#xff1f;场景 8.简单描述每个周期具体适…...

Edit And Resend测试接口工具(浏览器上的Postman)

优点 可以不用设置Cookie或者Token&#xff0c;只设置参数进行重发接口测试API 使用Microsoft Rdge浏览器 F12——然后点击网络——在页面点击发起请求——然后选择要重发的请求右键选择Edit And Resend——在网络控制台设置自己要设置的参数去测试自己写的功能...

maven常用打包命令,值传递和引用传递,Java包 ,JDK 中常用的包有哪些,import java和javax有什么区别

文章目录 maven常用打包命令Java程序设计语言对对象采用的不是引用调用&#xff0c;实际上&#xff0c;对象引用是按值传递的。值传递和引用传递有什么区别Java包 &#xff0c;JDK 中常用的包有哪些import java和javax有什么区别 谈谈java基础的内容&#xff0c;而且很多人都回…...

【c++随笔14】虚函数表

【c随笔14】虚函数表 一、虚函数表&#xff08;Virtual Function Table&#xff09;1、定义2、查看虚函数表2.1、 问题&#xff1a;三种类型&#xff0c;包含一个int类型的class、一个int类型的变量、int类型的指针&#xff1a;这三个大小分别是多少呢&#xff1f;2.2、怎么发现…...

分布式链路追踪实战篇-日志库集成opentelemetry的思路

由上文分布式链路追踪入门篇-基础原理与快速应用可以知道分布式链路追踪的作用&#xff0c;但是距离应用到项目中&#xff0c;我们还需要对项目中一些关键组件进行opentelemetry的集成&#xff0c;例如日志库&#xff0c;ORM、http框架、rpc框架等。 一、日志库如何集成opentel…...

电脑投屏到电视的软件,Mac,Linux,Win均可使用

电脑投屏到电视的软件&#xff0c;Mac&#xff0c;Linux&#xff0c;Win均可使用 AirDroid Cast的TV版&#xff0c;可以上笔记本电脑或台式电脑直接投屏到各种安卓电视上。 无线投屏可以实现本地投屏及远程投屏&#xff0c;AirPlay协议可以实现本地投屏&#xff0c;大家可以按需…...

基于vue+element-plus+echarts编写动态绘图页面

我们都知道网页的echarts可以画图&#xff0c;但是很多情况下都需要编码实现绘图逻辑&#xff0c;如果有一个前端页面可以让我输入数据然后动态生成图表的话那么该多好&#xff0c;其实这个需求不难实现&#xff0c;先看效果。 整体页面分为左右两个部分&#xff0c;其中左边的…...

无人机巡检如何做到实时识别,从数据到模型全流程解读

在数字化和自动化飞速发展的今天&#xff0c;AI识别算法正在加速进入行业生产系统。 基于巡检数据的智能开发&#xff0c;识别算法突破性进展的核心驱动力在于需求——从全天候巡视的平安城市&#xff0c;到潮汐变化的交通网络&#xff0c;从广阔的水域&#xff0c;到繁忙的街道…...

zlmediakit实现rtsp流服务器

本次实现是将内存中的H264数据经过zlmediakit实现为rtsp流。 我是用的是CAPI的方式&#xff0c;将zlmediakit作为一个sdk嵌入到自己的程序中而不是作为一个独立的进进程服务。 1.编译完成zkmedialit后会得到bin include lib三个文件夹如图 其中bin中的MediaServer是作为独立的…...

保姆级 ARM64 CPU架构下安装部署Docker + rancher + K8S 说明文档

1 K8S 简介 K8S是Kubernetes的简称&#xff0c;是一个开源的容器编排平台&#xff0c;用于自动部署、扩展和管理“容器化&#xff08;containerized&#xff09;应用程序”的系统。它可以跨多个主机聚集在一起&#xff0c;控制和自动化应用的部署与更新。 K8S 架构 Kubernete…...

耶鲁博弈论笔记

编辑记录&#xff1a; 1126&#xff1a;开个新坑&#xff0c;耶鲁大学的博弈论课程&#xff0c; 和专业相关不大&#xff0c;纯兴趣&#xff0c;尽量写好一点吧 1. 首先指出博弈论是一种研究策略形式的方法&#xff0c;对于经济学中&#xff0c;完全竞争市场只能被动接受均衡…...

一个简易的URL爬虫程序(java)

该程序是一个简单的Java程序&#xff0c;用于从指定的URL中获取网页内容并保存到本地文件。通过URL类打开指定的URL链接&#xff0c;并使用openStream()方法获取输入流。然后使用Scanner类读取输入流中的内容&#xff0c;并使用PrintWriter类将读取到的内容写入到本地文件中。 …...

Deep Learning(wu--46)

文章目录 ContentsBeginBasic逻辑回归SGD导数计算图&#xff08;反向传播&#xff09;向量化广播numpy Neural Network向量化激活函数梯度下降深层表示反向传播 Contents Begin Basic 逻辑回归 SGD 导数 计算图&#xff08;反向传播&#xff09; 向量化 广播 numpy Neural Netw…...

【OSG学习笔记】Day 18: 碰撞检测与物理交互

物理引擎&#xff08;Physics Engine&#xff09; 物理引擎 是一种通过计算机模拟物理规律&#xff08;如力学、碰撞、重力、流体动力学等&#xff09;的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互&#xff0c;广泛应用于 游戏开发、动画制作、虚…...

UDP(Echoserver)

网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法&#xff1a;netstat [选项] 功能&#xff1a;查看网络状态 常用选项&#xff1a; n 拒绝显示别名&#…...

Linux-07 ubuntu 的 chrome 启动不了

文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了&#xff0c;报错如下四、启动不了&#xff0c;解决如下 总结 问题原因 在应用中可以看到chrome&#xff0c;但是打不开(说明&#xff1a;原来的ubuntu系统出问题了&#xff0c;这个是备用的硬盘&a…...

CMake 从 GitHub 下载第三方库并使用

有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...

自然语言处理——循环神经网络

自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元&#xff08;GRU&#xff09;长短期记忆神经网络&#xff08;LSTM&#xff09…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

python报错No module named ‘tensorflow.keras‘

是由于不同版本的tensorflow下的keras所在的路径不同&#xff0c;结合所安装的tensorflow的目录结构修改from语句即可。 原语句&#xff1a; from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后&#xff1a; from tensorflow.python.keras.lay…...

音视频——I2S 协议详解

I2S 协议详解 I2S (Inter-IC Sound) 协议是一种串行总线协议&#xff0c;专门用于在数字音频设备之间传输数字音频数据。它由飞利浦&#xff08;Philips&#xff09;公司开发&#xff0c;以其简单、高效和广泛的兼容性而闻名。 1. 信号线 I2S 协议通常使用三根或四根信号线&a…...

Modbus RTU与Modbus TCP详解指南

目录 1. Modbus协议基础 1.1 什么是Modbus? 1.2 Modbus协议历史 1.3 Modbus协议族 1.4 Modbus通信模型 🎭 主从架构 🔄 请求响应模式 2. Modbus RTU详解 2.1 RTU是什么? 2.2 RTU物理层 🔌 连接方式 ⚡ 通信参数 2.3 RTU数据帧格式 📦 帧结构详解 🔍…...

uni-app学习笔记三十五--扩展组件的安装和使用

由于内置组件不能满足日常开发需要&#xff0c;uniapp官方也提供了众多的扩展组件供我们使用。由于不是内置组件&#xff0c;需要安装才能使用。 一、安装扩展插件 安装方法&#xff1a; 1.访问uniapp官方文档组件部分&#xff1a;组件使用的入门教程 | uni-app官网 点击左侧…...