【Linux进程间通信】共享内存

【Linux进程间通信】共享内存

目录

  • 【Linux进程间通信】共享内存
    • system V共享内存
    • 共享内存示意图
    • 共享内存的数据结构
    • 共享内存函数
      • 将共享内存挂接到对应的进程
      • 将共享内存取消挂接
      • 释放共享内存
    • 共享内存的特性
    • 共享内存扩展
    • 共享内存配合管道进行使用

作者:爱写代码的刚子

时间:2024.3.3

前言:本篇博客将介绍system V共享内存,进程通信的本质就是让不同的进程先看到同一份资源。

system V共享内存

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

共享内存示意图

共享内存让不同进程看到同一份资源的方式就是,通过一种接口在物理内存当中申请一块内存空间,然后通过此接口将这块内存空间分别与各个进程各自的页表之间建立映射,再在虚拟地址空间当中开辟空间并将虚拟地址填充到各自页表的对应位置,使得虚拟地址和物理地址之间建立起对应关系,至此这些进程便看到了同一份物理内存,这块物理内存就叫做共享内存。

img

注意:
这里所说的开辟物理空间、建立映射等操作都是调用系统接口完成的,也就是说这些动作都由操作系统来完成。所以操作系统需要提供具有如下功能的接口:

  • 创建共享内存 —— 删除共享内存(OS内部帮我们做)
  • 关联共享内存 —— 去关联共享内存(进程做,实际也是OS做)

这个操作不能由进程来执行,而由操作系统来执行,因为进程是需求方,操作系统是执行方。

(操作系统需要管理共享内存,先描述(多大,谁申请的,有多少进程关联,使用了多少,引用计数),再组织!),所以存在struct结构体,对共享内存的管理变成了对数据结构的增删查改。

共享内存的数据结构

在系统当中可能会有大量的进程在进行通信,因此系统当中就可能存在大量的共享内存,那么操作系统必然要对其进行管理,所以共享内存除了在内存当中真正开辟空间之外,系统一定还要为共享内存维护相关的内核数据结构。共享内存的数据结构如下:

struct shmid_ds {struct ipc_perm shm_perm; /* operation perms */int shm_segsz; /* size of segment (bytes) */__kernel_time_t shm_atime; /* last attach time */__kernel_time_t shm_dtime; /* last detach time */__kernel_time_t shm_ctime; /* last change time */__kernel_ipc_pid_t shm_cpid; /* pid of creator */__kernel_ipc_pid_t shm_lpid; /* pid of last operator */unsigned short shm_nattch; /* no. of current attaches */unsigned short shm_unused; /* compatibility */void* shm_unused2; /* ditto - used by DIPC */void* shm_unused3; /* unused */
};

共享内存函数

  • shmget函数

在这里插入图片描述

  • 功能:用来创建共享内存

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

  • 参数:

    • key:表示待创建共享内存在系统中的唯一标识
    • size:表示带创建共享内存的大小(单位是字节)(建议设置为页[4KB]的整数倍)
    • shmflg:表示创建共享内存的方式,由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的

    在这里插入图片描述

IPC_CREAT(单独):如果你申请的共享内存不存在,就创建,存在,就获取并返回

IPC_CREAT | IPC_EXCL :如果你申请的共享内存不存在,就创建,存在,就出错返回。这样确保我们成功申请的这个共享内存一定是新的!

IPC_EXCL不单独使用

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

【问题】:如何保证让不同的进程看见同一个共享内存呢?/ 如何知道这个共享内存存在还是不存在?

  1. 第一个参数key(共享内存标识符)可以保证,key是几并不重要。关键在于它必须在内核中具有唯一性,能够让不同的进程进行唯一性标识。
  2. 第一个进程可以通过key创建共享内存,第二个之后的进程,只要拿着同一个key就可以和第一个进程看到同一个共享内存了。
  3. 对于一个已经创建好的共享内存,key在哪?key在共享内存的描述对象中!
  4. 第一次创建的时候,必须要有key。
  5. key——类似——路径——唯一
  • ftok函数

在这里插入图片描述

ftok函数并没有在内核里做任何事情,只是一套算法,将pathname和proj_id进行数值计算,形成冲突概率比较小的key即可!理论上pathname和proj_id可以由用户指定(用户约定的)。

举一个例子:

common.hpp:

