当前位置: 首页 > news >正文

【Linux】共享内存

 🌻个人主页:路飞雪吖~

       🌠专栏:Linux


目录

 

☃️共享内存

🪄 shmget函数 用来创建共享内存

✨共享内存的管理指令:

🌠 shmid VS key

 ✨共享内存函数

🍔 shmget() 创建共享内存

🍔 shmctl() 用于控制共享内存

🍔 shmat() 将共享内存段连接到自己的进程地址空间

🍔 shmdt() 将共享内存段与当前进程脱离

✨共享内存的特点:

✨共享内存实现:


☃️共享内存

system V 共享内存

通过系统调用,1.开辟一段物理内存空间,把新开辟的物理内存映射到某一个进程的虚拟地址空间的共享区中的某一个位置【与动态库的加载原理类似】,映射完成之后,2.这个进程对用户返回内存空间的虚拟起始地址,3.另外一个进程类似,也对用户返回内存空间的虚拟起始地址。

让两个进程通过各自的地址空间,映射到同一块物理内存 ---- 共享内存。【先让不同的进程,看到同一份资源】。

共享内存 = 共享内存的内核数据结构 + 内存块

操作系统里可以有多个不同的进程,使用不同的共享内存来通信。共享内存 在任何时刻,可以在OS内部同时存在很多个,所以OS要对共享内存进行管理【先描述,再组织】,共享内存要有唯一的标识符【标识符 --- 用户传入的key】。

🪄 shmget函数 用来创建共享内存

IPC_CREAT:若单独使用,如果shm共享内存不存在,就创建;存在就获取它,并返回 --- 保证调用进程能拿到共享内存。

IPC_EXCL:单独使用无意义;

IPC_CREAT | IPC_EXCL :若shm共享内存不存在,就创建它;若存在,出错返回 --- 只要成功,一定是新的共享内存,不拿老的共享内存。

key_t key :系统调用接口的参数;

1.必须是由用户输入;

2.如何保证shm的唯一性?--- 让用户传入唯一的键值;

这个key,为什么要用户传入?内核自己生成不就行了吗?若内核自己生成 另一个进程就无法判断前一个进程使用的是哪一个shm共享内存,所以要让用户自己传入。

3.怎么保证不同进程看到的是同一个共享内存?

在进程1 和 进程2 自己的源代码上定义一个全局的【int key 要保证key的唯一性不被修改】,这个全局的key分别被两个进程包含,在进程1创建时,把全局的key设置进内核里,设置成功后,进程2也就知道是哪一块共享内存了【key在编码上进程1和进程2都知道】。

如何设置key?设置的key会不会冲突?----- 系统调用 ftok()

🌠共享内存 VS 命名管道 是如何让不同的进程看到同一份资源的?

共享内存:通过 ftok() 【路径 + 项目ID】;

命名管道: 通过文件路径

 Makefile:

SERVER=server
CLIENT=client 
cc=g++
SERVER_SRC=Server.cc
Client_SRC=Client.cc.PHONY:all
all: $(SERVER) $(CLIENT)$(SERVER):$(SERVER_SRC)$(cc) -o $@ $^ -std=c++11
$(CLIENT):$(Client_SRC)$(cc) -o $@ $^ -std=c++11.PHONY:clean
clean:rm -f $(SERVER) $(CLIENT)

Comm.hpp:

#include <iostream>
#include <string>
#include <cstdio>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>const std::string gpath = "/home/zxl/study/stu0512";
int gprojID = 0x6666;
int gshmsize = 4096;std::string ToHex(key_t k)
{char buffer[64];snprintf(buffer, sizeof(buffer), "%0x", k);return buffer;
}

 Client.cc:

#include <iostream>
#include "Comm.hpp"int main()
{key_t k = ::ftok(gpath.c_str(), gprojID);std::cout << "k : " << ToHex(k) << std::endl;return 0;
}

Server.cc:

#include <iostream>
#include "Comm.hpp"int main()
{// 1. 创建 keykey_t k = ::ftok(gpath.c_str(), gprojID);if(k < 0){std::cerr << "fork error" << std::endl;return 1;}std::cout << "k : " << ToHex(k) << std::endl;// 2. 创建共享内存 && 获取int shmid = ::shmget(k, gshmsize, IPC_CREAT | IPC_EXCL);if(shmid < 0){std::cout << "shmget error" << std::endl;return 2;}std::cout << "shmid : " << shmid << std::endl;return 0;
}

共享内存的生命周期:随内核!

1. 用户必须让OS释放;

    • 指令释放

    • 代码编写(共享内存函数)

2. OS重启。 

共享内存的管理指令:

 • ipcs  -m :查看共享内存的属性

 • ipcrm  -m  1 :删除指定的共享内存

shmid 是按照顺序排序的 --- 类似于数组下标

🌠 shmid VS key

 • shmid : 只给用户用的一个标识shm的标识符【类似 fd 和 FILE*】。

 • key :只作为内核中,区分shm唯一性的标识符,不作为用户管理 shm 的 id 值【类似文件描述符表中,struct file 的地址】。

 ✨共享内存函数

🍔 shmget() 创建共享内存

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

参数:

• key : 这个共享内存段名字

• size :共享内存大小

• shmflg :由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的。

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

🌻注意:共享内存也有权限!!! 

