【Linux学习笔记】mmap-共享内存进程通信 vs 有名信号量和无名信号量

news2025/1/13 13:23:35

mmap和信号量实现进程间通信相关

  • mmap
    • 1. mmap 使用的注意事项
    • 2. mmap的两种映射
    • 3. mmap调用接口以及参数
    • 4. 使用存储映射区实现父子进程间通信(有名)
        • 父子进程通信的三种方式
        • unlink
    • 5. 创建匿名存储映射区
    • 6. 通过存储映射区实现非血缘关系进程间的通信
  • 信号量
    • 无名信号量+mmap,父子进程间通信
    • 有名信号量实现不想关进程间通信(同步)

mmap

高赞-通透的解释-解决自己内存映射相关问题的解释
原理参考:mmap原理、mmap原理参考
在这里插入图片描述

1. mmap 使用的注意事项

(1)当 open 一个文件时,如果指定了 O_CREAT 标志并且文件不存在,就会新创建一个文件作为映射文件,此时必须调用 ftruncate 或者 lseek+write 设置文件长度,否则任然可以调用 mmap,但是对存储映射区的引用会产生 SIGBUS。另外,如果映射的长度超过了文件长度,访问超过文件长度的映射区也会出错。
(2)munmap 释放映射区时传入的指针必须指向最初分配的位置,否则将会出错(中途可移动映射区指针,但是必须从最开始分配的位置开始释放)。
(3)mmap 指定对映射区的访问权限时不能超过 open 对文件指定的访问权限。
(4)mmap 建立映射区时隐含一次对文件的读,因此打开文件时读权限是必须有的,即使你只进行写操作。

2. mmap的两种映射

对于有血缘关系的进程间通信:

  • 有名内存映射区
  • 匿名内存映射区(推荐)

对于无血缘关系的进程间通信:

  • (只能用)有名内存映射区

3. mmap调用接口以及参数

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

4. 使用存储映射区实现父子进程间通信(有名)

父子进程通信的三种方式

  1. mmap MAP_ANONYMOUS:
    在支持MAP_ANONYMOUS的系统上,直接用匿名共享内存即可,
    mmap(NULL, sizeof(int), PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_SHARED, -1, 0);
  2. mmap /dev/zero:
    有些系统不支持匿名内存映射,则可以使用fopen打开/dev/zero文件,然后对该文件进行映射,可以同样达到匿名内存映射的效果。
  3. shmget shmat shmctl:
    shmget 是老式的system V 共享内存模式,很多系统都支持这种方法。

注意:父子进程的内存空间遵循 读时共享、写时复制,但打开的文件和 mmap 建立的存储映射区在父子进程之间是一直共享的,因此可通过 mmap 建立存储映射区实现父子进程之间的通信。通过以下代码可说明:

#include <stdio.h>                                                                      
#include <stdlib.h>                                                                     
#include <unistd.h>                                                                     
#include <fcntl.h>                                                                      
#include <sys/mman.h>                                                                   
                                                                                        
int var = 10;                                                                                                                                                                                                                                                                      
int main() {                                                                            
    int* p;                                                                             
    int fd;                                                                             
    pid_t pid;                                                                          
                                                                                        
    if ((fd = open("tmpfile", O_RDWR | O_CREAT | O_TRUNC, 0644)) < 0) {                 
        perror("open tmpfile");                                                         
        exit(1);                                                                        
    }                                                                                           
    unlink("tmpfile");      //解除硬链接,即删除了临时文件唯一的目录项,使之具备被删除的条件,但此时并未删除
    ftruncate(fd, 4);       //创建文件大小                                              
                                                                                        
    p = (int*)mmap(NULL, 4, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);                 
               
    if (p == MAP_FAILED) {                                                              
        perror("mmap failed");                                                          
        exit(1);                                                                        
    }                                                                                   
    close(fd);                                                                          
                                                                                        
    if ((pid = fork()) == 0) {      //child                                             
        sleep(1);                                                                       
        printf("In child: *p = %d, var = %d\n", *p, var);                               
    }                                                                                   
    else if (pid > 0) {     //parent                                                    
        *p = 2000;          //修改映射区                                                
        var = 1000;         //修改全局变量                                              
                                                                                        
        printf("In parent: *p = %d, var = %d\n", *p, var);                              
        wait(NULL);         //等待子进程执行完成                                        
                                                                                        
        if (munmap(p, 4) < 0) {     //释放映射区                                        
            perror("munmap failed");                                                    
            exit (1);                                                                   
        }                                                                               
    }                                                                                   
                                                                                        
    return 0;                                                                           
}                  

