Linux | 进程间通信 | system V共享内存 | 介绍和使用

news2025/2/27 21:05:22

文章目录

    • system V共享内存介绍
    • 共享内存的创建
      • shmget共享内存的获取
      • shmctl,可用于共享内存的删除
      • shmat && shmdt
    • 共享内存的使用
    • Linux对system V的设计思路

system V共享内存介绍

进程间通信的前提是:使不同进程看到同一份资源,在使用匿名管道和命名管道进行进程间通信时,同一份资源指的就是管道文件。system V作为一种通信标准,在此标准下有一套共享内存机制,即通过系统接口得到一块共享内存,这块内存就对应着通信前提中的同一份资源,让不同的进程都“看到”这块资源后,不同进程就可以进行通信,与管道一样,共享内存也是一种IPC技术。

共享内存的本质:之前在聊进程地址空间时,我们知道每个进程都有属于自己的进程空间,栈区,堆区,代码区,已初始化全局数据区…并且这些空间都是虚拟空间,虚拟空间通过页表映射到真实的物理空间。而共享内存就是调用系统接口,向系统申请的一块物理内存资源,我们只要通过系统,使进程虚拟空间的共享区与物理空间的共享内存之间建立映射关系,就代表该进程可以访问这块物理空间。同理,其他进程通过页表也能映射到这块物理空间,这样多个进程就看到了同一份资源,完成了通信的前提。

共享内存的创建

要使用一块共享内存就需要先创建一块共享内存,有创建就需要有销毁,创建了共享内存还需要使进程关联(使用)共享内存,有关联就需要有去关联。所以关于共享内存一共有四个操作,每个操作都是系统接口的调用

shmget共享内存的获取

在这里插入图片描述
shmget可以向系统申请一块共享内存,其中的参数

key:用户对共享内存的唯一标识符
size:以字节为单位,表示申请空间的大小
shmflg:申请空间时的申请方式

其中有的参数令人费解,下面一个个解释这些参数的含义。一个系统下肯定存在着很多进程,这些进程中有部分进程在进行通信,通信的方式可能是共享内存,以共享内存的方式进行通信就需要使用共享内存块,对于这些位于内存上的共享内存块,操作系统就需要进行管理,所以操作系统中有着描述这些内存块的结构体,结构体的名字为struct shmid_ds
在这里插入图片描述
这个结构体存储了关于共享内存的信息,最后一次关联时间,创建者的pid等等…其中有一个struct ipc_perm结构体,该结构体保存了共享内存块的权限信息
在这里插入图片描述
其中有一个key对象,这个key就是操作系统用来识别共享内存块的唯一符号。所以这个key值是事先约定好,进程彼此间知道的符号,进程通过传递key值给系统接口,系统通过key值查找共享内存块,这就使进程可以看到同一份资源

size是共享内存块的大小,单位为字节,size最好设置为page(4KB)的整数倍,因为系统IO的基本单位为4KB。系统以4KB为最小单位,将所有内存描述成一个个页(page),对应的结构体为struct page。所以系统分配内存是以page为基本单位进行分配的,如果申请的共享内存大小为4097(4KB + 1B),系统就要为你分配两个page,实际申请内存的大小为8192KB,但你能使用的内存大小只有4097KB,所以将共享内存的大小设置为4KB的整数倍,其实是为了方便系统的管理。

剩下一个参数shmflg,这是共享内存获取方式的参数,用来表示获取方式的参数,大多都是位图结构,可以给shmflg这个参数传递两个宏,IPC_CREAT,IPC_EXCL。其中IPC_CREAT表示如果对应key值的共享内存块不存在,就创建之。如果对应key值的共享内存块已经存在,就获取之(返回一个标识符shmid)。

IPC_EXCL表示如果对应key值的共享内存块不存在,就创建之。如果对应key值的共享内存块已经存在,就返回出错信息,也就是说,这个宏保证了获取的共享内存块一定是新分配的。但是这个宏需要和IPC_CREAT一起使用,也就是将IPC_CREAT | IPC_EXCL作为shmflg的参数。

