【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. 共享存储映射的原理

共享存储映射的原理是将一个文件或设备映射到进程的虚拟地址空间中的一段连续的内存区域。这个内存区域被称为共享内存段。多个进程可以通过访问共享内存段来实现数据的共享和通信。

共享存储映射的过程包括以下几个步骤:

  1. 创建或打开一个共享内存段。
  2. 将共享内存段映射到进程的虚拟地址空间中。
  3. 读写共享内存段中的数据。
  4. 解除共享内存段的映射。

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 注意事项

  1. 参数的正确设置:mmap函数的参数包括起始地址、映射长度、访问权限、映射方式等。需要根据具体需求正确设置这些参数,以确保映射的行为符合预期。

  2. 错误处理:mmap函数的返回值为映射区域的起始地址,如果映射失败,返回值为MAP_FAILED。在调用mmap函数后,需要检查返回值是否为MAP_FAILED,并根据errno的值来确定具体的错误原因,并进行相应的错误处理。

  3. 内存对齐:mmap函数返回的映射区域的起始地址通常是按照系统的内存页大小( 4096 )进行对齐的。在使用映射区域时,需要确保按照正确的偏移和长度进行访问,以避免访问越界或者未映射的内存。

  4. 解除映射:在不再需要映射区域时,应该使用munmap函数解除对映射区域的映射。解除映射后,之前映射的内存区域将不再可访问,对该区域的访问将导致段错误。因此,需要确保解除映射的时机和范围是正确的,避免出现内存泄漏或者访问非法内存的情况。

  5. 共享内存的同步:如果使用mmap函数创建了一个共享内存区域(使用MAP_SHARED标志),需要在多个进程之间进行同步,以避免竞争条件和数据不一致的问题。可以使用信号量、互斥锁等机制来实现进程间的同步。

  6. 文件映射的同步:如果使用mmap函数将文件映射到内存中,需要注意对内存中的数据的修改可能不会立即写入到文件中。可以使用msync函数来将修改的数据同步到文件中,或者使用munmap函数解除映射时自动进行同步。

  7. 安全性:使用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);

参数:

  1. addr参数:

    • 指向要解除映射的起始地址。
    • 通常是通过mmap函数返回的映射区域的指针。
  2. 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 具体步骤如下:

  1. 父进程创建一个共享内存区域,可以使用shm_open函数创建一个具名的共享内存对象,也可以使用mmap函数将一个文件映射到内存中。

  2. 父进程使用fork函数创建子进程。

  3. 子进程通过继承父进程的共享内存的描述符或者文件映射的地址,可以直接访问共享内存。

  4. 父子进程可以通过共享内存进行通信,可以在共享内存中定义一个数据结构,父进程和子进程通过读写该数据结构来进行通信。

  5. 父子进程需要使用同步机制(例如信号量、互斥锁等)来确保对共享内存的访问是安全的,避免竞争条件和数据不一致的问题。

  6. 当通信完成后,父进程和子进程需要使用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 具体步骤如下:

  1. 创建一个共享内存区域,可以使用shm_open函数创建一个具名的共享内存对象,也可以使用mmap函数将一个文件映射到内存中。

  2. 所有需要通信的进程都使用shm_open或者mmap函数打开或者映射同一个共享内存对象。

  3. 进程通过共享内存进行通信,可以在共享内存中定义一个数据结构,进程通过读写该数据结构来进行通信。

  4. 进程需要使用同步机制(例如信号量、互斥锁等)来确保对共享内存的访问是安全的,避免竞争条件和数据不一致的问题。

  5. 当通信完成后,进程需要使用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. 结语

共享内存是一种用于进程间通信的机制,它允许多个进程访问同一块内存区域,从而实现高效的数据共享。在使用共享内存进行进程间通信时,需要注意以下几个关键点:

  1. 创建共享内存:可以使用shm_open函数创建具名的共享内存对象,也可以使用mmap函数将一个文件映射到内存中。创建共享内存时需要指定大小。

  2. 访问共享内存:进程通过mmap函数将共享内存映射到自己的地址空间中,从而可以直接读写共享内存中的数据。需要注意保证共享内存对象的创建和打开的顺序一致。

  3. 同步机制:由于多个进程同时访问共享内存可能会导致竞争条件和数据不一致的问题,因此需要使用适当的同步机制来确保对共享内存的访问是安全的。常用的同步机制包括信号量、互斥锁、条件变量等。

  4. 解除映射和删除共享内存:当通信完成后,进程需要使用munmap函数解除对共享内存的映射,以及使用shm_unlink函数删除共享内存对象。

