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; // 打开文件失败返回falsereturn 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) // 关闭写端fdclose(_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:// 获取keykey_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";elsereturn "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) // 如果是创建者就调用专门为创建者准备的函数GetShmForCreaterGetShmForCreater();else if (_who == gUser) // 如果是使用者就调用专门为使用者准备的函数GetShmForUserGetShmForUser();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(); // 以写方式打开管道// 当成stringchar 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.mzph.cn/pingmian/12397.shtml

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

相关文章

笔记本黑屏,重新开机主板没有正常运作的解决办法

拆开笔记本后壳&#xff0c;打开看到主板&#xff0c;将主板上的这颗纽扣电池拆下来&#xff0c;如果是带连接线的&#xff08;如下图&#xff09;&#xff0c;可以将接口处线头拔出&#xff0c;等1分钟再把线接上。 ------------- 以下是科普 首先&#xff0c;电脑主板上的这…

力扣例题(循环队列)

链接 . - 力扣&#xff08;LeetCode&#xff09; 描述 思路 我们使用数组来创建循环队列 数组的大小我们就额外对开辟一块空间 MyCircularQueue(k) 开辟一个结构体&#xff0c;存放队列的相关数据 分别为size,数组指针_a,起始位置head,结束位置tail 注意&#xff1a;我们…

移动端自动化测试工具 Appium 之持续集成

文章目录 一、背景二、前置条件三、代码部分1、pom.xml文件配置2、main入口代码 四、Jenkins 部分1、下载Jenkins2、安装插件3、job配置4、选择构建 五、工程目录六、报告示例七、总结 一、背景 持续集成是老生话谈的事情&#xff0c;用的好不好&#xff0c;看自己公司与使用场…

能播放SWF文件的FlashPlayer播放器

问题&#xff1a; 你是不是遇到了 flash 动画 放不了了&#xff1f; 以前的flash游戏玩不了了 在网上很难找到好用的&#xff0c;免费Flashplayer播放器&#xff0c; 找到的也没法保存.exe 以前买的课件放不了了 一打开就更新提示&#xff1a; 再不就是意外能打开了但【创建…

IBM Granite模型开源:推动软件开发领域的革新浪潮

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

高中数学:平面向量-加减运算

一、向量的加法运算 三角形法则&#xff08;推荐&#xff09; 两个或多个向量收尾相连的加法运算&#xff0c;用三角形法则 简便算法 首尾相连的多个向量&#xff0c;去掉中间点&#xff0c;就是最终的和。 也可以用三角形法则证明 向量加法交换律 向量加法结合律 平行四…

讲解SSM的xml文件

概述&#xff1a;这些配置文件很烦&#xff0c;建议直接复制粘贴 springMVC.xml文件 <?xml version"1.0" encoding"UTF-8"?> <beans xmlns"http://www.springframework.org/schema/beans"xmlns:xsi"http://www.w3.org/2001/XM…

【Image captioning】In Defense of Grid Features for Visual Question Answering实现流程