除了shmget的参数需要解释,其返回值也需要聊一聊。如果shmget创建共享内存成功,将共享内存块的标识符shmid返回。如果shmget创建共享内存块失败,将返回-1,并设置错误码errno。这个shmid和key一样也是唯一的,那么shmid与key有什么区别? key是用户层生成的一个标识符,而shmid是系统返回的IPC资源标识符,用来访问IPC资源,如果要删除一个共享内存块,我们就需要用到shmid。

此外如果只有操作系统返回的shmid这个标识符,当前进程要怎么传递shmid给另一个进程(要传递就必须进行通信,但传递shmid的目的就是为了通信),所以只能再引入一个自己确定的标识符key,使不同进程通过事先约定好的key进行通信,这一点与命名管道相似,命名管道的通过事先约定好指定文件名,然后通过该文件进行通信。

介绍完shmget后,就是使用shmget,使用前需要有一个key值,要生成一个唯一的key,可以使用函数ftok
在这里插入图片描述
该函数内置了一个算法,将传入的两个参数pathname和proj_id换算成一个system V标准下的用于进程间通信的key值,通过函数的描述,我们知道只要pathname和proj_id相同,无论何时调用ftok函数,得到的key值都是相同的在这里插入图片描述
所以使用共享内存之前,只要先约定好pathname和proj_id,将这两个参数固定,得到的key就是相同的,这就保证了key的唯一性,有了唯一的key值后,进程就能看到同一块共享内存,也就能进行通信了。

// Comm.hpp
#pragma once

#include <iostream>
#include <cstdio>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <cstring>

using namespace std;

#define IPC_PATH "/home/cw/daily"
#define IPC_ID 0x88

int CreatKey()
{
    int key = ftok(IPC_PATH, IPC_ID);
    if (key != -1) // 生成key值成功
    {
        return key;
    }
    else // 生成key失败,打印错误信息
    {
        cerr << "ftok: " << strerror(errno) << endl;
        exit(-1);
    }
    return -1;
}

// IpcShmCli.cc
#include "comm.hpp"
using namespace std;

int main()
{
    // key的创建
    int key = CreatKey();
    // 创建共享内存
    int shmid = shmget(key, 4096, IPC_CREAT | IPC_EXCL);
    if (shmid == -1)
    {
        printf("key: %x, creat fail: %s\n", key, strerror(errno));
        exit(-1);
    }
    // 打印共享内存信息
    printf("key: %x, creat shm: %d\n", key, shmid);
    return 0;
}

// IpcShmSer.cc
#include "comm.hpp"
using namespace std;

int main()
{
    // key的创建
    int key = CreatKey();
    // 创建共享内存
    int shmid = shmget(key, 4096, IPC_CREAT | IPC_EXCL);
    if (shmid == -1)
    {
        printf("key: %x, creat fail: %s\n", key, strerror(errno));
        exit(-1);
    }
    // 打印共享内存信息
    printf("key: %x, creat shm: %d\n", key, shmid);
    return 0;
}

现在有两个文件,一个客户端,一个服务器,两者通过共享内存进行进程通信。首先通过ftok函数依赖一个文件名与一个id生成一个相同的key值,然后用这个key值调用shmget函数,将共享内存块的大小设置为4096,创建方式为申请一个全新的共享内存块。两个源文件的代码目前是一样的,都是以相同的key值创建共享内内存,运行两个程序。第一个程序创建共享内存成功,第二个创建失败,原因是创建共享内存的方式决定了创建的共享内存必须是全新的,所以第二个程序运行出错。

由此可以推测,共享内存的生命周期不是跟随进程的,因为第一个进程申请了共享内存,但其退出时,共享内存的资源却没有释放,所以第二个程序以相同的key值申请共享内存失败。
在这里插入图片描述
共享内存的生命周期是随内核的,只有内核退出了,共享内存才会释放,也就是说重启系统可以释放申请的共享内存。除了系统的重启,还能手动释放共享内存,使用ipcs -m指令可以查找当前内核中正在使用的共享内存

