Linux进程通信--共享内存

news2024/11/13 22:02:00

在这里插入图片描述

文章目录

  • 概述
  • 共享内存基本原理
  • 共享内存的操作
    • 创建共享内存函数接口
      • 形成key--fotk
      • 创建共享内存代码演示
      • 补充指令集--ipc的指令
      • key和shmid区别
      • 创建并获取共享内存代码
    • 删除共享内存函数接口
      • 删除共存内存函数代码演示
    • 共享内存段连接到进程地址空间函数接口
      • 代码演示
    • 取消关联
      • 代码演示
    • 共享内存优缺点
    • 对共享内存进行保护
    • 获取共享内存属性
  • 关于共享内存的完整代码

概述

共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据。

本地通信方案:system V IPC:

  1. 共享内存
  2. 消息队列
  3. 信号量

共享内存基本原理

每一个进程有自己的地址空间,经过页表转化,找到物理内存,由于进程具有独立性,每个进程有自己的代码和数据,内核数据结构独立。

如何实现共享内存呢?
假设目前有两个进程,进程A和进程B。
首先由操作系统在物理内存中开辟一段内存空间,共享内存虽然是操作系统创建的,但是这些进程中的某一个需要来创建这个共享内存,这个共享内存属于操作系统的。
其次,将这个内存空间经过页表,映射到进程A的共享区,在共享区申请一段空间,然后将起始虚拟地址返回给用户,进程就可以通过地址和页表直接向共享内存中写内容。进程B也可以执行操作。

我们将这种用地址空间进行映射让进程A和进程B可以看到同一段共享内存,称之为共享内存。

在这里插入图片描述

上述操作都是操作系统来完成,操作系统可以提供上述的系统调用,让进程A和进程B进行调用。此时如果进程C、进程D、进程E、进程F等进程也需要通信,但是不使用进程和进程B的共享内存,因此共享内存在系统中可以同时存在多份,让不同个数、不同进程进行通信。由此,操作系统就要对共享内存进程管理(先描述,再组织),共享内存不是简单的一段内存空间,也要有描述并管理共享内存的数据结构和匹配算法。简单来说,对共享内存的管理,就变成了对链表的增删查改。

总结一下:
共享内存=内存空间(数据)+共享内存的属性

共享内存的操作

创建共享内存函数接口

创建共享内存函数接口为:shmget

#include <sys/ipc.h>
#include <sys/shm.h>

int shmget(key_t key, size_t size, int shmflg);

参数:

  • key:这个共享内存段名字,具有唯一性,使用户设置的
  • size:共享内存大小
  • shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的。
    标志位IPC_CREAT:如果创建的共享内存不存在,那就创建它;如果存在获取该共享内存并返回,意味着总能获取到一个。
    标志位IPC_EXCL:单独使用没有意义,一般和IPC_CREAT组合才有意义。
    标志位IPC_EXCL | IPC_CREAT:如果创建的共享内存不存在,就创建该共享内存;如果存在,出错返回。也就是说,成功返回了,那么创建的共享内存就是全新的。

进程如何知道该共享内存存不存在?
共享内存有自己对应的属性,这个属性有一个标识共享内存唯一性的字段,因此对应的共享内存存不存在,可以看对应的唯一性标识符。

返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1

形成key–fotk

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

key_t ftok(const char *pathname, int proj_id);

  • pathname:是一个指向包含路径名的字符串的指针,通常是一个现有的文件的路径。这个文件在使用 ftok 函数时不会被打开或读取,只是用于生成唯一的键值。

  • proj_id:是一个用户定义的整数,通常是0到255之间的数字。它用来区分同一个路径名下不同IPC对象的不同标识符。

返回值: ftok 函数返回一个key_t类型的键值,如果发生错误则返回 -1,并设置 errno 来指示错误的原因。

创建共享内存代码演示

//Shm.hpp
#ifndef __SHM_HPP__
#define __SHM_HPP__

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

const std::string gpathname="/home/gwj/111/lesson22_shm";
const int gproj_id=0x66;

std::string ToHex(key_t k)
{
    char buffer[128];
    snprintf(buffer,sizeof(buffer),"0x%x",k);
    return buffer;
}