注意:对于创建的文件需要使用truncate或者ftruncate先进行文件大小的开辟,才能往文件里写东西,写得超过这个大小会报SIGSEGV或者SEGBUS错误。具体解释看解释

unlink

unlink()会删除参数pathname 指定的文件。 如果该文件名为最后连接点, 但有其他进程打开了此文件, 则在所有关于此文件的文件描述词皆关闭后才会删除. 如果参数pathname 为一符号连接, 则此连接会被删除。

我们知道Linux中文件是用inode节点来区分文件的,当我们删除一个文件的时候并不一定系统就会释放inode节点的内容。当满足下面的要求的时候系统才会释放inode节点的内容:

  1. inode中记录指向该节点的硬链接数为0,
  2. 没有进程打开指向该节点的文件

使用unlink函数删除文件的时候,只会删除 目录项 ,并且将inode节点的硬链接数目减一而已,并不一定会释放inode节点。如果此时没有进程正在打开该文件或者有其他文件指向该inode节点,该inode节点将会被释放;如果此时有进程正在打开一个文件,而此时使用unlink删除了该文件,那么此时只是删除了目录项,并没有释放,因为此时仍然有进程在打开这个文件。

unlink函数的另一个用途就是用来创建临时文件,如果在程序中使用open创建了一个文件后,然后立即使用 unlink 函数删除文件,由于此时进程正在打开该文件,所以系统并不会释放该文件的 inode 节点,而只是删除其目录项。当进程退出时,该inode节点将会立即被释放。临时文件可以用在进程间通信中的 有名管道 通信中

5. 创建匿名存储映射区

以上方法虽然实现了父子进程之间的通信,但是每次都要依赖一个文件,如果是一个临时文件,打开后马上进行了 unlink 使文件具备了被释放的条件,在进程结束后文件就被释放,因此这个文件根本就没有存在的必要,可通过匿名映射区避免这种情况。但是匿名映射区只能实现有血缘关系的进程间的通信。所有类 Unix 系统可以借助文件 /dev/zero 实现匿名映射区

#include <stdio.h>                                                                                                                                                                                                                                   
#include <stdlib.h>                                                                     
#include <unistd.h>                                                                     
#include <fcntl.h>                                                                      
#include <sys/mman.h>                                                                                                                                                         
int var = 10;                                                                                                                                                                                                                                                
int main() {                                                                            
    int* p;                                                                             
    pid_t pid;                                                                          
                                                                                        
                                                                                        
    //注意这里加上 MAP_ANON 参数并将文件描述符指定为 -1                                 
    p = (int*)mmap(NULL, 4, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);   
    if (p == MAP_FAILED) {                                                              
        perror("mmap failed");                                                          
        exit(1);                                                                        
    }                                                                                   
                                                                                        
    if ((pid = fork()) == 0) {      //child                                             
        sleep(1);                                                                       
        printf("In child: *p = %d, var = %d\n", *p, var);                               
    }                                                                                   
    else if (pid > 0) {     //parent                                                    
        *p = 2000;          //修改映射区                                                
        var = 1000;         //修改全局变量                                              
                                                                                        
        printf("In parent: *p = %d, var = %d\n", *p, var);                              
        wait(NULL);         //等待子进程执行完成                                        
                                                                                        
        if (munmap(p, 4) < 0) {     //释放映射区                                        
            perror("munmap failed");                                                    
            exit (1);                                                                   
        }                                                                               
    }                                                                                   
                                                                                        
    return 0;                                                                           
}        