使用指令ipcrm -m 进程描述符,可以删除指定描述符的共享内存,通过刚才程序的运行结果,我们知道申请的共享内存的shmid为8,所以ipcrm -m 8就可以删除这个共享内存块
在这里插入图片描述

shmctl,可用于共享内存的删除

除了手动执行指令删除共享内存,还能调用系统接口shmctl删除共享内存
在这里插入图片描述
这个接口可以查看或设置共享内存的一些属性,此外还可以用于删除共享内存
在这里插入图片描述

shmid:共享内存块的shmid
cmd:使用该函数的方式
buf:共享内存块的结构体指针

使用shmctl删除共享内存块时,将cmd设置为IPC_RMID,buf设置为nullptr,所以shmctl(shmid, IPC_RMID, nullptr),就能删除一个共享内存块

#include "comm.hpp"
using namespace std;


int main()
{
	// key的创建
    int key = CreatKey();
    // 创建共享内存
    int shmid = shmget(key, 4096, IPC_CREAT | IPC_EXCL);
    if (shmid == -1)
    {
        printf("key: %x, creat fail: %s\n", key, strerror(errno));
        exit(-1);
    }
    // 打印共享内存信息
    printf("key: %x, creat shm: %d\n", key, shmid);
    // 删除共享内存
    shmctl(shmid, IPC_RMID, nullptr);

    return 0;
}

运行程序,由于进程退出前调用shmctl删除共享内存块,所以每次执行程序都可以创建全新的共享内存块,而不会报错。
在这里插入图片描述

shmat && shmdt

虽然进程创建了共享内存,但是进程并不是创建的共享内存的拥有者,该进程只是一个创建者角色,所以要使用共享内存就需要与共享内存关联,有关联的操作肯定也有去关联操作。
在这里插入图片描述
shmat,使进程与共享内存关联

shmid:要关联的共享内存的标识符
shmaddr:共享内存的地址,这个参数在特殊场景下使用,不使用时,传入nullptr即可
shmflg:关联共享内存的方式

对于shmflg,SHM_RDONLY表示以只读的方式关联共享内存,没有以只写的方式关联共享内存的选项,如果要以可读可写的方式关联共享内存,可以将0作为shmflg参数,0表示可读可写
在这里插入图片描述
所以shmat(shmid, nullptr, 0)就可以关联表示符为shmid的共享内存
在这里插入图片描述
shmat返回共享内存块的首地址,由于共享内存块的大小是由我们确定的,所以通过首地址+偏移量的方式,我们就能使用所有的共享内存。当shmat关联失败时,返回-1。

关联共享内存块需要当前进程有相关权限,shmget申请共享内存时可以通过shmflg设置其权限,shmflg为IPC_CREAT | IPC_EXCL表示每次申请的共享内存都是全新的,同时还能添加权限的信息,比如IPC_CREAT | IPC_EXCL | 0600表示该共享内存只对于拥有者有读和写权限。0600是八进制表示权限的方式,在设置文件权限时经常使用,对于共享内存的权限我们也可以这样设置

在这里插入图片描述
对于共享内存的去关联,只需要将关联共享内存时shmat返回的地址作为参数,调用shmdt函数即可。成功返回0,错误返回-1
在这里插入图片描述

// IpcShmCli.cc
int main()
{
	// key的创建
    int key = CreatKey();
    // 创建共享内存,并设置其权限
    int shmid = shmget(key, 4096, IPC_CREAT | IPC_EXCL | 0600);
    if (shmid == -1)
    {
        printf("key: %x, creat fail: %s\n", key, strerror(errno));
        exit(-1);
    }
    
    // 打印共享内存信息
    printf("key: %x, creat shm: %d\n", key, shmid);
    sleep(3);
    // 关联共享内存
    // 由于shmat返回的地址是void*,使用时要注意类型的强转
    char* shmadr = (char*)shmat(shmid, nullptr, 0);
    sleep(3);

    // 去关联共享内存
    shmdt((void*)shmadr);
    sleep(3);
    
    // 删除共享内存
    shmctl(shmid, IPC_RMID, nullptr);

    return 0;
}