🍔 shmctl() 用于控制共享内存

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

参数:

• shmid:由shmget返回的共享内存标识码;

• cmd:将要采取的动作(有三个可取值);

• buf:指向一个保存着共享的模式状态和访问权限的数据结构;

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

命令说明
IPC_STAT把shmid_ds结构中的数据设置为共享内存的当前关联值
IPC_SET在进程有足够权限的前提下,把共享内存的当前关联值设置为shmid_ds 数据结构中给
IPC_RMID删除共享内存段

对共享内存 用户层,进行完整的生命周期的管理,创建 --到--> 释放。

Server.cc :IPC_RMID 删除共享内存 

#include <iostream>
#include <unistd.h>
#include "Comm.hpp"int main()
{// 1. 创建 keykey_t k = ::ftok(gpath.c_str(), gprojID);if(k < 0){std::cerr << "fork error" << std::endl;return 1;}std::cout << "k : " << ToHex(k) << std::endl;// 2. 创建共享内存 && 获取// 注意:共享内存也有权限!int shmid = ::shmget(k, gshmsize, IPC_CREAT | IPC_EXCL | gmode);if(shmid < 0){std::cout << "shmget error" << std::endl;return 2;}std::cout << "shmid : " << shmid << std::endl;sleep(1);// n. 删除共享内存shmctl(shmid, IPC_RMID, nullptr);std::cout << "delete shm done" << std::endl;sleep(5);return 0;
}

🍔 shmat() 将共享内存段连接到自己的进程地址空间

这个进程应该如何与共享内存关联呢? --- shmat () 系统调用

在底层:把申请好的共享内存 和 基于现在的进程地址空间里 开辟一个地址空间【虚拟地址】,要知道共享内存的开始和长度,虚拟地址空间在内核当中【mm_struct --> vm_area_struct,里面插入一个新的节点,构建一个start,再加一个偏移量】就能够映射到地址空间,同时填充一下页表。

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

• shmid:共享内存标识;

• shmaddr :指定连接的地址;【用户指定,挂接到什么虚拟地址】

• shmflg:它的两个可能取值是 SHM_RND 和 SHM_RDONLY ;

• 返回值:成功返回一个指针,指向共享内存第一个节;失败返回-1。

🌠说明:

shmaddr为NULL,核⼼⾃动选择⼀个地址

shmaddr不为NULL且shmflg⽆SHM_RND标记,则以shmaddr为连接地址。

shmaddr不为NULL且shmflg设置了SHM_RND标记,则连接的地址会⾃动向下调整为SHMLBA的整数倍。

公式:shmaddr - (shmaddr % SHMLBA)

shmflg=SHM_RDONLY,表⽰连接操作⽤来只读共享内存

 Server.cc :将共享内存段连接到自己的进程地址空间

#include <iostream>
#include <unistd.h>
#include "Comm.hpp"int main()
{// 1. 创建 keykey_t k = ::ftok(gpath.c_str(), gprojID);if(k < 0){std::cerr << "fork error" << std::endl;return 1;}std::cout << "k : " << ToHex(k) << std::endl;// 2. 创建共享内存 && 获取// 注意:共享内存也有权限!int shmid = ::shmget(k, gshmsize, IPC_CREAT | IPC_EXCL | gmode);if(shmid < 0){std::cout << "shmget error" << std::endl;return 2;}std::cout << "shmid : " << shmid << std::endl;sleep(1);// 3. 共享内存挂接到自己的地址空间中void *ret = shmat(shmid, nullptr, 0);std::cout << "attach done:" << (long long)ret << std::endl;sleep(1);// n. 删除共享内存shmctl(shmid, IPC_RMID, nullptr);std::cout << "delete shm done" << std::endl;sleep(5);return 0;
}

🌠小贴士:

void *ret = shmat(shmid, nullptr, 0); // 挂接失败,原因:

int shmid = ::shmget(k, gshmsize, IPC_CREAT | IPC_EXCL | gmode); // gmode:权限的大小【0600 自己设置】

 perms: 的大小表示是否允许,创建的共享内存,是否允许

注意:共享内存也有权限,和文件一样。

🍔 shmdt() 将共享内存段与当前进程脱离

当不使用共享内存时,要去关联【把映射关系去掉】。

int shmdt(const void *shmaddr); 

参数:

•  shmaddr:由shmat所返回的指针;

• 返回值:成功返回 0,失败返回 -1;

注意:将共享内存段与当前进程脱离不等于删除共享内存段。

  Server.cc :将共享内存段与当前进程脱离

#include <iostream>
#include <unistd.h>
#include "Comm.hpp"int main()
{// 1. 创建 keykey_t k = ::ftok(gpath.c_str(), gprojID);if(k < 0){std::cerr << "fork error" << std::endl;return 1;}std::cout << "k : " << ToHex(k) << std::endl;// 2. 创建共享内存 && 获取// 注意:共享内存也有权限!int shmid = ::shmget(k, gshmsize, IPC_CREAT | IPC_EXCL | gmode);if(shmid < 0){std::cout << "shmget error" << std::endl;return 2;}std::cout << "shmid : " << shmid << std::endl;sleep(1);// 3. 共享内存挂接到自己的地址空间中void *ret = shmat(shmid, nullptr, 0);std::cout << "attach done:" << (long long)ret << std::endl;sleep(1);// 4. 将共享内存段与当前进程脱离::shmdt(ret);std::cout << "detach done: "  << std::endl;sleep(1);// n. 删除共享内存shmctl(shmid, IPC_RMID, nullptr);std::cout << "delete shm done" << std::endl;sleep(1);return 0;
}

 