6. 通过存储映射区实现非血缘关系进程间的通信

非血缘关系的进程间不能通过匿名映射区实现
写进程:

#include <stdio.h>                                                                      
#include <stdlib.h>                                                                     
#include <unistd.h>                                                                     
#include <fcntl.h>                                                                      
#include <string.h>                                                                     
#include <sys/mman.h>                                                                   
                                                                                        
//这里换一种数据结构进行通信                                                            
struct STU {                                                                            
    int id;                                                                             
    char name[20];                                                                      
    char sex;                                                                           
};                                                                                      
                                                                                        
void sys_err(char* s) {                                                                 
    perror(s);                                                                          
    exit(1);                                                                            
}                                                                                       
                                                                                        
int main() {                                                                            
    int fd;                                                                             
    struct STU stu = {10, "xiaoming", 'm'};                                             
    struct STU* mm;                                                                     
                                                                                        
    if ((fd = open("tmpfile", O_RDWR | O_CREAT | O_TRUNC, 0644)) < 0)                   
        sys_err("open tmpfile");                                                        
    //unlink("tmpfile");            //写进程不能立即进行 unlink,因为他要保证写进程能找到这个目录项                       
    ftruncate(fd, sizeof(stu));     //创建文件大小                                                                        
                                                                                     
    mm = mmap(NULL, sizeof(stu), PROT_WRITE, MAP_SHARED, fd, 0);                        
    if (mm == MAP_FAILED)                                                               
        sys_err("mmap failed");                                                         
    close(fd);                                                                          
                                                                                        
    while (stu.id++ < 100) {                                                            
        memcpy(mm, &stu, sizeof(stu));                                                  
        sleep(1);                                                                       
    }                                                                                   
                                                                                        
    unlink("tmpfile");                                                                                                                                                                                                                               
    munmap(mm, sizeof(stu));                                                            
                                                                                        
    return 0;                                                                        
}           

读进程:

#include <stdio.h>                                                                      
#include <stdlib.h>                                                                     
#include <unistd.h>                                                                     
#include <fcntl.h>                                                                      
#include <sys/mman.h>                                                                   
                                                                                        
//这里换一种数据结构进行通信                                                            
struct STU {                                                                            
    int id;                                                                             
    char name[20];                                                                      
    char sex;                                                                           
};                                                                                      
                                                                                        
void sys_err(char* s) {                                                                 
    perror(s);                                                                          
    exit(1);                                                                            
}                                                                                       
                                                                                        
int main() {                                                                            
    int fd;                                                                             
    struct STU stu;                                                                     
    struct STU* mm;                                                                     
                                                                                        
    if ((fd = open("tmpfile", O_RDONLY)) < 0)                                           
        sys_err("open tmpfile");                                                        
    unlink("tmpfile");          //读进程可以立即进行 unlink                             
    ftruncate(fd, sizeof(stu));                                                         
                                                                                        
    mm = mmap(NULL, sizeof(stu), PROT_READ, MAP_SHARED, fd, 0);                                                                                                                                                                                      
    if (mm == MAP_FAILED)                                                               
        sys_err("mmap failed");                                                         
    close(fd);                                                                          
                                                                                        
    while (mm->id < 100) {                                                              
        printf("id = %d, name = %s, sex = %c \n", mm->id, mm->name, mm->sex);           
        sleep(1);                                                                       
    }                                                                                   
                                                                                        
    munmap(mm, sizeof(stu));                                                            
                                                                                        
    return 0;                                                                           
}           

信号量