key_t GetCommKey(const std::string &pathname,int proj_id)
{
    key_t k=ftok(pathname.c_str(),proj_id);
    if(k<0)
    {
        perror("ftok");
    }
    return k;
}
int ShbGet(key_t key,int size)
{
    int shmid=shmget(key,size,IPC_CREAT|IPC_EXCL);
    if(shmid)
    {
        perror("shmget");
    }
    return shmid;
}


#endif
//client.cc
#include"Shm.hpp"

int main()
{
    key_t key=GetCommKey(gpathname,gproj_id);
    std::cout<<"key: "<<ToHex(key)<<std::endl;
    return 0;
}
//server.cc
#include"Shm.hpp"

int main()
{
    key_t key=GetCommKey(gpathname,gproj_id);
    std::cout<<"key: "<<ToHex(key)<<std::endl;
    
    int shmid=ShbGet(key,4096);
    std::cout<<"shmid: "<<shmid<<std::endl;
    return 0;
}

运行结果:
在这里插入图片描述

当我们再次运行该程序时,shmid会报错,是因为使用了标志位IPC_EXCL | IPC_CREAT
当我们删除该程序,重新生成,再次运行,shmid依然报错,是因为共享内存不随着进程的结束而释放内存,一直存在,直到系统重启,因此我们需要通过手动释放(指令或者其他系统调用),共享内存的周期随内核,文件是生命周期随进程。

补充指令集–ipc的指令

查看进程的共享内存指令:ipcs -m
在这里插入图片描述

删除创建的共享内存:ipcrm -m shmid
在这里插入图片描述

key和shmid区别

key属于用户形成,是内核使用的一个字段,用户不能使用key来进行shm的管理,内核进行区分shm的唯一性。

shmid:是内核给用户返回的一个标识符,用来进行用户级对共享内存进行管理的id值(fd)。

因此在适应指令对共享内存操作时,使用的是shmid。

创建并获取共享内存代码

//Shm.hpp

#ifndef __SHM_HPP__
#define __SHM_HPP__

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

const int gCreater = 1;
const int gUser = 2;
const std::string gpathname = "/home/gwj/111/lesson22_shm";
const int gproj_id = 0x66;
const int gShmSize = 4096;

class Shm
{
private:
    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)
        {
            perror("shmget");
        }
        return shmid;
    }

public:
    Shm(const std::string &pathname, int proj_id, int who)
        : _pathname(pathname), _proj_id(proj_id), _who(who)
    {
        _key = GetCommKey();
        if(_who==gCreater) GetShmUseCreate();
        else if(_who==gUser) GetShmForUse();
        std::cout<<"shmid: "<<_shmid<<std::endl;
        std::cout<<"_key: "<<ToHex(_key)<<std::endl;

    }

    ~Shm() 
    {

    }

    std::string ToHex(key_t key)
    {
        char buffer[128];
        snprintf(buffer, sizeof(buffer), "0x%x", key);
        return buffer;
    }

    bool GetShmUseCreate()
    {
        if (_who == gCreater)
        {
            _shmid = GetShmHelper(_key, gShmSize, IPC_CREAT | IPC_EXCL);
            if (_shmid >= 0)
                return true;
        }
        return false;
    }

    bool GetShmForUse()
    {
        if (_who == gUser)
        {
            _shmid = GetShmHelper(_key, gShmSize, IPC_CREAT);
            if (_shmid >= 0)
                return true;
        }
        return false;
    }

private:
    key_t _key;
    int _shmid;
    std::string _pathname;
    int _proj_id;
    int _who;
};

#endif
//client.cc

#include"Shm.hpp"

int main()
{
    Shm shm(gpathname,gproj_id,gUser);

    return 0;
}
//server.cc

#include"Shm.hpp"

int main()
{
    Shm shm(gpathname,gproj_id,gCreater);
    return 0;
}

运行结果:

在这里插入图片描述

删除共享内存函数接口

删除共享内存函数接口:

#include <sys/ipc.h>
#include <sys/shm.h>

int shmctl(int shmid, int cmd, struct shmid_ds *buf);

