Linux:共享内存

news2024/11/20 1:47:37

文章目录

  • System V
  • 共享内存的原理
  • 管理共享内存
    • shmget
    • shmat
    • shmdt
    • shmctl
  • 共享内存和管道实现进程间同步通信

前面介绍完了匿名管道和命名管道,那么本篇要引入的主题是共享内存

System V

作为进程通信部分的内容,共享内存必然有其存在的意义和价值,例如对于网络来说,有了对应的服务端和客户端,那么在服务端中有了一个用户发送的消息,这些消息都被放到了管道中,之后经过加工后,要不然会选择把信息放到数据库中,要不然会选择把内容返回到上层,这是管道的作用,而对于System V版本的共享内存来说,也有它自己的作用

对于操作系统来说,通信的场景是有很多很多种的,有的是要传输数据为目的,有的是要以传输特定数据块为目的,也有的是以进程之间进行协同控制为目的,但是不管出于什么场景,只要是通信,那么就必然意味着要有固定的通信方式,就会有对应的接口参数返回值等等,再基于一定的格式进行统一,打包整体就叫做进程之间的通信,那么现在问题是,统一的格式是什么?哪里有格式的问题呢?为了解决这样的问题,有专门的人去定标准,再去基于这些标准进行具体的视线,实现出的这通信的模式就叫做System V,也叫做系统V,所以说对于这个模式来说有很多的通信方式,例如有共享内存,消息队列,信号量等等诸多方式,但是不管是什么模式,它们的接口,参数返回值,都是基于一定的标准来实现的,具有一定的相似性,这样对于使用者来说是比较方便的

所以接下来本篇介绍的内容就是这个System V模式下的第一种通信方式–共享内存

共享内存的原理

共享内存是基于通信的目的的,那么通信的本质是让不同的进程看到同一份资源,那么现在就要将注意点转移到同一份资源这个角度,这个资源不能是和进程挂钩,必须是操作系统与来提供的,所以在前面的管道中,不管是匿名管道还是命名管道,其实都是操作系统提供的,包括文件缓冲区和文件结构体,所以对于共享内存来说也是一样的道理,首先要创建出一个资源,其次是要让不同的进程看到这份资源,这就是共享内存的基本原理

在这里插入图片描述
上图是前面已经讲述过很多次的一个逻辑,对于共享内存也会从这里入手进行讲解,每一个进程都有自己的进程控制块,也有自己的地址空间,这个进程地址空间最后会根据页表映射到物理内存上,而又由于缺页中断申请内存这样的机制存在,所以说在代码中申请内存的时候,其实真正的物理内存没有进行开辟,而是在地址空间中开辟好,当首次尝试访问这块空间的时候,再去触发缺页中断这个机制,来在对应的物理内存中进行开辟内存,从中也能侧面看出,操作系统具有直接在内存中申请空间的能力

所以想要实现通信的第一步已经实现了,操作系统已经在物理内存中给进程开辟好了一块资源,可以等待它们使用,第二步要通信,起码是需要两个进程,在有了进程之后,就会在堆栈之间开辟一块区域,这块区域用来实现进程之间的通信

堆栈之间的这块共享区也不是第一次接触了,在之前的动静态库中就有过提及,动态库的加载就是加载到内存中,然后映射到堆栈之间的共享区中,进行加载库的这个过程,本质上就是把物理内存中的库数据映射到堆栈之间,只不过这里操作系统做的是在物理内存中开辟一块空间,而在逻辑地址的堆栈之间开辟的是一块新的空的空间,里面没有任何数据,而加载库的过程中这块区域是有空间的

在这里插入图片描述
在操作系统中,在物理内存中已经申请好了一块空间,并且也映射到了共享区中,那么这个共享区的起始地址也就已经知晓,所以在上层用户就可以调用这块空间,直接对这块空间进行数据的写入等等操作

关于用户空间和内核空间