sem_t分为有名和无名。有名的sem_t通过sem_open来创建, 而无名的sem_t通过sem_init的初始化。
无名信号量-主要线程间通信
在这里插入图片描述

无名信号量主要用于线程间的通信,保存在内存中,如果想要在进程间同步就必须把无名信号量放在进程间的共享内存中。而在进程间的通信中同步用的通常是有名信号量。有名信号量一般保存在/dev/shm/ 目录下。像文件一样存储在文件系统中。
有名信号量参数详解
有名信号量和无名信号量的区别和联系:

  • 无名信号量的创建信号量函数是sem_init,有名信号量的则是sem_open函数。
  • 无名信号量的删除信号量函数是sem_destroy,有名信号量的则是用sem_close函数关闭有名信号量,但是想要把信号量从文件系统删除得用sem_unlink函数。

无名信号量+mmap,父子进程间通信

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

void *createSharedMemory(size_t size) {
    void *addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0);
    if (addr == MAP_FAILED) {
        return NULL;
    }
    return addr;
}

void freeSharedMemory(void *addr, size_t size)
{
    if (munmap(addr, size) == -1) {
        printf("munmap(%p, %d) failed", addr, (int)size);
    }
}

int main(int argc, char **argv)
{
    sem_t* mutex = (sem_t*)createSharedMemory(sizeof(sem_t));
    int fd, i, count = 0, nloop = 200, zero = 0, *ptr;
    // sem_t mutex;
    //open a file and map it into memory
    fd = open("log.txt", O_RDWR | O_CREAT, S_IRWXU);
    write(fd, &zero, sizeof(int));
        unlink("log.txt");      //解除硬链接,即删除了临时文件唯一的目录项,使之具备被删除的条件,但此时并未删除
    // ftruncate(fd, 4);  
    ptr = (int*)mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    close(fd);
    /* create, initialize semaphore */
    if (sem_init(mutex, 1, 1) < 0) //
    {
        perror("semaphore initilization");
        exit(0);
    }
    if (fork() == 0) { /* child process*/
        for (i = 0; i < nloop; i++) {
            sem_wait(mutex);
            printf("child: %d\n", (*ptr)++);
            sem_post(mutex);
        }
        exit(0);
    }
    /* back to parent process */
    for (i = 0; i < nloop; i++) {
        sem_wait(mutex);
        printf("parent: %d\n", (*ptr)++);
        sem_post(mutex);
    }
    exit(0);
}

注意:信号量也需要在共享区才能使得两个进程访问到,不然即使pshare=1也没用

有名信号量实现不想关进程间通信(同步)

/* share_memory_name_sem_1.c*/
#include <unistd.h>  
#include <stdio.h>  
#include<stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/shm.h>
#include <string.h>

#define BUFF 128

int main ()   
{   
    int count=0;  
    char write_buf[] = "helloworld";
    int i = 0;
    sem_t *sem_1 = NULL; 
    sem_t *sem_2 = NULL; 
    sem_1 = sem_open("write_name_sem_1", O_CREAT|O_RDWR, 0666, 1); //信号量值为 1  
    sem_2 = sem_open("write_name_sem_2", O_CREAT|O_RDWR, 0666, 0); //信号量值为 1  
    if( (sem_1 == SEM_FAILED) | (sem_2 == SEM_FAILED))
    {
        perror("sem_open");  
        exit(-1);  
    }
    int shmid;
    char *shmaddr;//共享内存地址
    // 使用约定的键值创建共享内存
    //if((shmid=shmget(IPC_PRIVATE,BUFF,0666))<0)
    if((shmid=shmget((key_t) 1234,BUFF, 0666|IPC_CREAT))<0)
    {
        perror("shmget");
        exit(-1);
    }
    else
        printf("Create shared memory,id = %d\n",shmid);

    /*映射共享内存*/
    if((shmaddr=shmat(shmid,0,0))<(char *)0)
    {
        perror("shmat");
        exit(-1);
    }
    else
        printf("process 1 shmat shared memory success\n");  

    /*信号量减一,P 操作*/
    sem_wait(sem_1);
    //往共享内存追加写数据
    strncat(shmaddr,write_buf,sizeof(write_buf));       
    /*信号量加一,V 操作*/
    sem_post(sem_2);

    sem_close(sem_1); //关闭有名信号量  sem_1
    sem_close(sem_2); //关闭有名信号量  sem_2
    //把共享内存从当前进程中分离  
    if(shmdt(shmaddr) == -1)  
    {  
        perror("shmdt");  
        exit(-1);  
    }  

    return 0;
}
/* share_memory_name_sem_2.c*/

