linux下共享内存的3种使用方式

news2024/9/22 14:18:33

进程是资源封装的单位,内存就是进程所封装的资源的一种。一般情况下,进程间的内存是相互隔离的,也就是说一个进程不能访问另一个进程的内存。如果一个进程想要访问另一个进程的内存,那么必须要进过内核这个桥梁,这就是共享内存。

在linux中,共享内存有3种方式,分别是POSIX接口,mmap以及system V风格的接口。本文分别介绍这3种共享内存的使用方式。在3种方式中,POSIX接口简洁易用,是最常使用的;system V易用性不是很好,很少使用。

另外,在工作中,共享内存作为一种进程间通信的方式,我们很轻易就能想到它的优点:减少拷贝次数。但是一项技术有优点,同时也必然有局限性,共享内存的局限性就是,在使用的时候往往需要在进程间做同步,进程间同步也会带来性能上的损耗。

1POSIX

1.1/dev/shm

POSIX共享内存接口使用了/dev/shm临时文件系统。

在介绍POSIX共享内存接口之前,有必要先了解linux下的临时文件系统/dev/shm,从名字也能看出来,这个文件系统是专门用作共享内存的。从mount显示的信息中可以看出来,临时文件系统tmpfs被mount到了/dev/shm下。

这是一个临时文件系统,同时也是一个内存文件系统,也就是说在这个文件系统上创建的文件,都是保存在内存中的,而不是保存在磁盘上。可想而知,性能会比较高。可以像普通文件系统一样使用临时文件系统,打开、读写、关闭、删除、拷贝等操作和普通文件是一样的。但是要注意,临时文件系统是保存在内存中的,机器重启之后不再存在。

/dev/shm默认大小是机器物理内存的一半:

使用df -i /dev/shm可以查看默认的inode个数:

修改大小和inode个数,比如我想将大小修改为4G,inode个数修改为1000,使用如下命令进行修改,可以看到,修改是生效的。

1.2example

如下例子是linux文档中的例子,通过man shm_open可以看到这个例子。从例子的实现可以看出来,共享内存依赖项有两个:一个是/dev/shm临时文件系统,一个是mmap。mmap本身就是一种共享内存的方式。所以说POSIX共享内存和mmap并不是完全割裂的,前者依赖于后者。其实我们也可以完全不使用shm_open、shm_unlink接口,而是直接使用mmap,在/dev/shm下创建共享内存也是可以的。

pshm_ucase_bounce.c和pshm_ucase_send.c中分别创建共享内存,大小是struct shmbuf的大小。后者向内存中写hello,前者将hello改成大写的,然后后者打印数据。

pshm_ucase.h:

#include <sys/mman.h>
#include <fcntl.h>
#include <semaphore.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
                        } while (0)

#define BUF_SIZE 1024   /* Maximum size for exchanged string */

/* Define a structure that will be imposed on the shared
    memory object */

struct shmbuf {
    sem_t  sem1;            /* POSIX unnamed semaphore */
    sem_t  sem2;            /* POSIX unnamed semaphore */
    size_t cnt;             /* Number of bytes used in 'buf' */
    char   buf[BUF_SIZE];   /* Data being transferred */
};

pshm_ucase_bounce.c:

#include <ctype.h>
#include "pshm_ucase.h"

