Linux的命名管道 共享内存

news2025/1/10 15:59:01

目录

命名管道

mkfifo函数

unlink函数

命名管道类

服务端

客户端

共享内存

shmget函数

ftok函数

key和shmid的区别

snprintf函数 

ipcs指令 

ipcrm指令

shmctl函数

shmat函数 

void*做返回值

创建共享内存空间

服务端

客户端 


命名管道

基本概念:用于实现进程间通信的有名管道,让不同的进程看到同一份资源

注意事项:

1、创建时需要提供文件的路径

2、命名管道在文件系统中作为一个特殊的文件而存在,但命名管道中的内容却存放在内存中,向命名管道中写入的数据不会刷新到磁盘中

mkfifo函数

函数原型:int mkfifo(const char *pathname, mode_t mode);

包含头文件: <sys/types.h>  和  <sys/stat.h>

参数:指定文件路径,创建文件时的权限

返回值:成功创建返回0,创建失败返回-1

功能:在文件系统中创建一个命名管道文件,看起来是文件但其实是管道

注意事项:管道文件在磁盘上有文件名和路径,但不占用磁盘空间来存储数据

unlink函数

函数原型:int unlink(const char *pathname);

包含头文件:<unistd.h>

参数:要删除的文件的路径

返回值:成功删除返回0,删除失败返回-1

功能:删除指定的文件

注意事项:若文件的硬链接计数为零,则释放该文件占用的磁盘空间(但是命名管道文件不占用磁盘)

命名管道类

功能负责管道的创建和销毁