复制当前渠道,通过两个窗口观察共享内存的创建,关联,去关联以及删除的过程,ipcm -s 打印信息中的nattch为与共享内存关联的进程数,在当前进程关联之前,nattch为0,进程关联后变为1,去关联后又变为0
在这里插入图片描述

共享内存的使用

当进程与共享内存关联时,实际上就是将物理上的共享内存块地址映射到进程的虚拟地址空间上。这一点与管道不同,管道的本质是一个文件,对于匿名管道,进程只有它的fd标识符,对于命名管道,进程只有它的文件名(或者说inode标识符),说白了,对于管道,进程只拥有标识符,对于共享内存,进程不仅拥有标识符,还拥有共享内存的地址在虚拟空间上的映射。两者的区别就是进程可以直接通过地址访问共享内存,而访问管道却需要通过唯一标识符,调用系统接口(类似read,write接口),在系统的帮助下完成对管道的访问。

所以共享内存的使用很简单,与数组的使用相同,或者说共享内存块本质上也能看成一个数组,由于shmget返回它的首地址,我们就能通过首地址+偏移量,然后解引用的方式使用共享内存。

在shmget函数的说明中有这样一句话在这里插入图片描述
当新的共享内存被创建,它的内容会被用0初始化,这是一个细节点。

// IpcShmSer.cc创建共享内存
#include "comm.hpp"
using namespace std;

int main()
{
    int key = CreatKey();
    // 共享内存的获取
    int shmid = shmget(key, 4096, IPC_CREAT | IPC_EXCL | 0666);
    if (shmid == -1)
    {
        printf("key: %x, creat fail: %s\n", key, strerror(errno));
        exit(-1);
    }
    printf("server creat shm, shmid:%d, key:%d\n", shmid, key);

    // 关联
    char* shmadr = (char*)shmat(shmid, nullptr, 0);
    printf("server attach shm, shmid:%d\n", shmid);

    // 服务器读取数据
    while (true)
    {
        printf("%s\n", shmadr);
        sleep(1);
    }

    // 去关联
    shmdt((void*)shmadr);
    printf("server detach shm, shmid:%d\n", shmid);

    // 共享内存的释放
    shmctl(shmid, IPC_RMID, nullptr);
    printf("delete detach shm, shmid:%d\n", shmid);
    return 0;
}

// IpcShmCli使用共享内存
#include "comm.hpp"
using namespace std;

int main()
{
	// key的创建
    int key = CreatKey();
    // 获取共享内存的id
    int shmid = shmget(key, 4096, IPC_CREAT);
    if (shmid == -1)
    {
        printf("key: %x, creat fail: %s\n", key, strerror(errno));
        exit(-1);
    }
    printf("client creat shm, shmid:%d, key:%d\n", shmid, key);
    
    // 关联共享内存
    char* shmadr = (char*)shmat(shmid, nullptr, 0);
    printf("client attach shm, shmid:%d\n", shmid);

    // 使用
    int cnt = 0;
    while (cnt <= 26)
    {
    	// 客户端写入数据
        shmadr[cnt] = 'A' + cnt;
        cnt++;
        sleep(1);
    }

    // 去关联共享内存
    shmdt((void*)shmadr);
    printf("client detach shm, shmid:%d\n", shmid);

    // 共享内存的释放
    shmctl(shmid, IPC_RMID, nullptr);
    printf("delete detach shm, shmid:%d\n", shmid);
    return 0;
}

(由服务器创建共享内存,客户端使用共享内存,创建共享内存的步骤已经用了大量篇幅讲述,使用共享内存只是在共享内存的关联和去关联中加入代码)

