Linux的命名管道 共享内存

目录

命名管道

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~

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

0509BBQ的Maven项目

0509BBQ的Maven项目包-CSDN博客 数据库字段 登录界面 点餐主页 登录完成进入点餐主页&#xff0c;显示已点餐的数据 点击点餐按钮&#xff0c;生成对应订单数据。 取消勾选点餐按钮&#xff0c;删除对应订单数据。 点击加减图片&#xff0c;实现对数量的加减 展示订单 ajax动态…

供水设备数据采集

随着城市化进程的加快&#xff0c;供水系统作为城市基础设施的重要组成部分&#xff0c;其运行效率和稳定性直接关系到市民的日常生活。在这个信息化、智能化的时代&#xff0c;如何利用先进技术提升供水系统的管理水平&#xff0c;成为了摆在我们面前的重要课题。HiWoo Cloud平…

c++——类和对象(中)

1.类的六个默认成员函数 在一个空类中真的什么都没有吗&#xff0c;错&#xff01;在创建类的时候&#xff0c;编译器自动生成六个函数&#xff0c;这六个函数叫默认成员函数。但是&#xff0c;如果我们自己实现六个同名函数&#xff08;依旧有默认成员函数的特性&#xff0c;…

内容安全(DPI和DFI解析)

内容安全前言&#xff1a; 防火墙的本质其实就是包过滤&#xff0c;我们通常所说的安全设备&#xff08;如&#xff1a;IPS、IDS、AV、WAF&#xff09;的检测重心是应用层。下一代防火墙基于传统防火墙的拓展能力&#xff0c;就是可以将以上的安全设备模块集成在一起&#xff0…

2024国考行测、申论资料大全,做好备考真的很重要!

1. 国考是什么? 国考,全称国家公务员考试,是选拔国家公务员的重要途径。通过国考,你将有机会进入政府部门,为国家建设贡献力量。 2. 国考难在哪里? 国考之所以难,主要体现在以下几个方面: (1) 竞争激烈 每年国考报名人数都在百万以上,而录取率却不足2%。千军万马过独木桥…

【java-数据结构15-模拟实现栈的方法】

上篇文章中&#xff0c;我们已经手动实现了栈&#xff0c;下面&#xff0c;我们将继续手动实现栈的方法~ 1.在栈中存放元素 1.定义一个usedsize&#xff0c;用来临时存放下标 2.当存放一个元素后&#xff0c;下标加一 3.不要忘记判满 如图 代码如下 判满方法 public boolea…

C++——超简单登录项目

程序入口文件 #include <QtWidgets/QApplication> // 包含登录页面头文件 #include "DlgLogin.h"int main(int argc, char *argv[]) {QApplication a(argc, argv);// 程序入口// 调页面起来//DlgMain w;//w.show();// 换成登录页面DlgLogin w;w.show();return…

高级查询(一)

解决需求&#xff1a;Excel都可以轻松搞定 分组查询原理&#xff1a; 语法&#xff1a; SELECT …<列名> FROM <表名> [WHERE<条件表达式> ] GROUP BY 参与分组的列 可以与 聚合函数作用的列 一块显示 注意 SELECT列表中只能包含&#xff1a; 1、被分…

Linux中如何配置虚拟机网络(NAT方法)

首先我们要在Linux中找到配置文件的路径/etc/sysconfig/network-scripts/&#xff0c;然后找到配置文件的名称ifcfg-xxx&#xff08;如&#xff1a;ifcfg-ens33&#xff09;&#xff0c;然后打开这个文件内 容如下&#xff1a; TYPEEthernet # 指定网卡类型是以太网 BOOTPROT…

gin框架学习笔记(三) ——路由请求与相关参数

参数种类与参数处理 查询参数 在讲解查询参数的定义之前&#xff0c;我们先来看一个例子&#xff0c;当我打开了CSDN&#xff0c;我现在想查看我的博客浏览量&#xff0c;那么我就需要点击我的头像来打开我的个人主页,像下面这样: 我们现在把浏览器的网址取下来&#xff0c;…

分布式版本控制工具git

1 安装Git 在linux上我们建议你用二进制的方式来安装git&#xff0c;可以使用发行版包含的基础软件包管理工具来安装&#xff0c;如果你是 是CentOS或者Fedora的操作系统&#xff0c;可以使用yum命令来安装git&#xff1a; $ sudo yum install git如果你是ubuntu或者是Debian…

YOLOv9独家原创改进: 特征融合创新 | 一种基于内容引导注意力(CGA)的混合融合 | IEEE TIP 2024 浙大

💡💡💡创新点:提出了一种基于内容引导注意力(CGA)的混合融合方案,将编码器部分的低级特征与相应的高级特征有效融合。 💡💡💡在多个数据集实现暴力涨点,适用于小目标,低对比度场景 💡💡💡如何跟YOLOv9结合:将backbone和neck的特征融合,改进结构图如下…

sql实践

1.从excel导入数据 在excel导入数据时要先在数据库中创建对应的数据库表 CREATE TABLE your_table_name (crawl_datetime DATE,url CHAR(255),company_name CHAR(255),company_size CHAR(255),company_type CHAR(255),job_type CHAR(255),job_name CHAR(255),edu CHAR(255),e…

LLM推理入门指南③:剖析模型性能

在本系列文章《LLM推理入门指南①&#xff1a;文本生成的初始化与解码阶段》中&#xff0c;作者对Transformer解码器的文本生成算法进行了高层次概述&#xff0c;着重介绍了两个阶段&#xff1a;提示的处理阶段和逐个生成补全词元的多步生成阶段。在上一篇文章《LLM推理入门指南…

【递归、回溯和剪枝】综合训练<二>

1.组合总和 组合总和 解法一&#xff1a; class Solution { public:vector<vector<int>> ret;vector<int> path;int aim;vector<vector<int>> combinationSum(vector<int>& nums, int target) {aim target;dfs(nums, 0, 0);return …

整合Tess4J图文识别技术

1. OCR图文识别介绍 OCR&#xff08;全称 Optical Character Recognition&#xff0c;直译为光学字符识别&#xff09;用于图片文字识别&#xff0c;例如 提取图片中车牌号等等。 Java中实现OCR的技术方案有&#xff1a; 百度OCR&#xff0c;花米Tesseract-OCR&#xff0c;Go…

文心一言 VS 讯飞星火 VS chatgpt (259)-- 算法导论19.3 2题

二、使用聚合分析来证明 FIB-HEAP-DECREASE-KEY 的 O(1) 摊还时间是每一个操作的平均代价。如果要写代码&#xff0c;请用go语言。 文心一言&#xff1a; FIB-HEAP-DECREASE-KEY 是斐波那契堆&#xff08;Fibonacci Heap&#xff09;操作中的一个&#xff0c;它允许我们减少堆…

【LangChain系列 15】语言模型——LLMs(一)

原文地址&#xff1a;【LangChain系列 15】语言模型——LLMs(一) 本文速读&#xff1a; 异步API 自定义LLM Fake LLM HumanInput LLM 本文将介绍LLMs在LangChain中的一些用法&#xff0c;帮助我们更好地了解LLM模块。 01 异步API LangChain通过异步库实现了对异步的支持&a…

楼宇智慧公厕建设新方案-集成更简单!成本价更低!

在当今的大厦和写字楼中&#xff0c;公厕面临着诸多痛点。 办公楼公厕常常存在厕位难找的问题&#xff0c;使用者不得不花费时间逐一查看&#xff0c;导致效率低下&#xff1b;环境质量也令人担忧&#xff0c;异味、脏污等情况时有发生&#xff0c;影响使用者的心情和健康&…

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

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