int
main(int argc, char *argv[])
{
    if (argc != 2) {
        fprintf(stderr, "Usage: %s /shm-path\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    char *shmpath = argv[1];

    /* Create shared memory object and set its size to the size
        of our structure */
    printf("size:%d\n", sizeof(struct shmbuf));
    int fd = shm_open(shmpath, O_CREAT | O_EXCL | O_RDWR,
                        S_IRUSR | S_IWUSR);
    if (fd == -1)
        errExit("shm_open");

    if (ftruncate(fd, sizeof(struct shmbuf)) == -1)
        errExit("ftruncate");

    /* Map the object into the caller's address space */

    struct shmbuf *shmp = mmap(NULL, sizeof(*shmp),
                                PROT_READ | PROT_WRITE,
                                MAP_SHARED, fd, 0);
    if (shmp == MAP_FAILED)
        errExit("mmap");

    /* Initialize semaphores as process-shared, with value 0 */

    if (sem_init(&shmp->sem1, 1, 0) == -1)
        errExit("sem_init-sem1");
    if (sem_init(&shmp->sem2, 1, 0) == -1)
        errExit("sem_init-sem2");

    /* Wait for 'sem1' to be posted by peer before touching
        shared memory */

    if (sem_wait(&shmp->sem1) == -1)
        errExit("sem_wait");

    /* Convert data in shared memory into upper case */

    for (int j = 0; j < shmp->cnt; j++)
        shmp->buf[j] = toupper((unsigned char) shmp->buf[j]);

    /* Post 'sem2' to tell the to tell peer that it can now
        access the modified data in shared memory */

    if (sem_post(&shmp->sem2) == -1)
        errExit("sem_post");

    /* Unlink the shared memory object. Even if the peer process
        is still using the object, this is okay. The object will
        be removed only after all open references are closed. */

    shm_unlink(shmpath);

    exit(EXIT_SUCCESS);
}

pshm_ucase_send.c:

#include <string.h>
#include "pshm_ucase.h"

int
main(int argc, char *argv[])
{
    if (argc != 3) {
        fprintf(stderr, "Usage: %s /shm-path string\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    char *shmpath = argv[1];
    char *string = argv[2];
    size_t len = strlen(string);

    if (len > BUF_SIZE) {
        fprintf(stderr, "String is too long\n");
        exit(EXIT_FAILURE);
    }

    /* Open the existing shared memory object and map it
        into the caller's address space */

    int fd = shm_open(shmpath, O_RDWR, 0);
    if (fd == -1)
        errExit("shm_open");

    struct shmbuf *shmp = mmap(NULL, sizeof(*shmp),
                                PROT_READ | PROT_WRITE,
                                MAP_SHARED, fd, 0);
    if (shmp == MAP_FAILED)
        errExit("mmap");

    /* Copy data into the shared memory object */

    shmp->cnt = len;
    memcpy(&shmp->buf, string, len);

    /* Tell peer that it can now access shared memory */

    if (sem_post(&shmp->sem1) == -1)
        errExit("sem_post");

    /* Wait until peer says that it has finished accessing
        the shared memory */

    if (sem_wait(&shmp->sem2) == -1)
        errExit("sem_wait");

    /* Write modified data in shared memory to standard output */

    write(STDOUT_FILENO, &shmp->buf, len);
    write(STDOUT_FILENO, "\n", 1);

    exit(EXIT_SUCCESS);
}

2mmap

mmap在linux中是经常使用的,mmap不仅仅可以用来共享内存,当我们使用malloc申请内存时,默认情况下如果申请的内存大于128K,那么底层便会使用mmap来从系统申请内存;mmap同样也可以将系统的设备内存映射到用户态。

void *mmap(void *addr, size_t length, int prot, int flags,
                  int fd, off_t offset);

MAP_SHARED共享,一个进程修改之后,另一个进程能看到。如果我们要使用共享内存,那么需要设置这个标志
MAP_PRIVATE

私有,也就是不共享,即使两个进程使用mmap映射的是同一个文件,偏移量都是一样的,那么一个进程的修改,另一个进程也看不到。

从下边的注释可以看到,设置MAP_PRIVATE,使用copy on write机制,当一个进程要写的时候,进程内会拷贝一份。另外,私有的情况下,数据最终会不会保存到文件中,是不确定的。

MAP_ANONYMOUS匿名映射,mmap的倒数第二个参数是一个fd,如果要进行文件映射,那么需要首先打开这个文件,再使用mmap进行映射;如果是匿名映射,那么就不需要指定fd,将fd设置为-1即可。

匿名映射常常用于父子进程间的内存共享,文件映射常常用于没有父子关系的进程间的内存共享。 如果我们想要内存中的内容会保存到一个文件中,并且开机之后还能够使用,那么就必须使用共享和文件映射的方式。

2.1文件映射

文件映射就是要基于文件系统的一个文件来映射,第一节中的POSIX接口,就是使用的临时文件系统中的文件进行映射。如下两个文件aa.c和bb.c,编译之后先运行aa,再运行bb,可以看到aa写的数据,bb能够读到;bb写的数据,aa也能读到。

aa.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <string.h>

int main() {
    const char *filename = "./shared_file.txt";
    const size_t length = 100;

    int fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, 0666);
    ftruncate(fd, length);

    char *shared_mem = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    close(fd);

    if (shared_mem == MAP_FAILED) {
        perror("mmap");
        return 1;
    }
    strcpy(shared_mem, "Hello from aa!");
    printf("after aa write\n");
    sleep(5);
    printf("aa read:%s\n", shared_mem);
    munmap(shared_mem, length);
    return 0;
}

bb.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <string.h>

int main() {
    const char *filename = "./shared_file.txt";
    const size_t length = 100;

    int fd = open(filename, O_RDWR, 0666);
    char *shared_mem = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    close(fd);

    if (shared_mem == MAP_FAILED) {
        perror("mmap");
        return 1;
    }

    printf("bb read:%s\n", shared_mem);
    strcpy(shared_mem, "Hello from bb!");
    munmap(shared_mem, length);
    return 0;
}

2.2匿名映射

匿名映射可以用于父子进程间的内存共享,如下是一个例子。

①父进程中首先向共享内存中写"Hello from parent!"

②父进程调用fork创建子进程

③子进程读取内存中的内容,然后向内存中写数据“Hello from child!”

④父进程等待子进程退出,然后读取共享内存中的数据

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>
#include <string.h>

int main() {
    // 创建匿名共享内存
    size_t length = 100;
    char *shared_mem = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);

    if (shared_mem == MAP_FAILED) {
        perror("mmap");
        return 1;
    }

    strcpy(shared_mem, "Hello from parent!");
    pid_t pid = fork();  // 创建子进程

    if (pid == 0) {
        // 子进程
        printf("child read:%s\n", shared_mem);
        strcpy(shared_mem, "Hello from child!");
        printf("child write:%s\n", shared_mem);
    } else {
        // 父进程
        wait(NULL);  // 等待子进程结束
        printf("parent read:%s\n", shared_mem);  // 读取共享内存内容
    }

    munmap(shared_mem, length);  // 解除映射
    return 0;
}

运行结果如下,从结果可以看出来,子进程写的数据,父进程可以读出来,说明内存在父子进程之间是共享的。

如果把mmap中的标志MAP_SHARED改为MAP_PRIVATE,那么内存在父子进程间是不共享的。子进程写数据之后,父进程也看不到,父进程中读出来还是“Hello from parent!”。这个时候内存在父进程和子进程中各有一份。MAP_PRIVATE下,使用写时拷贝的机制,只有写的时候,才会分配一份内存,读的时候不会,所以在子进程中一开始读内存中数据的时候还能看到一开始父进程写的数据。

2.3查看文件映射和匿名映射

/pro/pid/maps中显式了进程的内存映射。

只有在共享映射的时候,才能在maps文件中看到对应的映射,私有映射的时候看不到。

如下是文件映射,可以看到shared_file.txt映射的内存范围。

如下是匿名映射,可以看到匿名映射映射的是/dev/zero。

2.4mmap使用注意问题

2.4.1offset应为PAGE_SIZE的整数倍

mmap的最后一个形参offset,需要是页大小的整数倍,页大小通过sysconf(_SC_PAGE_SIZE)来获取。如果offset不是页的整数倍,那么会返回错误Invalid argument。本人测试,无论是文件映射还是匿名映射,MAP_SHARED还是MAP_PRIVATE,这条限制都存在。

2.4.2文件映射时,映射的内存不能超过文件的大小

当使用文件映射时,如果映射的内存的大小大于文件本身的大小,那么在调用mmap时并不会返回错误,但是在写数据的时候会出现段错误。

如下代码,如果shared_file.txt是不存在的,那么调用open的时候会创建该文件,默认大小是0。不调用ftruncate,直接调用mmap,映射的内存大小是100,这个时候mmap不会返回错误,但是在写内存的时候会出现段错误。所以在使用mmap映射文件的时候,必须要保证offset+length的数据不超过文件的大小才可以。ftruncate可以设置文件的大小。

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <string.h>

int main() {
    const char *filename = "./shared_file.txt";
    const size_t length = 100;

    int fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, 0666);
    // ftruncate(fd, length);

    char *shared_mem = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
    close(fd);

    if (shared_mem == MAP_FAILED) {
        perror("mmap");
        return 1;
    }
    strcpy(shared_mem, "Hello from aa!");
    printf("after aa write\n");
    sleep(5);
    printf("aa read:%s\n", shared_mem);
    munmap(shared_mem, length);
    return 0;
}

3system V

systemV类型的共享内存,要使用4个api。

①首先要使用ftok来获取一个key

②使用shmget获取一个id

③使用shmat获取共享内存的地址

④共享内存使用完毕之后使用shmdt删除共享内存

writer.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#include <unistd.h>

#define SHM_SIZE 100  // 共享内存大小

int main() {
    key_t key = ftok("./shmfile", 65);  // 创建一个唯一的键
    int shmid = shmget(key, SHM_SIZE, 0666 | IPC_CREAT);  // 获取共享内存段

    if (shmid < 0) {
        perror("shmget");
        return 1;
    }

    char *str = (char *)shmat(shmid, NULL, 0);  // 将共享内存附加到当前进程地址空间

    if (str == (char *)(-1)) {
        perror("shmat");
        return 1;
    }

    // 向共享内存写入数据
    strcpy(str, "Hello from writer!");
    printf("Writer wrote: %s\n", str);

    sleep(20);
    shmdt(str);  // 解除共享内存
    return 0;
}

reader.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#include <unistd.h>

#define SHM_SIZE 100  // 共享内存大小

int main() {
    key_t key = ftok("./shmfile", 65);  // 创建一个唯一的键
    int shmid = shmget(key, SHM_SIZE, 0666);  // 获取共享内存段

    if (shmid < 0) {
        perror("shmget");
        return 1;
    }

    char *str = (char *)shmat(shmid, NULL, 0);  // 将共享内存附加到当前进程地址空间

    if (str == (char *)(-1)) {
        perror("shmat");
        return 1;
    }

    // 读取共享内存内容
    printf("Reader read: %s\n", str);

    shmdt(str);  // 解除共享内存
    return 0;
}

如果共享内存只是在父子进程间共享,那么不需要使用ftok通过文件来获取一个key,在使用shmget的时候,直接使用IPC_PRIVATE即可。如下是一个例子,在父子进程间共享内存。

#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#include <unistd.h>

#define SHM_SIZE 100  // 共享内存大小

int main() {
    int shmid = shmget(IPC_PRIVATE, SHM_SIZE, 0666 | IPC_CREAT);  // 获取共享内存段

    if (shmid < 0) {
        perror("shmget");
        exit(1);
    }

    // 创建子进程
    pid_t pid = fork();

    if (pid < 0) {
        perror("fork");
        exit(1);
    }

    if (pid > 0) {
        // 父进程
        char *str = (char *)shmat(shmid, NULL, 0);  // 将共享内存附加到父进程

        if (str == (char *)(-1)) {
            perror("shmat");
            exit(1);
        }

        // 向共享内存写入数据
        strcpy(str, "Hello from parent!");
        printf("Parent wrote: %s\n", str);
        sleep(4);
        printf("Parent read: %s\n", str);
        shmdt(str);  // 解除共享内存
        wait(NULL);  // 等待子进程结束
    } else {
        // 子进程
        sleep(1);  // 等待父进程写入数据

        char *str = (char *)shmat(shmid, NULL, 0);  // 将共享内存附加到子进程

        if (str == (char *)(-1)) {
            perror("shmat");
            exit(1);
        }

        // 读取共享内存内容
        printf("Child read: %s\n", str);
        strcpy(str, "Hello from child!");
        printf("child wrote: %s\n", str);
        shmdt(str);  // 解除共享内存
    }

    // 删除共享内存段
    if (pid > 0) {
        shmctl(shmid, IPC_RMID, NULL);  // 只有父进程删除共享内存
    }

    return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

中国雕塑—孙溟㠭凿刻印《自然贼》

中国雕塑孙溟㠭凿刻作品《自然贼》 孙溟㠭凿刻印《自然贼》 遵循自然之法谓之道&#xff0c;脱离自然之道谓之贼&#xff0c;道法自然。丙申秋月溟展刊。 孙溟㠭凿刻印《自然贼》 这方《自然贼》&#xff0c;红木章料&#xff0c;半尺见方&#xff0c;自然古朴&#xff0c;浑…

摆脱困境并在iPhone手机上取回删除照片的所有解决方案

您是否无意中从 iPhone 中删除了照片&#xff1f;您&#xff0c;无需惊慌&#xff0c;因为您可以使用以下方法恢复所有照片。 如果您长时间使用 iPhone&#xff0c;您应该知道 iOS 提供了许多 Android 不提供的备份功能。例如&#xff0c;您的所有照片都会自动备份到 iCloud 存…

【机器学习(七)】分类和回归任务-K-近邻 (KNN)算法-Sentosa_DSML社区版

文章目录 一、算法概念二、算法原理&#xff08;一&#xff09;K值选择&#xff08;二&#xff09;距离度量1、欧式距离2、曼哈顿距离3、闵可夫斯基距离 &#xff08;三&#xff09;决策规则1、分类决策规则2、回归决策规则 三、算法优缺点优点缺点 四、KNN分类任务实现对比&am…

音视频生态下Unity3D和虚幻引擎(Unreal Engine)的区别

技术背景 好多开发者跟我们做技术交流的时候&#xff0c;会问我们&#xff0c;为什么有Unity3D的RTMP|RTSP播放模块&#xff0c;还有RTMP推送和轻量级RTSP服务模块&#xff0c;为什么不去支持虚幻引擎&#xff1f;二者区别在哪里&#xff1f;本文就Unity3D和虚幻引擎之间的差异…

idea上传jar包到nexus

注意&#xff1a;确保idea中项目为maven项目&#xff0c;并且在nexus中已经创建了maven私服。 1、配置pom.xml中推送代码配置 <distributionManagement> <repository> <id>releases</id> <url>http://127.0.0.1:8001/repository/myRelease/<…

鼻咽癌中三级淋巴结构的单细胞与空间转录组分析|文献精析·24-09-22

小罗碎碎念 研究团队通过单细胞和空间转录组分析&#xff0c;揭示了与鼻咽癌进展和免疫治疗反应相关的三级淋巴结构。 作者角色作者姓名单位&#xff08;中文&#xff09;第一作者Yang Liu/通讯作者Jin-Xin Bei国家癌症中心南方肿瘤学重点实验室&#xff0c;鼻咽癌诊断治疗广东…

机器学习04-逻辑回归(python)-02原理与损失函数

​​​​​​​ 1. 逻辑回归概念 逻辑回归&#xff08;Logistic Regression&#xff09; 是一种 分类模型&#xff0c;主要用于解决 二分类问题&#xff08;即分成两类&#xff0c;如是否通过、是否患病等&#xff09;。逻辑回归的目标是根据输入的特征预测一个 概率&#xff0…

C++——关联式容器(4):set和map

在接触了诸如二叉搜索树、AVL树、红黑树的树形结构之后&#xff0c;我们对树的结构有了大致的了解&#xff0c;现在引入真正的关联式容器。 首先&#xff0c;先明确了关联式容器的概念。我们之前所接触到的如vector、list等容器&#xff0c;我们知道他们实际上都是线性的数据结…

C++门迷宫

目录 开头程序程序的流程图程序游玩的效果下一篇博客要说的东西 开头 大家好&#xff0c;我叫这是我58。 程序 #include <iostream> using namespace std; void printmaze(const char strmaze[11][11]) {int i 0;int ia 0;for (; i < 11; i) {for (ia 0; ia <…

部署林风社交论坛/社交论坛linfeng-community遇到问题集合

部署开源版本遇到的问题 1.管理端前端部署 npm install报错 “ERR! gyp verb ensuring that file exists: C:\Python27\python.exe” “ERR! gyp ERR! node -v v20.10.0” “ ERR! gyp ERR! node-gyp -v v3.8.0” 原因:node版本和node-gyp版本不匹配 解决方法: 1&…

航拍房屋检测系统源码分享

航拍房屋检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Vis…

基于stm32物联网身体健康检测系统

在当今社会&#xff0c;由于经济的发展带来了人们生活水平不断提高&#xff0c;但是人们的健康问题却越来越突出了&#xff0c;各种各样的亚健康随处可在&#xff0c;失眠、抑郁、焦虑症&#xff0c;高血压、高血糖等等侵袭着人们的健康&#xff0c;人们对健康的关注达到了一个…

职业发展如何进入人工智能领域

基础知识和技能 进入人工智能领域需要学习一系列的基础知识和技能&#xff0c;以下是一些关键的步骤和领域&#xff1a; 基础数学知识&#xff1a;人工智能领域涉及到大量的数学概念&#xff0c;包括线性代数、概率论、统计学和微积分。这些数学工具对于理解和设计算法至关重要…

Java流程控制语句——跳转语句详解:break 与 continue 有什么区别?

&#x1f310;在Java编程中&#xff0c;break和continue是两个重要的控制流语句&#xff0c;它们允许开发者根据特定条件改变程序的执行流程。虽然两者都用于中断当前的行为&#xff0c;但它们的作用方式不同。本文将通过生动的例子来详细解释这两个语句&#xff0c;并使用流程…

[Redis][Set]详细讲解

目录 0.前言1.常用命令1.SADD2.SMEMBERS3.SISMEMBER4.SCARD5.SPOP6.SMOVE7.SREM 2.集合间操作0.是什么&#xff1f;1.SINTER2.SINTERSTORE3.SUNION4.SUNIONSTORE5.SDIFF6.SDIFFSTORE 3.内部编码1.intset(整数集合)2.hashtable(哈希表) 4.使用场景 0.前言 集合类型也是保存多个字…

SpringBoot 整合 Caffeine 实现本地缓存

目录 1、Caffeine 简介1.1、Caffeine 简介1.2、对比 Guava cache 的性能主要优化项1.3、常见的缓存淘汰算法1.4、SpringBoot 集成 Caffeine 两种方式 2、SpringBoot 集成 Caffeine 方式一2.1、缓存加载策略2.1.1、手动加载2.1.2、自动加载【Loading Cache】2.1.3、异步加载【As…

智能AC管理系统信息泄露漏洞

文章目录 免责声明漏洞描述搜索语法漏洞复现yaml修复建议 免责声明 本文章仅供学习与交流&#xff0c;请勿用于非法用途&#xff0c;均由使用者本人负责&#xff0c;文章作者不为此承担任何责任 漏洞描述 智能AC管理系统是一个控制管理系统因存在未授权访问导致信息泄露 搜…

大厂面试真题:SpringBoot的核心注解

其实理解一个注解就行了&#xff20;SpringBootApplication&#xff0c;我们的启动类其实就加了这一个 但是这么答也不行&#xff0c;因为面试官要的答案肯定不止这一个 我们打开SpringBootApplication的源码&#xff0c;会发现上面加了一堆的注解 相对而言比较重要是下面三个…

【微处理器系统原理与应用设计第十三讲】通用同/异步收发器USART轮询模式应用设计

USART提供两设备之间的串行双工通信&#xff0c;并支持中断和DMA工作。采用轮询、中断和DMA三种方式进行数据收发。 一、功能需求 实现远程串行通信数据的回传确认。微处理器系统构成的测控设备通过USART&#xff08;串口&#xff09;与用户设备&#xff08;上位机&#xff0…

YOLO原理实现

YOLO&#xff08;You Only Look Once&#xff09;是一个标志性的目标检测模型&#xff0c;可以快速分类并定位图像中的多个对象。本文总结了YOLO模型中所有关键的数学操作。 第一步&#xff1a;定义输入 要使用YOLO模型&#xff0c;首先必须将RGB图像转换为448 x 448 x 3的张…