学习记录——day26 进程间的通信(IPC)无名管道 无名管道 信号通信 特殊的信号处理

news2024/9/22 19:27:36

目录

一、进程间通信引入

二、无名管道

1、无名管道相关概念

2、无名管道的API接口函数 pipe(int pipefd[2]);

3、管道通信的特点

4、管道的读写特点

 三、无名管道

1、有名管道:有名字的管道文件,其他进程可以调用

2、可以用于亲缘进程间的通信,也可以用于非亲缘进程间的通信

3、有名管道的API函数 mkfifo(const char *pathname, mode_t mode);

 四、信号通信

1、信号通信相关概念

2、信号通信相关指令: kil -  man 7 signal

3、将信号与信号处理方式连接函数

 五、特殊的信号处理

1、SIGCHLD信号:以非阻塞的形式回收僵尸进程

2、SIGALRM:定时器信号

3、信号的发送函数

 使用有名管道实现两个进程间相互通信

 使用有名管道实现,一个进程用于给另一个进程发消息,另一个进程收到消息后,展示到终端上,并且将消息保存到文件上


一、进程间通信引入

1、对于多个线程间的通信,可以使用使用临界资源来完成,通过一个线程任务对临界资源进行修改,另一个线程也可以使用已经修改过的临界资源,但是要注意使用同步互斥机制完成对临界资源的保护

2、对于进程而言,用户空间是独立的,全局变量也是独立拥有的,更改一个进程的全局变量,其他进程的全局变量不受影响

3、可使用文件完成进程间的通信,但是要求写进程先完成向文件中写入数据,然后读进程才能读取数据,必须要加入进程的同步操作

4、对于进程而言,内核空间是共享的,可通过内核空间存取数据的方式,来完成两个进程之间信息的交流

5、进程间通信方式:

1)内核提供的原始通信方式
        无名管道
        有名管道
        信号
2)system V提供三种方式
        消息队列
        共享内存
        信号量集

二、无名管道

1、无名管道相关概念

1)无名管道:没有名字的管道文件,其他进程不能使用无名管道文件的文件描述符,但可以遗传,所以该方法仅适用于亲缘进程(父子进程)

2)原理:,通过内核,在内存中创建出一个管道文件,存放在内存空间,并不在文件系统中真实存在。当打开该文件时,会返回该文件的两个文件描述符,分别对应读端和写端。可以通过读端从管道中读取数据,从写端向管道中写入数据。当该文件的读写两端全部被关闭后,该管道文件会在内存中消失

3)注意:由于打开管道时返回的是文件描述符,所以,对该文件的操作,只能使用文件IO;

               对于存入管道中的数据的读取是一次性的,当数据读取结束后,数据就不存在于管道文件中了

2、无名管道的API接口函数 pipe(int pipefd[2]);

       #include <unistd.h>

       int pipe(int pipefd[2]);
       功能:在通过内核在内存中创建一个无名管道,并通过参数将该管道文件的两个文件描述符返回
       参数:接收文件描述符的数组,pipefd[0]表示管道文件的读端,pipefd[1]表示管道的写端
       返回值:成功返回0,失败返回-1并置位错误码

#include <myhead.h>

int main(int argc, char const *argv[])
{
    // 成绩用于通讯的管道an文件,并返回文件对应的ean文件as描述符
    int pipefd[2];
    if (pipe(pipefd) == -1)
    {
        perror("pipe error");
        return -1;
    }

    // 创建子进程
    pid_t pid = fork();
    if (pid < 0)
    {
        perror("fork error");
        return -1;
    }
    else if (pid == 0)
    {
        // 子进程
        char rbuf[128] = "";

        // 关闭写端
        close(pipefd[1]);
        while (1)
        {

            bzero(rbuf, sizeof(rbuf));

            read(pipefd[0], rbuf, sizeof(rbuf));

            if (strcmp(rbuf, "quit") == 0)
            {
                break;
            }

            printf("收到父进程的信息:%s\n", rbuf);
        }

        // 关闭读端
        close(pipefd[0]);

        // 退出
        exit(EXIT_SUCCESS);
    }

    // 父进程
    char sbuf[128] = "";

    // 关闭读端
    close(pipefd[0]);
    while (1)
    {

        usleep(10);
        printf("父进程中输入:");
        fgets(sbuf, sizeof(sbuf), stdin); // 从终端获取数据
        sbuf[strlen(sbuf) - 1] = 0;

        // 发送数据给子进程:将数据通过写端写入管道 pipefd[1]
        write(pipefd[1], sbuf, strlen(sbuf));

        if (strcmp(sbuf, "quit") == 0)
        {
            break;
        }
    }
    // 关闭写端
    close(pipefd[1]);
    // 回收子进程
    wait(NULL);
    return 0;
}