#ifndef __COMM_HPP__
#define __COMM_HPP__#include <iostream>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include "log.hpp"
using namespace std;const string pathname = "/home/Harvey/ceshi/共享内存";
const int proj_id = 0x5555;
const int size = 4096;
Log mylog;key_t GetKey()
{key_t k = ftok(pathname.c_str(),proj_id);if(k<0){mylog(Fatal,"ftok error:%s",strerror(errno));exit(1);}mylog(Info,"ftok success!key is %d",k);return k;
}int GetShareMem()
{key_t k =GetKey();int shmid = shmget(k,size,IPC_CREAT | IPC_EXCL);if(shmid<0){mylog(Fatal,"create share memory error:%s",strerror(errno));exit(2);}mylog(Info,"create share memory success,shmid:%d",shmid);return shmid;
}#endif

log.hpp:

#pragma once#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <stdarg.h>#define Info 0
#define Debug 1
#define Warning 2
#define Error 3
#define Fatal 4#define Screen 1
#define Onefile 2
#define Classfile 3#define SIZE 1024#define LogFile "log.txt"class Log{
public:Log():printMethod(Screen),path("./log/"){}~Log(){}void Enable(int method){printMethod = method;}std::string levelToString(int level){switch(level){case Info:return "Info";case Debug:return "Debug";case Warning:return "Warning";case Error:return "Error";case Fatal:return "Fatal";default:return "None";}}void printLog(int level,const std::string &logtxt){switch(printMethod){case Screen:std::cout<<logtxt<<std::endl;break;case Onefile:printOneFile(LogFile,logtxt);break;case Classfile:printClassFile(level,logtxt);break;default:break;}}void printOneFile(const std::string &logname,const std::string &logtxt){std::string _logname = path+logname;int fd = open(_logname.c_str(),O_WRONLY | O_CREAT | O_APPEND, 0666);if(fd<0)return;write(fd,logtxt.c_str(),logtxt.size());close(fd);}void printClassFile(int level,const std::string &logtxt){std::string filename =LogFile;//为文件名添加后缀filename+=".";filename+=levelToString(level);printOneFile(filename,logtxt);}void operator()(int level,const char *format,...){time_t t = time(nullptr);struct tm *ctime =localtime(&t);char leftbuffer[SIZE];snprintf(leftbuffer,sizeof(leftbuffer),"[%s][%d-%d-%d %d:%d:%d]",levelToString(level).c_str(),ctime->tm_year + 1900,ctime->tm_mon+1,ctime->tm_mday,ctime->tm_hour,ctime->tm_min,ctime->tm_sec);//提取可变参数va_list s;va_start(s,format);char rightbuffer[SIZE];vsnprintf(rightbuffer,sizeof(rightbuffer),format,s);va_end(s);//格式:默认部分——自定义部分char logtxt[SIZE*2];snprintf(logtxt,sizeof(logtxt),"%s %s\n",leftbuffer,rightbuffer);printLog(level,logtxt);}private:int printMethod;std::string path;
};

makefile:

.PHONY:all
all:processa processb
processa:processa.ccg++ -o $@ $^ -g -std=c++11
processb:processb.ccg++ -o $@ $^ -g -std=c++11.PHONY:clean
clean:rm -f processa processb

processa.cc:

#include "common.hpp"
using namespace std;
extern Log mylog;
int main()
{int shmid= GetShareMem();mylog(Debug,"shmid:%d",shmid);sleep(20);return 0;
}
  • 运行一下processa:

在这里插入图片描述

  • key vs shmid:

key用于操作系统内标定唯一性

shmid只在自己的进程内,用来标识资源的唯一性

  • 再运行一下processa:
    在这里插入图片描述

我们发现了报错,进程退出了,但是资源还在。

因为ipc资源是操作系统申请的,由操作系统进行管理。

  • ipcs -m查看所有的IPC资源(1.共享内存2.信号量3.消息队列)

在这里插入图片描述

共享内存的生命周期是随内核的!用户不主动关闭,共享内存会一直存在。除非内核重启,或者用户释放

  • ipcrm -m +shmid使用shmid释放共享内存,为什么不用key,因为key是在内核态的,shmid是在用户层的。

在这里插入图片描述

在这里插入图片描述

【问题】:如何设置共享内存的权限?

  • **int shmid = shmget(k,size,IPC_CREAT | IPC_EXCL | 0666);**将选项带上权限即可

在这里插入图片描述