#include <unistd.h>  
#include <stdio.h>  
#include<stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/shm.h>
#include <string.h>
#include <stddef.h>

#define BUFF 128

int main ()   
{   
    int count=0;  
    char write_buf[] = "2";
    int i = 0;
    sem_t *sem_1 = NULL; 
    sem_t *sem_2 = NULL; 
    sem_1 = sem_open("write_name_sem_1", O_CREAT|O_RDWR, 0666, 1); //信号量值为 1  
    sem_2 = sem_open("write_name_sem_2", O_CREAT|O_RDWR, 0666, 0); //信号量值为 1  
    if( (sem_1 == SEM_FAILED) | (sem_2 == SEM_FAILED))
    {
        perror("sem_open");  
        exit(-1);  
    }
    int shmid;
    char *shmaddr;//共享内存地址
    // 使用约定的键值创建共享内存
    //if((shmid=shmget(IPC_PRIVATE,BUFF,0666))<0)
    if((shmid=shmget((key_t) 1234,BUFF, 0666|IPC_CREAT))<0)
    {
        perror("shmget");
        exit(-1);
    }
    else
        printf("Create shared memory,id = %d\n",shmid);

    /*映射共享内存*/
    if((shmaddr=shmat(shmid,0,0))<(char *)0)
    {
        perror("shmat");
        exit(-1);
    }
    else
        printf("process 2 shmat shared memory success\n");  

    /*信号量减一,P 操作*/
    sem_wait(sem_2);
    //读取共享内存的数据
    printf("read from share memory: %s\n",shmaddr);
    /*信号量加一,V 操作*/
    sem_post(sem_1);
    sem_close(sem_1); //关闭有名信号量  sem_1
    sem_close(sem_2); //关闭有名信号量  sem_2
    sleep(2);
    printf("remove write_name_sem_1\n");
    //删除有名信号量
    if(sem_unlink("write_name_sem_1") < 0)  
    {  
        perror("sem_unlink");  
    }
    printf("remove write_name_sem_2\n");
    if(sem_unlink("write_name_sem_2") < 0)  
    {  
        perror("sem_unlink");  
    }
    printf("detach shmaddr\n");
    //把共享内存从当前进程中分离  
    if(shmdt(shmaddr) == -1)  
    {  
        perror("shmdt");  
        exit(-1);  
    }
    printf("remove shmaddr\n");
    //删除共享内存  
    if(shmctl(shmid, IPC_RMID, 0) == -1)  
    {  
        perror("shmctl"); 
        exit(-1);  
    } 
    printf("it is end!\n");
    return 0;
}

更多有名信号量例子

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

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

相关文章

SiteSucker for macOS + CRACK

SiteSucker for macOS CRACK SiteSucker是一个简单的macOS应用程序&#xff0c;允许您下载网站。它还可以将网站、网页、背景图片、视频和许多其他文件复制到Mac的硬盘上。 SiteSucker是一个Macintosh应用程序&#xff0c;可以自动下载Internet上的网页。它通过将网站的页面、…

遥感影像道路提取算法——SGCN