注:对于单个进程,读写端一般只保留其中之一

3、管道通信的特点

1)管道通信是半双工的通信方式
    单工:任意时刻只能A向B发送消息,B不能向A发送消息
    半双工:同一时刻,只能A向B发送消息或者B向A发消息
    全双工:任意时刻,AB可以互相发送消息
2)无名管道只适用于亲缘进程间通信
3)无名管道也能完成自己跟自己的通信
4)无名管道文件的大小:64KB   2的16次方字节(65536)   

4、管道的读写特点

1)当管道读端存在时,写管道有多少写多少,直到写满64K为止
2)当管道读端不存在时,写管道写入数据时,会出现管道破裂,此时内核空间会向用户空间发送一个SIGPIPE信号
3)当写端存在时,读管道有多少读多少,没有数据会在read处阻塞
4)当写端不存在时,读管道有多少读多少,没有数据也不会在read处阻塞

 三、有名管道

1、有名管道:有名字的管道文件,其他进程可以调用

2、可以用于亲缘进程间的通信,也可以用于非亲缘进程间的通信

3、有名管道的API函数 mkfifo(const char *pathname, mode_t mode);

       #include <sys/types.h>
       #include <sys/stat.h>

       int mkfifo(const char *pathname, mode_t mode);
    功能:在文件系统中创建一个有名管道,但是并没有打开该文件
    参数1:有名管道的名称
    参数2:管道文件的权限
    返回值:成功返回0,失败返回-1并置位错误码

 四、信号通信

1、信号通信相关概念

1)信号通信原理图:两个异步通信的进程之间,通过发送相关信号,完成任务间的通信

2) 信号是linux中软件模拟硬件的“中断”的一种方式

4)对于信号的处理方式有三种:默认、捕获、忽略

6)信号的发送者可以是内核、其他进程、用户自己

      有两个信号既不能被捕获,也不能被忽略:SIGKILL、SIGSTOP(只要放入signal函数中就会报错:参数不合法)

2、信号通信相关指令: kil -  man 7 signal

1)能够发送的信号可以通过指令:kil - 进行查看

 1) SIGHUP     2) SIGINT     3) SIGQUIT     4) SIGILL     5) SIGTRAP
 6) SIGABRT     7) SIGBUS     8) SIGFPE     9) SIGKILL    10) SIGUSR1
11) SIGSEGV    12) SIGUSR2    13) SIGPIPE    14) SIGALRM    15) SIGTERM
16) SIGSTKFLT    17) SIGCHLD    18) SIGCONT    19) SIGSTOP    20) SIGTSTP
21) SIGTTIN    22) SIGTTOU    23) SIGURG    24) SIGXCPU    25) SIGXFSZ
26) SIGVTALRM    27) SIGPROF    28) SIGWINCH    29) SIGIO    30) SIGPWR
31) SIGSYS    34) SIGRTMIN    35) SIGRTMIN+1    36) SIGRTMIN+2    37) SIGRTMIN+3
38) SIGRTMIN+4    39) SIGRTMIN+5    40) SIGRTMIN+6    41) SIGRTMIN+7    42) SIGRTMIN+8
43) SIGRTMIN+9    44) SIGRTMIN+10    45) SIGRTMIN+11    46) SIGRTMIN+12    47) SIGRTMIN+13
48) SIGRTMIN+14    49) SIGRTMIN+15    50) SIGRTMAX-14    51) SIGRTMAX-13    52) SIGRTMAX-12
53) SIGRTMAX-11    54) SIGRTMAX-10    55) SIGRTMAX-9    56) SIGRTMAX-8    57) SIGRTMAX-7
58) SIGRTMAX-6    59) SIGRTMAX-5    60) SIGRTMAX-4    61) SIGRTMAX-3    62) SIGRTMAX-2
63) SIGRTMAX-1    64) SIGRTMAX    