服务器不断地读取shmadr中的数据,而客户端会向shmadr中每隔1秒写入数据。因为shmadr中的数据已经被系统用0初始化过了,所以正常情况下,以%s打印时不用关心字符串的结束。先运行服务器程序,服务器直接开始读取shmadr中的数据,因此显示器上不断被换行符刷新,直到客户端运行,向shmadr中写入数据,显示器才读到这些数据,向显示器打印读取到的数据。以上结果表明,共享内存没有访问控制机制,关联共享内存的进程可以直接看到里面的数据,因为在每个进程看来,共享内存块都是属于自己的一块空间,每个进程可以随时向共享内存写入或读取,这样的访问是无序,不安全的。
在这里插入图片描述
正是由于共享内存的地址被映射到每个进程的虚拟地址空间这一特点,进程可以直接通过地址访问内存,不用像管道需要通过read和write等系统调用接口访问共享资源(使用管道还需要先将数据拷贝到进程的地址空间上,再从地址空间拷贝到管道,而共享内存位于进程的地址空间上,进程可以直接将数据拷贝到共享内存上,这样一步到位的拷贝也比管道快),可以说共享内存的通信速度是所有IPC中最快的

我们将能被多个进程看到的同一份资源称为临界资源,管道,共享内存都是临界资源。如果没有对临界资源进行任何保护,那么多个进程访问临界资源时,会是一种乱序的状态,多个进程的交叉读写可能导致临界资源的乱码,废弃代码等问题。所以使用共享内存时需要与其他技术进行结合,以添加访问控制,保护临界资源。

Linux对system V的设计思路

操作系统描述system V下IPC资源的结构体中含有一个ipc_ids结构体,该结构体中有一个指针entries,指向了一个柔性数组ipc_id_ary,该数组元素存储的数据类型为struct ipc_perm*,暂时不说struct ipc_perm,先了解在system V标准下的三个IPC技术,共享内存,信息队列以及信号量。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
三种IPC资源都有对应的结构体对其进行描述,这三个结构体中有一个相同的成员,struct ipc_perm,存储IPC资源中与权限有关的信息。ipc_id_ary数组中存储就是指向struct ipc_perm结构体的指针,在练习共享内存的使用代码时,我发现了一个现象,就是申请的共享内存块的shmid一直在增加,虽然不知道为什么,但是增长的shmid其实就是数组ipc_id_ary的下标,无论你创建的IPC资源是共享内存还是消息队列,或者是信号量,它们结构体中的struct ipc_perm结构体的地址就会被保存到ipc_id_ary上,shmid作为IPC资源的struct ipc_perm在数组中的下标被返回给用户。

而struct ipc_perm成员作为IPC资源结构体中的第一个成员,其地址与结构体地址相同,通过struct ipc_perm的地址不仅可以访问struct ipc_perm结构体中的数据,还可以将其强转成struct shmid_ds或者struct semid_ds或者struct msqid_ds*,通过强转后的IPC资源结构体地址访问struct shmid_ds,struct semid_ds或者struct msqid_ds中的成员。

也就是说无论struct ipc_perm保存的是共享内存,消息队列还是信号量的权限,只要将其转换成对应IPC资源的指针就能访问其所在结构体的数据。又或者说struct ipc_perm只是一个基类,struct shmid_ds,struct semid_ds以及struct msqid_ds是其派生类,因为它们都具有struct ipc_perm,而通过基类struct ipc_perm的指针的强转就能访问其派生类的数据,这种操作有点像C++中的多态调用,Linux系统通过使用统一的规则(struct ipc_perm)实现对不同IPC资源(struct shmid_ds,struct semid_ds以及struct msqid_ds)的访问,而C++通过对派生类进行切片形成的基类对象,实现对不同函数的调用。

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

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

相关文章

循环中的闭包

目录 1. 什么是闭包&#xff1f;闭包的作用&#xff1f; 1.1 可以访问 外部作用域 中变量的内部函数 1.2 闭包可以访问外部作用域中的变量及传参 2. 异步操作中 变量 的生命周期&#xff0c;取决于 闭包 的生命周期 2.1 Timer 定时器&#xff08;保留到 定时器回调执行完…