再次注意!!共享内存的大小一般建议是4096字节(4kb)的整数倍。如果创建4097字节,实际上操作系统给出的是4096*2的大小

将共享内存挂接到对应的进程

  • shmat函数

在这里插入图片描述

  • char shmaddr = (char)shmat(shmid,nullptr,0);**将共享内存进行挂接

将共享内存取消挂接

  • shmdt函数

在这里插入图片描述

  • shmdt(shmaddr);

小知识,为什么释放共享内存时只需要传入起始地址?因为这个块空间还存了一些这块空间的属性(我们叫做cookie)

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

在这里插入图片描述

释放共享内存

  • shmctl函数

在这里插入图片描述

  • **shmctl(shmid,IPC_RMID,nullptr);**销毁共享内存

Common.hpp:

#ifndef __COMM_HPP__
#define __COMM_HPP__#include <iostream>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include "log.hpp"
using namespace std;const string pathname = "/home/Harvey/ceshi/共享内存";
const int proj_id = 0x5555;
const int size = 4096;
Log mylog;key_t GetKey()
{key_t k = ftok(pathname.c_str(),proj_id);if(k<0){mylog(Fatal,"ftok error:%s",strerror(errno));exit(1);}mylog(Info,"ftok success!key is 0x%x",k);return k;
}int GetShareMemHelper(int flag)
{key_t k =GetKey();int shmid = shmget(k,size,flag);if(shmid<0){mylog(Fatal,"create share memory error:%s",strerror(errno));exit(2);}mylog(Info,"create share memory success,shmid:%d",shmid);return shmid;
}int CreateShareMem()
{return GetShareMemHelper(IPC_CREAT | IPC_EXCL | 0666);
}int GetShareMem()
{return GetShareMemHelper(IPC_CREAT);
}#endif

processa.cc:

#include "common.hpp"
using namespace std;
extern Log mylog;
int main()
{int shmid= CreateShareMem();mylog(Debug,"shmid:%d",shmid);char* shmaddr = (char*)shmat(shmid,nullptr,0);mylog(Debug,"attach shm down,shmaddr:0x%x",shmaddr);sleep(5);shmdt(shmaddr);mylog(Debug,"detach shm down,shmaddr:0x%x",shmaddr);sleep(5);shmctl(shmid,IPC_RMID,nullptr);mylog(Debug,"destory shm down,shmaddr:0x%x",shmaddr);return 0;
}

processb.cc:

#include "common.hpp"
using namespace std;
extern Log mylog;
int main()
{int shmid= GetShareMem();mylog(Debug,"shmid:%d",shmid);char* shmaddr = (char*)shmat(shmid,nullptr,0);mylog(Debug,"attach shm down,shmaddr:0x%x",shmaddr);sleep(5);shmdt(shmaddr);mylog(Debug,"detach shm down,shmaddr:0x%x",shmaddr);sleep(5);// shmctl(shmid,IPC_RMID,nullptr);// mylog(Debug,"destory shm down,shmaddr:0x%x",shmaddr);return 0;
}

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • 但是目前为止进程a和进程b并没有进行通信

共享内存创建出来后是给用户去使用的,当我们已经将它转化为字符串后我们可以将它当作字符串进行使用。(直接访问)

processa:

#include "common.hpp"
using namespace std;
extern Log mylog;
int main()
{int shmid= CreateShareMem();char* shmaddr = (char*)shmat(shmid,nullptr,0);//IPC code//一旦有人把数据写入到共享内存,其实我们立马能看到了//不需要经过系统调用,直接就能看到数据了while(true){cout<<"client say@"<<shmaddr<<endl;//直接访问共享内存}shmdt(shmaddr);shmctl(shmid,IPC_RMID,nullptr);return 0;
}

processb:

#include "common.hpp"
using namespace std;
extern Log mylog;
int main()
{//一旦有了共享空间,挂接到自己的地址空间中,直接把它当成你的内存空间来用即可//不需要调用系统接口int shmid= GetShareMem();char* shmaddr = (char*)shmat(shmid,nullptr,0);//IPC codewhile(true){//char buffer[1024];cout<<"Please Enter@";//fgets(buffer,sizeof(buffer),stdin);fgets(shmaddr,4096,stdin);//memcpy(shmaddr,buffer,strlen(buffer)+1);}shmdt(shmaddr);return 0;
}