那为什么说,有了这块起始地址,就能直接访问申请的内存了呢?其实原因就在于,有起始地址,并且整个空间有多大也是清楚的,那么未来就可以通过指针的方式,向对应的缓冲区中写数据,数据就会通过页表映射到对应的物理内存中,那在操作系统内部,是如何用地址空间对于物理内存进行访问?虽然现在已经有了虚拟地址,并且也有页表进行自动转换,但是为什么有地址就可以直接访问呢?原因就在内存中的用户空间,对于管道文件,它其实是属于内核数据结构,那么所有的缓冲区文件的属性都是会在这个地址空间的这个内核区域内,想要访问就必须调用对应的系统调用,而现在创建的这个堆栈之间的共享区,是属于用户空间的,用户空间是可以直接访问的,不用对应的系统调用就可以访问,相当于是有了一块内存空间,支持随机访问

在这里插入图片描述

所以现在进程就和共享内存之间建立了对应的映射关系,只要创建好内存,让当前进程把内存块映射到自己的地址空间中,那此时如果有另外一个进程,想要实现进程的通信,就也要进行相同的操作来进行映射,此时这个新的进程也会获得一个虚拟地址,对于这两个进程来说,它们的虚拟地址可以相同也可以不同,到此,这两个进程就都可以使用各自地址空间内的虚拟地址,借助页表来对物理内存中进行访问,相当于是间接的借助地址空间访问同一块内存空间,这样就通过共享内存的原理达到了进程间通信的前提,叫做让不同的进程看到同一份资源

共享内存的本质,在系统层面上把内存申请好,再映射到两个进程的地址空间中,映射结束之后,此时只需要把映射在虚拟地址中的起始地址返回给用户,用户就可以通过起始地址进行访问了

谈谈释放的问题

对于共享内存的释放,只需要把虚拟地址和物理地址之间的这层关系取消掉就可以了,具体的实操来说,就是把页表清空就可以,清空了页表,虚拟空间的所有地址就都失去了对应的意义,这也就是为什么说,所有的地址的概念,都是建立在有页表的基础上,如果没有页表,所有的地址其实都没有多大的意义,而对应与malloc或者是new申请的内存,也都是在页表上申请的,而物理内存并不需要立刻映射,而是在访问的时候再借助缺页中断来填充

管理共享内存

综合上述的内容,可以得出的一个结论是,在操作系统中,一定会存在多个共享内存被创建,在操作系统中会有很多个共享内存,那操作系统当然需要对于共享内存进行管理,那管理的前提是要描述,所以在操作系统内部一定会存在管理共享内存的概念,于是就有了下面的话题:管理共享内存

在管理之前,要有的第二个概念是,对于这块空间的识别问题,如何保证两个想要通信的进程可以识别到同一块资源呢?说明共享内存被创建出来之后,一定是具有一定的识别能力,有它独特的标识,才能让另外一个进程能够找到这块内存,进而进行后面的通信工作,所以下面就要研究这个识别的东西到底是什么

  1. 标识由什么组成?
  2. 怎么传递给另外一个进程?

Linux在内部提供了很多的接口,那么就要对于这些接口进行一定的认识了

shmget

在这里插入图片描述
这个命令就是创建共享内存的命令,对于参数的解析来说,抛开最前面的key值,对于第二个参数size来说,这个参数的意思是要开辟的共享内存有多大,函数的返回值会返回一个标识符,也就是内存标识符,如果创建失败会返回-1,并且会设置错误码,第三个参数是选项,不再多说,主要是介绍有两个选项

在这里插入图片描述
第一个选项的意思是,如果这个shm不存在就创建,存在就获取,并且返回
第二个选项不会单独使用,它一般会和第一个选项组合起来使用,表示的意思是,如果shm不存在就创建,存在就会提示出错并且返回,这样的意义是可以保证每次申请的共享内存都是全新的内存

这个内存标识符实际上就是前面所说的识别问题,这个整数的作用就有些类似于文件描述符的作用,在文件的接口中,都是靠这个文件描述符来工作,同理,在共享内存的接口中也是根据这个值来运转的

key值的问题

