[操作系统] 基础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中,最多读取countbytes(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_structfile_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 结构体。具体的流程如下:

  1. 文件打开:进程通过 open() 系统调用请求打开一个磁盘中的文件文件。内核会分配一个新的文件描述符(fd),并在 fd_table[] 中为该进程创建一个指向该文件的 file 结构体,属性存于结构体,内容存于结构体指向的缓冲区中。

冯诺依曼体系中,CPU不直接与硬件交互,所以需要通过内存来交互,缓冲区在内存中形成。对文件内容做任何操作,都必须先把文件加载到内核对应的文件缓冲区内,从磁盘到内存的拷贝。

  1. 文件读写:通过 read()write() 系统调用,进程会通过文件描述符访问 file 结构体中的数据,并对文件进行操作。read()本质就是内核到用户空间的拷贝函数。
  2. 文件关闭:当文件操作完成时,进程通过 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 通过封装了平台特定的调用接口,提供了跨平台的标准库,如 ossys 等。Python 程序员通常不需要关心底层操作系统的细节,Python 会处理这些差异。
  • Go:Go 语言内置对多平台的支持,编译器可以直接生成不同操作系统和架构的二进制文件,从而确保 Go 程序具有较高的可移植性。
  • PHP:PHP 是一种主要用于 Web 开发的语言,它通过 Web 服务器(如 Apache、Nginx)和平台无关的接口(如数据库驱动)使得 PHP 程序具有一定的可移植性。

所以语言的移植性可以总结为:语言在底层库中的使用系统调用的函数针对不同的系统会将系统调用部分更改,更换为不同操作系统的系统调用(条件编译来解决)。

如此在上层使用语言的时候不会感受到差异,因为只是使用语言的语法,底层库的差异在语言层面进行屏蔽,增加了语言的可移植性。

语言增加可移植性让更多人愿意去使用,增加市场占有率。

不可移植性的原因?

  1. 操作系统依赖:

不同的操作系统有不同的API和系统调用。例如,Linux和windows的文件操作、内存管理、线程处理等API不同。如果现在有一个程序,在编写的时候直接调用了某个操作系统特有的API,它在其他操作系统上就无法工作。必须将调用特有API更换为要在上面执行的操作系统的API才可以正常运行。

  1. 硬件依赖:

不同平台使用的编译器可能会有不同的行为,或者某些编辑器不支持某些特性。例如,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进行打印的时候,该函数默认的拷贝到的文件fd1,本来是向显示屏进行打印,实际上因为新文件的占用,将内容拷贝进行新文件中。

这就是重定向,数组的下标不变,更改文件描述符的指针指向。

使用 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 的工作原理。掌握这些概念和技术,对于编写高效、可靠的系统级程序具有重要意义。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Qt MainWindow

文章目录 0. 概述1. 菜单栏 QMenuBar1.1 例子1&#xff0c;使用图形化界面1.2 例子2&#xff0c;使用代码创建1.3 例子3&#xff0c;添加快捷键1.4 例子4&#xff0c;添加子菜单1.5 例子5&#xff0c;添加分割线和图标1.6 内存泄漏问题 2. 工具栏 QToolBar2.1 例子1&#xff0c…

阅读论文“用于车联网安全车载通信的机器学习技术“的学习笔记

前言 论文全称为Machine Learning Technologies for Secure Vehicular Communication in Internet of Vehicles: Recent Advancesc and Applications 智能交通系统&#xff08;ITS&#xff09;和计算系统的快速发展为智能交通安全提供了新的科学研究&#xff0c;并提供了舒适和…

[java] 集合-Collection、ArrayList、LinkedList源码篇

目录 Collection集合 集合类体系结构 常用方法 遍历方式 迭代器遍历 增强for lambda表达式 List集合 特有方法 五种遍历方式 细节点注意 List集合的实现类 List集合子类的特点 LinkedList集合的特有功能 源码分析 ArrayList源码分析 LinkedList源码分析 迭代…

DeepSeek自动化写作软件

DeepSeek写作软件的三大核心功能 对于内容创作者来说&#xff0c;写作不仅是表达思想的过程&#xff0c;更是一项需要投入大量时间和精力的任务。面对日益增长的内容需求&#xff0c;写作效率低下、内容质量不高等问题&#xff0c;常常让创作者感到焦虑。而 DeepSeek 写作软件…

前端里的this指向问题

目录 1.代码输出结果 2.代码输出结果 3.代码输出结果 4.代码输出结果 5.代码输出结果 6.代码输出结果 7.代码输出结果 8.代码输出结果 9.代码输出结果 10.代码输出结果 11.代码输出结果 12.代码输出结果 13.代码输出结果 14.代码输出结果 总结 1.代码输出结果 f…

苹果CMS新版站群管理更新_新增批量生成插件优势何在

引言 随着互联网的发展&#xff0c;站群管理成为了网站运营者提升流量和SEO效果的重要策略。苹果CMS新版站群管理系统通过引入批量生成插件&#xff0c;为用户提供了更高效、更智能的解决方案。本文将详细介绍这一更新的功能特点及其优势。 站群管理功能特点 多域名独立配置…

时序约束进阶八:时钟抖动Jitter与不确定性Uncertainty

目录 一、前言 二、时钟抖动 2.1 时钟抖动类型 2.2 set_input_jitter 2.3 set_system_jitter 2.4 set_clock_uncertainty 2.5 设计代码 2.6 约束解析 2.7 Input_jitter报告 2.8 System Jitter报告 2.9 Clock Uncertainty报告 2.9.1 Uncertainty的计算 2.9.2 Uncer…

小米 R3G 路由器(Pandavan)实现网络打印机功能

小米 R3G 路由器&#xff08;Pandavan&#xff09;实现网络打印机功能 一、前言 家中有多台 PC 设备需要打印服务&#xff0c;但苦于家中的 Epson L380 打印机没有网络打印功能&#xff0c;并且配置 Windows 共享打印机实在是过于繁琐且需要共享机保持唤醒状态过于费电。想到…

Leetcode Hot100 第30题 416.分割等和子集

class Solution { public:bool canPartition(vector<int>& nums) {int sum0;for(int num:nums){sumnum;}if(sum%21) return false;int bag_size sum/2;// return dfs(nums,nums.size()-1,bag_size);//递归做法vector<vector<bool>> dp(nums.size()1,vec…

技术晋升读书笔记—阿里管理三板斧(二)

一、引子 美团王兴问马云&#xff1a;“你最强的地方是什么&#xff1f;” 马云反问王兴&#xff1a;“你觉得呢&#xff1f;” 王兴回答&#xff1a;“战略和忽悠。” 马云哈哈大笑&#xff0c;笑完&#xff0c;他一本正经地说&#xff1a;“我最强的地方是管理。” &quo…

引入了 Disruptor 后,系统性能大幅提升!

Disruptor 是一个很受欢迎的内存消息队列&#xff0c;它源于 LMAX 对并发、性能和非阻塞算法的研究。今天一起来学习一下这个消息队列。 简介 对于主流的分布式消息队列来说&#xff0c;一般会包含 Producer、Broker、Consumer、注册中心等模块。比如 RocketMQ 架构如下&…

【WPSOffice】汇总

写在前面 PPT篇 幻灯片母版 通过母版功能统一幻灯片的样式、字体、颜色等&#xff0c;提高整体一致性。 统一设置模板样式 字体安装 查找到字体并安装。 在WPS PPT&#xff08;WPS演示&#xff09;中&#xff0c;以下是最常用的十个功能&#xff0c;能够帮助用户高效制作…

鸿蒙开发:熟知@BuilderParam装饰器

前言 本文代码案例基于Api13。 在实际的开发中&#xff0c;我们经常会遇到自定义组件的情况&#xff0c;比如通用的列表组件&#xff0c;选项卡组件等等&#xff0c;由于使用方的样式不一&#xff0c;子组件是动态变化的&#xff0c;针对这一情况&#xff0c;就不得不让使用方把…

在Nodejs中使用kafka(一)安装使用

安装 方法一、使用docker-compose安装 1、创建docker-compose.yml文件。 services:zookeeper:image: docker.io/bitnami/zookeeper:3.9ports:- "2181:2181"volumes:- "./data/zookeeper:/bitnami"environment:- ALLOW_ANONYMOUS_LOGINyeskafka:image: …

CRISPR spacers数据库;CRT和PILER-CR用于MAGs的spacers搜索

iPHoP&#xff1a;病毒宿主预测-CSDN博客 之前介绍了这个方法来预测病毒宿主&#xff0c;今天来介绍另一种比较用的多的方法CRISPR比对 CRISPR spacers数据库 Dash 在这可以下载作者搜集的spacers用于后期比对 CRT和PILER-CR 使用 CRT 和 PILERCR 识别 CRISPR 间隔区&#x…

深入理解Java的 JIT(即时编译器)

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/literature?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;…

LabVIEW开发CANopen紧急对象读取

本示例展示了如何通过CANopen协议读取设备的紧急对象&#xff08;Emergency object&#xff09;。紧急对象用于报告设备发生故障或异常情况时的紧急信息。通过该示例&#xff0c;用户可以配置并读取设备发送的紧急消息&#xff0c;确保在设备发生紧急状况时能够及时响应。 主要…

DeepSeek官方推荐的AI集成系统

DeepSeek模型虽然强大先进&#xff0c;但是模型相当于大脑&#xff0c;再聪明的大脑如果没有输入输出以及执行工具也白搭&#xff0c;所以需要有配套工具才能让模型发挥最大的作用。下面是一个典型AI Agent架构图&#xff0c;包含核心组件与数据流转关系&#xff1a; #mermaid-…

【第13章:自监督学习与少样本学习—13.4 自监督学习与少样本学习的未来研究方向与挑战】

凌晨三点的实验室里,博士生小张盯着屏幕上的训练曲线——他设计的跨模态少样本学习模型在医疗影像诊断任务上突然出现了诡异的性能断崖。前一秒还在92%的准确率高位运行,下一秒就暴跌到47%。这个看似灾难性的现象,却意外揭开了自监督学习与少样本学习技术深藏的核心挑战… 一…

论文解读之DeepSeek R1

今天带来DeepSeek R1的解读 一、介绍 deepseek主打复杂推理任务&#xff0c;如数学、代码任务。 R1以预训练过的V1-base初始化&#xff0c;主要发挥了RL在长思维链上的优势&#xff0c;R1-Zero直接RL而在前置步骤中不进行SFT&#xff0c;即缺少了有监督的指令微调阶段&#…