在这里插入图片描述

参数:

  • shmid:由shmget返回的共享内存标识码
  • cmd:将要采取的动作(有三个可取值)
  • buf:指向一个保存着共享内存的模式状态和访问权限的数据结构

删除共存内存函数代码演示

删除共享内存的函数接口写在上述析构函数中:

~Shm() 
{
	if(_who==gCreater)
	{
 		int res=shmctl(_shmid,IPC_RMID,nullptr);
	}
	std::cout<<"shm remove done..."<<std::endl;
}

运行结果:

在这里插入图片描述

共享内存段连接到进程地址空间函数接口

共享内存段连接到进程地址空间函数为shmat

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

void *shmat(int shmid, const void *shmaddr, int shmflg);  //关联


参数:

  • shmid: 共享内存标识
  • shmaddr:指定连接的地址
  • shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY

返回值:失败返回0,成功返回地址空间中共享内存的起始地址。

代码演示

void *AttachShm()
{
  void *shmaddr=shmat(_shmid,nullptr,0);
  if(shmaddr==nullptr)
  {
      perror("shmat");
  }
  std::cout<<"who: "<<RoleToString(_who)<<"attch shm..."<<std::endl;
  return shmaddr;
}

运行结果演示:

在这里插入图片描述

注意: perms是共享内存的权限,需要修改成666或者664

取消关联

取消函数关联的函数为:

int shmdt(const void *shmaddr);  //取消关联

代码演示

void DetachShm(void *shmaddr)
{
    if(shmaddr==nullptr) return;
    shmdt(shmaddr);
    std::cout<<"who: "<<RoleToString(_who)<<"detcah shm..."<<std::endl;

}

共享内存优缺点

共享内存不提供对共享内存的任何保护机制,双方进程不会出现等待进程的现象,会造成数据不一致问题。例如下面的例子,客户端不写入任何内容,但是服务端一直在读入

在这里插入图片描述

在访问共享内存时没有使用任何系统调用,共享区是所有进程IPC中速度最快的,因为共享内存大大减少数据拷贝次数。

对共享内存进行保护

虽然共享内存不存在保护机制,但是我们可以=使用管道,管道有自我保护机制,于是可以在创建共享内存时把管道也建立好。服务器在读数据之前,都需要去读管道。

//client.cc

#include"Shm.hpp"
#include"namedPipe.hpp"

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

    //打开管道
    NamePiped fifo(comm_path,User);
    fifo.OpenForWrite();

    //当成string进行通信
    char ch='A';
    while(ch<='Z')
    {
        shmaddr[ch-'A']=ch;
        ch++;

        std::string temp="weakup";
        fifo.WriteNamedPipe(temp);
        sleep(2);
    }

    return 0;
}
//server.cc

#include"Shm.hpp"
#include"namedPipe.hpp"
int main()
{
    //创建共享内存
    Shm shm(gpathname,gproj_id,gCreater);
    char *shmaddr=(char*)shm.Addr();
    //创建管道
    NamePiped fifo(comm_path,Creater);
    fifo.OpenForRead();
    while(true)
    {
        std::string temp;
        fifo.ReadNamedPipe(&temp);
        std::cout<<"shm memory content: "<<shmaddr<<std::endl;
        sleep(1);
    }

    return 0;
}

在这里插入图片描述

当客户端不去写入,服务端就不会读取,只有当客户端开始写入时,才开始读取。

获取共享内存属性

通过 shmctl 函数区获取共享内存的属性。struct shmid_ds 结构体就是用户层面去描述一个共享内存的结构体。

在这里插入图片描述

在这里插入图片描述

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;
}

关于共享内存的完整代码

//Shm.hpp

#ifndef __SHM_HPP__
#define __SHM_HPP__

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

const int gCreater = 1;    //用于标识创建者
const int gUser = 2;       //标识使用者
const std::string gpathname = "/home/gwj/111/lesson22_shm";
const int gproj_id = 0x66;
const int gShmSize = 4096;