In Defense of Grid Features for Visual Question Answering实现流程 网格特征预训练代码 这是该论文的特征预训练代码发布: @InProceedings{jiang2020defense,title={In Defense of Grid Features for Visual Question Answering},author={Jiang, Huaizu and Misra, Ishan…

霍廷格电源 Tru plasma DC3030 通快DC3040 MF3030

霍廷格电源 Tru plasma DC3030 通快DC3040 MF3030

jdk安装多个版本,但是java -version显示最早安装的版本,换掉Path或者JAVA_HOME不生效问题

问题一&#xff1a;当你的电脑上又多个jdk版本&#xff0c;如17 或者8时&#xff0c;使用命令行 java -version显示最早安装的&#xff0c;如下图所示&#xff1a;环境变量配置的17&#xff0c;但是命令行显示的是8。 原因&#xff1a;windows电脑装jdk17后 会在你的环境变量…

5.14_练习

1、字符串逆序 编写一个函数reverse_string(char* string)(递归实现) 实现&#xff1a;将参数字符串中的字符反向排列&#xff0c;不是逆序打印 要求&#xff1a;不能使用C函数库中的字符串操作函数 比如&#xff1a; char arr[ ]"abcdef"; 逆序之后数组的内容…

全面提升数据采集效率:亮数据产品的应用与评估详解

全面提升数据采集效率&#xff1a;亮数据产品的应用与评估详解 文章目录 全面提升数据采集效率&#xff1a;亮数据产品的应用与评估详解背景应用场景&#xff1a;平台首页信息抓取准备评测素材详细的产品使用和评测流程产品介绍亮数据的IP代理服务亮数据的爬虫工具及采集技术 注…

云商城系统源码,无后门,一站式系统Java源码

云商城系统&#xff0c;无后门&#xff0c;一站式系统Java源码&#xff0c;心权益商品数量不限数量 系统对接 手动发货 自动发货 兑 换 码 订单监控 商品监控 对象存储 邮箱提醒 加价模板 密价功能 三方支付 会员体系 财务明细 交易分析 售后服务 技术支持 【Java源码】云商…

Dubbo3.x 异步转同步源码

底层netty通信是异步的&#xff0c;那我们平时调用采取的同步是如何将底层的异步转为同步的呢&#xff1f; dubbo远程rpc协议和网络框架有多种&#xff0c;我们以默认的dubbo协议、网络框架netty作为切入点. 注意点&#xff1a;debug时将过期时间设置长一点&#xff1a; 调用…

使用vue3+ts+vite从零开始搭建bolg(五):layout(持续更新中)

五、layout搭建 5.1静态搭建 在src下创建如图文件夹 这里用logo举例&#xff0c;在scripts里export <script lang"ts">export default {name: Logo,}</script> 然后在layout里引入 //引入左侧菜单顶部用户信息 import Logo from ./logo/index.vue 接…

java版数据结构:堆,大根堆,小根堆

目录 堆的基本概念&#xff1a; 如何将一个二叉树调整成一个大根堆&#xff1a; 转成大根堆的时间复杂度 根堆中的插入&#xff0c;取出数据&#xff1a; 堆的基本概念&#xff1a; 堆是一种特殊的树形数据结构&#xff0c;它满足以下两个性质&#xff1a; 堆是一个完全二叉…

【半夜学习MySQL】表结构的操作(含表的创建、修改、删除操作,及如何查看表结构)

&#x1f3e0;关于专栏&#xff1a;半夜学习MySQL专栏用于记录MySQL数据相关内容。 &#x1f3af;每天努力一点点&#xff0c;技术变化看得见 文章目录 创建表查看表结构修改表删除表 创建表 语法&#xff1a; create table table_name(field1 datatype,field2 datatype,fiel…

JWT令牌技术实现登录校验

一.简单登录功能 在登录界面中&#xff0c;我们可以输入用户的用户名以及密码&#xff0c;然后点击 "登录" 按钮就要请求服务器&#xff0c;服务端判断用户输入的用户名或者密码是否正确。如果正确&#xff0c;则返回成功结果&#xff0c;跳转至系统首页面。 1.功能…

[笔试训练](二十二)064:添加字符065:数组变换066:装箱问题

目录 064:添加字符 065:数组变换 066:装箱问题 064:添加字符 添加字符_牛客笔试题_牛客网 (nowcoder.com) 题目&#xff1a; 题解&#xff1a; 枚举所有A&#xff0c;B字符串可能的对应位置&#xff0c;得出对应位置不同字符数量的最小情况 两字符串的字符数量差n-m&…

springboot月度员工绩效考核管理系统

摘要 本月度员工绩效考核管理系统采用java语言做为代码编写工具&#xff0c;采用mysql数据库进行系统中信息的存储与处理。框架采用springboot。 本系统的功能分为管理员和员工两个角色&#xff0c;管理员的功能有&#xff1a; &#xff08;1&#xff09;个人中心管理功能&am…