阿里内部总结的微服务笔记,从入门到精通,初学者也能学的会

前言 随着互联网的发展&#xff0c;网站应用的规模也在不断的扩大&#xff0c;进而导致系统架构也在不断的进行变化。 一、系统架构演变 从互联网早起到现在&#xff0c;系统架构大体经历了下面几个过程: 单体应用架构--->垂直应用架构--->分布式架构--->SOA 架构-…

Flutter 单元测试例子

Flutter 单元测试例子 原文 https://medium.com/app-dev-community/flutter-unit-testing-with-simple-examples-9c07499e4079 前言 执行单元测试来验证软件的每个组件。因此&#xff0c;我们需要尽可能多地测试每个单独的微 widget 。这些都是由开发人员在开发阶段完成的。单元…

回归分析(2) 一元回归模型

如上所述&#xff0c;为了易于确定回归函数μ(x)中的未知参数&#xff0c;我们来讨论变量Y与x之间存在着线性相关关系的情形 散布在某一条直线的周围&#xff0e;于是&#xff0c;我们可以用线性回归方程 来描述Y与x之间的相关关系&#xff0c;并假设相应的误差&#xff08;称为…

Go-zero框架学习+xorm+gorm配置

Go-zero框架学习xormgorm配置 文章目录Go-zero框架学习xormgorm配置一、框架介绍二、go-zero快速搭建1.下载go-zero2.安装goctl3.项目初始化4.测试5.项目结构6.快速生成rpc服务7.快速添加api接口8.快速生成model服务8.快速生成Dockerfile9.快速生成K8s部署文件三.golang的ORM框…

[go学习笔记.第十七章.redis的使用] 1.redis的使用

1.redis基本介绍 (1).Redis 是 NoSQL 数据库&#xff0c;不是传统的关系型数据库,官网: https://redis.io/ 和http://redis.cn/ (2).Redis: REmote Dlctionary Sever&#xff08;远程字典服务器&#xff09;, Redis 性能非常高&#xff0c;单机能够达到 15w qps,通常适合做缓存…

刷爆力扣之盛最多水的容器

刷爆力扣之盛最多水的容器 HELLO&#xff0c;各位看官大大好&#xff0c;我是阿呆 &#x1f648;&#x1f648;&#x1f648; 今天阿呆继续记录下力扣刷题过程&#xff0c;收录在专栏算法中 &#x1f61c;&#x1f61c;&#x1f61c; 该专栏按照不同类别标签进行刷题&#xff…

安卓的分区一点有用知识:super、lpunpack、lpdump

我们知道这个安卓的镜像分区有很多个。 那么这个文章要介绍什么呢&#xff1f; 三个点&#xff1a; 一是现在的android支持动态分区&#xff0c;很多的东西都被放到super分区里面了&#xff0c;这个应该是可以配置的。然后super里面有比如system、vendor这种比较大的分区。那…

教务排课系统毕业设计,大学排课系统设计与实现,排课系统论文作品参考

功能清单 【后台管理员功能】 录入分院&#xff1a;录入分院名称&#xff0c;简介&#xff0c;详情 分院管理&#xff1a;管理已经录入分院&#xff0c;支持修改和删除 老师录入&#xff1a;录入老师姓名、联系方式、头像、老师简介 老师管理&#xff1a;管理所有已经录入老师…

基于Matlab模拟用于海况海洋学研究的 X 波段雷达系统(附源码)

目录 一、定义雷达系统参数 二、对海面进行建模 三、配置雷达收发器 四、生成数据多维数据集 五、处理海面回波 六、总结 七、程序 海事雷达系统在充满挑战的动态环境中运行。为了改进对感兴趣目标的检测并评估系统性能&#xff0c;必须了解海面返回的性质。 在本例中&a…

【操作系统】2.4 死锁