论文介绍 Split Depth-wise Separable Graph Convolution Network for Road Extraction in Complex Environment from High-resolution Remote Sensing Imagery&#xff08;TGRS&#xff09; 用于从高分辨率遥感图像&#xff08;TGRS&#xff09;中提取复杂环境中道路的分割深…

java对象的创建与内存分配机制

文章目录对象的创建与内存分配机制对象的创建类加载检查分配内存初始化零值设置对象头指向init方法其他&#xff1a;指针压缩对象内存分配对象在栈上分配对象在Eden区中分配大对象直接分配到老年代长期存活的对象进入老年代对象动态年龄判断老年代空间分配担保机制对象的内存回…

Spring的核心模块:Bean的生命周期(内含依赖循环+业务场景)。

Bean的生命周期前言为什么要学习Bean的生命周期前置知识Spring Post-processor&#xff08;后置处理器&#xff09;Aware接口简单介绍Bean的实例化过程为什么会有bean的实例化&#xff1f;过程Bean的初始化阶段为什么会有Bean的初始化&#xff1f;Bean的初始化目的是什么&#…

线性和非线性最小二乘问题的常见解法总结

线性和非线性最小二乘问题的各种解法 先看这篇博客&#xff0c;非常好&#xff1a;线性和非线性最小二乘问题的各种解法 1. 线性最小二乘问题有最优解 但是面对大型稀疏矩阵的时候使用迭代法效率更好。 迭代法 有Jacobi迭代法、 Seidel迭代法及Sor法 【数值分析】Jacobi、Se…

[ubuntu][GCC]gcc源码编译

1.下载gcc安装包 https://ftp.gnu.org/gnu/gcc/ 选择一个需要的gcc版本&#xff0c;下载。 2.下载依赖包 查看下载的gcc安装包中contrib文件夹下的download_prerequisites文件&#xff0c;查看需要的依赖包版本。 根据download_prerequisites中红框位置的信息&#xff0c;在下…

JSON.stringify()的5种使用场景

JSON.stringify() 方法将一个JavaScript对象或值转换为JSON字符串&#xff0c;如果指定了一个replacer函数&#xff0c;则可以选择性地替换值&#xff0c;或者指定的replacer是数组&#xff0c;则可选择性地仅包含数组指定的属性。 语法如下&#xff1a; JSON.stringify(value…

电子技术课程设计基于FPGA的音乐硬件演奏电路的设计与实现

wx供重浩&#xff1a;创享日记 对话框发送&#xff1a;乐曲电路 免费获取完整无水印论文报告&#xff08;包含电路图&#xff09; 文章目录一、设计任务要求二、总体框图三、选择器件四、功能模块五、总体设计电路图六、结束语一、设计任务要求 1、课程设计题目 设计一个乐曲演…

【Flutter从入门到入坑之三】Flutter 是如何工作的

【Flutter从入门到入坑之一】Flutter 介绍及安装使用 【Flutter从入门到入坑之二】Dart语言基础概述 【Flutter从入门到入坑之三】Flutter 是如何工作的 本文章主要以界面渲染过程为例&#xff0c;介绍一下 Flutter 是如何工作的。 页面中的各界面元素&#xff08;Widget&…

使数组和能被P整除[同余定理+同余定理变形]

同余定理同余定理变形前言一、使数组和能被P整除二、同余定理变形总结参考资料前言 同余定理非常经典&#xff0c;采用前缀和 map&#xff0c;当两个余数前缀和为一个值时&#xff0c;则中间一段子数组刚好对P整除。但是能否找到前面是否有一段子数组和可以对P整除呐&#xf…

认识CSS之元素显示模式

&#x1f31f;所属专栏&#xff1a;前端只因变凤凰之路&#x1f414;作者简介&#xff1a;rchjr——五带信管菜只因一枚&#x1f62e;前言&#xff1a;该系列将持续更新前端的相关学习笔记&#xff0c;欢迎和我一样的小白订阅&#xff0c;一起学习共同进步~&#x1f449;文章简…