class Shm
{
private:
    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)
        {
            perror("shmget");
        }
        return shmid;
    }

    std::string RoleToString(int who)
    {
        if(who==gCreater) return "Creater";
        else if(who==gUser) return "gUser";
        else return "None";
    }

        void *AttachShm()
    {
        if(_addrshm!=nullptr) DetachShm(_addrshm);

        void *shmaddr=shmat(_shmid,nullptr,0);
        if(shmaddr==nullptr)
        {
            perror("shmat");
        }
        std::cout<<"who: "<<RoleToString(_who)<<"attch shm..."<<std::endl;
        return shmaddr;
    }

    void DetachShm(void *shmaddr)
    {
        if(shmaddr==nullptr) return;
        shmdt(shmaddr);
        std::cout<<"who: "<<RoleToString(_who)<<"detcah 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) GetShmUseCreate();
        else if(_who==gUser) GetShmForUse();
        _addrshm=AttachShm();

        std::cout<<"shmid: "<<_shmid<<std::endl;
        std::cout<<"_key: "<<ToHex(_key)<<std::endl;

    }

    ~Shm() 
    {
        if(_who==gCreater)
        {
            int res=shmctl(_shmid,IPC_RMID,nullptr);
        }
        std::cout<<"shm remove done..."<<std::endl;
    }

    std::string ToHex(key_t key)
    {
        char buffer[128];
        snprintf(buffer, sizeof(buffer), "0x%x", key);
        return buffer;
    }

    bool GetShmUseCreate()
    {
        if (_who == gCreater)
        {
            _shmid = GetShmHelper(_key, gShmSize, IPC_CREAT | IPC_EXCL | 0666);
            
            if (_shmid >= 0)
                return true;
            std::cout<<"shm create done..."<<std::endl;

        }
        return false;
    }

    bool GetShmForUse()
    {
        if (_who == gUser)
        {
            _shmid = GetShmHelper(_key, gShmSize, IPC_CREAT | 0666);

            if (_shmid >= 0)
                return true;
            std::cout<<"shm get done..."<<std::endl;
            
        }
        return false;
    }

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

    void *Addr()
    {
        return _addrshm;
    }

private:
    key_t _key;
    int _shmid;

    std::string _pathname;
    int _proj_id;

    int _who;
    void *_addrshm;
};

#endif
#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";
#define DefaultFd -1
#define Creater 1
#define User 2
#define Read O_RDONLY
#define Write O_WRONLY
#define BaseSize 4096

class NamePiped
{
private:
    bool OpenNamedPipe(int mode)
    {
        _fd = open(_fifo_path.c_str(), mode);
        if (_fd < 0)
            return false;
        return 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);
    }
    // const &: const std::string &XXX
    // *      : std::string *
    // &      : std::string & 
    int ReadNamedPipe(std::string *out)
    {
        char buffer[BaseSize];
        int n = read(_fd, buffer, sizeof(buffer));
        if(n > 0)
        {
            buffer[n] = 0;
            *out = buffer;
        }
        return n;
    }
    int WriteNamedPipe(const std::string &in)
    {
        return write(_fd, in.c_str(), in.size());
    }
    ~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) close(_fd);
    }

private:
    const std::string _fifo_path;
    int _id;
    int _fd;
};
//server.cc

#include"Shm.hpp"
#include"namedPipe.hpp"
int main()
{
    //创建共享内存
    Shm shm(gpathname,gproj_id,gCreater);
    char *shmaddr=(char*)shm.Addr();
    //创建管道
    NamePiped fifo(comm_path,Creater);
    fifo.OpenForRead();
    while(true)
    {
        std::string temp;
        fifo.ReadNamedPipe(&temp);
        std::cout<<"shm memory content: "<<shmaddr<<std::endl;
        sleep(1);
    }

    return 0;
}
//client.cc

#include"Shm.hpp"
#include"namedPipe.hpp"

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

    //打开管道
    NamePiped fifo(comm_path,User);
    fifo.OpenForWrite();

    //当成string进行通信
    char ch='A';
    while(ch<='Z')
    {
        shmaddr[ch-'A']=ch;
        std::string temp="weakup";
        std::cout<<"add  "<<ch<<"into Shm, "<<"weakup reader"<<std::endl;
        fifo.WriteNamedPipe(temp);
        sleep(2);
        ch++;

    }

    return 0;
}