对于key值是多少,其实没有一个明确的标准,想写多少就写多少,在创建共享内存的时候,只要让通信的这两个进程之间约定一个数字,把这个数字作为标记符,那么在创建共享内存的时候,就会把这个数字表示写到共享内存的属性中,未来另外一个进程只需要在创建好的这些共享内存中去寻找这个标识符,就能找到前面的这个共享内存,进而就可以进行通信了

这个数字的取值是有讲究的,如果出现两个共享内存的key值相同,那必然是会出现严重的问题,所以对于这个key值的取值需要做足准备,而在接口中当然是有对应的解决措施的,这个函数就叫做ftok函数:

在这里插入图片描述
这个函数的作用就是专门用来,把一个地址和id转换成一个key值,所以借助这个东西就能生成一个不错的key值,以来区分内存

所以此时,对于创建共享内存的这个接口的参数就都介绍结束了,下面就实践一下,创建一个共享内存:

int main()
{
    int key = ftok("linux-system-and-network/shm", 1234);
    int shmid = shmget(key, 4096, IPC_CREAT | IPC_EXCL);
    // 创建失败就返回
    if (shmid < 0)
    {
        std::cerr << "errno: " << errno << ", errstring: " << strerror(errno) << std::endl;
        exit(2);
    }
    cout << "create success" << endl;
    return 0;
}

运行后,就会创建出一个共享内存了!

在这里插入图片描述
但是,如果再次异常会出错,提示现在已经有共享内存了,这说明一个结论,共享内存和文件不一样,打开的文件的生命周期是随进程的,进程结束这个打开的文件生命周期也就结束了,而共享内存是在System V当中单独设计出的用来进阶通信的方案,这个方案的特点是,共享内存必须让用户主动释放,也就是说如果不释放,这个内存就会一直存在,所以这里的结论是,共享内存以及未来的和通信有关的这些资源,和普通文件是不一样的,除非手动关闭,否则会一直存在

查看共享内存信息

ipcs -m

在这里插入图片描述
删除共享内存信息

ipcrm -m [shmid]

权限问题

在创建的时候,也可以在选项中带上权限的选项:

int main()
{
    int key = ftok("linux-system-and-network/shm", 1234);
    int shmid = shmget(key, 4096, IPC_CREAT | IPC_EXCL | 0666);
    // 创建失败就返回
    if (shmid < 0)
    {
        std::cerr << "errno: " << errno << ", errstring: " << strerror(errno) << std::endl;
        exit(2);
    }
    cout << "create success" << endl;
    return 0;
}

在这里插入图片描述

开辟空间大小的问题

正常来说,开辟空间是可以随意设置的,但是建议开辟大小设置成4096字节,原因在于,在操作系统申请内存的时候,是以4096为单位进行申请的,也就是说,如果申请大小为4097,实际上申请的是两个4096字节,只不过是用户层面上只用了4097个字节,即便未来被占用了,也不会进行分配,所以未来在越界方面可能会有异常,因此建议是以4096的整数倍进行大小的分配

shmat

在这里插入图片描述
这个命令可以将指定的共享内存挂接到自己的地址空间中

对于第二个参数shmaddr来说,这个shmaddr默认设置成nullptr就可以了,这个参数的意义是如果想要手动把共享内存挂接地址中的一个指定起始地址处,但是如果对于地址空间的不了解的情况下,直接传参传nullptr就可以了,让操作系统来进行选择就可以了

对于第三个参数shmflag来说,这个选项代表的是挂接到共享内存中的对应方式,这个不需要进行管控,因为在创建的时候就已经有权限来进行控制了,直接设置成0就可以了

对于返回值来说,如果挂接成功后,进程就会凭空多出来一块空间,有点类似于c语言中的malloc函数,所以在用法上也和它一样,需要进行强转成需要的类型

下面做出下面的实验:

int main()
{
    int key = ftok("linux-system-and-network/shm", 1234);
    int shmid = shmget(key, 4096, IPC_CREAT | IPC_EXCL | 0666);
    // 创建失败就返回
    if (shmid < 0)
    {
        std::cerr << "errno: " << errno << ", errstring: " << strerror(errno) << std::endl;
        exit(2);
    }
    cout << "create success" << endl;
    // 创建成功后就挂接到当前进程
    cout << "开始挂接" << endl;
    sleep(2);
    char *s = (char*)shmat(shmid, nullptr, 0);
    cout << "进程退出" << endl;
    sleep(2);
    return 0;
}

在这里插入图片描述
从中看出这也确实完成了初步的预期,在进程退出的时候会删除页表,页表清空后就随之解除了映射关系,这样共享内存也就随之没有关联的必要了,所以这里关联数就减去1即可,这里可以类比是一种引用计数,但是又不完全是,因为不会伴随着减到0而自动释放这段空间,所以只能算是继承了这样的一种思想

shmdt

在这里插入图片描述
这个命令的意思是去关联,其实也就是和上述的函数意思相反,要不然是在特定的空间内取消映射关系,最终的参数就是获得共享内存的起始地址,其实也就是上面这个函数的返回值,就可以用做这个函数的参数

如何理解这个过程呢?其实可以从关联的角度来讲,关联的角度就是修改页表,所以,只需要找到虚拟地址所对应的起始地址就可以了,虚拟地址的起始地址知道,并且共享内存的大小也知道,所以就可以从共享内存的起始地址释放空间,解除对应空间大小的关联关系,那么在这样的基础下,也就将整个的挂接关系都去掉了,所以就实现了解除关联的效果

int main()
{
    int key = ftok("linux-system-and-network/shm", 1234);
    int shmid = shmget(key, 4096, IPC_CREAT | IPC_EXCL | 0666);
    // 创建失败就返回
    if (shmid < 0)
    {
        std::cerr << "errno: " << errno << ", errstring: " << strerror(errno) << std::endl;
        exit(2);
    }
    cout << "create success" << endl;
    // 创建成功后就挂接到当前进程
    cout << "开始挂接" << endl;
    char *s = (char*)shmat(shmid, nullptr, 0);
    sleep(2);

    cout << "解除关联" << endl;
    shmdt(s);
    sleep(1);
    cout << "进程退出" << endl;
    sleep(2);
    return 0;
}

现象也比较简单,这里就不再展示了

shmctl

在这里插入图片描述
这个接口的作用就是删除指定的共享内存

在对于这个函数了解前,先从源码看一下共享内存:

在这里插入图片描述
在这里插入图片描述
这是在内核中关于共享内存的描述,上面的这个struct shmid_ds结构体中包含了的内容就有,挂接的时间,最后使用时间等等信息,而在其中的这个struct ipc_perm,其实描述的就是关于共享内存的属性信息,例如有key值和多种id的属性,而操作系统也是用这些信息来对共享内存进行管理的,操作系统对于共享内存的管理,就转换成了对于这些数据结构的管理

其实在前面用到的例如有ipcs -m命令或者是其他的删除命令,从本质上来说就是从系统中获取已经被创建的共享内存的属性,例如有共享内存的大小,最近的使用时间,有多少个挂接数,权限等等,都是从这里来的

那转回到我们的这个函数,对于这个函数来说,第一个参数不多解释,第二个参数一般是IPC_RMID,大致意思就可以理解为是立即删除的意思,第三个参数是一个结构体,这里暂时先设置为nullptr,未来有使用场景再继续补充

共享内存和管道实现进程间同步通信

有了上面的基础,对于代码进行简单的封装,可以得到如下的成品

// comm.hpp
#pragma once
#include <iostream>
#include <cstdlib>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

const std::string pathname = "/home/test/linux-system-and-network/shm";
const int proj_id = 0x11223344;
const int size = 4096;
const std::string filename = "fifo";

// 利用路径名和特定id获取key值
key_t GetKey()
{
    key_t key = ftok(pathname.c_str(), proj_id);
    if (key < 0)
    {
        std::cerr << "errno: " << errno << ", errstring: " << strerror(errno) << std::endl;
        exit(1);
    }
    return key;
}