2)上面的信号触发条件以及默认处理方式可以通过指令 man 7 signal 进行查看

       Signal     Value     Action   Comment
       ────────────────────────────────────────────────────────        SIGHUP        1       Term    Hangup detected on controlling terminal
                                                or death of controlling process
       SIGINT        2        Term    Interrupt from keyboard
       SIGQUIT       3      Core    Quit from keyboard
       SIGILL        4         Core    Illegal Instruction
       SIGABRT       6      Core    Abort signal from abort(3)
       SIGFPE        8       Core    Floating-point exception
       SIGKILL       9        Term    Kill signal
       SIGSEGV      11       Core    Invalid memory reference
       SIGPIPE      13       Term    Broken pipe: write to pipe with no
                                                   readers; see pipe(7)
       SIGALRM      14       Term    Timer signal from alarm(2)
       SIGTERM      15       Term    Termination signal
       SIGUSR1   30,10,16    Term    User-defined signal 1
       SIGUSR2   31,12,17    Term    User-defined signal 2
       SIGCHLD   20,17,18    Ign     Child stopped or terminated
       SIGCONT   19,18,25    Cont    Continue if stopped
       SIGSTOP   17,19,23    Stop    Stop process
       SIGTSTP   18,20,24    Stop    Stop typed at terminal
       SIGTTIN   21,21,26    Stop    Terminal input for background process
       SIGTTOU   22,22,27    Stop    Terminal output for background process
 

3、将信号与信号处理方式连接函数

       #include <signal.h>

       typedef void (*sighandler_t)(int);

       sighandler_t signal(int signum, sighandler_t handler);
       功能:将信号与信号处理函数绑定到一起
       参数1:要绑定的信号
       参数2:信号处理函数
               SIG_IGN:表示忽略信号
               SIG_DFL:表示默认处理
               填自定义函数的入口地址
        返回值:成功返回处理方式的起始地址,失败返回 SIG_ERR

 五、特殊的信号处理

1、SIGCHLD信号:以非阻塞的形式回收僵尸进程

#include <myhead.h>
void handler(int signo)
{
    if (signo == SIGCHLD)
    {
        while(waitpid(-1,NULL,WNOHANG));
    }
    
}
int main(int argc, char const *argv[])
{
    if (signal(SIGCHLD,handler) == SIG_ERR)
    {
        perror("signal error");
        return -1;
    }
    
    for (int i = 0; i < 10; i++)
    {
        if (fork() == 0)
        {
            exit(EXIT_SUCCESS);
            return -1;
        }
        
    }

    for(;;);  //此处不使用while,是因为while对cpu的占用是100%,导致handler函数没有执行
    
    return 0;
}

2、SIGALRM:定时器信号

        程序允许启动一个定时器,当所定的时间到位后,会发送一个SIGALRM信号,将该信号绑定到对应的信号处理函数中,给定时间,到时间自动处理自定义函数

        该信号需要使用一个函数来发送超时信号:alarm闹钟函数

       #include <unistd.h>

       unsigned alarm(unsigned seconds);
    功能:给进程设置一个定时器,以秒为单位,当定时器到位后,后向该进程发送一个SIGALRM的信号
    参数:秒数,如果参数设置成0,表示删除定时器
    返回值:>0:表示返回的上一个定时器剩余的秒数,并且重置上一个定时器
                    0:表示之前没有设置定时器

3、信号的发送函数

       #include <signal.h>

       int kill(pid_t pid, int sig);
       功能:向指定的进程发送指定的信号
       参数1:要发送的进程号
           >0:表示向指定的进程发送信号
           =0:表示向某个进程组(当前进程所在的进程组)中发送信号
           =-1:向所有进程发送信号
           <-1:表示向其他进程组(组id为给定pid的绝对值)发送信号
       参数2:要发送的信号号
       返回值:      成功返回0,失败返回-1并置位错误码
       
       #include <signal.h>

       int raise(int sig);
       功能:向自己所在的进程发送指定的信号
       参数:要发送的信号
       返回值:成功返回0,失败返回非0数字
                    
 

 使用有名管道实现两个进程间相互通信

00.create.c  

#include <myhead.h>
int main(int argc, char const *argv[])
{
    // 创建一个有名管道文件
    if (mkfifo("./linux", 0664) == -1)
    {
        perror("mkfifo error");
        return -1;
    }

    // 创建一个有名管道文件
    if (mkfifo("./linux1", 0664) == -1)
    {
        perror("mkfifo error");
        return -1;
    }

    getchar();

    system("rm linux");
    system("rm linux1");
    return 0;
}

01snd.c