共享内存的特性

  • 共享内存没有同步互斥之类的保护机制(所以需要被保护,可以结合管道进行使用)
  • 共享内存是所有的进程间通信中,速度最快的(拷贝少,采用地址空间映射的方法)
  • 共享内存内部的数据,由用户自己维护

共享内存扩展

  • man shmctl通过shmctl函数查看共享内存的属性

在这里插入图片描述

修改成这一串代码可以查看共享内存具体的属性

 struct shmid_ds shmds;while(true){cout<<"client say@"<<shmaddr<<endl;//直接访问共享内存sleep(1);shmctl(shmid,IPC_STAT,&shmds);cout<<"shm size: "<<shmds.shm_segsz<<endl;cout<<"shm nattch: "<<shmds.shm_nattch<<endl;//cout<<"shm __key: " << shmds.shm_perm.__key<<endl;printf("shm __key:0x%x\n",shmds.shm_perm.__key);cout<<"shm mode: "<<shmds.shm_perm.mode<<endl;}

在这里插入图片描述

共享内存配合管道进行使用

common.h:

#ifndef __COMM_HPP__
#define __COMM_HPP__#include <iostream>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include "log.hpp"
using namespace std;const string pathname = "/home/Harvey/ceshi/共享内存";
const int proj_id = 0x5555;
const int size = 4096;
Log mylog;key_t GetKey()
{key_t k = ftok(pathname.c_str(),proj_id);if(k<0){mylog(Fatal,"ftok error:%s",strerror(errno));exit(1);}mylog(Info,"ftok success!key is 0x%x",k);return k;
}int GetShareMemHelper(int flag)
{key_t k =GetKey();int shmid = shmget(k,size,flag);if(shmid<0){mylog(Fatal,"create share memory error:%s",strerror(errno));exit(2);}mylog(Info,"create share memory success,shmid:%d",shmid);return shmid;
}int CreateShareMem()
{return GetShareMemHelper(IPC_CREAT | IPC_EXCL | 0666);
}int GetShareMem()
{return GetShareMemHelper(IPC_CREAT);
}
#define FIFO_FILE "./myfifo"
#define MODE 0664enum
{FIFO_CREATE_ERR = 1,FIFO_DELETE_ERR,FIFO_OPEN_ERR
};class Init
{
public:Init(){// 创建管道int n = mkfifo(FIFO_FILE, MODE);if (n == -1){perror("mkfifo");exit(FIFO_CREATE_ERR);}}~Init(){int m = unlink(FIFO_FILE);if (m == -1){perror("unlink");exit(FIFO_DELETE_ERR);}}
};
#endif

processa:

#include "common.hpp"extern Log mylog;int main()
{Init init;int shmid = CreateShareMem();char *shmaddr = (char*)shmat(shmid, nullptr, 0);// ipc code 在这里!!// 一旦有人把数据写入到共享内存,其实我们立马能看到了!!// 不需要经过系统调用,直接就能看到数据了!int fd = open(FIFO_FILE, O_RDONLY); // 等待写入方打开之后,自己才会打开文件,向后执行, open 阻塞了!if (fd < 0){mylog(Fatal, "error string: %s, error code: %d", strerror(errno), errno);exit(FIFO_OPEN_ERR);}struct shmid_ds shmds;while(true){char c;ssize_t s = read(fd, &c, 1);if(s == 0) break;else if(s < 0) break;cout << "client say@ " << shmaddr << endl; //直接访问共享内存sleep(1);shmctl(shmid, IPC_STAT, &shmds);cout << "shm size: " << shmds.shm_segsz << endl;cout << "shm nattch: " << shmds.shm_nattch << endl;printf("shm key: 0x%x\n",  shmds.shm_perm.__key);cout << "shm mode: " << shmds.shm_perm.mode << endl;}shmdt(shmaddr);shmctl(shmid, IPC_RMID, nullptr);close(fd);return 0;
}

processb:

#include "common.hpp"
extern Log mylog;
int main()
{int shmid = GetShareMem();char *shmaddr = (char*)shmat(shmid, nullptr, 0);int fd = open(FIFO_FILE, O_WRONLY); // 等待写入方打开之后,自己才会打开文件,向后执行, open 阻塞了!if (fd < 0){mylog(Fatal, "error string: %s, error code: %d", strerror(errno), errno);exit(FIFO_OPEN_ERR);}// 一旦有了共享内存,挂接到自己的地址空间中,你直接把他当成你的内存空间来用即可!// 不需要调用系统调用// ipc codewhile(true){cout << "Please Enter@ ";fgets(shmaddr, 4096, stdin);write(fd, "c", 1); // 通知对方}shmdt(shmaddr);close(fd);return 0;
}

运行结果:

在这里插入图片描述

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

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

相关文章

用docker部署后端项目

一、搭建局域网 1.1、介绍前后端项目搭建 需要4台服务器&#xff0c;在同一个局域网中 1.2、操作 # 搭建net-ry局域网&#xff0c;用于部署若依项目 net-ry&#xff1a;名字 docker network create net-ry --subnet172.68.0.0/16 --gateway172.68.0.1#查看 docker network ls…

3d模型合并后一片漆黑是什么原因,怎么解决---模大狮模型网

当合并多个3D模型后&#xff0c;发现整个合并后的模型显示为一片漆黑通常是由以下几个可能的原因导致的&#xff1a; 材质设置问题&#xff1a;合并后的模型可能存在材质设置错误&#xff0c;导致模型无法正确显示。检查每个模型的材质属性&#xff0c;确保其正确设置&#xff…

老隋蓝海项目有哪些?能赚钱吗?

在创业的海洋中&#xff0c;每个人都渴望找到那片属于自己的“蓝海”&#xff0c;而“老隋蓝海项目”便是许多人心中的那片未知海域。那么&#xff0c;老隋蓝海项目究竟是指什么?它们又能否成为创业者的新财富之源? 蓝海项目的定义 我们要明白&#xff0c;蓝海项目通常指的是…

【漏洞复现】某厂商明御WEB应用防火墙任意用户登录漏洞

Nx01 产品简介 安恒明御WEB应用防火墙&#xff08;简称WAF&#xff09;是杭州安恒信息技术股份有限公司自主研发的一款专业应用安全防护产品&#xff0c;专注于为网站、APP等Web业务系统提供安全防护。 Nx02 漏洞描述 安恒明御WEB应用防火墙report.php文件存在硬编码设置的Con…

yolov7添加spd-conv注意力机制

一、spd-conv是什么&#xff1f; SPD-Conv&#xff08;Symmetric Positive Definite Convolution&#xff09;是一种新颖的卷积操作&#xff0c;它主要应用于处理对称正定矩阵&#xff08;SPD&#xff09;数据。在传统的卷积神经网络&#xff08;CNN&#xff09;中&#xff0c;…

人工智能_大模型013_AIGC生成式模型的增强检索_RAG知识补充检索_补充私域和实时场景知识_关键字检索增强---人工智能工作笔记0149

什么是RAG,RAG的意思就是,如果一套生成式AIGC大模型,你昨天训练了以后,那么今天的知识,还没有给他进行训练,那么回答的时候,他就会遗漏今天的知识,那么我们就可以通过检索的手段,把今天的知识,检索出来,然后补充道prompt中,给这个大模型.让他参考,这样就包含了今天的知识相当于…

CY8C42(1.PSoC4 Pioneer Kit开箱及基本使用)

1.开箱 最近了解到赛普拉斯有一种芯片&#xff0c;属于PSoC系列&#xff0c;与传统MCU不同&#xff0c;有点类似跨界芯片&#xff0c;于是就买来玩玩了&#xff0c;老实说用完还是很特别的&#xff0c;因为我没有用过FPGA&#xff0c;不确定是不是FPGA的开发流程&#xff08;有…

MySQL的事务与隔离级别

1. 什么是事务&#xff1f; 数据库中的事务是指对数据库执行一批操作&#xff0c;而这些操作最终要么全部执行成功&#xff0c;要么全部失败&#xff0c;不会存在部分成功的情况。这个时候就需要用到事务。 最经典的例子就是转账&#xff0c;你要给朋友小白转 1000 块钱&…

一代传奇宗庆后:把员工宠成上帝

作者&#xff1a;积溪 琥珀酒研社快评&#xff1a; 梅子真是哭了 一代传奇就此陨落 咱们又少了一个良心企业家 2月25日10时30分 娃哈哈集团创始人、董事长宗庆后 在杭州逝世&#xff0c;享年79岁 在过去一个多月的病危期间 他的病房里最显眼的 不是呼吸机、检测仪 而…

智慧城市中的公共服务创新:让城市生活更便捷

目录 一、引言 二、智慧城市公共服务创新的实践 1、智慧交通系统 2、智慧医疗服务 3、智慧教育系统 4、智慧能源管理 三、智慧城市公共服务创新的挑战 四、智慧城市公共服务创新的前景 五、结论 一、引言 随着信息技术的迅猛发展&#xff0c;智慧城市已成为现代城市发…

技术总结: PPT绘图

目录 写在前面参考文档技巧总结PPT中元素的连接立方体调整厚度调整图形中的文本3D 图片调整渐变中的颜色 写在前面 能绘制好一个好看的示意图非常重要, 在科研和工作中好的示意图能精准表达出自己的想法, 减少沟通的成本, 可视化的呈现也可以加强自身对系统的理解, 时间很久后…

分分钟搞定JSON解析

json 库能够解析字符串或文本中的 JSON 内容。 该库将 JSON 解析为 Python 字典或列表&#xff0c;也能将 Python 字典或列表转换为 JSON 字符串。 解析 JSON 如下的 JSON 格式的字符串&#xff1a; json_string {"first_name": "Guido", "last_na…

瑞芯微RK3588 C++部署Yolov8检测和分割模型

最近这一个月在研究国产瑞芯微板子上部署yolov8的检测和分割模型&#xff0c;踩了很多坑&#xff0c;记录一下部署的过程和遇到的一些问题&#xff1a; 1 环境搭建 需要的环境和代码主要包括&#xff1a; &#xff08;1&#xff09;rknn-toolkit2-1.5.2&#xff1a;工具链&am…

微服务day04-基于Feign的远程调用

一.Feign的认识 是http客户端&#xff0c;因为使用RestTemplate存在一些问题&#xff1a;代码可读性差&#xff0c;参数配置费事&#xff0c;不够优雅… String url"http://userservice/user/"order.getUserId(); User userrestTemplate.getForObject(url,User.cla…

初始计算机组成原理

1.初始计算机组成原理 本人相关文章&#xff1a;Linux之计算机概论 声明&#xff1a;大部分图片均来自网络&#xff0c;侵删 一个完整的计算机系统包括硬件子系统和软件子系统两大部分。 组成一台计算机的物理设备的总称叫做计算机硬件子系统,是看得见摸得着的实体,是计算机工…

华工的各类型PPT模板

华工的各类型PPT模板&#xff0c;包括原创的PPT及改良内容的PPT&#xff0c;适合科研/比赛/组会汇报等 前言各种毕业答辩夏令营答辩复试答辩奖学金答辩比赛/项目答辩组会汇报 前言 设计不易&#xff0c;排版不易&#xff0c;内容编排不易 待更新项目1 原创声明&#xff1a;不经…

【论文阅读】(2006)Dual-Optimal Inequalities for Stabilized Column Generation

文章目录 摘要一、介绍二、对偶最优不等式&#xff08;Dual-Optimal Inequalities&#xff09;三、确定P的最优原始解四、二元切割下料问题4.1 约束聚合4.2 相等约束4.3 计算结果 五、切割下料问题5.1 计算结果 六、三元组的深度对偶最优不等式&#xff08;Deep Dual-Optimal I…

C/C++ 乘积尾零问题(蓝桥杯)

如下的10行数据&#xff0c;每行有10个整数&#xff0c;请你求出它们的乘积的末尾有多少个零&#xff1f; 5650&#xff0c;4542 3554 473 946 4114 3871 9073 90 4329 2758 7949 6113 5659 5245 7432 3051 4434 6704 3594 9937 1173 6866 3397 4759 7557 3070 2287 1453 9899…

做测试还是测试开发,选职业要慎重!

【软件测试面试突击班】2024吃透软件测试面试最全八股文攻略教程&#xff0c;一周学完让你面试通过率提高90%&#xff01;&#xff08;自动化测试&#xff09; 突然发现好像挺多人想投测开和测试的&#xff0c;很多人面试的时候也会被问到这几个职位的区别&#xff0c;然后有测…

每日五道java面试题之mysql数据库篇(三)

目录&#xff1a; 第一题. 百万级别或以上的数据如何删除&#xff1f;第二题. 前缀索引第三题. 什么是最左前缀原则&#xff1f;什么是最左匹配原则?第四题. B树和B树的区别第五题. 使用B树和B树好处 第一题. 百万级别或以上的数据如何删除&#xff1f; 关于索引&#xff1a;…