需要注意的是,使用共享内存进行进程间通信需要考虑同步和数据一致性的问题。在实际使用中,还需要根据具体需求进行适当的修改和扩展,以确保通信的正确性和可靠性。

常用的函数和系统调用包括:

  • shm_open:创建或打开具名的共享内存对象。
  • ftruncate:设置共享内存的大小。
  • mmap:将共享内存映射到进程的地址空间中。
  • munmap:解除对共享内存的映射。
  • shm_unlink:删除具名的共享内存对象。

此外,还可以使用其他的同步机制函数,如sem_init、sem_wait、sem_post等来实现对共享内存的同步访问。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Rabbitmq的应用场景

Rabbitmq的应用场景 一、异步处理 场景说明&#xff1a;用户注册后&#xff0c;需要发注册邮件和注册短信,传统的做法有两种 1.串行的方式 2.并行的方式 ​​串行方式​​: 将注册信息写入数据库后,发送注册邮件,再发送注册短信,以上三个任务全部完成后才返回给客户端。 这有…

前端 -- 基础 网页、HTML、 WEB标准 扫盲详解

什么是网页 : 网页是构成网站的基本元素&#xff0c;它通常由 图片、链接、文字、声音、视频等元素组成。 通常我们看到的网页 &#xff0c;常见以 .html 或 .htm 后缀结尾的文件&#xff0c; 因此俗称 HTML 文件 什么是 HTML : HTML 指的是 超文本标记语言&#xff0c…

kafka--kafka基础概念-ISR详解

kafka基础概念-ISR详解 主要是讲 主 往 从同步中的问题 当绿色P1接收到写入的数据&#xff0c;要同步到紫色的P1S1和P1S2 如何保证一致性呢&#xff1f; 使用In Sync Replicas 也就是ISR概念 为什么不一致的&#xff1f; 因为P1S1同步数据 可能花费 50ms P1S2可能花费60ms…

github拉取自己的私有仓库(Token方式、本地秘钥方式)

github拉取自己的私有仓库(Token方式、本地秘钥方式) 问题背景 日常开发和学习过程中&#xff0c;经常碰到需要从GitHub或者其他类似网站&#xff0c;拉取私有仓代码的需求。本文将总结常用的两种方式&#xff0c;Token方式和本地秘钥方式&#xff0c;方便后续查阅和优化。 …

拒绝摆烂!C语言练习打卡第四天

&#x1f525;博客主页&#xff1a;小王又困了 &#x1f4da;系列专栏&#xff1a;每日一练 &#x1f31f;人之为学&#xff0c;不日近则日退 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 目录 一、选择题 &#x1f4dd;1.第一题 &#x1f4dd;2.第二题 &#x1f4d…

要跟静音开关说再见了!iPhone15新变革,Action按钮引领方向

有很多传言称iPhone 15 Pro会有很多变化&#xff0c;但其中一个变化可能意味着iPhone体验从第一天起就有的一项功能的终结。我说的是静音开关&#xff0c;它可以让你轻松地打开或关闭iPhone的铃声。 根据越来越多的传言&#xff0c;iPhone 15 Pro和iPhone 15 Pro Max将拆除静音…

FPGA:RS编码仿真过程

FPGA&#xff1a;RS编码仿真过程 RS码是一种纠错性能很强的线性纠错码&#xff0c;能够纠正随机错误和突发错误。RS码是一种多进制BCH码&#xff0c;能够同时纠正多个码元错误。 之前已经记录了在MATLAB中进行rs编解码的过程&#xff0c;现在利用FPGA的IP核实现RS编码的过程&…

vue项目预览pdf功能(解决动态文字无法显示的问题)

最近&#xff0c;因为公司项目需要预览pdf的功能&#xff0c;开始的时候找了市面上的一些pdf插件&#xff0c;都能用&#xff0c;但是&#xff0c;后面因为pdf变成了需要根据内容进行变化的&#xff0c;然后&#xff0c;就出现了需要动态生成的文字不显示了。换了好多好多的插件…

步步向前,曙光已现:百度的大模型之路

大模型&#xff0c;是今年全球科技界最火热&#xff0c;最耀眼的关键词。在几个月的狂飙突进中&#xff0c;全球主要科技公司纷纷加入了大模型领域。中国AI产业更是开启了被戏称为“百模大战”的盛况。 但喧嚣与热闹之后&#xff0c;新的问题也随之而来&#xff1a;大模型的力量…

算法通关村第十关 | 快速排序