#include <myhead.h>
int main(int argc, char const *argv[])
{
    //创建子程序
    pid_t pid = fork();
    if (pid < 0)
    {
        perror("fork error");
        return -1;
    }
    else if (pid == 0)
    {
        // 以读的形式打开管道文件
        int rfd = open("./linux1", O_RDONLY);
        if (rfd == -1)
        {
            perror("ropen error");
            return -1;
        }
        printf("./linux1 read open\n");

        // 接收数据
        char rbuf[128] = "";
        while (1)
        {
            // 清空容器
            bzero(rbuf, sizeof(rbuf));
            // 读取数据
            read(rfd, rbuf, sizeof(rbuf));
            if (strcmp(rbuf, "quit") == 0)
            {
                break;
            }

            printf("接收到:%s\n", rbuf);
            sleep(1);
        }

        // 关闭文件描述符
        close(rfd);
        exit(EXIT_SUCCESS);
    }

    // 以写的形式打开管道文件
    int wfd = open("./linux", O_WRONLY);
    if (wfd == -1)
    {
        perror("wopen error");
        return -1;
    }
    printf("./linux write open\n");

    // 发送数据
    char wbuf[128] = "";
    while (1)
    {
        printf("输入:");
        fgets(wbuf, sizeof(wbuf), stdin);
        wbuf[strlen(wbuf)-1] = 0;

        // 发送数据
        write(wfd, wbuf, strlen(wbuf));
        if (strcmp(wbuf, "quit") == 0)
        {
            break;
        }
    }

    // 关闭文件描述符
    close(wfd);

    wait(NULL);
    return 0;
}

03recv.c

#include <myhead.h>
int main(int argc, char const *argv[])
{
    // 创建子程序
    pid_t pid = fork();
    if (pid < 0)
    {
        perror("fork error");
        return -1;
    }

    else if (pid == 0)
    {
        // 以写的形式打开管道文件
        int wfd = open("./linux1", O_WRONLY);
        if (wfd == -1)
        {
            perror("open error");
            return -1;
        }
        printf("./linux1 write open\n");

        // 发送数据
        char wbuf[128] = "";
        while (1)
        {
            printf("输入:");
            fgets(wbuf, sizeof(wbuf), stdin);
            wbuf[strlen(wbuf) - 1] = 0;

            // 发送数据
            write(wfd, wbuf, strlen(wbuf));
            if (strcmp(wbuf, "quit") == 0)
            {
                break;
            }
        }

        // 关闭文件描述符
        close(wfd);

        exit(EXIT_SUCCESS);
    }

    usleep(10);
    // 以读的形式打开管道e文件
    int rfd = open("./linux", O_RDONLY);
    if (rfd == -1)
    {
        perror("open error");
        return -1;
    }
    printf("./linux read open\n");

    // 接收数据
    char rbuf[128] = "";
    while (1)
    {
        // 清空容器
        bzero(rbuf, sizeof(rbuf));
        // 读取数据
        read(rfd, rbuf, sizeof(rbuf));
        if (strcmp(rbuf, "quit") == 0)
        {
            break;
        }

        printf("接收到:%s\n", rbuf);

        sleep(1);
    }
    // 关闭文件描述符
    close(rfd);

    wait(NULL);
    return 0;
}

 使用有名管道实现,一个进程用于给另一个进程发消息,另一个进程收到消息后,展示到终端上,并且将消息保存到文件上

00.create.c

#include <myhead.h>
int main()
{
    // 创建一个有名管道文件
    if (mkfifo("./file", 0664) == -1)
    {
        perror("mkfifo error");
        return -1;
    }

    getchar();

    system("rm linux");
    return 0;
}

01.send.c

#include<myhead.h>

int main()
{
    int wfd=open("./file",O_WRONLY);
    if(wfd==-1)
    {
        perror("open error");
        return -1;
    }
    char buf[128]="";
    while(1)
    {
        printf("请输入>>>");
        
        //从终端读取数据
        fgets(buf,sizeof(buf),stdin);
   
        buf[strlen(buf)-1]=0; //末位置空('\0')
       
         //写入通道
        write(wfd,buf,strlen(buf));
        if(strcmp(buf,"quit")==0)
        {
            break;
        }
        
    }
    //关闭文件操作符
    close(wfd);
    return 0;
}

02rece.c

#include<myhead.h>

