[操作系统] 基础IO:系统文件I/O
在 Linux 操作系统中,文件 I/O(输入/输出)是程序与文件系统交互的基础。理解文件 I/O 的工作原理对于编写高效、可靠的程序至关重要。本文将深入探讨系统文件 I/O 的机制。
一种传递标志位的方法
在 Linux 中,文件的打开操作通常使用标志位来指定文件的访问模式。open()
系统调用用于打开文件,其原型如下:
int open(const char *pathname, int flags, mode_t mode);
pathname
:要打开的文件路径。flags
:打开文件时的标志位,指定文件的访问模式和行为。mode
:文件权限,仅在创建新文件时使用。
常见的标志位包括:
参数必须包括以下三个访问方式之一。
- `O_RDONLY`:只读模式。
- `O_WRONLY`:只写模式。
- `O_RDWR`:读写模式。
其他的访问模式。
- `O_CREAT`:如果文件不存在,则创建文件。
- `O_TRUNC`:如果文件存在,则将其长度截断为零。
- `O_APPEND`:每次写入都追加到文件末尾。
标志位的原理:
原理就是位图。不同的访问模式位图上的标记位置不同,传参是通过或操作( | )即可得到需要访问模式的位图所有标记位置。然后再打开或操作文件时就会按照传入的访问模式进行。
文件权限mode
:
新创建文件的最终权限 = mode & ~umask
例如,以下代码以读写模式打开文件 example.txt
,如果文件不存在则创建:
int fd = open("example.txt", O_RDWR | O_CREAT, 0666);
在此,0666
是文件的权限掩码,表示文件所有者、所属组和其他用户均具有读写权限。
hello.c 写文件
在 C 语言中,使用 open()
打开文件后,可以使用 write()
系统调用向文件写入数据。以下是一个示例:
#include <fcntl.h>
#include <unistd.h>
#include <string.h>int main() {int fd = open("example.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);if (fd == -1) {// 错误处理return 1;}const char *text = "Hello, Linux!";ssize_t bytes_written = write(fd, text, strlen(text));if (bytes_written == -1) {// 错误处理close(fd);return 1;}close(fd);return 0;
}
向fd
中写入buf
,一次最多count
个。
在此示例中:
open()
以写入模式打开文件example.txt
,如果文件不存在则创建,权限为0666
。write()
将字符串"Hello, Linux!"
写入文件。close()
关闭文件描述符,释放资源。
每次写入字符串不用留
'\0'
的位置,文件本身可以看做数组,如果中间存在'\0'
,则在读取文件时会造成错误。
当向文件内写入内容时,可以进行文本写入和二进制写入,两者的区别写入是语言层面的概念,系统不会关心类型,只要写入内容就会直接写入。
hello.c 读文件
读取文件的过程与写入类似,使用 read()
系统调用从文件中读取数据。示例如下:
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>int main() {int fd = open("example.txt", O_RDONLY);if (fd == -1) {// 错误处理return 1;}char buffer[128];ssize_t bytes_read = read(fd, buffer, sizeof(buffer) - 1);if (bytes_read == -1) {// 错误处理close(fd);return 1;}buffer[bytes_read] = '\0'; // 确保字符串以 null 结尾printf("File content: %s\n", buffer);close(fd);return 0;
}
从fd
中读取,拷贝到buf
中,最多读取count
bytes(sizeof(buf) - 1
( - 1是为了在buf
的末尾存贮'\0'
))。
在此示例中:
open()
以只读模式打开文件example.txt
。read()
从文件中读取数据到缓冲区buffer
。close()
关闭文件描述符。
open 函数返回值
区分两个概念:**系统调用**
和**库函数**
。
- 向
fopen``fclose``fread``fwrite
等都是C标准库中的函数,称之为库函数(libc)。open``close``read``write``lseek
等属于系统提供的接口,称之为系统调用接口。
通过上图可以理解库函数和系统调用之间的关系。可以认为f*
系列的函数是对系统调用的封装,方便二次开发。
open()
函数的返回值是一个文件描述符(fd),用于标识打开的文件。成功时返回非负整数,失败时返回 -1
,并设置 errno
以指示错误类型。常见的错误包括:
EACCES
:权限不足。ENOENT
:文件不存在。EINVAL
:无效的标志位。
例如:
int fd = open("example.txt", O_RDONLY);
if (fd == -1) {perror("Error opening file");return 1;
}
文件描述符 fd
文件描述符(fd)是一个非负整数,用于标识进程打开的文件。标准输入、标准输出和标准错误分别对应文件描述符 0、1 和 2。文件描述符的分配规则如下:
- 默认情况下,标准输入、标准输出和标准错误分别占用 0、1 和 2。
- 通过
open()
打开的文件从 3 开始分配。
所以当我们查看在程序中打开的文件的fd
时发现都是3之后的,就是因为在程序运行前就有自动升层的代码在开头打开了三个标准流文件,已经占据了0,1,2。
0 & 1 & 2
0
:标准输入(stdin),通常对应键盘输入。1
:标准输出(stdout),通常对应屏幕输出。2
:标准错误(stderr),用于输出错误信息。
通过6
可知
通过6
中关于FILE
的讲解,当向open
等函数返回值fd
实际上就是进程内管理文件的数组的下标。所以当传入close
等函数调用时就会通过下标来找寻这个文件,然后进行文件操作。
而对于库函数来说,返回值为FILE
,作为将fd
包装好的结构体,在函数内部使用系统调用的时候会自行进行处理。
FILE
FILE
是什么呢?
在 C 语言标准库中,FILE
是一个用于描述文件的结构体,通常由 stdio.h
提供。它提供了一种便捷的接口,让我们可以操作文件而无需直接涉及底层的文件描述符。
FILE
结构体的内部实现
FILE
结构体并不是操作系统原生的,而是由 C 标准库(如 GNU C 库)定义的,它封装了文件的元数据,并提供了缓冲机制以提高 I/O 操作的效率。虽然不同的系统和编译器可能有不同的实现,以下是 FILE
结构体的一种典型实现:
struct _iobuf {char *_ptr; // 指向缓冲区的指针int _cnt; // 缓冲区的剩余字节数char *_base; // 缓冲区的起始位置int _flag; // 文件状态标志(如是否可读、是否可写)int _file; // 文件描述符int _charbuf; // 读缓存区的状态int _bufsiz; // 缓冲区大小char *_tmpfname; // 临时文件名
};
typedef struct _iobuf FILE;
重要字段解释:
_ptr
:指向当前缓冲区位置的指针,文件数据会存储在这里。_cnt
:缓冲区中剩余的可用空间字节数。_base
:缓冲区的起始位置。_flag
:存储文件的状态标志,如文件是否处于读写模式等。_file
:该文件对应的系统级文件描述符,这是最直接的文件标识。_bufsiz
:缓冲区的大小。_tmpfname
:如果文件是临时的,存储其文件名。
FILE
结构体内部使用缓冲机制,这使得每次文件 I/O 操作时,程序并不直接与磁盘交互,而是将数据存入内存中的缓冲区,等缓冲区满时才将数据批量写入磁盘,从而提高 I/O 性能。
缓冲机制具体本文不做解释,之后文章会讲解。
task_struct
和 file_struct
Linux 中的进程是由 task_struct
结构体来描述的。每个进程的 task_struct
中都包含一个 *file
指向一个file_struct
,这个结构体管理着该进程打开的文件。
task_struct
和文件操作的联系
task_struct
结构体代表一个进程。每个进程有自己的文件描述符表,文件描述符表由一个 file_struct
来表示。file_struct
存储了进程打开的所有文件的描述符、文件指针等信息。
struct task_struct {...struct files_struct *files; // 文件描述符表...
};
files_struct
结构体
files_struct
是与 task_struct
相关联的结构体,存储了该进程的文件描述符表(fd_table[]
)。它提供了一个对文件描述符的索引和文件操作的抽象管理。每个进程的 files_struct
都有一个 fd_table[]
数组,这个数组的索引即为文件描述符(fd)。
struct files_struct {atomic_t count; // 引用计数,表示该文件描述符表被多少个进程共享struct fdtable *fdt; // 文件描述符表(fd_table[])spinlock_t file_lock; // 保护文件描述符表的锁
};
fd_table[]
数组与 file_struct
fd_table[]
是一个数组,可以被看做文件描述符表,每个元素对应一个 file
结构体,表示一个文件。文件描述符(fd)就是 fd_table[]
数组的索引值。例如,文件描述符 0 对应标准输入(stdin),文件描述符 1 对应标准输出(stdout),文件描述符 2 对应标准错误(stderr)。
struct fdtable {unsigned int max_fds; // 最大文件描述符数struct file **fd; // 文件描述符数组,fd[i] 为进程打开的文件
};
fd[i]
表示索引为i
的文件描述符指向的文件。max_fds
表示文件描述符表的最大文件描述符数。- 不同的
fd
可以打开同一个文件,引用计数来维护,形成1 : n。
file
结构体
在 Linux 中,file
结构体表示一个打开的文件。它不仅包含了文件的数据指针和操作,还包含了与文件操作相关的状态信息。file
结构体的关键部分包括:
struct file
{属性mode读写位置读写选项缓冲区操作方法struct file *next; // 指向下一个fd的file结构体
}
f_op
:文件操作结构体,包含了对文件的操作方法(如读取、写入、关闭等)。f_pos
:文件的当前偏移量,表示文件指针的位置。f_mode
:文件的访问模式(如只读、只写、读写)。f_count
:引用计数,表示有多少进程引用了这个文件,所以真正的文件关闭指的是引用计数为0的时候。- 文件属性存储于结构体中,文件的内容存在缓冲区中。
文件操作的实质
从文件描述符到内核实现,文件操作的核心机制依赖于 fd_array[]
和 file_struct
。
文件描述符的使用流程
每当一个进程打开文件时,内核会为文件分配一个文件描述符(fd)。这个文件描述符将作为 fd_array[]
数组的索引,指向一个 file
结构体。具体的流程如下:
- 文件打开:进程通过
open()
系统调用请求打开一个磁盘中的文件文件。内核会分配一个新的文件描述符(fd
),并在fd_table[]
中为该进程创建一个指向该文件的file
结构体,属性存于结构体,内容存于结构体指向的缓冲区中。
冯诺依曼体系中,CPU不直接与硬件交互,所以需要通过内存来交互,缓冲区在内存中形成。对文件内容做任何操作,都必须先把文件加载到内核对应的文件缓冲区内,从磁盘到内存的拷贝。
- 文件读写:通过
read()
或write()
系统调用,进程会通过文件描述符访问file
结构体中的数据,并对文件进行操作。read()
本质就是内核到用户空间的拷贝函数。 - 文件关闭:当文件操作完成时,进程通过
close()
系统调用关闭文件。内核会减少文件描述符表中file
结构体的引用计数,若引用计数为 0,则释放该文件描述符的资源。
通过文件描述符与 file
结构体的映射
文件描述符实际上是一个索引,它将用户空间的文件 I/O 操作映射到内核空间的 file
结构体。进程每次对文件进行读写操作时,都会通过文件描述符查找对应的 file
结构体,然后通过 file
中的操作指针(f_op
)调用具体的文件操作函数,如 read()
, write()
或 flush()
。
文件操作的效率
- 缓冲机制:Linux 内核使用缓冲区来提升文件 I/O 的效率。文件数据首先被写入内核缓冲区,只有缓冲区满了或程序显式调用
flush
操作时,数据才会写入磁盘。这样可以减少磁盘 I/O 的频率。 - 文件操作锁:内核使用锁来同步文件操作,确保多个进程对同一文件的访问不会引发冲突。
结论
通过深入分析 FILE
结构体、task_struct
中的 file_struct
以及 fd_array[]
数组的关系,我们能够更清晰地理解 Linux 系统中文件操作的底层机制。文件描述符作为用户空间与内核空间的桥梁,file
结构体封装了对文件的访问接口,而内核通过文件描述符表、缓冲区机制和文件操作锁等技术,保证了高效且可靠的文件 I/O 操作。
编程语言的可移植性
编程语言的可移植性指的是程序能否在不同的平台或操作系统上顺利运行。语言的设计、标准库的实现以及对底层硬件的抽象都直接影响着程序的可移植性。
C 语言的可移植性
C 语言作为一种接近硬件的低级编程语言,直接与操作系统的底层交互。由于各个操作系统有不同的系统调用,C 语言的标准库为不同平台提供了相对一致的接口,使得 C 语言具备一定的可移植性。
不过,C 语言标准库的实现也可能因操作系统而异。比如,Windows 和 Linux 都有 C 语言的实现,但它们的文件 I/O 操作部分会有所不同,Windows 可能使用 CreateFile()
,而 Linux 使用 open()
。为了增强 C 语言的可移植性,开发者常常通过条件编译来区分不同操作系统下的实现。
例如,在 Windows 和 Linux 上都需要实现文件操作的代码:
#ifdef _WIN32
#include <windows.h>
HANDLE hFile = CreateFile("log.txt", GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
#else
#include <fcntl.h>
#include <unistd.h>
int fd = open("log.txt", O_CREAT | O_WRONLY | O_APPEND, 0666);
#endif
通过使用预处理指令 #ifdef
和 #endif
,程序可以根据不同操作系统选择不同的文件打开方式,从而增加跨平台的可移植性。
语言的可移植性?
除了 C 语言,其他高级编程语言(如 C++、Java、Python、Go、PHP)也通过各自的标准库和虚拟机来增强跨平台的可移植性。
- C++:C++ 通过标准库(如 STL)提供了一套跨平台的接口,使得程序能在不同操作系统上编译和运行。然而,当涉及到直接与操作系统底层交互时,C++ 仍然需要依赖平台特定的系统调用和 API。
- Java:Java 提供了 Java 虚拟机(JVM),使得 Java 程序可以在不同的操作系统上运行。JVM 会屏蔽底层系统的差异,使得 Java 代码具有良好的可移植性。Java 的字节码可以在任何实现了 JVM 的操作系统上运行。
- Python:Python 通过封装了平台特定的调用接口,提供了跨平台的标准库,如
os
、sys
等。Python 程序员通常不需要关心底层操作系统的细节,Python 会处理这些差异。 - Go:Go 语言内置对多平台的支持,编译器可以直接生成不同操作系统和架构的二进制文件,从而确保 Go 程序具有较高的可移植性。
- PHP:PHP 是一种主要用于 Web 开发的语言,它通过 Web 服务器(如 Apache、Nginx)和平台无关的接口(如数据库驱动)使得 PHP 程序具有一定的可移植性。
所以语言的移植性可以总结为:语言在底层库中的使用系统调用的函数针对不同的系统会将系统调用部分更改,更换为不同操作系统的系统调用(条件编译来解决)。
如此在上层使用语言的时候不会感受到差异,因为只是使用语言的语法,底层库的差异在语言层面进行屏蔽,增加了语言的可移植性。
语言增加可移植性让更多人愿意去使用,增加市场占有率。
不可移植性的原因?
- 操作系统依赖:
不同的操作系统有不同的API和系统调用。例如,Linux和windows的文件操作、内存管理、线程处理等API不同。如果现在有一个程序,在编写的时候直接调用了某个操作系统特有的API,它在其他操作系统上就无法工作。必须将调用特有API更换为要在上面执行的操作系统的API才可以正常运行。
- 硬件依赖:
不同平台使用的编译器可能会有不同的行为,或者某些编辑器不支持某些特性。例如,C++中某些编译器特性只在特定的编译器中有效,导致代码在其他平台或编辑器中无法运行。
重定向
文件描述符的分配规则
当进程打开文件时,操作系统会分配一个最小的未使用文件描述符。例如:
int fd = open("example.txt", O_RDONLY);
如果文件描述符 3 未被占用,则 fd
将被赋值为 3。
重定向
重定向的核心原理在于操作文件描述符。文件描述符在file_struct
中的数组中存放管理,通过改变文件描述符的指向,我们可以将输入或输出流重定向到文件、设备或其他流。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h> // 包含close函数的声明int main() {// 关闭标准输出文件描述符1close(1);// 打开(或创建)一个名为"myfile"的文件,以只写方式打开// 如果文件不存在则创建,权限设置为644int fd = open("myfile", O_WRONLY | O_CREAT, 00644);if (fd < 0) {// 如果打开文件失败,输出错误信息并返回1perror("open");return 1;}// 输出文件描述符printf("fd: %d\n", fd);// 刷新标准输出缓冲区,确保输出立即显示fflush(stdout);// 关闭文件描述符close(fd);// 程序正常退出exit(0);
}
已知文件描述符的分配规则和重定向的原理,那么通过以上代码理解。先关闭fd = 1
的文件,也就是标准输出流文件。此时再打开文件时就会按照文件描述符的分配规则,将新打开的文件描述符设置为按照顺序最小的下标,也就是刚关闭fd = 1
。然后当使用printf
进行打印的时候,该函数默认的拷贝到的文件fd
为1
,本来是向显示屏进行打印,实际上因为新文件的占用,将内容拷贝进行新文件中。
这就是重定向,数组的下标不变,更改文件描述符的指针指向。
使用 dup2()
系统调用
在 Linux 中,dup2()
系统调用用于复制一个文件描述符,并将其指向另一个指定的文件描述符。这对于实现输入输出的重定向非常有用。
函数原型:
int dup2(int oldfd, int newfd);
oldfd
:现有的文件描述符。newfd
:目标文件描述符。
功能:
- 将
oldfd
指向的文件复制到newfd
。 - 如果
newfd
已经打开,则先关闭它。 - 返回新的文件描述符
newfd
,如果出错则返回-1
。
示例代码:
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>int main() {// 打开文件,获取文件描述符int fd = open("output.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);if (fd == -1) {perror("打开文件失败");return 1;}// 将标准输出重定向到文件if (dup2(fd, STDOUT_FILENO) == -1) {perror("重定向标准输出失败");close(fd);return 1;}// 关闭原始文件描述符close(fd);// 现在 printf 的输出将写入 output.txtprintf("这行文本将被写入到 output.txt 文件中。\n");return 0;
}
在上述示例中:
- 我们首先使用
open()
打开output.txt
文件,并获取文件描述符fd
。 - 然后,使用
dup2()
将标准输出(STDOUT_FILENO
)重定向到output.txt
文件。 - 关闭原始的文件描述符
fd
。 - 之后,所有通过
printf()
输出的内容都会写入output.txt
文件,而不是显示器。
在 minishell 中添加重定向功能
#include <iostream>
#include <ctype.h>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <cstring>
#include <unordered_map>
#include <sys/stat.h>
#include <fcntl.h>#define COMMAND_SIZE 1024 // 命令行最大长度
#define FORMAT "[%s@%s %s]# " // 提示符格式// ================== 全局数据结构声明 ==================
// 1. 命令行参数表
#define MAXARGC 128
char *g_argv[MAXARGC]; // 存储解析后的命令行参数
int g_argc = 0; // 参数个数// 2. 环境变量表
#define MAX_ENVS 100
char *g_env[MAX_ENVS]; // 存储环境变量
int g_envs = 0; // 环境变量数量// 3. 别名映射表(当前代码未完整实现)
std::unordered_map<std::string, std::string> alias_list;// 4. 重定向相关配置
#define NONE_REDIR 0 // 无重定向
#define INPUT_REDIR 1 // 输入重定向 <
#define OUTPUT_REDIR 2 // 输出重定向 >
#define APPEND_REDIR 3 // 追加重定向 >>int redir = NONE_REDIR; // 记录当前重定向类型
std::string filename; // 重定向文件名// ================== 辅助函数声明 ==================
// [省略部分环境获取函数...]// ================== 环境初始化 ==================
void InitEnv() {extern char **environ;// 从父进程复制环境变量到g_env数组for(int i = 0; environ[i]; i++) {g_env[i] = strdup(environ[i]); // 使用strdup复制字符串g_envs++;}// 设置新环境变量(示例)g_env[g_envs++] = strdup("HAHA=for_test");g_env[g_envs] = NULL;// 更新进程环境变量for(int i = 0; g_env[i]; i++) {putenv(g_env[i]);}environ = g_env; // 替换全局environ指针
}// ================== 重定向处理核心函数 ==================
void TrimSpace(char cmd[], int &end) {// 跳过连续空白字符while(isspace(cmd[end])) end++;
}void RedirCheck(char cmd[]) {// 开始前先将文件操作的信息初始化redir = NONE_REDIR;filename.clear();int start = 0;int end = strlen(cmd)-1;// 从命令末尾向前扫描寻找重定向符号while(end > start) {if(cmd[end] == '<') { // 输入重定向cmd[end] = '\0'; // 截断命令字符串end++;TrimSpace(cmd, end); // 跳过空格redir = INPUT_REDIR;filename = cmd + end;break;}else if(cmd[end] == '>') {// 判断是>>还是>if(end > 0 && cmd[end-1] == '>') { // 追加重定向cmd[end-1] = '\0'; // 截断命令字符串end++; // 移动到>后的位置redir = APPEND_REDIR;} else { // 普通输出重定向cmd[end] = '\0';end++;redir = OUTPUT_REDIR;}// 这时end在最后的运算符后面,然后用TrimSpace向后查找文件开头字母TrimSpace(cmd, end);filename = cmd + end; // end为文件名开头字母位置,直接cmd定位到文件名部分break;}else {end--; // 继续向前扫描}}
}// ================== 命令执行 ==================
int Execute() {pid_t id = fork();if(id == 0) { // 子进程int fd = -1;switch(redir) {case INPUT_REDIR:fd = open(filename.c_str(), O_RDONLY);dup2(fd, STDIN_FILENO); // 重定向标准输入break;case OUTPUT_REDIR:fd = open(filename.c_str(), O_CREAT|O_WRONLY|O_TRUNC, 0666);dup2(fd, STDOUT_FILENO); // 重定向标准输出break;case APPEND_REDIR:fd = open(filename.c_str(), O_CREAT|O_WRONLY|O_APPEND, 0666);dup2(fd, STDOUT_FILENO);break;default: // 无重定向不做处理break;}if(fd != -1) close(fd); // 关闭不再需要的文件描述符execvp(g_argv[0], g_argv); // 执行程序exit(EXIT_FAILURE); // exec失败时退出}// 父进程等待子进程int status = 0;waitpid(id, &status, 0);lastcode = WEXITSTATUS(status); // 记录退出状态return 0;
}// ================== 主循环 ==================
int main() {InitEnv(); // 初始化环境变量while(true) {PrintCommandPrompt(); // 打印提示符char commandline[COMMAND_SIZE];if(!GetCommandLine(commandline, sizeof(commandline))) continue;RedirCheck(commandline); // 重定向解析if(!CommandParse(commandline)) continue; // 命令解析if(CheckAndExecBuiltin()) continue; // 内建命令Execute(); // 执行外部命令}return 0;
}
总结
通过深入探讨文件描述符(fd)的使用,以及如何在 C 语言中实现文件的重定向功能,我们可以更好地理解 Linux 系统文件 I/O 的工作原理。掌握这些概念和技术,对于编写高效、可靠的系统级程序具有重要意义。
相关文章:
[操作系统] 基础IO:系统文件I/O
在 Linux 操作系统中,文件 I/O(输入/输出)是程序与文件系统交互的基础。理解文件 I/O 的工作原理对于编写高效、可靠的程序至关重要。本文将深入探讨系统文件 I/O 的机制。 一种传递标志位的方法 在 Linux 中,文件的打开操作通常…...
CNN手写数字识别1——模型搭建与数据准备
模型搭建 我们这次使用LeNet模型,LeNet是一个经典的卷积神经网络(Convolutional Neural Network, CNN)架构,最初由Yann LeCun等人在1998年提出,用于手写数字识别任务 创建一个文件model.py。实现以下代码。 源码 #…...
基于Istio Ambient Mesh的无边车架构:实现零侵入式服务网格的云原生革命
引言:轻量化时代的服务通信进化论 当传统Sidecar模式面临内存开销暴增的困境,Istio社区推出的Ambient Mesh架构给出终极解决方案。某证券交易系统实测显示,采用该架构后服务延迟降低至1.7ms(降幅达73%),同…...
数位dp入门详解
1. 介绍 数位 d p dp dp一般出现在来求一个范围 [ a , b ] [a, b] [a,b]内满足条件的数有多少。数位 d p dp dp的解决比较公式化,考虑每一位对最终答案的影响。 2. 案例 Luogu P2602: 求给定范围 [ a , b ] [a,b] [a,b]各个数位 k k k出现了多少次。 …...
【黑马点评优化】2-Canel实现多级缓存(Redis+Caffeine)同步
【黑马点评优化】2-Canel实现多级缓存(RedisCaffeine)同步 0 背景1 配置MySQL1.1 开启MySQL的binlog功能1.1.1 找到mysql配置文件my.ini的位置1.1.2 开启binlog 1.2 创建canal用户 2 下载配置canal2.1 canal 1.1.5下载2.2 配置canal2.3 启动canal2.4 测试…...
牛顿法:用泰勒级数求解平方根的秘籍
目录 一、引言二、牛顿法的理论基础——泰勒级数三、牛顿法的原理与推导3.1 原理概述3.2 推导过程3.3 几何解释 四、牛顿法的应用场景4.1 数值计算4.2 优化问题 五、牛顿法求平方根的具体案例5.1 原理推导5.2 具体步骤5.3 代码实现(Python)5.4 示例计算过…...
四、敏捷方法论:敏捷开发中的关键实践
敏捷开发中的关键实践 在敏捷开发中,许多关键实践帮助团队提升效率、增强协作和确保高质量交付。通过合理运用这些实践,团队能够灵活应对需求变化、缩短交付周期,并持续优化工作流程。本文将详细介绍敏捷开发中的一些核心实践,包括用户故事、需求优先级排序、持续交付、持…...
基于Qt 和微信小程序的用户管理系统:WebSocket + SQLite 实现注册与登录
目录 一. 概要 二. 技术栈 三. 系统功能设计 3.1 功能模块 3.2 数据表设计 四. 具体实现 4.1 Qt 服务端 4.1.1 初始化 WebSocket 服务器 4.1.2 用户管理界面 4.2 微信小程序端 4.2.1 注册功能 4.2.2 登录功能 五. 运行效果 六. 源码下载 一. 概要 在物联网和智能设备…...
DeepSeek R1 与 OpenAI O1:机器学习模型的巅峰对决
我的个人主页 我的专栏:人工智能领域、java-数据结构、Javase、C语言,希望能帮助到大家!!!点赞👍收藏❤ 一、引言 在机器学习的广袤天地中,大型语言模型(LLM)无疑是最…...
【硬件设计细节】缓冲驱动器使用注意事项
一、缓冲驱动器核心功能与选型原则 信号增强与隔离 驱动能力匹配:根据负载电流需求选择缓冲器,例如CMOS缓冲器驱动能力通常为4-8mA,需搭配大电流负载时选用图腾柱输出或专用驱动芯片(如TI的SN74LVC系列)。电压域转换&…...
基于LVGL的简易聊天室
创建过程: 在GUI里配置好了一个大概的界面。聊天界面需要一个滑动页、一个输入框、一个文本框、一个发送按键。其中文本框属于滑动页,并且给发送按键添加上事件。 保存界面后,打开工程代码。 第一件事,是打开键盘使用的宏定义 随…...
DeepSeek 助力 Vue 开发:打造丝滑的开关切换(Switch)
前言:哈喽,大家好,今天给大家分享一篇文章!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏关注哦 💕 目录 Deep…...
IDEA集成DeepSeek
引言 随着数据量的爆炸式增长,传统搜索技术已无法满足用户对精准、高效搜索的需求。 DeepSeek作为新一代智能搜索技术,凭借其强大的语义理解与深度学习能力,正在改变搜索领域的游戏规则。 对于 Java 开发者而言,将 DeepSeek 集成…...
pycharm社区版有个window和arm64版本,到底下载哪一个?还有pycharm官网
首先pycharm官网是这一个。我是在2025年2月16日9:57进入的网站。如果网站还没有更新的话,那么就往下滑一下找到 community Edition,这个就是社区版了免费的。PyCharm:适用于数据科学和 Web 开发的 Python IDE 适用于数据科学和 Web 开发的 Python IDE&am…...
LeetCode热题100- 缺失的第一个正数【JavaScript讲解】
题目: 解题一: 如果不考虑时间复杂度和空间复杂度的话,我们最先想到的办法是先将该数组进行排序和去重,将最初的res结果值设置为1;将然后进行遍历,如果第一项不为1,则返回1,否则根…...
基于矢量轨道角动量波的透射超表面设计
摘 要:针对轨道角动量(Orbital Angular Momentum,OAM)在无线通信系统中相位奇点的问题,提出了一种产生矢量OAM波的透射超表面。设计了一种超表面透射单元实现透射极化和相位的调控,并由此单元排布组成透射超表面。采用透射超表面调控透射电磁波…...
全方位探索DeepSeek
目录 前言1. DeepSeek的基础功能与应用场景2. 使用DeepSeek的多种方式2.1 通过Web界面快速体验2.2 调用API实现自动化处理2.3 集成到本地开发环境2.4 结合第三方工具扩展功能 3. 高效使用DeepSeek的进阶技巧3.1 参数调优与性能优化3.2 数据处理与结果分析 4. 实际案例分析与应用…...
详解Redis数据结构(附源码)
引言 只有弄明白Redis数据结构,才能理解它如此快速的原因,并不只是它存储于内存,本篇文章将拆开Redis数据结构分析它高效的原因 字符串(String) 基本概念:字符串是 Redis 中最基本的数据结构,…...
基于Flask的茶叶销售数据可视化分析系统设计与实现
【FLask】基于Flask的茶叶销售数据可视化分析系统设计与实现(完整系统源码开发笔记详细部署教程)✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 该系统的创新之处在于系统不仅提供了基础的图表展示,如价格分布、付款分…...
基于推荐算法的在线课程推荐系统设计与实现
开发语言:Java框架:springbootJDK版本:JDK1.8服务器:tomcat7数据库:mysql 5.7(一定要5.7版本)数据库工具:Navicat11开发软件:eclipse/myeclipse/ideaMaven包:…...
计时器任务实现(保存视频和图像)
下面是一个简单的计时器任务实现,可持续地每秒保存一幅图像,也可持续地每60秒保存一个视频,图像和视频均以当前时间命名: TimerTask类的实现如下: class TimerTask { public:TimerTask(const std::string& path):…...
FreeRTOS第3篇:链表的“精密齿轮”——列表与列表项
文章目录 1 列表与列表项:FreeRTOS的“排队系统”2 列表操作:FreeRTOS的“排队算法”3 列表的应用场景:FreeRTOS的“任务调度枢纽”4 源码级洞察:列表的“灵魂代码”5 实战:列表操作实验6 总结与思考引言:嵌入式系统的“任务候车厅” 想象你正在管理一座繁忙的火车站:乘…...
Linux(ubuntu)下载ollama速度慢解决办法
国内安装Ollama都很慢,因为一直卡在下载中,直接通过官网的链接地址下载方法: curl -fsSL https://ollama.com/install.sh | sh速度大概是10min下载1%,完全不能接受啊! 其中很好的一个加速方式是通过使用github文件加速…...
【Java】分布式锁Redis和Redisson
https://blog.csdn.net/weixin_44606481/article/details/134373900 https://www.bilibili.com/video/BV1nW421R7qJ Redis锁机制一般是由 setnx 命令实现,set if not exists,语法setnx key value,将key设置值为value,如果key不存在…...
网络编程-
文章目录 网络编程套接字UDP/TCP的api使用 网络编程套接字 socket,是操作系统给应用程序(传输层给应用层)提供的api,Java也对这个api进行了封装。 socket提供了两组不同的api,UDP有一套,TCP有一套&#x…...
DeepSeek助力学术论文写作[特殊字符]
宝子们,还在为学术论文写作发愁吗?DeepSeek来帮你!只要用对提示词,它就能变成你写作路上的超级助手。今天就来给大家分享一些超好用的提示词,助力学术论文写作,让你的论文在ChatGPT的辅助下闪闪发光✨。 一…...
从零创建DeepSeek:技术路径与实践探索
import tensorflow as tf摘要:本文详细阐述了从零开始创建DeepSeek的全过程,涵盖从项目启动的构思,到技术选型的考量,再到模型训练的精细操作,以及系统集成、测试优化和部署上线的各个环节。通过对这些步骤的深入解析&…...
MySQL技术公开课:Mysql-Server-8.4.4 Innodb 集群搭建与维护
MySQL技术公开课 - Mysql-Server-8.4.4 Innodb 集群搭建与维护 讲课内容: 1、Innodb集群框架介绍 2、Innodb集群部署(mysql-Server、mysql-shell、mysql-router安装配置) 3、Innodb集群维护(主备切换、启动与关闭、故障排除) Mysql-server商业版目前最新的是8.…...
VS Code User和System版区别【推荐使用System版本】and VSCode+Keil协同开发之Keil Assistant
VS Code User和System版区别 Chapter1 VS Code User和System版区别1. 对于安装而言2. 结束语 Chapter2 VS Code 安装、配置教程及插件推荐插件: Chapter3 VSCodeKeil协同开发之Keil Assistant1. 效果展示2. Keil Assistant简介3. Keil Assistant功能特性4. 部署步骤…...
动态规划两个数组dp问题系列一>最长重复子数组
目录 状态表示:状态转移方程:初始化:填表顺序:返回值:代码呈现: 状态表示: 状态转移方程: 初始化: 填表顺序: 返回值: 这里是以某一个位置为结尾定…...
在SpringBoot中使用UniHttp简化天地图路径规划调用实践
目录 写在最前面 前言 一、天地图路径规划简介 1、天地图相关服务 2、天地图路径规划接口 二、UniHttp简介 1、UniHttp是什么? 2、UniHttp能做什么? 三、UniHttp调用天地图接口 1、请求接口的定义 2、实际调用 3、相应结果展示 四、总结 写在…...
springboot与Freemarker
1 基本使用 1.1 介绍 FreeMarker 是一款模板引擎: 即一种基于模板和要改变的数据,并用来生成输出文本(HTML网页,电子邮件,配置文件,源代码等)的通用工具。 是一个Java类库。 FreeMarker 被设计用来生成 HTML Web 页面…...
CMake无法生成可执行文件,一直生成库文件
CMakeLists的内容如下,一直生成的main是库文件,而不是可执行文件。本人是在进行鸿蒙的交叉编译的时候遇到,归结为cmake属性的差异。原内容如下: # 设置最低CMake版本要求 cmake_minimum_required (VERSION 2.8.0)# 设置项目名称 …...
PrimeFaces实战:IdleMonitor与Ajax的完美结合
在现代的Web开发中,用户交互的实时反馈是一个重要的用户体验环节。PrimeFaces作为一个强大的Java EE UI库,提供了许多便捷的功能组件,其中之一就是IdleMonitor。通过IdleMonitor,我们可以轻松地检测用户何时处于空闲状态以及何时从…...
搭建一个经典的LeNet5神经网络
第一章:计算机视觉中图像的基础认知 第二章:计算机视觉:卷积神经网络(CNN)基本概念(一) 第三章:计算机视觉:卷积神经网络(CNN)基本概念(二) 第四章:搭建一个经典的LeNet5神经网络 一、LeNet-5背景 LeNet-…...
Transformer多头注意力并行计算原理与工业级实现:从数学推导到PyTorch工程优化
一、核心数学原理剖析 1.1 多头注意力矩阵分解 Q XW^Q ∈ R^{nd_k} K XW^K ∈ R^{nd_k} V XW^V ∈ R^{nd_v} 多头分解公式: head_i Attention(QW_i^Q, KW_i^K, VW_i^V) 其中 W_i^Q ∈ R^{d_kd_k/h}, W_i^K ∈ R^{d_kd_k/h}, W_i^V ∈ R^{d_vd_v/h} (h为头数…...
OpenAI 的变化对行业意味着什么?
哎呀,中国AI的发展可是搅动了一番风云。害怕自己正在失去对 AI 话语权的掌控,OpenAI 决定是时候全力出击了。 除了最近意外发布的 o3-mini 模型之外,Sam Altman 昨天还宣布了接下来几周/几个月的路线图,而这些变化相当显著&#…...
LinkedList
一.IDEA的链表库 IDEA上实现链表的包,实现的是无头双向不循环链表:(并且这个链表有头尾节点) 二.自己实现一个无头双向不循环链表 1.创建链表的类,在链表内中定义一个节点的内部类,并且在链表的类中定义头…...
半遮挡检测算法 Detecting Binocular Half-Occlusions
【1. 背景】: 本文分析【Detecting Binocular Half-Occlusions:Empirical Comparisons of Five Approaches】Geoffrey Egnal和Richard P. Wildes于2002年发表在IEEE Transactions on Pattern Analysis and Machine Intelligence上,这是1篇中…...
零基础购买阿里云服务器,XShell连接云服务器
目录 1.环境搭建方式 2. 使用云服务器 3.使用终端软件登录到Linux 4.使用XShell登录主机 5.连接失败的原因: 下一篇更新:Linux的基础指令以及如何Linux的环境搭建 1.环境搭建方式 主要有四种: 1.直接安装在物理机上,虽然Linux有图形化…...
Mac ARM 架构的命令行(终端)中,删除整行的快捷键是:Ctrl + U
在 Mac ARM 架构的命令行(终端)中,删除整行的快捷键是: Ctrl U这个快捷键会删除光标所在位置到行首之间的所有内容。如果你想删除光标后面的所有内容,可以使用: Ctrl K这两个快捷键可以帮助你快速清除当…...
ESP学习-1(MicroPython VSCode开发环境搭建)
下载ESP8266固件:https://micropython.org/download/ESP8266_GENERIC/win电脑:pip install esptools python.exe -m pip install --upgrade pip esptooo.py --port COM5 erase_flash //清除之前的固件 esptool --port COM5 --baud 115200 write_fla…...
微信小程序性能优化
微信小程序的性能优化是提升用户体验的关键。以下是一些常见的优化策略和技巧: 1. 减少 setData 的调用频率和数据量 setData 是小程序中更新视图的主要方式,但频繁调用或数据量过大会导致性能问题。 减少调用频率:避免在短时间内多次调用…...
五十天精通硬件设计第31天-阻抗
系列文章传送门 50天精通硬件设计第一天-总体规划-CSDN博客 目录 1. 核心概念:特性阻抗 2. 阻抗不匹配的后果 3. 关键影响因素 4. 阻抗匹配方法 5. 设计实践要点 6. 工具与测试 7. 常见问题解决 总结 信号完整性中的阻抗问题主要涉及传输线的特性阻抗匹配,是确保高…...
docker部署dify结合deepseek构建知识库
序 本文主要研究一下本地docker部署dify结合deepseek构建知识库 步骤 dify git clone https://github.com/langgenius/dify.git git co tags/0.15.3 -b 0.15.3 cd docker cp .env.example .env docker-comopse up启动之后访问localhost docker-comopse.yaml # # WARNING…...
11.C语言 malloc() calloc() realloc()分配内存
目录 malloc 好处 坏处 总结 calloc 参数说明 作用 与 malloc 的区别 示例 优点 缺点 总结 realloc 参数说明 作用 示例 优点 缺点 注意事项 总结 总结区别 对比表格 malloc 函数功能:分配内存给 void* malloc(size_t size); 来看一下deep…...
可信大模型:LLM + 神经符号推理,解决复杂推理任务
可信大模型:LLM 神经符号推理,解决复杂推理任务 论文大纲一、Why:研究要解决的现实问题二、What:核心发现或论点三、How:研究的整体方法与关键细节3.1 前人研究的局限性3.2 创新方法/视角3.3 关键数据或实验支持3.4 可…...
基于大数据的全国热门旅游景点数据分析系统的设计与实现
【大数据】基于大数据的全国热门旅游景点数据分析系统的设计与实现(完整系统源码开发笔记详细部署教程)✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 该系统主要包括登录注册、系统首页、图表分析、数据管理和个人信息五大功能模…...
Moya 网络框架
Moya 网络框架 定义enum类型,有多种接口就定义多少种,然后实现TargetType协议 import Foundation //导入网络框架 import Moyaenum DefaultService {//广告列表case ads(position : Int)case sheets(size:Int)case sheetDetail(data: String)case regi…...
【环境安装】重装Docker-26.0.2版本
【机器背景说明】Linux-Centos7;已有低版本的Docker 【目标环境说明】 卸载已有Docker,用docker-26.0.2.tgz安装包安装 1.Docker包下载 下载地址:Index of linux/static/stable/x86_64/ 2.卸载已有的Docker 卸载之前首先停掉服务 sudo…...