// 将十进制转换成十六进制
std::string ToHex(int id)
{
    char buffer[1024];
    snprintf(buffer, sizeof(buffer), "0x%x", id);
    return buffer;
}

// 按照特定选项创建对应共享内存
int CreateShmHelper(key_t key, int flag)
{
    int shmid = shmget(key, size, flag);
    if (shmid < 0)
    {
        std::cerr << "errno: " << errno << ", errstring: " << strerror(errno) << std::endl;
        exit(2);
    }

    return shmid;
}

// 以644权限创建共享内存
int CreateShm(key_t key)
{
    return CreateShmHelper(key, IPC_CREAT | IPC_EXCL | 0644);
}

// 获取共享权限的shmid
int GetShm(key_t key)
{
    return CreateShmHelper(key, IPC_CREAT);
}

// 创建一个命名管道
bool MakeFifo()
{
    int n = mkfifo(filename.c_str(), 0666);
    if (n < 0)
    {
        std::cerr << "errno: " << errno << ", errstring: " << strerror(errno) << std::endl;
        return false;
    }

    std::cout << "mkfifo success... read" << std::endl;
    return true;
}
// client.cc
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include "comm.hpp"

int main()
{
    // 利用key值获取共享内存并挂接到程序共享区
    key_t key = GetKey();
    int shmid = GetShm(key);
    char *s = (char *)shmat(shmid, nullptr, 0);
    std::cout << "attach shm done" << std::endl;
    int fd = open(filename.c_str(), O_WRONLY);

    // 向共享内存中写入信息,并利用管道形成同步
    sleep(5);
    for (char c = 'a'; c <= 'z'; c++)
    {
        s[c - 'a'] = c;
        std::cout << "write : " << c << " done" << std::endl;
        sleep(1);
        int code = 1;
        write(fd, &code, sizeof(4));
    }

    // 对共享内存解除挂接,并释放管道
    shmdt(s);
    std::cout << "detach shm done" << std::endl;
    close(fd);
    return 0;
}
// server.cc
#include <iostream>
#include <cstring>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <unistd.h>
#include "comm.hpp"

// 对共享内存和管道的初始化以及释放工作
class Init
{
public:
    Init()
    {
        bool r = MakeFifo();
        if (!r)
            return;

        key_t key = GetKey();
        std::cout << "key : " << ToHex(key) << std::endl;
        sleep(3);
        shmid = CreateShm(key);
        std::cout << "shmid: " << shmid << std::endl;

        sleep(10);
        std::cout << "开始将shm映射到进程的地址空间中" << std::endl;

        s = (char *)shmat(shmid, nullptr, 0);
        fd = open(filename.c_str(), O_RDONLY);
    }
    ~Init()
    {
        sleep(5);
        shmdt(s);
        std::cout << "开始将shm从进程的地址空间中移除" << std::endl;

        sleep(5);
        shmctl(shmid, IPC_RMID, nullptr);
        std::cout << "开始将shm从OS中删除" << std::endl;

        close(fd);
    }

public:
    int shmid;
    int fd;
    char *s;
};

int main()
{
    // 创建共享内存和管道
    Init init;
    sleep(5);

    // 从共享内存中读取信息
    while (true)
    {
        // wait
        int code = 0;
        ssize_t n = read(init.fd, &code, sizeof(code));
        if (n > 0)
        {
            std::cout << "共享内存的内容: " << init.s << std::endl;
            sleep(1);
        }
        else if (n == 0)
        {
            break;
        }
    }

    sleep(10);
    return 0;
}

从上面的代码中可以看出,在进行进程间通信的时候,使用了一个命名管道,那这个命名管道的作用是什么呢?

这就要涉及到共享内存的同步机制了,共享内存本身是不会存在同步机制的,所以加装一个管道,可以造成的效果就是向共享内存写入数据后,必须要另外一个进程得到了共享内存的信息后,再得到管道的信息,这样就能借助管道的同步性,使得共享内存也有了一个模拟的同步机制

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

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

相关文章

RabbitMQ快速实战

