(学习总结32)Linux 基础 IO
Linux 基础 IO
- 一、什么是 " 文件 "
- 二、C 文件接口
- 打开文件
- 写文件
- 读文件
- 其它介绍
- 三、系统文件 I/O
- 传递标志位
- 系统接口写文件
- 系统接口读文件
- 部分系统调用接口介绍
- 打开文件函数 open
- 关闭文件函数 close
- 写入文件函数 write
- 读取文件函数 read
- 文件描述符 fd
- fd 0 & 1 & 2 与内核缓冲区
- 文件描述符的分配规则
- 重定向基本原理
- 使用 dup2 系统调用
- 四、" Linux 中一切皆文件 "
- 五、缓冲区
- 缓冲区概念
- 缓冲区的意义
- 缓冲类型
- C 语言缓冲区 与 缓冲区数据丢失
- C 语言 FILE 结构体 与 fork 重复打印
以下代码环境为 Linux Ubuntu 22.04.5 gcc C语言。
一、什么是 " 文件 "
狭义理解:
-
文件在磁盘里
-
磁盘是永久性存储介质,因此文件在磁盘上的存储是永久性的
-
磁盘是外设(即是输出设备也是输入设备)
-
磁盘上的文件本质是对文件的所有权操作,都是对外设的输入和输出,简称 IO(Input Output)
广义理解:
对 Linux 来说,一切皆文件(键盘、显示器、网卡、磁盘 …… 被 Linux 抽象化)
文件操作的归类认知:
-
文件是文件属性(元数据) 和文件内容的集合,即
文件 = 属性 + 内容
-
对于 0 KB 的空文件是占用磁盘空间的
-
所有的文件操作本质是 文件内容操作 和 文件属性操作
系统角度:
-
用户对文件的操作本质是进程对文件的操作
-
磁盘的管理者是操作系统
-
文件的读写本质不是通过 C 语言 / C++ 的库函数来操作的(这些库函数只是为用户提供方便),而是通过文件相关的系统调用接口来实现的
二、C 文件接口
打开文件
#include <stdio.h>int main()
{FILE* fp = fopen("myFile.txt", "w");if (fp == NULL){ perror("fopen:");return 1;} while (1);fclose(fp); return 0;
}
其中,打开的 myFile 文件在程序的当前路径下,这是系统根据进程的符号 cwd 和 exe 来查看当前路径创建的:
-
cwd :指向当前进程运行目录的一个符号链接
-
exe :指向启动当前进程的可执行文件(完整路径)的符号链接
打开文件本质是用进程打开,而进程知道自己在哪里。当操作系统要创建文件时,根据进程提供的信息就能知道文件应该放在哪里。
写文件
#include <stdio.h>int main()
{FILE* fp = fopen("myFile.txt", "w");if (fp == NULL){ perror("fopen:");return 1;} const char* info = "Hello Linux!\n";int count = 5;while (count--){ fprintf(fp, "%s", info); } fclose(fp);return 0;
}
在执行 test 命令后,我们可以检查 myFiles.txt 中具体内容:
读文件
#include <stdio.h>int main()
{FILE* fp = fopen("myFile.txt", "r");if (fp == NULL){perror("fopen:");return 1;}char buf[6][64];int row = 0;while (1){fgets(buf[row++], 64, fp);if (feof(fp)){break;}}fclose(fp);for (int i = 0; i < 5; ++i){printf("%s", buf[i]); }return 0;
}
我们可以注意到,这个读取文件信息打印到屏幕的程序和 cat 命令原理是相似的。
其它介绍
stdin、stdout 和 stderr:C 语言默认会打开三个输入输出流,分别是 stdin、stdout 和 stderr。而且这三个流都是 FILE* 类型的,fopen 返回值类型也是文件指针:
如果想了解更多关于 C 语言文件操作,请参考:(学习总结7)C语言文件操作
三、系统文件 I/O
打开文件的方式不仅仅是 fopen 等流式。这是语言层的方案,而系统才是打开文件最底层的方案。
不过,在介绍系统文件 IO 之前,先要了解下如何给函数传递标志位,该方法在系统文件 IO 接口中会使用到。
传递标志位
传递标志位,本质是利用其中的比特位进行记录,借用位图的思想:
#include <stdio.h>#define PRINT_ONE (1 << 0)
#define PRINT_TWO (1 << 1)
#define PRINT_THREE (1 << 2)void printInfo(int num)
{if (num & PRINT_ONE){printf("one\n");}if (num & PRINT_TWO){printf("two\n");}if (num & PRINT_THREE) {printf("three\n");}
}int main()
{printInfo(PRINT_ONE);printf("---------------\n");printInfo(PRINT_THREE);printf("---------------\n");printInfo(PRINT_ONE | PRINT_TWO | PRINT_THREE);printf("---------------\n");return 0;
}
操作文件,除了上述的 C 接口,我们还可以采用系统接口来进行文件访问, 接下来先以系统代码的形式,实现和上部分操作一样的代码。
系统接口写文件
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>int main()
{int ret = open("myFile.txt", O_WRONLY | O_CREAT, 0666); if (ret == -1) { perror("open:");return 1;} const char* info = "Hello Linux!\n";int count = 5;int len = strlen(info);while (count--){ write(ret, info, len);} close(ret);return 0;
}
系统接口读文件
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>int main()
{int ret = open("myFile.txt", O_RDONLY, 0666);if (ret == -1){perror("open:");return 1;}char buf[6][64];int row = 0;while (1){ssize_t last = read(ret, buf[row++], 17); if (last == 0){break;}}close(ret);for (int i = 0; i < 4; ++i){printf("%s", buf[i]);}return 0;
}
部分系统调用接口介绍
在认识刚刚的系统调用函数之前,我们需要明确两个概念:系统调用函数 和 库函数。
之前的函数 fopen、fclose、fprintf、fgets 都是 C语言标准库当中的函数,我们称之为库函数(libc)。
而 open、close、read、write 都是系统提供的接口函数称之为系统调用接口。
之前文章曾提到过两者的关系和图片,图片放在此处:
所以可以认为, C语言当中相关函数,都是对系统调用的封装,方便 C 程序员二次开发。
这里要注意的是,系统并不关心文件的写入方式,文本写入和二进制写入是 C 语言提供的概念。
打开文件函数 open
open 返回值:成功返回文件描述符(非负整数),失败返回 -1 并设置 errno
open 参数 pathname :要打开或创建的文件路径(绝对或相对路径)
open 参数 mode :设置新文件的权限,当 flags 中存在 O_CREAT 时使用,受 umask 影响(实际权限为 mode & ~umask)
open 参数 flags :文件访问模式和选项
-
基本访问模式(至少要有一个):
O_RDONLY
:只读O_WRONLY
:只写O_RDWR
:读写
-
常用选项:
O_CREAT
:文件不存在时创建(需指定 mode)O_APPEND
:追加写入(避免多进程竞争)O_EXCL
:与 O_CREAT 联用,若文件存在则失败(用于原子性创建)O_TRUNC
:若文件存在,清空内容(长度截断为0)O_NONBLOCK
:非阻塞模式(用于设备文件或管道)O_SYNC
:同步写入(数据与元数据写入磁盘后才返回)O_CLOEXEC
:执行 exec 时关闭文件描述符(避免泄漏)O_DIRECTORY
:确保路径是目录,否则失败O_NOFOLLOW
:不解析符号链接
关闭文件函数 close
close 返回值:成功时返回 0,失败返回 -1,并设置 errno
close 参数 fd :需要关闭的文件描述符(由 open() 等系统调用返回的有效句柄)
写入文件函数 write
write 返回值:成功返回实际写入的字节数(可能小于 count),失败返回 -1,并设置 errno
write 参数 fd :文件描述符,由 open() 等系统调用返回的有效文件描述符句柄
write 参数 buf :指向准备写入数据的缓冲区(用户空间内存地址)
write 参数 count :要求写入的字节数
读取文件函数 read
read 返回值:成功时返回实际读取的字节数(可能小于 count),返回 0 表示已到达文件末尾(EOF),失败返回 -1,并设置 errno
read 参数 fd :文件描述符,由 open() 等系统调用返回的有效句柄,且必须以可读模式(O_RDONLY 或 O_RDWR)打开
read 参数 buf :读取文件数据时准备存放的空间地址
read 参数 count :请求读取的最大字节数(buf 大小需能够存储)
文件描述符 fd
通过 open 函数的理解,我们知道文件描述符其实是一个整数。
fd 0 & 1 & 2 与内核缓冲区
Linux 进程默认情况下会有 3 个已经打开的文件描述符,分别是标准输入 0(stdin)、标准输出 1(stdout)、标准错误 2(stderr)
0、1、2 对应的物理设备一般是:键盘、显示器、显示器,所以输入输出还可以采用如下方式:
#include <stdio.h>
#include <unistd.h>
#include <string.h>int main()
{char buf[1024];ssize_t ret = 0;while (ret = read(0, buf, sizeof(buf) - 1)) { buf[ret] = '\0';if (ret > 0){ printf("标准输出 stdout 内容:> "); // print 打印没有 \n 会存放到 C语言的缓冲区中fflush(stdout); // 使用 fflush 函数强制将C语言缓冲区内容写入对应流中write(1, buf, ret);printf("标准错误 stderr 内容:> "); fflush(stdout);write(2, buf, ret);} } return 0;
}
现在我们知道,文件描述符就是从 0 开始的整数。当我们打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件。于是有了 file 结构体,表示一个已经打开的文件对象。
而进程执行 open 系统调用,就必须让进程和文件关联起来。Linux 中每个进程的 task_struct 都有一个指针 files,指向一张表 files_struct,该表最重要的部分就是包含一个指针数组,每个元素都是一个指向打开文件的指针!
所以本质上,文件描述符就是该数组的下标。也就是说,只要拿着文件描述符就可以找到对应的文件。
另外,对文件内容做任何操作,都必须先将文件加载到内核对应的文件缓冲区内:
则对于 read 系统调用函数,本质是从内核对应文件缓冲区拷贝到用户空间的拷贝函数!
但为什么要分类同样是打印到显示器的 stdout 和 stderr 呢?这是为了方便使用重定向将普通信息与错误信息分离!
我们可以通过内核源码查看大致结构(Linux 内核 2.6.32):
文件描述符的分配规则
我们这里具体看看打开的 myFile.txt 文件 fd 为多少:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>int main()
{int ret = open("myFile.txt", O_WRONLY | O_CREAT, 0666);if (ret == -1) { perror("open:"); return -1; } printf("myFile.txt 对应 fd 为: %d\n", ret);close(ret);return 0;
}
输出发现是 fd 为 3,接下来关闭 fd 为 0 的文件再打开:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>int main()
{close(0); // 关闭 fd 为 0 的文件 int ret = open("myFile.txt", O_WRONLY | O_CREAT, 0666);if (ret == -1) { perror("open:");return -1; } printf("myFile.txt 对应 fd 为: %d\n", ret);close(ret);return 0;
}
发现结果 fd 为 0,可见文件描述符的分配规则是在 files_struct 数组当中找到当前没有被使用的最小的一个下标,作为新的文件描述符。
重定向基本原理
如果我们关闭 fd 为 1 的文件,也就是关闭 stdout 文件,再打开 myFile.txt 文件并用 printf 函数打印,原本输出到显示屏的内容会输出到 myFile.txt 文件中:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>int main()
{close(1);int ret = open("myFile.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);if (ret == -1) { perror("open:");return -1; } const char* info = "this is test.\n";int count = 3; while (count--){ printf("%s", info);} fflush(stdout); // 将 C语言缓冲区内容写到文件缓冲区中close(ret);return 0;
}
此时我们发现,原来的 Hello Linux! 字符串不见了,转而是当前程序输出的 this is test. 字符串。
事实上,本来应该输出到显示器上的内容,却输出到了规定的文件当中,这种现象就叫做输出重定向。常见的重定向有:>、>>、< 等等。
那重定向的原理是什么呢?根据上面的测试我们可以这样说,将进程原本指向标准输入、输出或错误的 file* 更改指向到其特定的文件,就是重定向的原理:
使用 dup2 系统调用
dup2 返回值:成功时返回新的文件描述符 newfd,失败返回 -1,并设置 errno
dup2 参数 oldfd :需要复制的源文件描述符(必须已打开且有效)
dup2 参数 newfd :目标文件描述符(用户指定的数值)
- 若 newfd 已打开,dup2() 会先自动关闭它,再复制 oldfd
- newfd 的取值需在文件描述符的有效范围内
通过 dup2 函数可以更规范的使用重定向:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>int main()
{int ret = open("myFile.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);if (ret == -1) { perror("open:");return -1; } dup2(ret, 1); const char* info = "use dup2 function.\n";int count = 3;while (count--){ printf("%s", info);} fflush(stdout);close(ret); return 0;
}
四、" Linux 中一切皆文件 "
Linux 中除了本来就是文件,其它结构如磁盘、显示器、键盘这样的硬件设备也被抽象成文件,可以使用访问文件的方法访问它们获得信息。
这样做最明显的好处是:开发者仅需要使用一套 API 和开发工具,即可调用 Linux 中绝大部分的资源。
举个简单的例子,Linux 中几乎所有读(读文件,读系统状态)的操作都可以用 read 函数来进行;几乎所有更改(更改文件,更改系统参数)的操作都可以用 write 函数来进行。
上述部分我们讲过,当打开一个文件时,操作系统为了管理所打开的文件,都会为这个文件创建一个 file 结构体。
值得关注的是 struct file 中的 f_op 指针指向了一个 file_operations 结构体,这个结构体中的成员除了 struct module* owner 其余都是函数指针:
而 file_operation 就是把系统调用和驱动程序关联起来的关键数据结构,这个结构的每一个成员都对应着一个系统调用。读取 file_operation 中相应的函数指针,接着把控制权转交给函数,从而完成 Linux 设备驱动程序的工作。从面向对象的语言来说,这种方法运用了多态的思想:
上图中的外设,每个设备都可以有自己的 read、write 函数,但一定是对应着不同的操作方法。通过 struct file 下 file_operation 中的各种函数回调,让开发者只用 file 便可调取 Linux 系统中绝大部分的资源,这便是 " Linux 中一切皆文件 " 的核心理解。
五、缓冲区
缓冲区概念
缓冲区是内存空间的一部分。也就是说,在内存空间中预留了一定的存储空间,这些存储空间用来临时存放输入或输出的数据,则被称为缓冲区。缓冲区根据其对应的是输入设备还是输出设备,分为输入缓冲区和输出缓冲区。
缓冲区的意义
读写文件如果不会开辟对文件操作的缓冲区,直接通过系统调用对磁盘进行操作(读、写等),那么每次对文件进行一次读写操作时,都需要使用读写系统调用来处理此操作,即需要执行一次系统调用。
而执行一次系统调用将涉及到 CPU 状态的切换,即从用户空间切换到内核空间,实现进程上下文的切换,这将损耗一定的 CPU 时间。频繁的磁盘访问对程序的执行效率造成很大的影响。为减少使用系统调用的次数来提高效率,我们就可以采用缓冲机制。
比如从磁盘里取信息,在磁盘文件进行操作时,可以一次从文件中读出大量的数据到缓冲区中,以后对这部分的访问就不需要再使用系统调用。等缓冲区的数据取完后再去磁盘中读取,这样就可以减少磁盘的读写次数,再加上计算机对缓冲区的操作快于对磁盘的操作(因为缓冲区在内存,磁盘为外存),故应用缓冲区可提高计算机的运行速度。
又比如使用打印机打印文档,由于打印机的打印速度相对较慢,先把文档输出到打印机相应的缓冲区,打印机再自行逐步打印,这时 CPU 可以处理别的事情。
可以看出缓冲区就是一块内存区,它用在输入输出设备和 CPU 之间,用来缓存数据。它使得低速的输入输出设备和高速的 CPU 能够协调工作,避免低速的输入输出设备频繁占用 CPU,让 CPU 能够高效率工作。
缓冲类型
标准 I/O 提供了 3 种类型的缓冲区:
-
全缓冲区:这种缓冲方式要求填满整个缓冲区后才进行 I/O 系统调用操作。对于磁盘文件的操作通常使用全缓冲的方式访问。
-
行缓冲区:在行缓冲情况下,输入或输出中遇到换行符时,标准 I/O 库函数将会执行系统调用操作。当所操作的流涉及一个终端时(例如标准输入和标准输出),使用行缓冲方式。因为标准 I/O 库每行的缓冲区长度是固定的,所以只要填满了缓冲区,即使还没有遇到换行符,也会执行 I/O 系统调用操作,默认行缓冲区的大小为 1024 。
-
无缓冲区:无缓冲区是指标准 I/O 库不对字符进行缓存,直接使用系统调用。标准错误流 stderr 通常是不带缓冲区的,这使得出错误信息能够尽快地显示出来。
除了上述列举的默认刷新方式,下列特殊情况也会引发缓冲区的刷新:
-
缓冲区满时刷新
-
进程退出
-
执行 flush 语句刷新(例如上面 C 语言的 fflush 函数强行刷新缓冲区)
C 语言缓冲区 与 缓冲区数据丢失
C 语言标准库中也有内置的行缓冲区,针对于标准输出 stdout 输出数据,若不带换行符且先关闭了内核的文件缓冲区,会导致数据丢失:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>int main()
{int ret = open("myFile.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);if (ret == -1){perror("open:");return -1;}dup2(ret, 1);const char* info = "this is test.";int count = 3;while (count--){printf("%s", info);}//fflush(stdout); // 关闭强制刷新close(ret);close(1); // 将文件缓冲区关闭// 当 C 语言缓冲区内容因为进程结束后刷新// 但文件缓冲区关闭了无法写入// 从而导致数据丢失 return 0;
}
这是因为 C语言的缓冲区规定只要将数据交给内核的文件缓冲区就算完成了它的任务,对于内核缓冲区使用何种刷新方式由操作系统决定。若内核缓冲区先一步关闭,C语言缓冲区刷新时只能将内部数据丢弃:
C语言缓冲区交给内核缓冲区时,使用的方法是拷贝。站在更高的角度上说,计算机数据流动的本质都是拷贝!
C 语言 FILE 结构体 与 fork 重复打印
因为 IO 相关函数与系统调用接口对应,并且 C 语言库函数都是封装的系统调用,所以本质上访问 Linux 文件都是通过 fd 访问的。
则 C 标准库当中的 FILE 结构体内部必定封装了 fd。
有兴趣的读者可以在 Linux 文件 /usr/include/stdio.h
当中查看更多有关 FILE 信息,这里就不展开了。
另外 C 语言缓冲区写入文件时是全缓冲,写入显示器是行缓冲,我们可以对比两者和系统调用 write 函数的差异:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>int main()
{const char* info = "use printf";int count = 3;while (count--){printf("%s %d \n", info, 3 - count);}const char* info2 = "use write\n";write(1, info2, strlen(info2)); fork();return 0;
}
我们发现对 myFile.txt 文件输出时,printf 循环的输出了两次,而 write 只输出了一次,这是因为:
-
一般 C 的库函数写入文件时是全缓冲的,而写入显示器是行缓冲。
-
printf 等库函数会自带缓冲区,当发生重定向到普通文件时,数据的缓冲方式由行缓冲变成了全缓冲。
-
其中放在缓冲区中的数据,即便 fork 之后也不会被立即刷新。
-
而进程退出之后,会统一刷新写入文件当中。
-
但调用 fork 时,父子数据会发生写时拷贝,所以当父进程准备刷新的时候,子进程也就有了同样的一份数据,两者刷新随即产生两份数据。
-
write 系统调用函数没有变化,说明没有所谓的缓冲。
综上所述,printf 等 C 库函数会自带缓冲区,而 write 系统调用没有带缓冲区。
相关文章:
(学习总结32)Linux 基础 IO
Linux 基础 IO 一、什么是 " 文件 "二、C 文件接口打开文件写文件读文件其它介绍 三、系统文件 I/O传递标志位系统接口写文件系统接口读文件部分系统调用接口介绍打开文件函数 open关闭文件函数 close写入文件函数 write读取文件函数 read 文件描述符 fdfd 0 & 1…...
操作系统(一):概念及主流系统全分析
目录 一.操作系统是什么 二.操作系统的分类 2.1 按应用场景分类 2.2 按实时性分类 2.3 按内核架构分类 2.4 按用户与任务分类 三.主流操作系统比较 四.未来趋势 一.操作系统是什么 操作系统(Operating System, OS)是计算机系统的核心软件&#x…...
三、GPIO
一、GPIO简介 GPIO(General Purpose Input Output)通用输入输出口GPIO引脚电平:0V(低电平)~3.3V(高电平),部分引脚可容忍5V 容忍5V,即部分引脚输入5V的电压,…...
Ceph异地数据同步之-RBD异地同步复制(上)
#作者:闫乾苓 文章目录 前言基于快照的模式(Snapshot-based Mode)工作原理单向同步配置步骤单向同步复制测试双向同步配置步骤双向同步复制测试 前言 Ceph的RBD(RADOS Block Device)支持在两个Ceph集群之间进行异步镜…...
fastapi完全离线环境(无外网)的访问Swagger所做特殊处理
在互联网环境中,只要 启动FastAPI 服务运行在本地机器上,访问 http://localhost:8000/docs(Swagger UI)就可以访问到Swagger界面,但是在完全离线环境(无外网)下如何访问Swagger页面呢࿱…...
在网络中加入预训练的多层感知机(MLP)有什么作用?
在网络中加入预训练的多层感知机(MLP)通常是为了引入先验知识、提升特征表示能力或dropout,具体作用取决于MLP的设计和预训练任务。以下是常见的应用场景和优势: 1. 特征融合与迁移学习:预训练的MLP可以作为特征提取器࿰…...
3.2/Q2,GBD数据库最新文章解读
文章题目:Temporal trends in the burden of vertebral fractures caused by falls in China and globally from 1990 to 2021: a systematic analysis of the Global Burden of Disease Study 2021 DOI:10.1186/s13690-025-01500-y 中文标题:…...
机器学习的一百个概念(9)学习曲线
前言 本文隶属于专栏《机器学习的一百个概念》,该专栏为笔者原创,引用请注明来源,不足和错误之处请在评论区帮忙指出,谢谢! 本专栏目录结构和参考文献请见[《机器学习的一百个概念》 ima 知识库 知识库广场搜索&…...
浅谈Tomcat数据源连接池
目录 为什么需要JDBC连接池 Tomcat JDBC Pool 相关参数 1. 基本配置 2. 连接池大小控制 3. 连接验证与测试 4. 空闲连接回收 5. 连接泄漏与超时 Tomcat JDBC Pool 源码分析(tomcat 8.5.3) DataSourceFactory DataSource ConnectionPool Pool…...
Techub 财报解读:Circle 冲刺 IPO,但收入增长难掩利润困局
作者:Techub 财报解读 撰文:Yangz,Techub News 4 月 1 日,Circle 向美国证券交易委员会(SEC)提交 S-1 文件,计划进行首次公开募股(IPO),股票代码为 CRCL&…...
C++中的链表操作
在C中,链表是一种常见的数据结构,它由一系列节点组成,每个节点包含数据部分和指向下一个节点的指针。C标准库(STL)中提供了std::list和std::forward_list两种链表实现,分别对应双向链表和单向链表。此外&am…...
Vue2 生命周期
文章目录 前言🔄 Vue2 生命周期流程(8个核心钩子)📝 代码中典型用法示例一、您的描述验证二、完整生命周期代码示例三、关键阶段行为说明🔍 常见问题 前言 提示:以下是本篇文章正文内容,下面案…...
2007-2022年 上市公司政府补助数据 -社科数据
上市公司政府补助数据(2007-2022年)-社科数据https://download.csdn.net/download/paofuluolijiang/90028547 https://download.csdn.net/download/paofuluolijiang/90028547 政府补助是指政府为支持企业发展,提供的资金或资源支持。对于上市…...
设计心得——状态机
一、状态机 在设计一些与硬件交互或者游戏等开发中,经常会听到状态机(State Machines)这个字眼,而在设计模式(GoF)中,又经常听到状态模式这个概念,它们之间有什么联系和不同呢&…...
python match case语法
学习路线:B站 普通的if判断 def if_traffic_light(color):if color red:return Stopelif color yellow:return Slow downelif color green:return Goelse:return Invalid colorprint(if_traffic_light(red)) # Output: Stop print(if_traffic_light(yellow)) …...
Lua中协程相关函数使用详解
目录 1. coroutine.create(f)2. coroutine.resume(co [, val1, ...])3. coroutine.yield([val1, ...])4. coroutine.status(co)5. coroutine.wrap(f)6. coroutine.running()7. coroutine.isyieldable()协程状态转换示例总结 Lua 中的协程(coroutine)提供…...
代码拟有感
最近的日子像被按了0.5倍速播放键。腱鞘炎让手腕转动时发出咯吱声,尾骨的钝痛让久坐变成酷刑,落枕的脖子和酸胀的手臂组成了“疼痛交响乐”——这些隐秘的、持续的身体抗议,让原本枯燥的代码练习变成了一场生理与意志的拉锯战。 我盯着屏幕苦…...
《实战AI智能体》MCP对Agent有哪些好处
首先MCP为Agent提供了标准化的方式来接入各种工具和数据源,无论是本地运行的工具,例如通过stdio服务器,还是远程托管的服务HTTP over SSE服务, Agent都可以通过统一的接口与它们进行交互,极大扩展了第三方工具库。 例如,在金融领域,Agent 可以接入股票分析的MCP工具。当…...
maptalks获取所有图层并把图层按照zIndex排序
maptalks获取所有图层并把图层按照zIndex排序 获取所有图层 通过调用 map.getLayers() 可以返回当前地图上所有的图层集合。此方法会返回一个数组形式的结果,其中包含了地图上的每一个图层层级对象。 图层属性中的 ZINDEX 每种图层类型(如矢量图层、…...
GUI-Guider 按钮按下 选项卡 右移动一个,到最右边停下
extern lv_ui guider_ui; // 在文件顶部添加// 在按钮事件中使用: lv_obj_t * tabview guider_ui.screen_tabview_1; // 替换为你的实际 TabView 名称 uint16_t current lv_tabview_get_tab_act(tabview); lv_tabview_set_act(tabview, current 1, LV_ANIM_ON); …...
让AI再次伟大-MCP-Client开发指南
👏作者简介:大家好,我是爱吃芝士的土豆倪,24届校招生Java选手,很高兴认识大家📕系列专栏:Spring原理、JUC原理、Kafka原理、分布式技术原理、数据库技术、JVM原理、AI应用🔥如果感觉…...
sql工具怎么选?
为什么大多数主流工具又贵又难用? 有没有一款免费好用的sql工具? 像大多人朋友常用的sql工具,应该都遇到过这种情况, 用着用着收到了来自品牌方的律师函, 或者处理数据经常卡死, 再或者不支持国产数据库…...
video标签播放mp4格式视频只有声音没有图像的问题
video标签播放mp4格式视频只有声音没有图像的问题 这是由于视频格式是hevc(H265)编码的,这种编码格式视频video播放有问题主要是由于以下两种原因导致的: 1、浏览器没有开启硬加速模式: 开启方法(以谷歌浏览器为例)&a…...
问题解决:glog中的LOG(INFO)与VLOG无法打印
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。 本作品 (李兆龙 博文, 由 李兆龙 创作),由 李兆龙 确认,转载请注明版权。 文章目录 MotivationProcess glog版本为:https://github.com/google/glog/archive/…...
【第2月 day16】Matplotlib 散点图与柱状图
好的!以下是针对初学者的 Matplotlib 散点图与柱状图 学习内容,用简单易懂的语言和示例讲解: 一、散点图(Scatter Plot) 作用:展示两个变量之间的关系(如相关性、分布等)。 1. 核心…...
汽车 HMI 设计的发展趋势与设计要点
一、汽车HMI设计的发展历程与现状 汽车人机交互界面(HMI)设计经历了从简单到复杂、从单一到多元的演变过程。2012年以前,汽车HMI主要依赖物理按键进行操作,交互方式较为单一。随着特斯拉Model S的推出,触控屏逐渐成为…...
Vue 3 中按照某个字段将数组分成多个数组
方法一:使用 reduce 方法 const originalArray [{ id: 1, category: A, name: Item 1 },{ id: 2, category: B, name: Item 2 },{ id: 3, category: A, name: Item 3 },{ id: 4, category: C, name: Item 4 },{ id: 5, category: B, name: Item 5 }, ];const grou…...
06-Spring 中的事件驱动机制
Spring 中的事件驱动机制(ApplicationEvent 源码解析) 本小结主要总结Spring的事件,如果对于观察者模式比较熟悉的话,理解这个应该不难。 这块涉及的面试题相对简单,主要还是以日常使用为主。 另外在Spring的源码中也运…...
Python学习笔记(8)关于列表内置函数和多维列表
列表访问计数 索引直接访问 index()#获得首次出现指定元素的索引 index(value,[start,[end]] #控制搜索索引范围 counr()#获得指定元素在列表中出现的次数 len()#返回列表长度 成员资格判断 incount()返回0,代表不存在 列表切片 slice[起始偏移量 start:终止…...
【算法学习计划】回溯 -- 递归
目录 leetcode 面试题08.06.汉诺塔问题 leetcode 21.合并两个有序链表 leetcode 206.反转链表 leetcode 24.两两交换链表中的节点 leetcode 50. Pow(x, n) 本篇文章将是我们回溯专题的第一篇文章,在这里我先浅浅讲一下什么是回溯 其实就是递归,只不…...
Unity中 JobSystem使用整理
Unity 的JobSystem允许创建多线程代码,以便应用程序可以使用所有可用的 CPU 内核来执行代码,这提供了更高的性能,因为您的应用程序可以更高效地使用运行它的所有 CPU 内核的容量,而不是在一个 CPU 内核上运行所有代码。 可以单独使…...
【从零实现Json-Rpc框架】- 项目实现 - 服务端主题实现及整体封装
📢博客主页:https://blog.csdn.net/2301_779549673 📢博客仓库:https://gitee.com/JohnKingW/linux_test/tree/master/lesson 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! &…...
JavaScript基础-移动端常用开发框架
随着移动互联网的发展,越来越多的应用和服务需要支持移动设备。为了提高开发效率和用户体验,开发者们依赖于一些成熟的JavaScript框架来构建响应迅速、功能丰富的移动Web应用。本文将介绍几款广泛使用的移动端开发框架,并通过具体的示例展示它…...
Tree - Shaking
Vue 3 的 Tree - Shaking 技术详解 Tree - Shaking 是一种在打包时移除未使用代码的优化技术,在 Vue 3 中,Tree - Shaking 发挥了重要作用,有效减少了打包后的代码体积,提高了应用的加载性能。以下是对 Vue 3 中 Tree - Shaking …...
VSCode历史版本的下载安装
VSCode历史版本的下载安装 文章目录 VSCode历史版本的下载安装VSCode安装下载历史版本地址查询VSCode历史版本的 commit id 安装参考资料 VSCode安装 Windows版本:Windows10VSCode版本:VScode1.65.0(64位User版本)本文编写时间&a…...
Websoft9分享:在数字化转型中选择开源软件可能遇到的难题
引言:中小企业数字化转型的必由之路 全球94.57%的企业已采用开源软件(数据来源:OpenLogic 2024报告),开源生态估值达8.8万亿美元。中小企业通过开源软件构建EPR系统、企业官网、数据分析平台等,可节省80%软件采购成本。…...
【无人机】无人机PX4飞控系统高级软件架构
目录 1、概述(图解) 一、数据存储层(Storage) 二、外部通信层(External Connectivity) 三、核心通信枢纽(Message Bus) 四、硬件驱动层(Drivers) 五、飞…...
新版本Xmind结合DeepSeek快速生成美丽的思维导图
前言 我的上一篇博客(https://quickrubber.blog.csdn.net/article/details/146518898)中讲到采用Python编程可以实现和Xmind的互动,并让DeepSeek来生成相应的代码从而实现对内容的任意修改。但是,那篇博客中提到的Xmind有版本的限…...
Windows查重工具,强烈推荐大家收藏!
我大家在用电脑的时候,是不是发现用得越久,电脑里的软件和文件就越多? 今天我给大家带来的这两款重复文件查找神器,简直就是电脑里的“清洁小能手”,能帮你把那些重复的文件和文件夹找出来。 Easy DupLicate Finder 重…...
数字孪生技术之争:UE、Unity还是飞渡DTS数字孪生平台?
作为深耕数字孪生内容创作的B站UP主,我们创作的内容广受数十万粉丝喜爱。后台私信经常提及两个问题:“这质感绝了!如此丝滑流畅是UE做的吗?”VS “请问用Unity能实现这个效果吗?” Unreal Engine凭借影视级渲染&#…...
【GCC警告报错4】warning: format not a string literal and no format arguments
文章主本文根据笔者个人工作/学习经验整理而成,如有错误请留言。 文章为付费内容,已加入原创保护,禁止私自转载。 文章发布于:《C语言编译报错&警告合集》 如图所示: 原因: snprintf的函数原型&#x…...
【Tauri2】013——前端Window Event与创建Window
前言 【Tauri2】012——on_window_event函数-CSDN博客https://blog.csdn.net/qq_63401240/article/details/146909801?spm1001.2014.3001.5501 前面介绍了on_window_event,这个在Builder中的方法,里面有许多事件,比如Moved,Res…...
修复SSL证书链不完整问题certificate verify failed unable to get local issuer certificate
文章目录 前言排查过程怀疑文章平台图片转存问题尝试使用 Python 代码下载图片使用 SSL Labs Server Test 验证猜想回顾 SSL 安装命令ACME 生成的证书 验证使用 [SSL Labs Server Test](https://www.ssllabs.com/ssltest/index.html) 验证文章发布平台转存验证 个人简介 前言 …...
管家婆财贸ERP BB102.采购销售订金管理
低适用版本: 财贸系列 23.8 插件简要功能说明: 采购订单/销售订单支持查询订金付款情况,联查下游付款/收款信息更多细节描述见下方详细文档 插件操作视频: 进销存类定制插件--采购销售订金管理 插件详细功能文档: …...
前端对接下载文件接口、对接dart app
嵌套在dart app里面的前端项目 1.前端调下载接口 ->后端返回 application/pdf格式的文件 ->前端将pdf处理为blob ->blob转base64 ->调用dart app的 sdk saveFile ->保存成功 async download() {try {// 调用封装的 downloadEContract 方法获取 Blob 数据const …...
牛客 简写单词
简写单词_牛客题霸_牛客网 主要是如何输入 #include <iostream> #include <string>using namespace std;int main() {string str;while(cin>>str){if(str[0]>a&&str[0]<z){cout<<char(str[0]-32);}else cout<<str[0];str.clear(…...
解决STM32CubeMX中文注释乱码
本人采用【修改系统环境变量】的方法 1. 使用快捷键 win X,打开【系统R】,点击【高级系统设置】 2. 点击【环境变量】 3. 点击【新建】 4.按图中输入【JAVA_TOOL_OPTIONS】和【-Dfile.encodingUTF-8】,新建环境变量后重启CubeMX即可。 解释…...
若依——基于AI+若依框架的实战项目(实战篇(下))
目录 前言 6. 设备管理 6.1 需求说明 6.2 生成基础代码 6.2.1 需求 6.2.2 步骤 ①创建目录菜单 ②添加数据字典 ③配置代码生成信息 ④下载代码并导入项目 6.3 设备类型改造 6.3.1 基础页面 需求 代码实现 6.4 设备管理改造 6.4.1 基础页面 需求 代码实现 …...
SpringBoot项目瘦身指南:从臃肿到高效的优化实践
精心整理了最新的面试资料和简历模板,有需要的可以自行获取 点击前往百度网盘获取 点击前往夸克网盘获取 一、问题背景 SpringBoot的"约定优于配置"特性极大提升了开发效率,但默认配置可能导致项目逐渐臃肿。典型的症状包括: 打…...
运筹帷幄:制胜软件开发
运筹学在软件开发项目中的作用主要体现在复杂系统建模、资源优化和决策支持中。通过数学建模、算法设计和数据分析,运筹学能够帮助开发团队更高效地实现软件需求,尤其是在涉及资源分配、路径规划、调度优化等场景时。 案例:电商物流配送系统的…...