/*
 * @Author: error: error: git config user.name & please set dead value or install git && error: git config user.email & please set dead value or install git & please set dead value or install git
 * @Date: 2024-05-12 13:57:58
 * @LastEditors: error: error: git config user.name & please set dead value or install git && error: git config user.email & please set dead value or install git & please set dead value or install git
 * @LastEditTime: 2024-05-14 20:39:56
 * @FilePath: /2024.5.8/home/ubuntu/2024.5.12/namePipe.hpp
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
#pragma once

#include <iostream>
#include <cstdio>
#include <cerrno>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

const std::string comm_path = "./myfifo"; // 提供文件的路径(相当于在当前目录下生成一个叫myfifo的命名管道),string字符串在使用时需要转换为c类型的字符串

#define DefaultFd -1 // 将-1用于标识管道的默认fd(有进程打开管道时再分配新的fd)

#define Creater 1 // 将1用于标识管道的申请者
#define User 2    // 将2用于标识管道的使用者

#define Read O_RDONLY  // 将Read用于标识只读方式
#define Write O_WRONLY // 将Write用于标识只写方式

#define BaseSize 4096 // 将4096作为读取内容的最大字节数

class NamePiped
{
private:
    bool OpenNamedPipe(int mode) // 依据对管道的操作方式
    {
        _fd = open(_fifo_path.c_str(), mode); // 每个进程打开管道时都会获得OS分配给该进程得文件描述符
        if (_fd < 0)
            return false; // 打开文件失败返回false
        return true;      // 打开文件成功返回true
    }

public:
    NamePiped(const std::string &path, int who) // 依据文件路径和身份信息构建管道
        : _fifo_path(path), _id(who), _fd(DefaultFd)
    {
        if (_id == Creater) // 只有申请者才能创建管道
        {
            int res = mkfifo(_fifo_path.c_str(), 0666); // 创建管道
            if (res != 0)
            {
                perror("mkfifo");
            }
            std::cout << "creater create named pipe" << std::endl;
        }
    }

    // 以读方式打开管道
    bool OpenForRead()
    {
        return OpenNamedPipe(Read);
    }

    // 以写方式打开管道
    bool OpenForWrite()
    {
        return OpenNamedPipe(Write);
    }

    // 读管道的方式
    int ReadNamedPipe(std::string *out) // 输出型参数
    {
        char buffer[BaseSize]; // 一次能读取到的最大内容
        int n = read(_fd, buffer, sizeof(buffer));//从管道中读取
        if (n > 0)
        {
            buffer[n] = 0;
            *out = buffer; //*out指向读取到的内容
        }
        return n;
    }

    // 写管道的方式
    int WriteNamedPipe(const std::string &in)
    {
        return write(_fd, in.c_str(), in.size()); // 依据wfd读取
    }

    ~NamePiped()
    {
        if (_id == Creater) // 只有申请者才能创建管道
        {
            int res = unlink(_fifo_path.c_str());
            if (res != 0)
            {
                perror("unlink");
            }
            std::cout << "creater free named pipe" << std::endl;
        }
        if (_fd != DefaultFd) // 关闭写端fd
            close(_fd);
    }

private:
    const std::string _fifo_path; // 提供的文件路径
    int _id;                      // 标识使用者的身份信息
    int _fd;                      // 标识文件描述符
};

服务端

功能:申请并创建管道,以及最后管道的销毁,读取管道中的内容(读端)

/*
 * @Author: error: error: git config user.name & please set dead value or install git && error: git config user.email & please set dead value or install git & please set dead value or install git
 * @Date: 2024-05-12 13:59:38
 * @LastEditors: error: error: git config user.name & please set dead value or install git && error: git config user.email & please set dead value or install git & please set dead value or install git
 * @LastEditTime: 2024-05-12 18:42:19
 * @FilePath: /2024.5.8/home/ubuntu/2024.5.12/sever.cpp
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
#include "namedPipe.hpp"

// server read: 管理命名管道的整个生命周期
int main()
{
    NamePiped fifo(comm_path, Creater);
    // 对于读端而言,如果我们打开文件,但是写还没来,我会阻塞在open调用中,直到对方打开
    // 进程同步
    if (fifo.OpenForRead()) // 调用以读方式打开管道函数
    {
        std::cout << "server open named pipe done" << std::endl;

        sleep(3);
        while (true) // 循环写入数据
        {
            std::string message;
            int n = fifo.ReadNamedPipe(&message);
            if (n > 0)
            {
                std::cout << "Client Say> " << message << std::endl;
            }
            else if (n == 0)
            {
                std::cout << "Client quit, Server Too!" << std::endl;
                break;
            }
            else
            {
                std::cout << "fifo.ReadNamedPipe Error" << std::endl;
                break;
            }
        }
    }

    return 0;
}

客户端

功能:向管道中写入消息,向管道中写入内容(写端)

/*
 * @Author: error: error: git config user.name & please set dead value or install git && error: git config user.email & please set dead value or install git & please set dead value or install git
 * @Date: 2024-05-12 13:57:27
 * @LastEditors: error: error: git config user.name & please set dead value or install git && error: git config user.email & please set dead value or install git & please set dead value or install git
 * @LastEditTime: 2024-05-12 18:52:30
 * @FilePath: /2024.5.8/home/ubuntu/2024.5.12/client.cc
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
#include "namedPipe.hpp"

// write
int main()
{
    NamePiped fifo(comm_path, User); // 管道的使用者
    if (fifo.OpenForWrite())         // 调用以写方式打开管道函数
    {
        std::cout << "client open namd pipe done" << std::endl; // 客户端持续输入
        while (true)
        {
            std::cout << "Please Enter> ";
            std::string message;
            std::getline(std::cin, message); // 将获取的命令行字符串作为message写入管道
            fifo.WriteNamedPipe(message);
        }
    }

    return 0;
}
  • 输入型参数:只读,不修改
  • 输出型参数:由函数初始化或修改,用于返回数据
  • 输入输出型参数:由调用者初始化,由函数修改,并返回修改后的结果

注意事项:

1、有了fifo才能客户端使用与管道相关的方法,即便有的方法根本用不(构造函数主要是为了服务端创建管道的)

2、read函数会一直等待管道中有数据输入,除非将管道的模式设置为非阻塞等待,但是一般不会这样设置

共享内存

  1. 共享内存空间是可以存在多份的,每一对儿通信的进程都有一个共享内存空间 
  2. ①和②都是由OS进行的,用户想要进行这些操作需要使用OS提供的系统调用接口
  3. 共享内存 =  内存空间(数据) + 共享内存的属性
  4. 共享内存不随着进程的结束而自动释放,它会一直存在直到系统重启,需要使用指令和系统调用接口手动销毁内存(共享内存生命周期随内核,文件的生命周期随进程)

shmget函数

函数原型:int shmget(key_t key, size_t size, int shmflg);

包含头文件:<sys/ipc.h>  和  <sys/shm.h>

参数:“协商”后的共享内存空间的标识符,要创建的共享内存空间的大小,操作方式

返回值:创建成功返回标识符

功能:创建新的共享内存段

注意事项:shmflg取值的不同会使得shmget函数产生不同的效果:

  • IPC_CREAT:不存在就新建。存在就,则什么都不干
  • IPC_EXCL:单独使用没有意义需要和IPC_CREAT配合使用
  • IPC_CREAT | IPC_EXCL:使用两个标识符,如果共享内存存在则返回错误,如果不存在则新建一个共享内存段
  • 使用 IPC_CREAT 时,还可以与权限位进行按位或运算来指定共享内存段的访问

ftok函数

函数原型:key_t ftok(const char *pathname, int proj_id);

包含头文件:<sys/ipc.h>  和  <sys/types.h>

参数:用于生成 key 的路径名,项目标识符

返回值:返回生成的 key

功能:根据给定的路径名和项目标识符生成一个 key,生成一个足够随机的用于标识共享内存空间的标识符

注意事项:该函数内部进行的操作是依据路径和项目标识符生成一个key_t类型的随机数,后续两个进程间通信时会使用相同的两个参数生成两个相同的key,从而可以找到对应的共享内存块

key和shmid的区别

        key是一个由自定义的(文件路径 + 随机值)经过ftok函数中的算法生成的一个随机值,服务端和客户端共用一个key,服务端将该key交给OS提供的系统调用接口shmget就可以创建一块共享内存空间并获取OS对于该块共享内存空间的标识符shmid,服务端也将该key交给shmget可以获得与服务端相同的shmid(相当于服务端会去创建一个共享内存空间用于二者间的通信,但是客户端会找不到,于是二者提前约定好一个key,服务端说我会用这个key交给shmget会创建一个共享内存空间并得到它的shmid,你找的时候就用这个key去找,找的方式就是也将key交给shmget同时你要保证你是寻找而不是创建,将咱俩约定好的key交给shmget后它会告诉你我创建的共享内存空间的shmid是什么,之后咱俩就拿着这个shmid交流了),最后客户端和服务端就要利用该shmid访问该块共享内存空间(服务端和客户端有一对儿一样的钥匙,服务端用钥匙去找前台shmget开了一个只能用该钥匙打开的房间号为shmid的房间,客户端来找服务端的时候也要去找前台shmget说之前有一个人拿着跟我一样的钥匙开了个房间你告诉我这个房间号吧,然后shmget就拿着你的钥匙去找之前的开房信息,发现你这把钥匙对应了某个房间号,然后后就将该房间号shmid,告诉客户端)

snprintf函数 

函数原型:int snprintf(char *str, size_t size, const char *format, ...);

包含头文件:<stdio.h>

参数:待写入的目标字符串,要写入的长度(可以包括\0),格式化字符串,可变参数列表

返回值:写入到字符串中的字符数

功能:向目标字符串中写入指定长度的字符

注意事项:是sprintf的升级版

ipcs指令 

功能:查看共享内存段信息的命令

常见用法:

1、ipcs -m查看共享内存段信息

  • 键值 (key):用于唯一标识共享内存段的键值
  • 权限 (perms):显示共享内存段的权限,包括所有者、组和其他用户的权限
  • 拥有者 (owner):拥有共享内存段的用户
  • 组 (group):拥有共享内存段的用户组
  • 大小 (bytes):共享内存段的大小(以字节为单位)
  • 连接的进程 (nattch):当前连接到共享内存段的进程数量
  • 最后连接时间 (attime):最后一个进程连接到共享内存段的时间

ipcrm指令

功能:删除共享内存段

常见用法:ipcrm -m 共享内存的shmid

shmctl函数

函数原型:int shmctl(int shmid, int cmd, struct shmid_ds *buf);

包含头文件: <sys/ipc.h>  和  <sys/shm.h>

参数:共享内存段的标识符,指定要执行的操作,指向共享内存空间属性结构体的指针

返回值:成功指向返回

功能:对共享内存段进行相关操作,比如:

  • 获取共享内存段的信息(cmd = IPC_STAT)
  • 设置共享内存段的权限(cmd = IPC_SET)
  • 删除共享内存段(cmd = IPC_RMID)
struct shmid_ds ds;
int n = shmctl(_shmid, IPC_STAT, &ds);

shmat函数 

函数原型:void *shmat(int shmid, const void *shmaddr, int shmflg);

包含头文件:<sys/types.h>  和  <sys/shm.h>

参数: 共享内存段的标识符,指定共享内存连接到进程地址空间的地址,IPC选项

返回值:连接成功返回指向共享内存空间(虚拟地址)首字节的指针,失败返回-1

功能:将共享内存空间连接到调用进程的地址空间,以便该进程可以访问和操作共享内存中的数据

注意事项:shmaddr的取值通常为NULL,表示让OS自动选择合适的地址,shmflg通常为0

int shmdt(const void* shmaddr)为取消连接函数,与shmat函数一样的头文件,shmaddr指向要分离的共享内存段的地址

void*做返回值

使用原因:void*是一种通用指针类型,可以指向任何类型的数据,当函数需要返回一个指针但该指针指向的数据类型可能不确定时(比如malloc函数只负责申请空间并返回一个指向该内存空间的void*类型的指针,调用者要根据需要将该void*指针转换为新类型的指针,从而告诉编译器我们在这块内存中存放的是什么类型的数据),通常会将返回类型声明为void*(内存中的某个位置本身并没有数据类型,数据类型是用来解释内存中的内容的方式。当我们将一个指针转换为特定类型的指针时,我们告诉编译器如何解释这块内存中的内容)

注意事项:在使用void* 类型的返回值时,通常需要将其转换为实际的数据类型,以便正确的访问和操作数据

创建共享内存空间

#ifndef _SHM_HPP_ // 头文件只能包含一次
#define _SHM_HPP_

#include <iostream>
#include <string>
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>

// 创建使用者的标识
const int gCreater = 1;
const int gUser = 2;

// 自定义全局全局参数用于让两个通信的进程生成同样的key
const std::string gpathname = "/home/ubuntu/2024.5.13";
const int gproj_id = 0x66;

// 自定义共享内存空间的大小,一般为4096 * n
const int gShmSize = 4096;

class Shm
{
    // 不希望将这些接口暴露给用户,用户只需要使用即可
private:
    // 获取key
    key_t GetCommKey()
    {
        key_t k = ftok(_pathname.c_str(), _proj_id);
        if (k < 0)
        {
            perror("ftok");
        }
        return k;
    }

    // 创建共享内存空间
    int GetShmHelper(key_t key, int size, int flag)
    {
        int shmid = shmget(key, size, flag);
        if (shmid < 0)
        {
            perror("shmget");
        }
        return shmid;
    }

    // 将角色转换为字符串
    std::string RoleToString(int who)
    {
        if (who == gCreater)
            return "Creater";
        else if (who == gUser)
            return "User";
        else
            return "None";
    }

    // 将共享内存空间挂接到进程地址空间,调用该函数相当于调用malloc函数
    void *AttachShm()
    {
        if (_addrshm != nullptr)
            DatechShm(_addrshm);

        std::cout
            << "进行挂接" << std::endl;
        void *shmaddr = shmat(_shmid, nullptr, 0);
        if (shmaddr == nullptr) // 挂接失败
        {
            perror("shmat");
        }

        std::cout << "who: " << RoleToString(_who) << " attach shm..." << std::endl;
        return shmaddr;
    }

    // 解除挂接
    void DatechShm(void *shmaddr)
    {
        std::cout << "解除挂接" << std::endl;
        if (shmaddr == nullptr)
            return;
        shmdt(shmaddr);
        std::cout << "who: " << RoleToString(_who) << " detach shm..." << std::endl;
    }

public:
    // 构造函数
    Shm(const std::string &pathname, int proj_id, int who)
        : _pathname(pathname), _proj_id(proj_id), _who(who), _addrshm(nullptr)
    {
        _key = GetCommKey();
        if (_who == gCreater) // 如果是创建者就调用专门为创建者准备的函数GetShmForCreater
            GetShmForCreater();
        else if (_who == gUser) // 如果是使用者就调用专门为使用者准备的函数GetShmForUser
            GetShmForUser();
        std::cout << "shmid: " << _shmid << std::endl;
        std::cout << "_key: " << ToHex(_key) << std::endl;
        if (_who == gUser)
        {
            std::cout << "shm get done..." << std::endl;
        }
        // 不论是谁只要获取到一个共享内存空间的shmid,就将该共享内存空间挂接到进程地址空间上
        _addrshm = AttachShm();
    }

    // 析构函数:删除共享内存空间
    ~Shm()
    {
        if (_who == gCreater)
        {
            int res = shmctl(_shmid, IPC_RMID, nullptr);
            std::cout << "shm remove done..." << std::endl;
        }
    }

    // 将key转换为16进制,便于后续的查看
    std::string ToHex(key_t key)
    {
        char buffer[128];
        snprintf(buffer, sizeof(buffer), "0x%x", _key);
        return buffer;
    }

    // 服务端创建共享内存空间
    bool GetShmForCreater()
    {
        _shmid = GetShmHelper(_key, gShmSize, IPC_CREAT | IPC_EXCL | 0666); // 如果没有就创建,如果有了就返回错误,即没有返回错误那么就是创建了一个新的共享内存空间
        if (_shmid >= 0)                                                    // 0666表示所有者、所属组和其他用户对共享内存空间都有读写权限,但没有执行权限
        {
            std::cout << "shm creat done..." << std::endl;
            return true; // 创建成功返回true
        }
        std::cout << "shm creat fail..." << std::endl;
        return false;
    }

    // 客户端使用共享内存
    bool GetShmForUser()
    {
        _shmid = GetShmHelper(_key, gShmSize, IPC_CREAT | 0666); // 使用者根据与创造者一样得_key获得依据创造者创造的共享内存空间得shmid,仅有IPC_CREAT是因为此时服务端已经创建好了共享内存空间此时用户端只需要找到它对应的共享内存空间的shmind即可
        if (_shmid >= 0)
        {
            return true; // 获取成功返回true
        }
        std::cout << "shm get fail..." << std::endl;
        return false;
    }

    // 获取进程空间的地址
    void *Addr()
    {
        return _addrshm;
    }

    // 清空共享内存空间
    void Zero()
    {
        if (_addrshm)
        {
            memset(_addrshm, 0, gShmSize);
        }
    }

    //  获取共享内存空间属性结构体中的内容
    void DebugShm()
    {
        struct shmid_ds ds;
        int n = shmctl(_shmid, IPC_STAT, &ds); //
        if (n < 0)
            return;
        std::cout << "ds.shm_perm.__key : " << ToHex(ds.shm_perm.__key) << std::endl;
        std::cout << "ds.shm_nattch: " << ds.shm_nattch << std::endl;
    }

private:
    key_t _key;
    int _shmid;

    std::string _pathname;
    int _proj_id;
    int _who;

    void *_addrshm;
};

#endif

服务端

/*
 * @Author: error: error: git config user.name & please set dead value or install git && error: git config user.email & please set dead value or install git & please set dead value or install git
 * @Date: 2024-05-12 23:03:25
 * @LastEditors: error: error: git config user.name & please set dead value or install git && error: git config user.email & please set dead value or install git & please set dead value or install git
 * @LastEditTime: 2024-05-14 20:02:49
 * @FilePath: /2024.5.8/home/ubuntu/2024.5.13/server.cc
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
#include "shm.hpp"
#include "namedPipe.hpp"

int main()
{
    // 1. 创建共享内存
    Shm shm(gpathname, gproj_id, gCreater);
    char *shmaddr = (char *)shm.Addr();

    shm.DebugShm();

    // 2. 创建管道
    NamePiped fifo(comm_path, Creater);
    fifo.OpenForRead(); // 以读方式打开管道

    while (true)
    {
        std::string temp;
        fifo.ReadNamedPipe(&temp); // 服务端读取管道

        std::cout << "shm memory content: " << shmaddr << std::endl;//打印此时共享内存空间中的内容(客户端已经向里面写入了),shmaddr是一个字符指针,打印它就会将它所指向字符串
    }

    sleep(5);

    return 0;
}

客户端 

/*
 * @Author: error: error: git config user.name & please set dead value or install git && error: git config user.email & please set dead value or install git & please set dead value or install git
 * @Date: 2024-05-12 23:03:16
 * @LastEditors: error: error: git config user.name & please set dead value or install git && error: git config user.email & please set dead value or install git & please set dead value or install git
 * @LastEditTime: 2024-05-14 20:01:41
 * @FilePath: /2024.5.8/home/ubuntu/2024.5.13/client.cc
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
#include "shm.hpp"
#include "namedPipe.hpp"

int main()
{
    // 1. 创建共享内存

    Shm shm(gpathname, gproj_id, gUser);
    shm.Zero();
    char *shmaddr = (char *)shm.Addr(); // shmaddr指向一段内存空间
    sleep(3);

    // 2. 打开管道(利用管道保护共享内存空间)
    NamePiped fifo(comm_path, User);//有了fifo才能使用与管道相关的方法,即便有的方法根本用不上
    fifo.OpenForWrite(); // 以写方式打开管道

    // 当成string
    char ch = 'A';
    while (ch <= 'Z')
    {
        shmaddr[ch - 'A'] = ch; // shmadrr是一个指针,而数组名相当于一个指针,这里我们将hmaddr视为一个数组,循环向数组中写入即循环向共享内存空间中写入,起始时ch-'A' = 0即shmaddr[0] = 'A'

        std::string temp = "wakeup";//定义一个提醒字符串
        std::cout << "add " << ch << " into Shm, " << "wakeup reader" << std::endl;//客户端打印自己此时向共享内存空间中所写字母
        fifo.WriteNamedPipe(temp);//向管道中写入提醒字符串,提醒服务端该读取了
        sleep(2);
        ch++;
    }
    return 0;
}

注意事项:使用管道是因为共享内存空间不会提供对自己本身的保护机制,这就会导致数据不一致问题(客户端还没写完,服务端就疯狂的读取),使用管道 + read函数的方式,当客户端向共享内存空间中写入数据后会向管道中发送一份可以读取提醒,否则服务端就会一直因read读取不到管道中的内容而阻塞,当服务端读取到客户端向管道中写入的提醒时就会开始读取(当然读取什么并不重要我们要的是能让他阻塞等待客户端写完)服务端不再阻塞后就会打印此时共享内存空间中的内容(相当于读取了)

~over~

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

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

相关文章

OpenAI GPT-4

本文翻译整理自&#xff1a;https://openai.com/index/gpt-4-research/ (March 14, 2023) 文章目录 一、关于 GPT-4二、能力视觉输入Visual inputs: chart reasoningSample 2 of 7 操纵性Steerability: Socratic tutorSample 1 of 3 三、局限性四、风险与缓解措施五、训练流程…

鱼哥好书分享活动第22期:《数字政府网络安全合规性建设指南》解锁数字政府网络安全新篇章

鱼哥好书分享活动第22期&#xff1a;《《数字政府网络安全合规性建设指南》》解锁数字政府网络安全新篇章 阅读对象&#xff1a;书籍目录&#xff1a;了解更多&#xff1a;赠书抽奖规则: 当今时代&#xff0c;数据已成为新型生产要素&#xff0c;不仅是个人、企业乃至国家的重要…

enable_shared_from_this使用笔记

解决了&#xff1a; 不能通过原指针增加引用次数的问题 &#xff0c;通过weak_ptr实现。 class MyCar:public std::enable_shared_from_this<MyCar> { public:~MyCar() { std::cout << "free ~Mycar()" << std::endl; } };int main() { MyCar* _…

算法-卡尔曼滤波之基本数学的概念

1.均值 定义&#xff1a;均值是一组数据中所有数值的总和除以数据的数量。均值是数据的中心趋势的一种度量&#xff0c;通常用符号 xˉ 表示。 &#xff1a;对于包含 n 个数据的数据集 {&#x1d465;1,&#x1d465;2,...,&#x1d465;&#x1d45b;}&#xff0c;均值 xˉ 计…

高扩展性便携式1553B总线测试仪,支持麒麟操作系统

手提式便携1553B总线测试仪&#xff0c;利用订制平台的PXIe扩展槽嵌入石竹科技自主研发的高性能T系列专用1553B测试板卡和高级协议分析和测试软件FP-1553B Pro、FP-5186集成的一款模块化、功能可订制的测试仪器。 基本配置可对1553B信号进行波形采集&#xff08;提供软件示波器…

goconvey测试框架的使用

尽管Golang已经内置了功能强大的testing包&#xff0c;其易用性令人称赞。然而&#xff0c;当我们希望更直观地处理和判断测试结果时&#xff0c;结合使用goconvey能为我们提供极大的便利。goconvey不仅为我们提供了丰富的断言函数&#xff0c;这些函数还极大地方便了我们在进行…

C++ QT设计模式 (第二版)

第3章 Qt简介 3.2 Qt核心模块 Qt是一个大库&#xff0c;由数个较小的库或者模块组成&#xff0c;最为常见的如下&#xff1a;core、gui、xml、sql、phonon、webkit&#xff0c;除了core和gui&#xff0c;这些模块都需要在qmake的工程文件中启用 QTextStream 流&#xff0c;Qdat…

本地安装nvm,管理多版本node

先卸载本地的nodejs(14.16.1) 卸载的直接可以点击win10图标→设置→应用→应用和功能 卸载nodejs即可 2. 安装nvm&#xff0c;地址&#xff1a;https://github.com/coreybutler/nvm-windows/releases 安装目录时尽量不要出现特殊字符还有空格&#xff0c;否则会在nvm use xxx的…

海外媒体发稿:如何在日本媒体投放新闻通稿-大舍传媒

导言 在全球化的时代背景下&#xff0c;海外媒体宣发对于企业来说非常重要。通过在海外媒体投放新闻通稿&#xff0c;企业能够拓展海外市场&#xff0c;增强知名度和影响力。本文将探讨如何在海外媒体投放新闻通稿&#xff0c;以帮助企业进行有效的海外宣传。 挖掘海外媒体资…

Alibaba SpringCloud集成Nacos、Sentinel实现服务治理-17

关于服务治理 总体而言&#xff0c;限流和降级作为微服务架构中的重要机制&#xff0c;尽管在实现上可能有多种方式&#xff0c;但它们都着眼于保护服务提供者和消费者&#xff0c;在面对异常情况时确保系统稳定运行。限流关注于保护服务提供者&#xff0c;控制请求流量&#…

20232810 2023-2024-2 《网络攻防实践》实验九

一、实践内容 1.1 反汇编 1.1.1 编程原理 编程的原理是一套指导软件开发和维护的概念、原则和实践&#xff0c;包括抽象以简化复杂系统、模块化以分解程序、封装以隐藏内部状态、继承以共享特性、多态以允许不同响应、算法和数据结构以组织计算和存储、控制结构以控制流程、…

UVa11419 SAM I AM

UVa11419 SAM I AM 题目链接题意分析AC 代码 题目链接 UVA - 11419 SAM I AM 题意 给出一个 RC 大小的网格&#xff0c;网格上面放了一些目标。可以在网格外发射子弹&#xff0c;子弹会沿着垂直或者水平方向飞行&#xff0c;并且打掉飞行路径上的所有目标&#xff0c;如下图所…

System V IPC(进程间通信)机制详解

文章目录 一、引言二、System V IPC的基本概念1、IPC结构的引入2、IPC标识符&#xff08;IPC ID&#xff09;3、S ystem V的优缺点 三、共享内存&#xff08;Shared Memory&#xff09;1、共享内存的基本概念2、共享内存的创建&#xff08;shmget&#xff09;3、共享内存的附加…

深入探索Android签名机制:从v1到v3的演进之旅

引言 在Android开发的世界中&#xff0c;APK的签名机制是确保应用安全性的关键环节。随着技术的不断进步&#xff0c;Android签名机制也经历了从v1到v3的演进。本文将带你深入了解Android签名机制的演变过程&#xff0c;揭示每个版本背后的技术细节&#xff0c;并探讨它们对开…

企业如何通过云服务器实现全球连通运营

如果说互联网是一座桥&#xff0c;连接起了全球各地的信息&#xff0c;那云服务器就如同一座高速公路&#xff0c;帮助企业轻松实现跨国家、跨时区的全球运营。 这个听起来像科幻电影的情节其实已经成为了我们现实生活的一部分。现在就来具体看一下如何做到这一点吧。 其一&…

Django开发实战之定制管理后台界面及知识梳理(中)

上一篇文章末尾讲到如何能够展示更多的字段在界面上&#xff0c;那么针对整个界面数据&#xff0c;如果我想按照某一个条件进行筛选&#xff0c;我该怎么做呢&#xff0c;只需要加上下面一行代码 注意&#xff1a;中途只有代码片段&#xff0c;文末有今天涉及的所有代码 1、增…

RuoYi-Vue-Plus (Logback 和 logback-plus.xml 、p6spy)

项目后本地日志 一、logback依赖 打开最外层的 pom.xml,查看 SpringBoot的依赖配置。 <dependencyManagement><dependencies><!-- SpringBoot的依赖配置--><dependency><groupId>org.springframework.boot</groupId><artifactId>s…

视频汇聚管理/安防监控系统EasyCVR如何开启和调用验证码登录接口?

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。视频汇聚融合管理平台EasyCVR既具备传统安防视…

项目实施方案:多点异地机动车典型系统试验状态可视监控系统

目录 一、需求分析 1.1项目背景 1.2项目概述 二、系统优势 2.1兼容性能力强 2.2接入协议多样 2.3并发能力强 2.3.1 单平台参数 2.3.2 多平台性能参数 2.4 系统稳定性 三、建设目标 3.1安全性 3.2可扩展性 3.3易用性 3.4兼容性 3.5 响应能力 四、系统整体解决方…

C#窗体程序设计笔记:如何调出控件工具箱,并设置控件的属性

文章目录 调出控件工具箱设置控件属性 调出控件工具箱 使用Visual Studio打开C#解决方案后&#xff0c;初始界面如下图所示&#xff1a; 接着&#xff0c;在上方的菜单栏依次选择“视图”“工具箱”&#xff0c;即可打开工具箱&#xff0c;如下图所示&#xff1a; 设置控件属…