目录 什么是消息队列&#xff1f; 消息队列的优势 应用解耦 异步提速 削峰填谷 总结 主流MQ产品特点比较 Rabbitmq快速上手 创建用户admin Exchange和Queue Connection和Channel RabbitMQ中的核心概念总结 什么是消息队列&#xff1f; MQ全称Message Queue&#xf…

从零学习Linux操作系统 第二十二部分 企业域名解析服务的部署及安全优化

# 一、dns的主要信息 关于dns的名词解释&#xff1a;dns: domain name service(域名解析服务) 关于客户端: /etc/resolv.conf dns指向文件 A记录 ##ip地址叫做域名的Address 记录 SOA ##授权起始主机 关于服务端 bind安装包named服务名称/etc/named.conf主配置文件/var/na…

【深度学习:多关节嵌入模型】 Meta 解释的 ImageBind 多关节嵌入模型

【深度学习&#xff1a;多关节嵌入模型】 Meta 解释的 ImageBind 多关节嵌入模型 Meta 发布开源人工智能工具的历史分段任何模型DINOv2 什么是多模态学习&#xff1f;什么是嵌入&#xff1f;什么是 ImageBind&#xff1f;集成在 ImageBind 中的模式图像绑定架构特定模式编码器跨…

window下如何安装ffmpeg(跨平台多媒体处理工具)

ffmpeg是什么? FFmpeg是一个开源的跨平台多媒体处理工具&#xff0c;可以用于录制、转换和流媒体处理音视频。它包含了几个核心库和工具&#xff0c;可以在命令行下执行各种音视频处理操作&#xff0c;如剪辑、分割、合并、媒体格式转换、编解码、流媒体传输等。FFmpeg支持多…

java设计模式:工厂模式

1&#xff1a;在平常的开发工作中&#xff0c;我们可能会用到不同的设计模式&#xff0c;合理的使用设计模式&#xff0c;可以提高开发效率&#xff0c;提高代码质量&#xff0c;提高系统的可拓展性&#xff0c;今天来简单聊聊工厂模式。 2&#xff1a;工厂模式是一种创建对象的…

Java TemporalAdjusters 时间调节器

提供了非常多处理日期相关的函数&#xff1a; 使用示例&#xff1a; /*** JCccc* param args*/public static void main(String[] args) {DateTimeFormatter pattern DateTimeFormatter.ofPattern("yyyy-MM-dd");LocalDateTime now LocalDateTime.now();//获取当月…

备战蓝桥杯---二分(入门)

话不多说&#xff0c;先来个模板题来回顾一下上次讲的&#xff1a; 下面是AC代码&#xff1a; 下面进入正题&#xff1a; 本题对1&#xff0c;2行与3&#xff0c;4行组合&#xff0c;再用二分查找即可实现n^2logn的复杂度。 下面是AC代码&#xff1a; 接题&#xff1a; 让我们…

基于springboot校园交友网站源码和论文

随着信息技术和网络技术的飞速发展&#xff0c;人类已进入全新信息化时代&#xff0c;传统管理技术已无法高效&#xff0c;便捷地管理信息。为了迎合时代需求&#xff0c;优化管理效率&#xff0c;各种各样的管理系统应运而生&#xff0c;各行各业相继进入信息管理时代&#xf…

QT学习日记 | QT的环境搭建

目录 前言 一、QT概述 二、QT的环境搭建 1、QT SDK安装 2、环境变量的配置 前言 本系列为小编新开的一个系列&#xff0c;主要记录小编学习QT的过程&#xff0c;作为笔记仅供各位参考&#xff1b; 一、QT概述 Qt是一个跨平台C图形应用界面框架&#xff1b;简单来说&#x…

Android 13.0 SystemUI下拉状态栏定制二 锁屏页面横竖屏时钟都居中功能实现二

1.前言 在13.0的系统rom定制化开发中,在关于systemui的锁屏页面功能定制中,由于在平板横屏锁屏功能中,时钟显示的很大,并且是在左旁边居中显示的, 由于需要和竖屏显示一样,所以就需要用到小时钟显示,然后同样需要居中,所以就来分析下相关的源码,来实现具体的功能 如图…