int main()
{
    int wfd=open("./lz",O_RDONLY);
    if(wfd==-1)
    {
        perror("open error");
        return -1;
    }
    int pt=-1;
    if((pt=open("./text.txt",O_WRONLY|O_CREAT|O_TRUNC,0664))==-1)
    {
        perror("open error");
        return -1;
    }
    char buf[128]="";
    while(1)
    {
        //清空
        bzero(buf, sizeof(buf));
        //从通道读取数据
        read(wfd, buf, sizeof(buf));
        //判断离开
        if(strcmp(buf,"quit")==0)
        {
            break;
        }
        printf("收到%s\n",buf);
        //写入文件
        write(pt,buf,strlen(buf));
    }
    //关闭文件指针
    close(pt);
    //关闭通道读写
    close(wfd);
    return 0;
}

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

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

相关文章

windows下,使用vs code远程连接云服务器【以CentOS7为例】

windows下&#xff0c;使用vs code远程连接云服务器 1. 下载VS code并准备相关插件2. 使用Remote - SSH远程控制Linux 1. 下载VS code并准备相关插件 1. VS code官网&#xff1a; 下载地址&#xff1a;https://code.visualstudio.com/Download下载合适版本的vs code。 2. 推荐…

20款奔驰S450升级原厂红外夜视系统,提升您夜晚行车安全

夜视辅助系统增强版不仅可以对处于潜在危险位置的行人进行探测&#xff0c;还可发现动物。当车辆进入没有路灯的区域&#xff0c;第三代夜视系统可自动将仪表盘的显示内容从车速表转变为锐度极高的夜视图像&#xff0c;并将探测到前方出现的行人或动物在系统中以鲜明的色彩突出…

CSS前端:元素的布局技巧

【备注】在线测试网站&#xff0c;点此进入。 一、内边距和外边距 【总结】padding增加本元素和子元素的距离&#xff0c;margin增加本元素和父元素的距离。 二、居中和对齐 【需求】 【HTML】 <div id"container"><p id"top">我在div容器…

打卡第33天------动态规划