这一节也非常重要 2.4.1 死锁的概念 2.4.1 死锁的概念_StudyWinter的博客-CSDN博客 在并发环境下&#xff0c;各种进程因竞争资源而造成的一种互相等待对方手里的资源&#xff0c;导致各进程都阻塞&#xff0c;都无法向前推进的现象。这就是死锁&#xff0c;死锁发生后&#…

Jest API使用方法

如上面的知识图谱所示&#xff0c;一个常见的测试框架通常需要实现这些功能: ● before/after 钩子函数: 如beforeEach&#xff0c;afterEach&#xff0c; ● Mock方法&#xff1a; 函数Mock&#xff0c;时间mock等。 ● 断言: 判断一个描述是否正确&#xff0c;在Jest中常为 e…

你心心念念的RabbitMQ个人实践来了来了它来了

前言 MQ&#xff08;Message Queue&#xff09;就是消息队列&#xff0c;其有点有很多&#xff1a;解耦、异步、削峰等等&#xff0c;本文来聊一下RabbitMQ的一些概念以及使用。 RabbitMq 案例 Springboot整合RabbitMQ简单案例 基本概念 Exchange&#xff1a;消息交换机&a…

云原生系列 【基于CCE Kubernetes编排实战二】

✅作者简介&#xff1a; CSDN内容合伙人&#xff0c;全栈领域新星创作者&#xff0c;阿里云专家博主&#xff0c;阿里云问答板块版主&#xff0c;华为云享专家博主&#xff0c;掘金后端评审团成员 &#x1f495;前言&#xff1a; 最近云原生领域热火朝天&#xff0c;那么云原生…

Hystirx限流:信号量隔离和线程池隔离

背景&#xff1a; 最近工作中要处理服务高并发的问题&#xff0c;大流量场景下限流熔断降级可以说是必不可少的&#xff0c;打算对限流做一次改造&#xff0c;所以要先了解一下hytrix相关内容&#xff0c;比如了解一下线程池隔离和信号量隔离的区别。 **信号量&#xff1a;**信…

[网络工程师]-应用层协议-DHCP

BOOTP是最早的主机配置协议&#xff0c;动态主机配置协议&#xff08;Dynamic Host Configuration Protocol&#xff0c;DHCP&#xff09;则是在其基础上进行了改良的协议&#xff0c;是一种用于简化主机IP配置管理的IP管理标准。通过DHCP协议&#xff0c;DHCP服务器为DHCP客户…

集合学习笔记——Collection 全家桶

Collection是我们日常开发中使用频率非常高的集合&#xff0c;它的主要实现有List和Set,区别是List是有序的&#xff0c;元素可以重复;Set是无序的&#xff0c;元素不可以重复&#xff0c;我们简单看下继承关系&#xff1a; List的实现类主要线程不安全的ArrayList和LinkedList…

推挽输出和开漏输出-三极管-mos管

一、推挽输出 1.1推挽输出的概念 推挽&#xff08;push-pull&#xff09;输出是由两个MOS或者三极管组成&#xff0c;两个管子始终保持一个导通&#xff0c;另一个截止的状态。 图1 推挽电路示意图 当输入高电平时&#xff0c;叫做推&#xff1b; 上管Q1导通&#xff0c;下管…

【目标检测】Faster R-CNN论文的讲解

目录&#xff1a;Faster R-CNN论文的讲解一、前言二、回顾Fast R-CNN三、引入Faster R-CNN四、Faster R-CNN的介绍4.1 框架结构4.2 RPN如何产生候选区域的4.3 损失函数4.4 训练候选框提取网络4.5 RPN和Fast R-CNN共享特征的方法4.5.1 交替训练法4.5.2 近似联合训练法一、前言 …

C语言——学生信息管理系统

目录 功能展示 界面展示 所有功能模块&#xff1a; 功能1&#xff1a;菜单模块&#xff08;显示功能菜单&#xff09; 功能2&#xff1a;增加学生信息 功能3&#xff1a;输出学生信息&#xff08;查看所有学习信息&#xff09; 功能4&#xff1a;修改学生信息 功能5&a…