在这里插入图片描述

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

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

相关文章

真空油炸机的特点是什么?

真空油炸机的特点&#xff0c;如同一位技艺精湛的厨师&#xff0c;不仅确保了食材的完美呈现&#xff0c;更在科技与传统工艺之间找到了完美的平衡。 首先&#xff0c;真空油炸机以其独特的真空环境&#xff0c;为食材打造了一个低氧、低压的烹饪空间。在这样的环境中&#xff…

LabVIEW比例压力控制阀自动测试系统

开发了一套基于LabVIEW编程和PLC控制的比例控制阀自动测试系统。该系统能够实现共轨管稳定的超高压供给&#xff0c;自动完成比例压力控制阀的耐久测试、流量滞环测试及压力-流量测试。该系统操作简便&#xff0c;具有高精度和高可靠性&#xff0c;完全满足企业对自动化测试的需…

vue3中谷歌地图+外网申请-原生-实现地址输入搜索+点击地图获取地址回显 +获取国外的geoJson实现省市区级联选择

一. 效果&#xff1a;输入后显示相关的地址列表&#xff0c;选中后出现标示图标和居中定位 1.初始化谷歌地图 在index.html加上谷歌api请求库 <script src"https://maps.googleapis.com/maps/api/js?key申请到的谷歌地图密钥&vweekly&librariesgeometry,place…

指针!!C语言(第一篇)

指针1 指针变量和地址1.取地址操作符(&)2.指针变量和解引用操作符(*) 指针变量的大小和类型指针的运算特殊指针1.viod*指针2.const修饰指针3.野指针 assert断言指针的使用和传址调用1.strlen的模拟实现2.传值调用和传址调用 指针变量和地址 在认识指针之前&#xff0c;我们…

鸿蒙实训笔记

第一天 #初始化一个新的NPM项目(根据提示操作) npm init #安装TSC、TSLint和NodeJS的类型声明 npm install -s typescript tslint types/node 在根目录中新建一个名为tsconfig.json的文件&#xff0c;然后在代码编辑器中打开&#xff0c;写入下述内容&#xff1a; {"co…

SpringBoot+Vue实现简单的文件上传(Excel篇)

SpringBootVue实现简单的文件上传 1 环境 SpringBoot 3.2.1&#xff0c;Vue 2&#xff0c;ElementUI 2 页面 3 效果&#xff1a;只能上传xls文件且大小限制为2M&#xff0c;选择文件后自动上传。 4 前端代码 <template><div class"container"><el…

性能测试(2)

jmeter参数化 loadrunner Jmeter IP欺骗&#xff0c;也称为IP欺诈&#xff0c;是指通过伪装、篡改IP地址的方式&#xff0c;进行网络攻击或欺骗行为。这种行为可能会导致网络安全问题&#xff0c;包括身份盗窃、数据泄露、DDoS攻击等。为了保护自己的网络安全&#xff0c;用户…

5.3 需求分析

软件需求 定义 分类 真题 需求工程 需求获取 真题 需求分析 状态转换图 数据流图 数据流图分层 顶层数据流图、0层数据流图 1层数据流图 真题 需求规约 需求定义方法 需求验证 需求验证内容 需求管理 版本控制 需求跟踪 变更控制 真题

mysql不初始化升级

1、下载mysql&#xff0c;下载地址&#xff1a;MySQL :: Download MySQL Community Server 2、解压下载好的mysql&#xff0c;修改配置文件的datadir指定目录为当前数据存储的目录 3、通过管理员cmd进入新版本mysql的bin目录&#xff0c; 然后执行命令安装mysql服务&#xff…

2024年山东省安全员B证证考试题库及山东省安全员B证试题解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年山东省安全员B证证考试题库及山东省安全员B证试题解析是安全生产模拟考试一点通结合&#xff08;安监局&#xff09;特种作业人员操作证考试大纲和&#xff08;质检局&#xff09;特种设备作业人员上岗证考试大…

LeetCode 441, 57, 79