建立完共享资源后【让不同的进程看见同一块资源】,该如何进行通信?

🌠小贴士:

操作系统,申请空间,是按照块为单位的:4KB,1KB,2KB,4MB;

当你申请一个4097个字节的空间【4096 --> 4KB】,对于操作系统来讲,在内部会申请 4096 * 2 的空间,但是用户只能用 4097 个字节,这样子就会造成空间浪费。

🌠小贴士:

strinfo[ch - 'A'] = ch; // 这里操作shm的时候,怎么没有用系统调用?

管道建立一旦完成之后,不管命名还是匿名,都需要用 read() 和 write() 来进行调用,把数据从用户拷贝到文件的缓冲去里面,然后从文件的缓冲区里面,通过read() 和 write() 拷贝到自己的用户缓冲区。【管道的缓冲区,没有指针的概念,用的是文件描述符struct file 只能通过系统调用来进行文件读写】

共享内存这里为什么不用系统调用呢?进程的地址空间当中【代码区、数据区、字符常量区、未/已初始化数据区、堆区、栈区、堆栈之间的共享区】这些都是用户空间!【当共享内存被挂接到虚拟地址空间上之后,用户可以直接使用,不需要使用系统调用,如同malloc出来的空间一样】。

共享内存的特点:

1. 通信速度最快的; 【将共享内存挂接到进程虚拟地址空间里面,就意味着作为共享内存,在用户空间拿虚拟地址,直接就能访问,往共享内存里面写东西,另一个进程也能立马获取】

2. 让两个进程在各自的用户空间共享内存块,但是 没有加任何的保护机制!【有无数据都一直读,另一个进程退出,也不会对当前进程限制,会一直读】

3. 共享内存,保护机制,需要由用户完成保护 --- 信号量 --- 命名管道。

共享内存就是这两个进程的共享资源,共享资源被加保护,叫做临界资源。访问公共资源的代码,叫临界区,其余的正常代码为非临界区。

把共享资源变成临界资源,保护起来,往往需要给临界区代码进行加锁。

进程具有独立性,两个进程要进行通信,就要让这两个进程能看到同一份资源,当读和写不完整【还没写完就读】,就会造成数据不一致的问题,为此就有了 临界区 && 临界资源 && 加锁 && 同步!

共享内存实现:

Makefile:

SERVER=server
CLIENT=client 
cc=g++
SERVER_SRC=Server.cc
Client_SRC=Client.cc.PHONY:all
all: $(SERVER) $(CLIENT)$(SERVER):$(SERVER_SRC)$(cc) -o $@ $^ -std=c++11
$(CLIENT):$(Client_SRC)$(cc) -o $@ $^ -std=c++11.PHONY:clean
clean:rm -f $(SERVER) $(CLIENT)

Fifo.hpp:

#pragma once#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>const std::string gpipeFile = "./fifo"; // 公共文件
const mode_t gfifomode = 0600;
const int gdefault = -1;
const int gsize = 1024;
const int gForRead = O_RDONLY;
const int gForWrite = O_WRONLY;class Fifo
{
private:void OpenFifo(int flag){// 如果读端打开文件时,写端还没有打开,读端对应的open就会阻塞_fd = ::open(gpipeFile.c_str(), flag);if (_fd < 0){std::cerr << "open error" << std::endl;}}public:Fifo() : _fd(-1){umask(0);int n = ::mkfifo(gpipeFile.c_str(), gfifomode);if (n < 0){std::cerr << "mkfifo error" << std::endl;return;}std::cout << "mkfifo success" << std::endl;// sleep(10);}bool OpenPipeForwrite(){OpenFifo(gForWrite);if (_fd < 0)return false;return true;}bool OpenPipeForRead(){OpenFifo(gForRead);if (_fd < 0)return false;return true;}int wait(){int code = 0;ssize_t n = ::read(_fd, &code, sizeof(code));if (n == sizeof(code)){return 0;}else if (n == 0){return 1;}else{return 2;}}void Signal(){int code = 1;::write(_fd, &code, sizeof(code));}~Fifo(){if (_fd >= 0)::close(_fd);int n = ::unlink(gpipeFile.c_str());if (n < 0){std::cerr << "unlink error" << std::endl;return;}std::cout << "unlink success" << std::endl;}private:int _fd;
};Fifo gpipe;

ShareMemory.hpp:

#include <iostream>
#include <string>
#include <cstdio>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include "StructFile.hpp"const std::string gpath = "/home/zxl/study/stu0512";
int gprojID = 0x6666;
// 操作系统,申请空间,是按照块为单位的:4KB、1KB、2KB、4MB
int gshmsize = 4096;
mode_t gmode = 0600;std::string ToHex(key_t k)
{char buffer[64];snprintf(buffer, sizeof(buffer), "%0x", k);return buffer;
}class ShareMemory
{
private:void CreateShmHelper(int shmflg){_key = ::ftok(gpath.c_str(), gprojID);if (_key < 0){std::cerr << "fork error" << std::endl;return;}_shmid = ::shmget(_key, gshmsize, shmflg);if (_shmid < 0){std::cout << "shmget error" << std::endl;return;}std::cout << "shmid : " << _shmid << std::endl;}public:ShareMemory() : _shmid(-1), _key(0), _addr(nullptr){}~ShareMemory() {}// 创建共享内存void CreateShm(){if (_shmid == -1)CreateShmHelper(IPC_CREAT | IPC_EXCL | gmode);}// 获取void GetShm(){CreateShmHelper(IPC_CREAT);}// 挂接void AttachShm(){_addr = shmat(_shmid, nullptr, 0);if ((long long)_addr == -1){std::cout << "attach error" << std::endl;}}// 将共享内存段与当前进程脱离void DetachShm(){if (_addr != nullptr)::shmdt(_addr);std::cout << "detach done: " << std::endl;}// 删除共享内存void DeleteShm(){shmctl(_shmid, IPC_RMID, nullptr);std::cout << "delete shm done" << std::endl;}void *GetAddr(){return _addr;}void ShmMeta(){}private:int _shmid;key_t _key;void *_addr;
};ShareMemory shm;

StructFile.hpp:

#include <iostream>struct data
{// 409 --> 4KBchar status[32];char lasttime[48];char image[4000];
};

 Time.hpp: 

#pragma once#include <iostream>
#include <string>
#include <ctime>std::string GetCurrTime()
{time_t t = time(nullptr);struct tm *curr = ::localtime(&t);char currtime[32];snprintf(currtime, sizeof(currtime), "%d-%d-%d %d:%d:%d",curr->tm_year + 1900,curr->tm_mon + 1,curr->tm_mday,curr->tm_hour,curr->tm_min,curr->tm_sec);return currtime;
}

Client.cc:

#include <iostream>
#include <string.h>
#include "ShareMemory.hpp"
#include "Time.hpp"
#include "Fifo.hpp"int main()
{shm.GetShm();shm.AttachShm();gpipe.OpenPipeForwrite();// sleep(10);// std::cout << "Client attach done" << std::endl;// 在这里进行IPC// char *strinfo = (char*)shm.GetAddr();// printf("Client虚拟地址:%p\n", strinfo);// char ch = 'A';// while(ch <= 'Z')// {//     sleep(3);//     strinfo[ch - 'A'] = ch;// 这里操作shm的时候,怎么没有用系统调用?//     ch++;// }// 把共享内存当做一个结构体struct data *image = (struct data *)shm.GetAddr(); char ch = 'A';while (ch <= 'Z'){strcpy(image->status, "最新");// 在缓冲区里面拷贝内容strcpy(image->lasttime, GetCurrTime().c_str());strcpy(image->image, "zxxxxxxxxxxxxxxxxxx");gpipe.Signal();sleep(3);}shm.DetachShm();// std::cout << "Client detach done" << std::endl;;// sleep(10);return 0;
}// int main()
// {
//     key_t k = ::ftok(gpath.c_str(), gprojID);
//     std::cout << "k : " << ToHex(k) << std::endl;
//     return 0;
// }

Server.cc:

#include <iostream>
#include <string.h>
#include <unistd.h>
#include "ShareMemory.hpp"
#include "Time.hpp"
#include "Fifo.hpp"int main()
{//std::cout << "time: " << GetCurrTime() << std::endl; shm.CreateShm();shm.AttachShm();gpipe.OpenPipeForRead();// sleep(10);// std::cout << "Server attach done" << std::endl;//sleep(10);// 在这里进行IPC// char *strinfo = (char*)shm.GetAddr();// printf("Server虚拟地址:%p\n", strinfo);// 若共享资源被保护起来了,访问公共资源的代码 ---- 临界区// while(true)// {//     printf("%s\n", strinfo);//     sleep(1);// }// 把共享内存当做一个结构体struct data *image = (struct data*)shm.GetAddr();while(true){gpipe.wait();// 等待写入完成,再输出printf("status: %s\n", image->status);printf("lasttime: %s\n", image->lasttime);printf("image: %s\n", image->image);strcpy(image->status, "过期");//sleep(5);}// sleep(10);shm.DetachShm();// std::cout << "Server detach done" << std::endl;// sleep(10);shm.DeleteShm();// std::cout << "delete shm" << std::endl;return 0;
}// int main()
// {
//     // 1. 创建 key
//     key_t k = ::ftok(gpath.c_str(), gprojID);
//     if(k < 0)
//     {
//         std::cerr << "fork error" << std::endl;
//         return 1;
//     }
//     std::cout << "k : " << ToHex(k) << std::endl;//     // 2. 创建共享内存 && 获取
//     // 注意:共享内存也有权限!
//     int shmid = ::shmget(k, gshmsize, IPC_CREAT | IPC_EXCL | gmode);
//     if(shmid < 0)
//     {
//         std::cout << "shmget error" << std::endl;
//         return 2;
//     }
//     std::cout << "shmid : " << shmid << std::endl;//     sleep(5);//     // 3. 共享内存挂接到自己的地址空间中
//     void *ret = shmat(shmid, nullptr, 0);
//     std::cout << "attach done:" << (long long)ret << std::endl;//     sleep(5);//     // 4. 将共享内存段与当前进程脱离
//     ::shmdt(ret);
//     std::cout << "detach done: "  << std::endl;//     sleep(5);//     // 在这里进行通信//     // n. 删除共享内存
//     shmctl(shmid, IPC_RMID, nullptr);
//     std::cout << "delete shm done" << std::endl;//     sleep(5);//     return 0;
// }

如若对你有帮助,记得关注、收藏、点赞哦~ 您的支持是我最大的动力🌹🌹🌹🌹!!!

若有误,望各位,在评论区留言或者私信我 指点迷津!!!谢谢 ヾ(≧▽≦*)o  \( •̀ ω •́ )/

相关文章:

【Linux】共享内存

&#x1f33b;个人主页&#xff1a;路飞雪吖~ &#x1f320;专栏&#xff1a;Linux 目录 ☃️共享内存 &#x1fa84; shmget函数 用来创建共享内存 ✨共享内存的管理指令&#xff1a; &#x1f320; shmid VS key ✨共享内存函数 &#x1f354; shmget() 创建共享内存 &a…...

Cookie、Session、Token

Cookie 1. 什么是cookie? Cookie 是一种由服务器发送到客户端浏览器的小数据片段&#xff0c;用于存储用户的状态信息。例如&#xff0c;用户登录状态或用户偏好设置可以通过Cookie进行管理。计算机cookie更正式地称为 HTTP cookie、网络 cookie、互联网 cookie 或浏览器 coo…...

设计模式Java

UML类图 概述 类图(Class diagram)是显示了模型的静态结构&#xff0c;特别是模型中存在的类、类的内部结构以及它们与其他类的关系等。类图不显示暂时性的信息。类图是面向对象建模的主要组成部分。 类图的作用 在软件工程中&#xff0c;类图是一种静态的结构图&#xff0c…...

Word文档图片排版与批量处理工具推荐

先放下载链接:夸克网盘下载 前几天给大家推荐了 Excel 图片调整软件&#xff0c;当时好多小伙伴问有没有 Word 相关的软件。我在网上找了一圈都没找到合适的&#xff0c;最后在我好久之前记录的一个文档里发现了&#xff0c;这不&#xff0c;马上就来给大家推荐&#xff0c;有…...

[案例五] 实体——赋值质量

最近翻阅了实验室其他人编写的一个“质量赋值”功能,能够直接为实体或组件设定质量。出于好奇,我对其进行了分析。由于自己平时没有用到该功能,所以也借此机会学习一下。 在分析过程中,我发现NX 官方其实并没有提供直接修改质量的功能。一般来说,质量是通过“密度 体积”…...

手撕四种常用设计模式(工厂,策略,代理,单例)

工厂模式 一、工厂模式的总体好处 解耦&#xff1a;客户端与具体实现类解耦&#xff0c;符合“开闭原则”。统一创建&#xff1a;对象创建交由工厂处理&#xff0c;便于集中控制。增强可维护性&#xff1a;新增对象种类时不需要大改动调用代码。便于扩展&#xff1a;易于管理…...

C# 深入理解类(静态函数成员)

静态函数成员 除了静态字段&#xff0c;还有静态函数成员。 如同静态字段&#xff0c;静态函数成员独立于任何类实例。即使没有类的实例&#xff0c;仍然可以调用静 态方法。静态函数成员不能访问实例成员&#xff0c;但能访问其他静态成员。 例如&#xff0c;下面的类包含一…...

数据类型转换

文章目录 基本数据类型&#xff08;primitive type&#xff09;整数类型&#xff1a;浮点类型字符类型boolean类型 类型转换类型转换注意点 基本数据类型&#xff08;primitive type&#xff09; 整数类型&#xff1a; ​ byte占1个字节范围&#xff1a;-128-127 ​ short占…...

深入理解 TypeScript 中的 unknown 类型:安全处理未知数据的最佳实践

在 TypeScript 的类型体系中&#xff0c;unknown 是一个极具特色的类型。它与 any 看似相似&#xff0c;却在安全性上有着本质差异。本文将从设计理念、核心特性、使用场景及最佳实践等方面深入剖析 unknown&#xff0c;帮助开发者在处理动态数据时既能保持灵活性&#xff0c;又…...

AI:人形机器人的应用场景以及商业化落地潜力分析

应用场景分析 人形机器人的设计使其能够适应人类环境&#xff0c;执行多样化任务。以下是未来主要的应用场景及其详细分析&#xff1a; 医疗与护理 具体应用&#xff1a; 老年护理&#xff1a;协助老年人穿衣、洗澡、喂食&#xff0c;或提供情感陪伴。康复辅助&#xff1a;帮助…...

JavaScript入门【3】面向对象

1.对象: 1.概述: 在js中除了5中基本类型之外,剩下得都是对象Object类型(引用类型),他们的顶级父类是Object;2.形式: 在js中,对象类型的格式为key-value形式,key表示属性,value表示属性的值3.创建对象的方式: 方式1:通过new关键字创建(不常用) let person new Object();// 添…...

亲测有效!OGG 创建抽取进程报错 OGG-08241,如何解决?

前言 今天在测试 OGG 一个功能的时候&#xff0c;需要重新初始化 oggca&#xff0c;所以重装了一下 OGG。重建完之后重新添加抽取进程报错&#xff0c;一直无法添加成功&#xff1a; 经过一翻分析&#xff0c;找到了解决方案&#xff0c;本文记录一下解决过程。 问题描述 OG…...

【第二篇】 初步解析Spring Boot

简介 SpringBoot是由Pivotal团队提供的全新框架&#xff0c;其设计目的是为了用来简化Spring应用的初始搭建以及开发过程的。本文章将详细介绍SpringBoot为什么能够简化项目的搭建以及普通的Spring程序的开发。文章内容若存在错误或需改进的地方&#xff0c;欢迎大家指正&#…...

JVM 机制

目录 一、什么是 JVM&#xff1a; 二、JVM 的运行流程&#xff1a; 三、JVM 内存区域划分&#xff1a; 1、( 1 ) 程序计数器&#xff1a; 1、( 2 ) 元数据区&#xff1a; 1、( 3 ) 栈&#xff1a; 1、( 4 ) 堆&#xff1a; 四、类加载&#xff1a; 1、什么时候会触…...

Java泛型详解

文章目录 1. 引言1.1 什么是泛型1.2 为什么需要泛型1.3 泛型的优势2. 泛型基础2.1 泛型类多个类型参数2.2 泛型方法2.3 泛型接口2.4 类型参数命名约定3. 类型擦除3.1 什么是类型擦除3.2 类型擦除的影响1. 无法获取泛型类型参数的实际类型2. 无法创建泛型类型的数组3. 无法使用`…...

机器学习,深度学习,神经网络,深度神经网络之间有何区别?

先说个人观点&#xff1a;机器学习>神经网络>深度学习≈深度神经网络。深度学习是基于深度神经网络的&#xff0c;深度神经网络和浅层神经网络都是神经网络&#xff0c;而机器学习是包括神经网络在内的算法。 一、机器学习 先说涵盖范围最广的机器学习。机器学习&#…...

AtomicInteger

AtomicInteger 是 Java 并发包 (java.util.concurrent.atomic) 中的一个原子类&#xff0c;用于在多线程环境下对整数进行原子操作。 核心特性 原子性 提供线程安全的原子操作&#xff08;如自增、加法、比较并交换等&#xff09;&#xff0c;确保在多线程环境中操作不会被中…...

威布尔比例风险模型(Weibull Proportional Hazards Model, WPHM)详解:原理、应用与实施

威布尔比例风险模型&#xff08;Weibull Proportional Hazards Model, WPHM&#xff09;详解&#xff1a;原理、应用与实施 一、核心原理&#xff1a;从威布尔分布到比例风险模型 1. 威布尔分布的数学本质 威布尔分布通过两个关键参数&#xff08;形状参数 (k) 和尺度参数 (\…...

Dubbo:Docker部署Zookeeper、Dubbo Admin的详细教程和SpringBoot整合Dubbo的实战与演练

&#x1fa81;&#x1f341; 希望本文能给您带来帮助&#xff0c;如果有任何问题&#xff0c;欢迎批评指正&#xff01;&#x1f405;&#x1f43e;&#x1f341;&#x1f425; 文章目录 一、背景二、Dubbo概述三、Dubbo与SpringCloud的关系四、Dubbo技术架构五、Docker安装Zoo…...

Windows 上安装下载并配置 Apache Maven

1. 下载 Maven 访问官网&#xff1a; 打开 Apache Maven 下载页面。 选择版本&#xff1a; 下载最新的 Binary zip archive&#xff08;例如 apache-maven-3.9.9-bin.zip&#xff09;。 注意&#xff1a;不要下载 -src 版本&#xff08;那是源码包&#xff09;。 2. 解压 Mave…...

Unbuntu 命令

Ubuntu 命令速查表​ ​分类​​命令​​功能描述​​示例/常用选项​​​​文件与目录​ls列出目录内容ls -a&#xff08;显示隐藏文件&#xff09;; ls -lh&#xff08;详细列表易读大小&#xff09; cd切换目录cd ~&#xff08;主目录&#xff09;; cd ..&#xff08;上级…...

机器学习-人与机器生数据的区分模型测试-数据处理1

附件为训练数据&#xff0c;总体的流程可以作为参考。 导入依赖 import pandas as pd import os import numpy as np from sklearn.model_selection import train_test_split,GridSearchCV from sklearn.ensemble import RandomForestClassifier,VotingClassifier from skle…...

【Linux】进程间通信(一):认识管道

&#x1f4dd;前言&#xff1a; 这篇文章我们来讲讲进程间通信——认识管道 &#x1f3ac;个人简介&#xff1a;努力学习ing &#x1f4cb;个人专栏&#xff1a;Linux &#x1f380;CSDN主页 愚润求学 &#x1f304;其他专栏&#xff1a;C学习笔记&#xff0c;C语言入门基础&a…...

AMD Vivado™ 设计套件生成加密比特流和加密密钥

概括 重要提示&#xff1a;有关使用AMD Vivado™ Design Suite 2016.4 及更早版本进行 eFUSE 编程的重要更新&#xff0c;请参阅AMD设计咨询 68832 。 本应用说明介绍了使用AMD Vivado™ 设计套件生成加密比特流和加密密钥&#xff08;高级加密标准伽罗瓦/计数器模式 (AES-GCM)…...

第三十四节:特征检测与描述-SIFT/SURF 特征 (专利算法)

一、特征检测:计算机视觉的基石 在计算机视觉领域中,特征检测与描述是实现图像理解的核心技术。就像人类通过识别物体边缘、角点等特征来认知世界,算法通过检测图像中的关键特征点来实现: 图像匹配与拼接 物体识别与跟踪 三维重建 运动分析 其中,SIFT(Scale-Invariant F…...

【AI】SpringAI 第二弹:基于多模型实现流式输出

目录 一、基于多模型实现流式输出 1.1 什么是流式输出 1.2 多模型引入 1.3 代码实现 1.3.1 流式输出的API介绍 1.3.2 Flux 源码分析 二、了解 Reactor 模型 三、SSE 协议 一、基于多模型实现流式输出 1.1 什么是流式输出 流式输出(Streaming Output)是指数据在生成过程…...

SQL语句执行问题

执行顺序 select [all|distinct] <目标列的表达式1> AS [别名], <目标列的表达式2> AS [别名]... from <表名1或视图名1> [别名],<表名2或视图名2> [别名]... [where <条件表达式>] [group by <列名>] [having <条件表达式>] [ord…...

模型量化AWQ和GPTQ哪种效果好?

环境&#xff1a; AWQ GPTQ 问题描述&#xff1a; 模型量化AWQ和GPTQ哪种效果好? 解决方案&#xff1a; 关于AWQ&#xff08;Adaptive Weight Quantization&#xff09;和GPTQ&#xff08;Generative Pre-trained Transformer Quantization&#xff09;这两种量化方法的…...

Github 2025-05-17 Rust开源项目日报 Top10

根据Github Trendings的统计,今日(2025-05-17统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Rust项目10Dart项目1RustDesk: 用Rust编写的开源远程桌面软件 创建周期:1218 天开发语言:Rust, Dart协议类型:GNU Affero General Public Li…...

借助 CodeBuddy 打造我的图标预览平台 —— IconWiz 开发实录

我正在参加CodeBuddy「首席试玩官」内容创作大赛&#xff0c;本文所使用的 CodeBuddy 免费下载链接&#xff1a;腾讯云代码助手 CodeBuddy - AI 时代的智能编程伙伴 想做一款自己的图标预览平台 这段时间我在做前端 UI 设计时&#xff0c;常常需要到处找图标素材&#xff0c;复…...

KL散度 (Kullback-Leibler Divergence)

KL散度&#xff0c;也称为相对熵 (Relative Entropy)&#xff0c;是信息论中一个核心概念&#xff0c;用于衡量两个概率分布之间的差异。给定两个概率分布 P ( x ) P(x) P(x) 和 Q ( x ) Q(x) Q(x)&#xff08;对于离散随机变量&#xff09;或 p ( x ) p(x) p(x) 和 q ( x …...

【Linux网络】NAT和代理服务

NAT 之前我们讨论了&#xff0c;IPv4协议中&#xff0c;IP地址数量不充足的问题。 原始报文途径路由器WAN口时&#xff0c;对报文中的源IP进行替换的过程&#xff0c;叫做NAT。 NAT技术当前解决IP地址不够用的主要手段&#xff0c;是路由器的一个重要功能&#xff1a; NAT能…...

DeepSeek赋能电商,智能客服机器人破解大型活动人力困境

1. DeepSeek 与电商客服结合的背景 1.1 电商行业客服需求特点 电商行业具有独特的客服需求特点&#xff0c;这些特点决定了智能客服机器人在该行业的必要性和重要性。 高并发性&#xff1a;电商平台的用户数量庞大&#xff0c;尤其是在促销活动期间&#xff0c;用户咨询量会…...

Unity序列化字段、单例模式(Singleton Pattern)

一、序列化字段 在Unity中&#xff0c;序列化字段是一个非常重要的概念&#xff0c;主要用于在Unity编辑器中显示和编辑类的成员变量&#xff0c;或者在运行时将对象的状态保存到文件或网络中。 1.Unity序列化字段的作用 在编辑器中显示和编辑字段&#xff1a;默认情况下&…...

一个可拖拉实现列表排序的WPF开源控件

从零学习构建一个完整的系统 推荐一个可通过拖拉&#xff0c;来实现列表元素的排序的WPF控件。 项目简介 gong-wpf-dragdrop是一个开源的.NET项目&#xff0c;用于在WPF应用程序中实现拖放功能&#xff0c;可以让开发人员快速、简单的实现拖放的操作功能。 可以在同一控件内…...

hadoop.proxyuser.代理用户.授信域 用来干什么的

在Hadoop的core-site.xml文件中存在三个可选配置&#xff0c;如下 <property><name>hadoop.proxyuser.root.hosts</name><value>*</value> </property> <property><name>hadoop.proxyuser.root.groups</name><value…...

python 自动化教程

文章目录 前言整数变量​字符串变量​列表变量​算术操作​比较操作​逻辑操作​if语句​for循环遍历列表​while循环​定义函数​调用函数​导入模块​使用模块中的函数​启动Chrome浏览器​打开网页​定位元素并输入内容​提交表单​关闭浏览器​发送GET请求获取网页内容​使…...

C++学习:六个月从基础到就业——C++11/14:列表初始化

C学习&#xff1a;六个月从基础到就业——C11/14&#xff1a;列表初始化 本文是我C学习之旅系列的第四十三篇技术文章&#xff0c;也是第三阶段"现代C特性"的第五篇&#xff0c;主要介绍C11/14中的列表初始化特性。查看完整系列目录了解更多内容。 引言 在C11之前&a…...

城市静音革命:当垃圾桶遇上缓冲器

缓冲垃圾桶的核心原理是通过机械或液压装置实现垃圾桶盖的缓慢闭合&#xff0c;包含以下技术要点&#xff1a;‌能量吸收机制‌液压式&#xff1a;通过活塞挤压油液产生阻尼力&#xff0c;将动能转化为热能耗散弹簧式&#xff1a;利用弹性变形储存和释放能量&#xff0c;配合摩…...

数据库的规范化设计方法---3种范式

第一范式&#xff08;1NF&#xff09;&#xff1a;确保表中的每个字段都是不可分割的基本数据项。 第二范式&#xff08;2NF&#xff09;&#xff1a;在满足1NF的基础上&#xff0c;确保非主属性完全依赖于主键。 第三范式&#xff08;3NF&#xff09;&#xff1a;在满足2NF的基…...

p024基于Django的网上购物系统的设计与实现

开发语言&#xff1a;Python框架&#xff1a;djangoPython版本&#xff1a;python3.7.7数据库&#xff1a;mysql 5.7数据库工具&#xff1a;Navicat11开发软件&#xff1a;PyCharm 系统展示 管理员登录 管理员功能界面 用户管理 商品类型管理 商品信息管理 系统管理 订单管理…...

C++跨平台开发:挑战与应对策略

C跨平台开发&#xff1a;挑战与应对策略 在如今设备多样、操作系统碎片化的开发环境中&#xff0c;跨平台能力已成为衡量软件生命力与团队工程效率的重要指标。C 作为高性能系统级语言&#xff0c;在游戏引擎、嵌入式系统、实时渲染等领域依旧坚挺。然而&#xff0c;实现“一次…...

Kotlin 作用域函数(let、run、with、apply、also)对比

Kotlin 的 作用域函数&#xff08;Scope Functions&#xff09; 是简化代码逻辑的重要工具&#xff0c;它们通过临时作用域为对象提供更简洁的操作方式。以下是 let、run、with、apply、also 的对比分析&#xff1a; 一、核心区别对比表 函数上下文对象引用返回值是否扩展函数…...

JavaScript性能优化实战(11):前沿技术在性能优化中的应用

引言 随着Web应用复杂度和性能需求不断提高,传统的JavaScript优化技术已经无法满足某些高性能计算场景的需求。本文将深入探讨前沿Web技术如何突破JavaScript的性能瓶颈,为Web应用提供接近原生应用的性能体验。从底层计算到图形渲染,从并发处理到动画优化,我们将通过实际案…...

数据结构【AVL树】

AVL树 1.AVL树1.AVL的概念2.平衡因子 2.AVl树的实现2.1AVL树的结构2.2AVL树的插入2.3 旋转2.3.1 旋转的原则 1.AVL树 1.AVL的概念 AVL树可以是一个空树。 它的左右子树都是AVL树&#xff0c;且左右子树的高度差的绝对值不超过1。AVL树是一颗高度平衡搜索二叉树&#xff0c;通…...

电动调节V型球阀:行业应用与材质选择全解析

电动调节V型球阀&#xff1a;行业应用与材质选择全解析 作为工业自动化控制中的关键设备&#xff0c;电动调节V型球阀凭借其独特的结构设计与高性能调节能力&#xff0c;在石油、化工、造纸等高要求领域广泛应用。本文将从核心功能、行业应用场景、材质选择要点等方面深入解析…...

页面上如何显示特殊字符、Unicode字符?

在前端开发中&#xff0c;显示特殊字符通常涉及到HTML实体&#xff08;HTML Entities&#xff09;或 Unicode 字符的使用。以下是一些常见的方法来处理特殊字符的显示&#xff1a; 1、HTML实体&#xff1a; HTML为一些常见的特殊字符提供了预定义的实体。例如&#xff0c;要显…...

桌面端进程通信

以下是关于 Electron 桌面端进程通信的基本知识点总结: 一、Electron 进程模型基础 1. 进程类型与职责 进程类型职责权限主进程(Main)创建窗口、系统级操作、IPC中枢完全Node.js访问权限渲染进程(Renderer)展示Web内容、UI交互默认受限(可配置开启Node.js)预加载脚本(Prelo…...

vue2 切换主题色以及单页面好使方法

今天要新增一个页面要根据不同公司切换不同页面主题色&#xff0c;一点一点来&#xff0c;怎么快速更改 el-pagination 分页组件主题色。 <el-pagination :page-size"pageSize" :pager-count"pageCount"layout"sizes, prev, pager, next, jumper,…...

三层固定实体架构:高效实现图上的检索增强生成(RAG)

知识图谱正在成为跨各个领域组织和检索信息的强大工具。它们越来越多地与机器学习和自然语言处理技术相结合,以增强信息检索和推理能力。在本文中,我介绍了一种用于构建知识图谱的三层架构,结合了固定本体实体、文档片段和提取的命名实体。通过利用嵌入和余弦相似度,这种方…...