深度学习训练营之数据增强

深度学习训练营学习内容原文链接环境介绍前置工作设置GPU加载数据创建测试集数据类型查看以及数据归一化数据增强操作使用嵌入model的方法进行数据增强模型训练结果可视化自定义数据增强查看数据增强后的图片学习内容 在深度学习当中,由于准备数据集本身是一件十分复杂的过程,…

Python 中 KeyError: 0 exception 错误

Python “KeyError: 0” 异常是在我们尝试访问不包含0 这个键的时候去访问该键而引起的。 要解决该错误&#xff0c;请在尝试访问字典之前在字典中设置键&#xff0c;或者如果键不存在&#xff0c;则使用 dict.get() 获取默认值。 下面是一个产生上述错误的示例 my_dict {1…

KDZD互感器二次负载测试仪

一、概述 电能计量综合误差过大是电能计量中普遍存在的一个关键问题。电压互感器二次回路压降引起的计量误差往往是影响电能计量综合误差的因素。所谓电压互感器二次压降引起的误差&#xff0c;就是指电压互感器二次端子和负载端子之间电压的幅值差相对于二次实际电压的百分数…

五分钟了解JumpServer V2.* 与 v3 的区别

一、升级注意项 1、梳理数据。JumpServer V3 去除了系统用户功能&#xff0c;将资产与资产直接绑定。当一个资产名下有多个同名账号&#xff0c;例如两个root用户时&#xff0c;升级后会自动合并最后一个root&#xff0c;不会同步其他root用户。升级前需保证每一个资产只拥有一…

即时通讯系列-N-客户端如何在推拉结合的模式下保证消息的可靠性展示

结论先行 原则&#xff1a; server拉取的消息一定是连续的原则&#xff1a; 端侧记录的消息的连续段有两个作用&#xff1a; 1. 记录消息的连续性&#xff0c; 即起始中间没有断层&#xff0c; 2. 消息连续&#xff0c; 同时意味着消息是最新的&#xff0c;消息不是过期的。同…

Java学习-MySQL-创建数据库表

Java学习-MySQL-创建数据库表 SHOW DATABASESUSE school CREATE TABLE IF NOT EXISTS student( id INT(10) NOT NULL AUTO_INCREMENT COMMENT 学号, name VARCHAR(30) NOT NULL DEFAULT 匿名 COMMENT 姓名, pws VARCHAR(20) NOT NULL DEFAULT 123456 COMMENT 密码, sex VARCHA…

算法题--二叉树(判断是不是平衡二叉树、二叉树的中序遍历、二叉树最大深度、对称二叉树、合并二叉树)

目录 二叉树 题目 判断是不是平衡二叉树 题链接 解析 核心思想 答案 二叉树的中序遍历 原题链接 解析 核心思想 答案 二叉树最大深度、对称二叉树、合并二叉树 二叉树 该类题目的解决一般是通过节点的遍历去实现&#xff0c;一般是分两种。 一是递归&#xff08;…

【记录】日常|shandianchengzi的三周年创作纪念日

机缘 接触 CSDN 之前&#xff0c;我已经倒腾过 hexo 搭建 github 博客、本地博客、图床&#xff1b;   接触 CSDN 之后&#xff0c;我还倒腾过纸质笔记、gitee 博客、博客园、知乎、b站、Notion、腾讯文档、有道云笔记、XMind、飞书文档、简书等一系列创作平台&#xff0c;但…

SAPUI5开发01_01-Installing Eclipse

1.0 简要要求概述: 本节您将安装SAPUI 5,以及如何在Eclipse Juno中集成SAPUI 5工具。 1.1 安装JDK JDK 是一种用于构建在 Java 平台上发布的应用程序、Applet 和组件的开发环境,即编写 Java 程序必须使用 JDK,它提供了编译和运行 Java 程序的环境。 在安装 JDK 之前,首…