C++:异常体系

异常体系 异常1.C语言传统的处理错误的方式2.C异常概念3.异常的使用3.1异常的抛出和捕获3.2 异常的重新抛出3.3异常安全3.4 异常规范 4.C标准库的异常体系5.异常的优缺点 异常 1.C语言传统的处理错误的方式 终止程序&#xff0c;如assert&#xff0c;缺陷&#xff1a;用户难以…

鸿蒙 ArkTs初识

前提&#xff1a;基于官网3.1/4.0文档。参考官网文档 基于Android开发体系来进行比较和思考。&#xff08;或有偏颇&#xff0c;自行斟酌&#xff09; 吐槽&#xff1a;官网上的案例只有代码和文档解释&#xff0c;没有可以直接运行查看效果的模拟器&#xff0c;这一点上&#…

Qt|大小端数据转换

后面打算写Qt关于网络编程的博客&#xff0c;网络编程就绕不开字节流数据传输&#xff0c;字节流数据的传输一般是根据协议来定义对应的报文该如何组包&#xff0c;那这就必然牵扯到了大端字节序和小端字节序的问题了。不清楚的大小端的可以看一下相关资料&#xff1a;大小端模…

【RTP】webrtc 学习3: webrtc对h264的rtp解包

rtp_rtcp\source\video_rtp_depacketizer_h264.cc【RTP】webrtc 学习2: webrtc对h264的rtp打包 中分析了打包过程的代码,这样再来看解析过程的源码就容易多了:本代码主要基于m79,m98类似。这里注明了jitterbuffer 会再次 做 解析stap-a 变为NAL units解析ParseFuaNalu 第一…

Jmeter 分布式测试

Jmeter单机进行压测&#xff0c;受到单台机器的性能影响&#xff0c;Jmeter支持分布式测试&#xff0c;用一个控制节点去控制多个工作节点去模拟更多的用户。 版本信息 内容版本号JDK1.8Jmeter5.6.2 分布式测试原理 jmeter 官网对分布式测试有说明&#xff0c;jmeter分布式…

RabbitMQ 死信交换机的详述➕应用

&#x1f973;&#x1f973;Welcome 的Huihuis Code World ! !&#x1f973;&#x1f973; 接下来看看由辉辉所写的关于RabbitMQ的相关操作吧 目录 &#x1f973;&#x1f973;Welcome 的Huihuis Code World ! !&#x1f973;&#x1f973; 一.什么是死信交换机 二. 死信队列…

说说你对vue的mixin的理解,有什么应用场景

mixin是什么 Vue中的mixin 局部混入全局混入注意事项: 使用场景源码分析Vue 的几种类型的合并策略 替换型合并型队列性叠加型小结 此文章&#xff0c;来源于印客学院的资料&#xff0c;这里只是分享&#xff0c;便于查漏补缺。 mixin是什么 Mixin 是 面向对象程序设计语言中…

一文理清楚-Docker 容器如何工作

Docker 容器如何工作 集装箱什么是虚拟机&#xff1f;虚拟化如何运作&#xff1f;什么是容器&#xff1f;什么是 Docker&#xff1f;总结 五星上将麦克阿瑟曾经说过&#xff1a;在docker面前&#xff0c;虚拟机就是个弟弟 集装箱 《盒子&#xff1a;集装箱如何让世界变得更小&…

车载电子电器架构 —— 多核处理器刷写策略

车载电子电器架构 —— 多核处理器刷写策略 我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 屏蔽力是信息过载时代一个人的特殊竞争力&#xff0c;任何消…

内存管理(mmu)/内存分配原理/多级页表

1.为什么要做内存管理&#xff1f; 随着进程对内存需求的扩大&#xff0c;和同时调度的进程增加&#xff0c;内存是比较瓶颈的资源&#xff0c;如何更好的高效的利于存储资源是一个重要问题。 这个内存管理的需求也是慢慢发展而来&#xff0c;早期总线上的master是直接使用物…