周末的时候刷题了,在代码随想录系统的学习了一遍算法,看到leetcode上的题不再那么懵了,有点思路了,心里面对题的恐惧感也在逐渐消失,滴水石穿,小坚持带来大改变。 一、不同路径 leetcode题目链接:62.不同路径 题目描述: 一个机器人位于一个 m x n 网格的左上角 (起始…

【更新2022】省级经济高质量发展指标体系测度 含代码 2000-2022

重磅更新&#xff01;【章汕】制作“省级经济高质量发展指标体系测度 含代码”&#xff0c;市面上有这个版本的数据&#xff0c;但其内容非常不全面&#xff0c;个别指标有误&#xff0c;没有stata和代码&#xff0c;即使有代码小白也很容易报错&#xff1b;没有权重、宽面板等…

大数据SQL面试题002-合并日期重叠的活动

在大数据SQL面试中&#xff0c;我们经常会遇到一些棘手的问题&#xff0c;比如合并日期重叠的活动。本文将为大家详细解析这个问题&#xff0c;并提供相应的解决方案。 一、题目分析 题目要求我们处理一个表&#xff0c;表中记录了每个品牌的活动开始日期和结束日期。如果两个…

鸿蒙(API 12 Beta2版)NDK开发【LLDB高性能调试器】调试和性能分析

概述 LLDB&#xff08;Low Level Debugger&#xff09;是新一代高性能调试器。 当前HarmonyOS中的LLDB工具是在[llvm15.0.4]基础上适配演进出来的工具&#xff0c;是HUAWEI DevEco Studio工具中默认的调试器&#xff0c;支持调试C和C应用。 工具获取 可通过HUAWEI DevEco S…

c语言-EasyX库编写的第一个项目-哪都通快递平台

目录 概要设计与详细设计文档 项目测试 相关文件下载 作为《程序设计微项目设计》进行为期一周的开发 概要设计与详细设计文档 1&#xff0e; 项目名称 “哪都通”快递平台 2.项目概述 2.1项目简介 本项目采用c语言全程编写&#xff0c;运用Easyx库创建窗口进行图形显…

shell函数的基本知识

文章目录 shell函数定义函数调用函数函数参数返回值 Shell 输入/输出重定向输入重定向输出重定向 Shell 函数是 Shell 脚本编程中的一个非常有用的特性&#xff0c;它允许你将一段代码封装起来&#xff0c;给它一个名字&#xff08;函数名&#xff09;&#xff0c;然后在脚本的…

力扣面试经典算法150题:合并两个有序数组

算法 本篇开始&#xff0c;正式进入算法刷题篇。 题目来源于力扣面试经典150题。 题目链接&#xff1a;https://leetcode.cn/studyplan/top-interview-150/ 合并两个有序数组 题目选自150题中的数组/字符串一类&#xff0c;题目难度&#xff1a;简单。 题目描述 给定两个按…

R 语言学习教程,从入门到精通,R 基础运算(5)

1、R 基础运算 本章介绍 R 语言的简单运算。 1.1、赋值 一般语言的赋值是 号&#xff0c;但是 R 语言是数学语言&#xff0c;所以赋值符号与我们数学书上的伪代码很相似&#xff0c;是一个左箭头 <- &#xff1a; a <- 123 b <- 456 print(a b)以上代码执行结果…

分享一个基于Node.js和Vue的农产品销售与交流平台(源码、调试、LW、开题、PPT)

&#x1f495;&#x1f495;作者&#xff1a;计算机源码社 &#x1f495;&#x1f495;个人简介&#xff1a;本人 八年开发经验&#xff0c;擅长Java、Python、PHP、.NET、Node.js、Android、微信小程序、爬虫、大数据、机器学习等&#xff0c;大家有这一块的问题可以一起交流&…

RabbitMQ-安装篇(阿里云主机)

一、操作系统 用的是Alibaba Cloud Linux release 3 (Soaring Falcon)系统&#xff0c;可以通过命令&#xff1a;lsb_release -a 查看系统信息。 二、安装RabbitMQ RabbitMQ 是基于 Erlang 语言构建的&#xff0c;要安装RabbitMQ&#xff0c;需先安装Erlang环境。通过Erlang V…

【IO】使用有名管道实现,一个进程用于给另一个进程发消息,另一个进程收到消息后,展示到终端上,并且将消息保存到文件上一份

目录 1、使用有名管道实现&#xff0c;一个进程用于给另一个进程发消息&#xff0c;另一个进程收到消息后&#xff0c;展示到终端上&#xff0c;并且将消息保存到文件上一份 2、使用有名管道实现两个进程间相互通信 1、使用有名管道实现&#xff0c;一个进程用于给另一个进程发…

Linux权限-普通权限

作者介绍&#xff1a;简历上没有一个精通的运维工程师。希望大家多多关注我&#xff0c;我尽量把自己会的都分享给大家&#xff0c;下面的思维导图也是预计更新的内容和当前进度(不定时更新)。 根据前面Linux用户介绍&#xff0c;里面涉及到超级管理员&#xff0c;普通用户&…

AI技术重塑招聘流程:效率与精准度的双重提升

一、引言 在21世纪的数字经济浪潮中&#xff0c;人工智能&#xff08;AI&#xff09;正以前所未有的速度渗透到社会经济的各个领域&#xff0c;其中&#xff0c;人力资源&#xff08;HR&#xff09;管理领域也不例外。作为组织发展与人才战略的核心环节&#xff0c;招聘流程正经…

VoNR网络架构与网元 IMS终端号码结构(VoLTE和VoNR适用)

目录 1. VoNR网络架构与网元 1.1 VoNR架构 vs VoLTE架构 1.2 回顾语音网络的演进与“分离” 1.3 TS23.228给出的5G的VoNR国际漫游 Home Routed 方案架构图 1.4 VoNR 网络架构图&#xff08;2022版&#xff09; 1.5 IMS 网元分类&#xff08;VoNR VoLTE 适用&#xff09…

【SpringBoot】集成Redis

[TOC] 1 redis ​ redis是一个用C语言开发的&#xff0c;基于内存结构进行键值对数据存储、高性能、非关系型NoSQL数据库 ​ 官网&#xff1a; https://redis.io/ 1.1 特点 基于内存存储&#xff0c;数据读写效率很高本身支持持久化虽然基于key-value存储&#xff0c;但是…

excel中有些以文本格式存储的数值如何批量转换为数字

一、背景 1.1 文本格式存储的数值特点 在平时工作中有时候会从别地方导出来表格&#xff0c;表格中有些数值是以文本格式存储的&#xff08;特点&#xff1a;单元格的左上角有个绿色的小标&#xff09;。 1.2 文本格式存储的数值在排序时不符合预期 当我们需要进行排序的时候…

HashMap中 put()方法的流程、扩容的思路(源码分析~)

文章目录 put() 方法的流程扩容流程为什么它会按照2的幂次方进行扩容呢&#xff1f; put() 方法的流程 下面我们通过分析源码来总结一下 put() 方法的流程 扩容流程 根据上图的分析&#xff0c;就可以总结出 HashMap 的扩容流程&#xff1a; 在插入元素时&#xff0c;会先…