目录 441. 排列硬币题目链接标签思路代码 57. 插入区间题目链接标签思路两个区间的情况对每个区间的处理最终的处理 代码 79. 单词搜索题目链接标签原理思路代码 优化思路代码 441. 排列硬币 题目链接 441. 排列硬币 标签 数学 二分查找 思路 由于本题所返回的 答案在区间…

通过 PPPOE 将 linux 服务器作为本地局域网 IPv4 外网网关

将 linux 服务器作为本地外网网关&#xff0c;方便利用 Linux 生态中的各种网络工具&#xff0c;对流量进行自定义、精细化管理… 环境说明 拨号主机&#xff1a;CentOS 7.9, Linux Kernel 5.4.257 拨号软件: rp-pppoe-3.11-7.el7.x86_64初始化 1、升级系统到新的稳定内核&a…

半年GMV狂飙166亿!酒水赛道正在崛起自播之路

从2022年开始&#xff0c;酒水以“兴趣”为核心的直播电商迎来爆发式增长。以抖音电商为例&#xff0c;2022年下半年整体销售额破百亿&#xff0c;环比增幅超100%&#xff0c;2023年全年更是破300亿大关&#xff01;兴趣电商成为酒行业的第二增长曲线。 今年上半年&#xff0c;…

机器学习第四十七周周报 CF-LT

文章目录 week47 CF-LT摘要Abstract1. 题目2. Abstract3. 网络结构3.1 CEEMDAN&#xff08;完全自适应噪声集合经验模态分解&#xff09;3.2 CF-LT模型结构3.3 SHAP 4. 文献解读4.1 Introduction4.2 创新点4.3 实验过程 5. 结论6.代码复现小结参考文献 week47 CF-LT 摘要 本周…

可视耳勺是不是智商税?五款好用挖耳勺推荐!

随着人们追求健康生活方式的需求日益增长&#xff0c;可视耳勺这一产品逐渐走入了人们的视野&#xff0c;并受到了广泛的青睐。 然而&#xff0c;市场上可视耳勺品牌和种类繁多&#xff0c;部分产品存在清晰度不高、亮度较暗等问题&#xff0c;在使用过程很容易存在损坏耳道的风…

负载箱如何帮助维持电气系统的最佳性能

负载箱在维持电气系统最佳性能方面发挥着至关重要的作用&#xff0c;以下是负载箱如何帮助维持电气系统最佳性能的详细分析&#xff1a; 一、保护电气设备 负载箱能够在电气系统中产生恒定的负载&#xff0c;使电气设备在正常工作状态下运行。这避免了因负载波动过大而导致的…

数据库管理1

数据库管理 数据库运维。 sql语句 数据库用来增删改查的语句 备份 数据库的数据进行备份 主从复制&#xff0c;读写分离&#xff0c;高可用。 数据库的概念和相关的语法和规范&#xff1a; 数据库&#xff1a;组织&#xff0c;存储&#xff0c;管理数据的仓库。 数据库的管理系…

FreeRTOS_定时器

定时器概述 定时器运行过程 定时器就像一个闹钟&#xff0c;它有超时时间、函数、是否为周期性这三个部分。 超时时间&#xff1a;什么时候到时间&#xff0c;就像闹钟响起函数&#xff1a;闹钟响起&#xff0c;要干什么是否为周期性&#xff1a;这个闹钟只响一次&#xff0…

杭州高校大学智能制造实验室数字孪生可视化系统平台建设项目验收

杭州高校大学智能制造数字孪生技术作为智能制造的重要支撑&#xff0c;通过构建虚拟世界的镜像&#xff0c;实现对物理世界的实时监控、预测和优化。杭州高校大学智能制造实验室数字孪生可视化系统平台建设项目&#xff0c;旨在通过引入先进的数字孪生技术&#xff0c;构建一个…

刚刚,Claude国内可直连!

刚刚&#xff0c;Claude国内可直连&#xff01; 大家好&#xff01;我是科技博主Maynor。今天我们要分享一个激动人心的消息&#xff1a;知名AI助手Claude现在可以在国内直接访问了&#xff01; 这对于科技爱好者、学生、研究人员和各行各业的专业人士来说都是一个重大利好。…