1.快速排序的基本过程 快速排序是分治法运用到排序问题的典型例子&#xff0c;基本思想是&#xff1a;通过一个标记pivot元素将n个元素的序列划分为左右两个子序列left和right&#xff0c;其中left中的元素都比pivot小&#xff0c;right的都比pivot的大&#xff0c;然后再次对l…

前端打开后端返回的HTML格式的数据

前端打开后端返回的 HTML格式 的数据&#xff1a; 后端返回的数据格式如下示例&#xff1a; 前端通过 js 方式处理&#xff08;核心代码如下&#xff09; console.log(回调, path); // path 是后端返回的 HTML 格式数据// 必须要存进localstorage&#xff0c;否则会报错&am…

STM32 CubeMX (第四步Freertos内存管理和CPU使用率)

STM32 CubeMX STM32 CubeMX &#xff08;第四步Freertos内存管理和CPU使用率&#xff09; STM32 CubeMX一、STM32 CubeMX设置时钟配置HAL时基选择TIM1&#xff08;不要选择滴答定时器&#xff1b;滴答定时器留给OS系统做时基&#xff09;使用STM32 CubeMX 库&#xff0c;配置Fr…

面试官:JVM是如何判定对象已死的?学JVM必会的知识!

本文已收录至GitHub&#xff0c;推荐阅读 &#x1f449; Java随想录 文章目录 引用计数算法可达性分析算法引用类型Dead Or Alive永久代真的"永久"吗&#xff1f;垃圾收集算法标记-清除算法标记-复制算法标记-整理算法标记-清除 VS 标记-整理 作为一名Java程序员&…

Unity 变量修饰符 之protected ,internal,const , readonly, static

文章目录 protectedinternalconstreadonlystatic protected 当在Unity中使用C#编程时&#xff0c;protected是一种访问修饰符&#xff0c;用于控制类成员&#xff08;字段、方法、属性等&#xff09;的可见性和访问权限。protected修饰的成员可以在当前类内部、派生类&#xf…

Windows上使用FFmpeg实现本地视频推送模拟海康协议rtsp视频流

场景 Nginx搭建RTMP服务器FFmpeg实现海康威视摄像头预览&#xff1a; Nginx搭建RTMP服务器FFmpeg实现海康威视摄像头预览_nginx rtmp 海康摄像头_霸道流氓气质的博客-CSDN博客 上面记录的是使用FFmpeg拉取海康协议摄像头的rtsp流并推流到流媒体服务器。 如果在其它业务场景…

嵌入式设计中对于只有两种状态的变量存储设计,如何高效的对循迹小车进行偏差量化

前言 &#xff08;1&#xff09;在嵌入式程序设计中&#xff0c;我们常常会要对各类传感器进行数据存储。大多时候的传感器&#xff0c;例如红外光传感器&#xff0c;返回的数据要么是0&#xff0c;要么是1。因此&#xff0c;只需要一bit就能够存储。而很多人却常常使用char型数…

SkyEye操作指南:连接TI CCS的IDE调试

现代电力电子控制系统的开发中&#xff0c;DSP芯片以其优越的运算性能在控制算法领域得到越来越广泛的应用。传统的DSP开发过程往往需要在完成控制系统仿真与程序设计后&#xff0c;才能根据比对结果进行程序修改&#xff0c;全过程还需要硬件电路工程师的配合&#xff0c;开发…

ES的索引结构与算法解析

提到ES&#xff0c;大多数爱好者想到的都是搜索引擎&#xff0c;但是明确一点&#xff0c;ES不等同于搜索引擎。不管是谷歌、百度、必应、搜狗为代表的自然语言处理(NLP)、爬虫、网页处理、大数据处理的全文搜索引擎&#xff0c;还是有明确搜索目的的搜索行为&#xff0c;如各大…

Git判断本地是否最新

场景需求 需要判断是否有新内容更新,确定有更新之后执行pull操作&#xff0c;然后pull成功之后再将新内容进行复制到其他地方 pgit log -1 --prettyformat:"%H" HEAD -- . "origin/HEAD" rgit rev-parse origin/HEAD if [[ $p $r ]];thenecho "Is La…

【Android】设置-显示-屏保-启用时机-默认选中“一律不“

设置-屏保-启用时机-默认选中"一律不" 解决步骤&#xff08;1&#xff09;理清思路&#xff08;2&#xff09;过程&#xff08;3&#xff09;效果图 解决步骤 &#xff08;1&#xff09;理清思路 操作步骤&#xff1a; 首先手机进入设置—》点进显示选项—》进入后…