【Linux操作系统】Linux系统编程中的共享存储映射(mmap)
在Linux系统编程中,进程之间的通信是一项重要的任务。共享存储映射(mmap)是一种高效的进程通信方式,它允许多个进程共享同一个内存区域,从而实现数据的共享和通信。本文将介绍共享存储映射的概念、原理、使用方法和注意事项,以帮助读者更好地理解和应用共享存储映射。
文章目录
- 1. 共享存储映射的概念
- 2. 共享存储映射的原理
- 3. 共享存储映射的使用方法
- 3.1 mmap
- 3.1.1 函数原型、参数及返回值
- 3.1.2 注意事项
- 3.1.3 函数示例
- 3.2 munmap
- 3.2.1 函数原型、参数及返回值
- 3.2.2 注意事项
- 3.2.3 函数示例
- 4. 父子进程mmap通信
- 4.1 具体步骤如下:
- 4.2 示例
- 4.3 代码解释
- 5. 非血缘关系进程间mmap通信
- 5.1 具体步骤如下:
- 5.2 示例
- 5.3 代码解释
- 6. 结语
1. 共享存储映射的概念
共享存储映射是一种将文件或设备映射到进程的虚拟地址空间的技术。通过共享存储映射,多个进程可以访问同一个物理内存区域,从而实现数据的共享和通信。
共享存储映射的主要优势包括:
- 高效性:共享存储映射避免了数据的复制和传输,提高了数据访问的效率。
- 灵活性:共享存储映射可以用于不同类型的数据,包括文件、设备和匿名内存等。
- 简单性:共享存储映射使用简单,只需要几个系统调用就可以完成映射和解除映射的操作。
2. 共享存储映射的原理
共享存储映射的原理是将一个文件或设备映射到进程的虚拟地址空间中的一段连续的内存区域。这个内存区域被称为共享内存段。多个进程可以通过访问共享内存段来实现数据的共享和通信。
共享存储映射的过程包括以下几个步骤:
- 创建或打开一个共享内存段。
- 将共享内存段映射到进程的虚拟地址空间中。
- 读写共享内存段中的数据。
- 解除共享内存段的映射。
3. 共享存储映射的使用方法
3.1 mmap
3.1.1 函数原型、参数及返回值
mmap函数的原型如下:
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
参数说明:
addr
:指定映射的起始地址,通常设置为NULL,让系统自动选择合适的地址。length
:指定映射的长度,以字节为单位。prot
:指定映射区域的保护方式,可以是以下几个值的组合:PROT_READ
:可读PROT_WRITE
:可写PROT_EXEC
:可执行PROT_NONE
:不可访问
flags
:指定映射区域的标志,可以是以下几个值的组合:MAP_SHARED
:共享映射,多个进程可以访问同一个映射区域MAP_PRIVATE
:私有映射,每个进程有自己独立的映射区域MAP_FIXED
:映射区域必须从指定的地址开始,如果指定地址已被占用,则映射失败MAP_ANONYMOUS
:创建一个匿名映射区域,不与文件关联
fd
:指定要映射的文件描述符,如果使用MAP_ANONYMOUS标志,则该参数为-1。offset
:指定文件的偏移量,通常设置为0。
返回值:
- 成功时,返回映射区域的起始地址。
- 失败时,返回MAP_FAILED。
3.1.2 注意事项
-
参数的正确设置:mmap函数的参数包括起始地址、映射长度、访问权限、映射方式等。需要根据具体需求正确设置这些参数,以确保映射的行为符合预期。
-
错误处理:mmap函数的返回值为映射区域的起始地址,如果映射失败,返回值为MAP_FAILED。在调用mmap函数后,需要检查返回值是否为MAP_FAILED,并根据errno的值来确定具体的错误原因,并进行相应的错误处理。
-
内存对齐:mmap函数返回的映射区域的起始地址通常是按照系统的内存页大小( 4096 )进行对齐的。在使用映射区域时,需要确保按照正确的偏移和长度进行访问,以避免访问越界或者未映射的内存。
-
解除映射:在不再需要映射区域时,应该使用munmap函数解除对映射区域的映射。解除映射后,之前映射的内存区域将不再可访问,对该区域的访问将导致段错误。因此,需要确保解除映射的时机和范围是正确的,避免出现内存泄漏或者访问非法内存的情况。
-
共享内存的同步:如果使用mmap函数创建了一个共享内存区域(使用MAP_SHARED标志),需要在多个进程之间进行同步,以避免竞争条件和数据不一致的问题。可以使用信号量、互斥锁等机制来实现进程间的同步。
-
文件映射的同步:如果使用mmap函数将文件映射到内存中,需要注意对内存中的数据的修改可能不会立即写入到文件中。可以使用msync函数来将修改的数据同步到文件中,或者使用munmap函数解除映射时自动进行同步。
-
安全性:使用mmap函数时需要注意安全性问题,尤其是在映射文件时。需要确保对映射区域的访问是合法的,避免出现潜在的安全漏洞,例如通过映射区域进行非法的内存访问或者代码注入等。
3.1.3 函数示例
下面是一个使用mmap函数的例子:
#include <sys/mman.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>int main() {int fd;char *shared_memory;// 打开文件fd = open("shared_memory", O_RDWR);if (fd == -1) {perror("open");return 1;}// 将文件映射到进程的虚拟地址空间中shared_memory = (char *)mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);if (shared_memory == MAP_FAILED) {perror("mmap");return 1;}// 读取共享内存段中的数据printf("Data read from shared memory: %s\n", shared_memory);// 解除文件的映射if (munmap(shared_memory, 1024) == -1) {perror("munmap");return 1;}// 关闭文件close(fd);return 0;
}
这个例子中,我们打开了一个名为"shared_memory"的文件,并将其映射到进程的虚拟地址空间中。然后,我们读取了共享内存段中的数据,并解除了文件的映射。
两个进程间进行通信:
首先,进程A创建一个共享内存段,并将其映射到自己的虚拟地址空间中:
#include <sys/mman.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>int main() {int fd;char *shared_memory;const char *message = "Hello from Process A!";// 创建或打开一个文件fd = open("shared_memory", O_CREAT | O_RDWR, 0666);if (fd == -1) {perror("open");return 1;}// 调整文件的大小if (ftruncate(fd, 1024) == -1) {perror("ftruncate");return 1;}// 将文件映射到进程的虚拟地址空间中shared_memory = (char *)mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);if (shared_memory == MAP_FAILED) {perror("mmap");return 1;}// 写入数据到共享内存段strcpy(shared_memory, message);// 解除文件的映射if (munmap(shared_memory, 1024) == -1) {perror("munmap");return 1;}// 关闭文件close(fd);return 0;
}
然后,进程B打开同一个共享内存段,并将其映射到自己的虚拟地址空间中:
#include <sys/mman.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>int main() {int fd;char *shared_memory;// 打开文件fd = open("shared_memory", O_RDWR);if (fd == -1) {perror("open");return 1;}// 将文件映射到进程的虚拟地址空间中shared_memory = (char *)mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);if (shared_memory == MAP_FAILED) {perror("mmap");return 1;}// 读取共享内存段中的数据printf("Data read from shared memory: %s\n", shared_memory);// 解除文件的映射if (munmap(shared_memory, 1024) == -1) {perror("munmap");return 1;}// 关闭文件close(fd);return 0;
}
在上面的例子中,进程A创建了一个共享内存段,并写入了数据"Hello from Process A!"。进程B打开了同一个共享内存段,并读取了进程A写入的数据。
3.2 munmap
3.2.1 函数原型、参数及返回值
munmap函数用于解除对映射区域的映射。
函数原型如下:
int munmap(void *addr, size_t length);
参数:
-
addr
参数:- 指向要解除映射的起始地址。
- 通常是通过mmap函数返回的映射区域的指针。
-
length
参数:- 要解除映射的长度。
- 通常是通过mmap函数传入的映射区域的长度。
返回值:
munmap函数的返回值为0表示成功,-1表示失败,并设置errno来指示错误的原因。
3.2.2 注意事项
- addr参数必须是一个有效的映射区域的起始地址,否则解除映射会失败。
- length参数必须与原始映射时使用的长度一致,否则解除映射会失败。
- 解除映射后,之前映射的内存区域将不再可访问,对该区域的访问将导致段错误。
- 解除映射后,如果之前使用了
MAP_SHARED
标志,并且有其他进程仍在访问该映射区域,可能会导致意想不到的结果。 - 解除映射后,如果之前使用了
MAP_ANONYMOUS
标志,并且没有保存映射区域的指针,将无法再次访问该映射区域。
在使用munmap函数时,需要确保解除映射的时机和范围是正确的,避免出现内存泄漏或者访问非法内存的情况。
3.2.3 函数示例
下面是一个使用munmap函数解除映射的示例:
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>int main() {int *data;size_t length = sizeof(int) * 10;// 使用mmap函数创建映射区域data = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);if (data == MAP_FAILED) {perror("mmap");exit(1);}// 在映射区域中写入数据for (int i = 0; i < 10; i++) {data[i] = i;}// 解除映射if (munmap(data, length) == -1) {perror("munmap");exit(1);}// 尝试访问解除映射的内存区域printf("%d\n", data[0]); // 该行代码将导致段错误return 0;
}
在这个示例中,首先使用mmap函数创建了一个大小为10个int的映射区域,并将其保存在data
指针中。然后,通过循环将0到9的数字写入映射区域。接下来,使用munmap函数解除对映射区域的映射。最后,尝试访问解除映射的内存区域,这会导致段错误。
4. 父子进程mmap通信
4.1 具体步骤如下:
-
父进程创建一个共享内存区域,可以使用shm_open函数创建一个具名的共享内存对象,也可以使用mmap函数将一个文件映射到内存中。
-
父进程使用fork函数创建子进程。
-
子进程通过继承父进程的共享内存的描述符或者文件映射的地址,可以直接访问共享内存。
-
父子进程可以通过共享内存进行通信,可以在共享内存中定义一个数据结构,父进程和子进程通过读写该数据结构来进行通信。
-
父子进程需要使用同步机制(例如信号量、互斥锁等)来确保对共享内存的访问是安全的,避免竞争条件和数据不一致的问题。
-
当通信完成后,父进程和子进程需要使用munmap函数解除对共享内存的映射,以释放资源。
4.2 示例
父子进程通过共享内存进行通信:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/wait.h>typedef struct {int value;
} SharedData;int main() {int fd = shm_open("/myshm", O_CREAT | O_RDWR, 0666);ftruncate(fd, sizeof(SharedData));SharedData* sharedData = mmap(NULL, sizeof(SharedData), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);close(fd);pid_t pid = fork();if (pid == 0) {// 子进程printf("Child process: value = %d\n", sharedData->value);sharedData->value = 100;printf("Child process: value = %d\n", sharedData->value);exit(0);} else if (pid > 0) {// 父进程sharedData->value = 50;printf("Parent process: value = %d\n", sharedData->value);wait(NULL);printf("Parent process: value = %d\n", sharedData->value);munmap(sharedData, sizeof(SharedData));shm_unlink("/myshm");} else {perror("fork");exit(1);}return 0;
}
4.3 代码解释
在这个示例中,首先使用shm_open函数创建了一个具名的共享内存对象,并设置了其大小为SharedData结构体的大小。然后,使用mmap函数将共享内存映射到内存中,并获取到共享内存的地址。接下来,使用fork函数创建子进程。
在子进程中,首先打印共享内存中的value值,然后将其修改为100,并再次打印。在父进程中,首先将共享内存中的value值设置为50,然后等待子进程退出。最后,打印共享内存中的value值,然后使用munmap函数解除对共享内存的映射,并使用shm_unlink函数删除共享内存对象。
5. 非血缘关系进程间mmap通信
5.1 具体步骤如下:
-
创建一个共享内存区域,可以使用shm_open函数创建一个具名的共享内存对象,也可以使用mmap函数将一个文件映射到内存中。
-
所有需要通信的进程都使用shm_open或者mmap函数打开或者映射同一个共享内存对象。
-
进程通过共享内存进行通信,可以在共享内存中定义一个数据结构,进程通过读写该数据结构来进行通信。
-
进程需要使用同步机制(例如信号量、互斥锁等)来确保对共享内存的访问是安全的,避免竞争条件和数据不一致的问题。
-
当通信完成后,进程需要使用munmap函数解除对共享内存的映射,以及shm_unlink函数删除共享内存对象。
5.2 示例
无血缘关系的进程通过共享内存进行通信:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/wait.h>typedef struct {int value;
} SharedData;int main() {int fd = shm_open("/myshm", O_CREAT | O_RDWR, 0666);ftruncate(fd, sizeof(SharedData));SharedData* sharedData = mmap(NULL, sizeof(SharedData), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);close(fd);pid_t pid = fork();if (pid == 0) {// 子进程printf("Child process: value = %d\n", sharedData->value);sharedData->value = 100;printf("Child process: value = %d\n", sharedData->value);exit(0);} else if (pid > 0) {// 父进程sharedData->value = 50;printf("Parent process: value = %d\n", sharedData->value);wait(NULL);printf("Parent process: value = %d\n", sharedData->value);munmap(sharedData, sizeof(SharedData));shm_unlink("/myshm");} else {perror("fork");exit(1);}return 0;
}
5.3 代码解释
这个示例与前面的父子进程间mmap通信的示例类似,但是在这里我们使用了具名的共享内存对象。无血缘关系的进程通过打开或者映射同一个具名的共享内存对象,实现了共享内存的通信。其他的步骤和前面的示例相同。
需要注意的是,在无血缘关系的进程间通信时,需要确保共享内存对象的创建和打开的顺序是一致的,以及共享内存的大小和数据结构的定义是一致的。此外,还需要使用适当的同步机制来确保对共享内存的访问是安全的。
6. 结语
共享内存是一种用于进程间通信的机制,它允许多个进程访问同一块内存区域,从而实现高效的数据共享。在使用共享内存进行进程间通信时,需要注意以下几个关键点:
-
创建共享内存:可以使用shm_open函数创建具名的共享内存对象,也可以使用mmap函数将一个文件映射到内存中。创建共享内存时需要指定大小。
-
访问共享内存:进程通过mmap函数将共享内存映射到自己的地址空间中,从而可以直接读写共享内存中的数据。需要注意保证共享内存对象的创建和打开的顺序一致。
-
同步机制:由于多个进程同时访问共享内存可能会导致竞争条件和数据不一致的问题,因此需要使用适当的同步机制来确保对共享内存的访问是安全的。常用的同步机制包括信号量、互斥锁、条件变量等。
-
解除映射和删除共享内存:当通信完成后,进程需要使用munmap函数解除对共享内存的映射,以及使用shm_unlink函数删除共享内存对象。
需要注意的是,使用共享内存进行进程间通信需要考虑同步和数据一致性的问题。在实际使用中,还需要根据具体需求进行适当的修改和扩展,以确保通信的正确性和可靠性。
常用的函数和系统调用包括:
- shm_open:创建或打开具名的共享内存对象。
- ftruncate:设置共享内存的大小。
- mmap:将共享内存映射到进程的地址空间中。
- munmap:解除对共享内存的映射。
- shm_unlink:删除具名的共享内存对象。
此外,还可以使用其他的同步机制函数,如sem_init、sem_wait、sem_post等来实现对共享内存的同步访问。
相关文章:

【Linux操作系统】Linux系统编程中的共享存储映射(mmap)
在Linux系统编程中,进程之间的通信是一项重要的任务。共享存储映射(mmap)是一种高效的进程通信方式,它允许多个进程共享同一个内存区域,从而实现数据的共享和通信。本文将介绍共享存储映射的概念、原理、使用方法和注意…...

2235.两整数相加:19种语言解法(力扣全解法)
【LetMeFly】2235.两整数相加:19种语言解法(力扣全解法) 力扣题目链接:https://leetcode.cn/problems/add-two-integers/ 给你两个整数 num1 和 num2,返回这两个整数的和。 示例 1: 输入:num…...

中国剩余定理及扩展
目录 中国剩余定理解释 中国剩余定理扩展——求解模数不互质情况下的线性方程组: 代码实现: 互质: 非互质: 中国剩余定理解释 在《孙子算经》中有这样一个问题:“今有物不知其数,三三数之剩二&#x…...

数据在内存中的存储(deeper)
数据在内存中的存储(deeper) 一.数据类型的详细介绍二.整形在内存中的存储三.浮点型在内存中的存储 一.数据类型的详细介绍 类型的意义: 使用这个类型开辟内存空间的大小(大小决定了使用范围)如何看待内存空间的视角…...
算法修炼Day52|● 300.最长递增子序列 ● 674. 最长连续递增序列 ● 718. 最长重复子数组
LeetCode:300.最长递增子序列 300. 最长递增子序列 - 力扣(LeetCode) 1.思路 dp[i]的状态表示以nums[i]为结尾的最长递增子序列的个数。 dp[i]有很多个,选择其中最大的dp[i]Math.max(dp[j]1,dp[i]) 2.代码实现 1class Solution {2 pub…...

使用 HTML、CSS 和 JavaScript 创建实时 Web 编辑器
使用 HTML、CSS 和 JavaScript 创建实时 Web 编辑器 在本文中,我们将创建一个实时网页编辑器。这是一个 Web 应用程序,允许我们在网页上编写 HTML、CSS 和 JavaScript 代码并实时查看结果。这是学习 Web 开发和测试代码片段的绝佳工具。我们将使用ifram…...

百望云联合华为发布票财税链一体化数智解决方案 赋能企业数字化升级
随着数据跃升为数字经济关键生产要素,数据安全成为整个数字化建设的重中之重。为更好地帮助企业发展,中央及全国和地方政府相继出台了多部与数据相关的政策法规,鼓励各领域服务商提供具有自主创新的软件产品与服务,帮助企业在合规…...

实现两个栈模拟队列
实现两个栈模拟队列 思路:可以想象一下左手和右手,两个栈:stack1(数据所在的栈) ,stack2(临时存放)。 入队:需要将入队 num 加在 stack1 的栈顶即可; 出队&am…...

无涯教程-TensorFlow - 单词嵌入
Word embedding是从离散对象(如单词)映射到向量和实数的概念,可将离散的输入对象有效地转换为有用的向量。 Word embedding的输入如下所示: blue: (0.01359, 0.00075997, 0.24608, ..., -0.2524, 1.0048, 0.06259) blues: (0.01396, 0.11887, -0.48963, ..., 0.03…...

Facebook AI mBART:巴别塔的硅解
2018年,谷歌发布了BERT(来自transformers的双向编码器表示),这是一种预训练的语言模型,在一系列自然语言处理(NLP)任务中对SOTA结果进行评分,并彻底改变了研究领域。类似的基于变压器…...

BDA初级分析——SQL清洗和整理数据
一、数据处理 数据处理之类型转换 字符格式与数值格式存储的数据,同样是进行大小排序, 会有什么区别? 以rev为例,看看字符格式与数值格式存储时,排序会有什么区别? 用cast as转换为字符后进行排序 SEL…...

汽车后视镜反射率测定仪
后视镜是驾驶员坐在驾驶室座位上直接获取汽车后方、侧方和下方等外部信息的工具。它起着“第三只眼睛”的作用。后视镜按安装位置划分通常分为车外后视镜、监视镜和内后视镜。外后视镜观察汽车后侧方监视镜观察汽车前下方内后视镜观察汽车后方及车内情况。用途不一样镜面结构也…...
Redis学习笔记
redis相关内容 默认端口6379 默认16个数据库,初始默认使用0号库 使用select 切换数据库 统一密码管理,所有库密码相同 dbsize:查看当前库key的数量 flushdb:清空当前库 flushall:清空全部库 redis是单线程 多路…...

韩顺平Linux 四十四--
四十四、rwx权限 权限的基本介绍 输入指令 ls -l 显示的内容如下 -rwxrw-r-- 1 root 1213 Feb 2 09:39 abc0-9位说明 第0位确定文件类型(d , - , l , c , b) l 是链接,相当于 windows 的快捷方式- 代表是文件是普通文件d 是目录,相…...
【支付宝小程序】分包优化教程
🦖我是Sam9029,一个前端 Sam9029的CSDN博客主页:Sam9029的博客_CSDN博客-JS学习,CSS学习,Vue-2领域博主 🐱🐉🐱🐉恭喜你,若此文你认为写的不错,不要吝啬你的赞扬,求收…...

语言基础2 矩阵和数组
语言基础2 矩阵和数组 矩阵和数组是matlab中信息和数据的基本表示形式 可以创建常用的数组和网格 合并现有的数组 操作数组的形状和内容 以及使用索引访问数组元素 用到的函数列表如下 一 创建 串联和扩展矩阵 矩阵时按行和列排列的数据元素的二维数据元素的二维矩…...
springMVC中过滤器抛出异常,自定义异常捕获
在过滤器中引入org.springframework.web.servlet.HandlerExceptionResolver AutowiredQualifier("handlerExceptionResolver")private HandlerExceptionResolver resolver; // doFilter中处理if (条件1) {if (条件2) {resolver.resolveException(request, response, …...
图像检索技术研究:深度度量与深度散列在相似性学习中的应用比较与实践 - 使用Python与Jupyter环境
引言 在计算机视觉领域,图像检索是一个长期存在并持续受到研究者关注的重要话题。随着大数据时代的到来,如何高效、准确地从海量数据中检索到相似的图像成为一个巨大的挑战。传统的检索方法在大数据环境下表现不佳,而深度学习技术的崛起为图…...

CSS加载失败的6个原因
有很多刚刚接触 CSS 的新手有时会遇到 CSS 加载失败这个问题,但测试时,网页上没有显示该样式的问题,这就说明 CSS 加载失败了。出现这种状况一般是因为的 CSS 路径书写错,或者是在浏览器中禁止掉了 CSS 的加载,可以重新…...

react之路由的安装与使用
一、路由安装 路由官网2021.11月初,react-router 更新到 v6 版本。使用最广泛的 v5 版本的使用 npm i react-router-dom5.3.0二、路由使用 2.1 路由的简单使用 第一步 在根目录下 创建 views 文件夹 ,用于放置路由页面 films.js示例代码 export default functio…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...
【网络】每天掌握一个Linux命令 - iftop
在Linux系统中,iftop是网络管理的得力助手,能实时监控网络流量、连接情况等,帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...

渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止
<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案
问题描述:iview使用table 中type: "index",分页之后 ,索引还是从1开始,试过绑定后台返回数据的id, 这种方法可行,就是后台返回数据的每个页面id都不完全是按照从1开始的升序,因此百度了下,找到了…...
土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等
🔍 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术,可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势,还能有效评价重大生态工程…...

Springboot社区养老保险系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,社区养老保险系统小程序被用户普遍使用,为方…...

保姆级【快数学会Android端“动画“】+ 实现补间动画和逐帧动画!!!
目录 补间动画 1.创建资源文件夹 2.设置文件夹类型 3.创建.xml文件 4.样式设计 5.动画设置 6.动画的实现 内容拓展 7.在原基础上继续添加.xml文件 8.xml代码编写 (1)rotate_anim (2)scale_anim (3)translate_anim 9.MainActivity.java代码汇总 10.效果展示 逐帧…...
Python常用模块:time、os、shutil与flask初探
一、Flask初探 & PyCharm终端配置 目的: 快速搭建小型Web服务器以提供数据。 工具: 第三方Web框架 Flask (需 pip install flask 安装)。 安装 Flask: 建议: 使用 PyCharm 内置的 Terminal (模拟命令行) 进行安装,避免频繁切换。 PyCharm Terminal 配置建议: 打开 Py…...
「Java基本语法」变量的使用
变量定义 变量是程序中存储数据的容器,用于保存可变的数据值。在Java中,变量必须先声明后使用,声明时需指定变量的数据类型和变量名。 语法 数据类型 变量名 [ 初始值]; 示例:声明与初始化 public class VariableDemo {publi…...
P10909 [蓝桥杯 2024 国 B] 立定跳远
# P10909 [蓝桥杯 2024 国 B] 立定跳远 ## 题目描述 在运动会上,小明从数轴的原点开始向正方向立定跳远。项目设置了 $n$ 个检查点 $a_1, a_2, \cdots , a_n$ 且 $a_i \ge a_{i−1} > 0$。小明必须先后跳跃到每个检查点上且只能跳跃到检查点上。同时࿰…...