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

【Linux】24.进程间通信(3)

文章目录

    • 3.6 systemv共享内存
      • 3.6.1 共享内存函数
      • 3.6.3 一个简单的共享内存代码实现
      • 3.6.4 一个复杂的共享内存代码实现
      • 3.6.4 key和shmid的主要区别:
    • 3.7 systemv消息队列(了解)
    • 3.8 systemv信号量(了解)
      • 进程互斥
      • 四个问题
      • 理解信号量


3.6 systemv共享内存

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

进程间通信的本质是:先让不同的进程,看到同一份资源。

72881d84255fc303b20a3ab2711e9853

如果要释放共享内存:要去除关联,释放共享内存

上面的操作都是进程直接做的吗?

不是。直接由操作系统来做。

共享内存的生命周期是随内核的。

用户不主动关闭,共享内存会一直存在。除非内核重启(用户释放)


生成IPC(进程间通信)的key值:

函数原型:

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

参数含义:

  1. pathname:一个已存在的文件路径
    • 必须是一个已存在的文件或目录的路径
    • 用于生成唯一的 key
    • 程序需要有该路径的访问权限
  2. proj_id:项目标识符
    • 项目标识符,用于区分不同的IPC资源
    • 只有低8位有效(1-255
    • 通常使用字符或数字

返回值:

  • 成功:成功:返回一个非负的 key 值
  • 失败:返回-1

3.6.1 共享内存函数

shmget函数

功能:用来创建共享内存
原型int shmget(key_t key, size_t size, int shmflg);
参数key:这个共享内存段名字size:共享内存大小shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1

shmat函数

功能:将共享内存段连接到进程地址空间
原型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,表示连接操作用来只读共享内存

shmdt函数

功能:将共享内存段与当前进程脱离
原型int shmdt(const void *shmaddr);
参数shmaddr: 由shmat所返回的指针
返回值:成功返回0;失败返回-1
注意:将共享内存段与当前进程脱离不等于删除共享内存段

shmctl函数

功能:用于控制共享内存
原型int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数shmid:由shmget返回的共享内存标识码cmd:将要采取的动作(有三个可取值)#define IPC_STAT    2   // 获取共享内存状态#define IPC_SET     1   // 设置共享内存状态#define IPC_RMID    0   // 删除共享内存段buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
返回值:成功返回0;失败返回-1

981fea39a7c0c97a0551b9f2613d1954

  1. shmget - 创建/获取共享内存
int shmid = shmget(key, 1024, IPC_CREAT | 0666);
  • 相当于"申请"一块共享内存
  • 类比文件操作中的 open 创建文件
  • 返回共享内存标识符(shmid),用于后续操作
  1. shmat - 挂载/连接共享内存
void *addr = shmat(shmid, NULL, 0);
  • 将共享内存映射到进程的地址空间
  • 类比把硬盘上的文件加载到内存
  • 返回可以直接操作的内存指针
  1. shmdt - 断开连接
shmdt(addr);
  • 解除进程与共享内存的映射关系
  • 类比关闭打开的文件
  • 不会删除共享内存,只是当前进程不再使用
  1. shmctl - 控制共享内存
shmctl(shmid, IPC_RMID, NULL);  // 删除共享内存
  • 用于删除或管理共享内存
  • 类比文件的删除、权限修改等操作

3.6.3 一个简单的共享内存代码实现

写进程 (writer.cpp):

#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#include <unistd.h>#define SHM_SIZE 1024int main() {// 1. 生成keykey_t key = ftok(".", 'x');// 这里的 "." 表示当前目录必须是一个存在且可访问的路径// 这里的 'x' 是一个字符,会被转换为8位整数,范围是1-255,只有低8位有效if(key == -1) {std::cout << "ftok失败" << std::endl;return 1;}// 2. 创建共享内存int shmid = shmget(key, SHM_SIZE, IPC_CREAT | 0666);if(shmid == -1) {std::cout << "shmget失败" << std::endl;return 1;}// 3. 连接共享内存void* shmaddr = shmat(shmid, NULL, 0);if(shmaddr == (void*)-1) { //(void*)-1 是 shmat 失败时的返回值,等同于 MAP_FAILED 或 -1std::cout << "shmat失败" << std::endl;return 1;}// 4. 写入数据const char* message = "Hello from shared memory!";strcpy((char*)shmaddr, message); // shmaddr 是共享内存的起始地址// (char*) 是类型转换,将 void* 转换为 char*// strcpy 将字符串复制到共享内存中std::cout << "写入数据: " << message << std::endl;// 5. 分离共享内存shmdt(shmaddr);return 0;
}

读进程 (reader.cpp):

#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#include <unistd.h>#define SHM_SIZE 1024int main() {// 1. 生成相同的keykey_t key = ftok(".", 'x');if(key == -1) {std::cout << "ftok失败" << std::endl;return 1;}// 2. 获取共享内存int shmid = shmget(key, SHM_SIZE, 0666);if(shmid == -1) {std::cout << "shmget失败" << std::endl;return 1;}// 3. 连接共享内存void* shmaddr = shmat(shmid, NULL, 0);if(shmaddr == (void*)-1) {std::cout << "shmat失败" << std::endl;return 1;}// 4. 读取数据std::cout << "读取数据: " << (char*)shmaddr << std::endl;// 5. 分离共享内存shmdt(shmaddr);// 6. 删除共享内存shmctl(shmid, IPC_RMID, NULL);return 0;
}

使用方法:

  1. 编译:
g++ writer.cpp -o writer
g++ reader.cpp -o reader
  1. 运行:
# 终端1
./writer# 终端2
./reader

主要函数说明:

  1. ftok(): 生成IPC键值
  2. shmget(): 创建或获取共享内存段
  3. shmat(): 连接共享内存段到进程地址空间
  4. shmdt(): 断开共享内存段连接
  5. shmctl(): 控制共享内存段(如删除)

执行时序:

写进程                     共享内存                      读进程|                          |                           ||                          |                           ||---ftok(".", 'x')         |                           ||生成key                    |                           ||                          |                           ||---shmget()               |                           ||创建共享内存--------------->|                           ||                          |                           ||---shmat()                |                           ||连接共享内存<-------------->|                           ||                          |                           ||---strcpy()               |                           ||写入数据------------------>|                           ||                          |                           ||---shmdt()                |                           ||断开连接------------------>|                           ||                          |                           ||                          |   ftok(".", 'x')----------||                          |   生成相同的key             ||                          |                           ||                          |   shmget()----------------||                          |<--获取共享内存              ||                          |                           ||                          |   shmat()-----------------||                          |<->连接共享内存              ||                          |                           ||                          |   读取数据-----------------||                          |-->读取内容                 ||                          |                           ||                          |   shmdt()-----------------||                          |<--断开连接                 ||                          |                           ||                          |   shmctl()----------------||                          |x--删除共享内存              ||                          |                           |

3.6.4 一个复杂的共享内存代码实现

makefile

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

log.hpp

#pragma once  // 防止头文件重复包含// 包含必要的系统头文件
#include <iostream>
#include <time.h>
#include <stdarg.h>  // 用于可变参数
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>#define SIZE 1024  // 缓冲区大小// 定义日志级别
#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 LogFile "log.txt"  // 默认日志文件名class Log
{public:Log(){printMethod = Screen;  // 默认输出到屏幕path = "./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);}~Log(){}// void logmessage(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);//     // printf("%s", logtxt); // 暂时打印//     printLog(level, logtxt);// }private:int printMethod;       // 日志输出方式std::string path;      // 日志文件路径
};// int sum(int n, ...)
// {
//     va_list s; // char*
//     va_start(s, n);//     int sum = 0;
//     while(n)
//     {
//         sum += va_arg(s, int); // printf("hello %d, hello %s, hello %c, hello %d,", 1, "hello", 'c', 123);
//         n--;
//     }//     va_end(s); //s = NULL
//     return sum;
// }

comm.hpp

#ifndef __COMM_HPP__
#define __COMM_HPP__// 包含必要的头文件
#include <iostream>
#include <string>
#include <cstdlib>
#include <cstring>
#include <sys/ipc.h>    // 系统IPC功能
#include <sys/shm.h>    // 共享内存
#include <sys/types.h>
#include <sys/stat.h>#include "log.hpp"using namespace std;Log mylog;  // 全局日志对象// 共享内存的大小一般建议是4096的整数倍
const int size = 4096; 
const string pathname="/home/ydk_108";  // 用于生成key的路径
const int proj_id = 0x6666;         // 项目ID// 获取IPC key
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 CreateShm()
{return GetShareMemHelper(IPC_CREAT | IPC_EXCL | 0666);
}// 获取已存在的共享内存
int GetShm()
{return GetShareMemHelper(IPC_CREAT); 
}#define FIFO_FILE "./myfifo"  // 命名管道文件路径
#define MODE 0664             // 文件权限// 错误码枚举
enum
{FIFO_CREATE_ERR = 1,FIFO_DELETE_ERR,FIFO_OPEN_ERR
};// 初始化类,用于创建和清理命名管道
class Init
{
public:Init(){// 先尝试删除已存在的管道文件unlink(FIFO_FILE);  // 忽略返回值,因为文件可能不存在// 创建命名管道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.cc

#include "comm.hpp"extern Log mylog;int main()
{Init init;  // 创建命名管道int shmid = CreateShm();  // 创建共享内存// 将共享内存映射到进程地址空间char *shmaddr = (char*)shmat(shmid, nullptr, 0);// 以只读方式打开命名管道int fd = open(FIFO_FILE, O_RDONLY);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.cc

#include "comm.hpp"int main()
{int shmid = GetShm();  // 获取已存在的共享内存// 将共享内存映射到进程地址空间char *shmaddr = (char*)shmat(shmid, nullptr, 0);// 以只写方式打开命名管道int fd = open(FIFO_FILE, O_WRONLY);if (fd < 0){mylog(Fatal, "error string: %s, error code: %d", strerror(errno), errno);exit(FIFO_OPEN_ERR);}while(true){cout << "Please Enter@ ";// 读取用户输入并直接写入共享内存fgets(shmaddr, 4096, stdin);// 向管道写入一个字符,通知接收端write(fd, "c", 1);}// 清理资源shmdt(shmaddr);  // 解除内存映射close(fd);  // 关闭管道return 0;
}

打印:

c297c18f8ca1b2702ec33357f46ef20e

关于key

  1. key是一个数字,这个数字是几,不重要。关键在于它必须在内核中具有唯一性,能够让不同的进程进行唯一性标识。

  2. 第一个进程可以通过kev创建共享内存,第二个之后的进程,只要拿着同一个key就可以和第一个进程看到同一个共享内存了。

  3. 对于一个已经创建好的共享内存,key在哪?

    key在共享内存的描述对象中。

  4. 第一次创建的时候,必须有一个key了。怎么有?

  5. key 类似路径唯一


3.6.4 key和shmid的主要区别:

  1. 基本概念

    key:是一个用户定义的值,用来标识共享内存段的访问权限,类似于文件路径名

    shmid:是系统分配的共享内存段标识符,是系统内部使用的唯一标识符

  2. 使用时机

    key:在创建或获取共享内存时使用

    shmid:在共享内存创建后由系统返回,后续操作都使用shmid

  3. 代码示例

#include <sys/shm.h>// 使用key创建共享内存
key_t key = ftok("/tmp", 'A');  // 创建key
int shmid = shmget(key, 1024, IPC_CREAT | 0666); // 用key获取shmid// 后续操作使用shmid
void *addr = shmat(shmid, NULL, 0);  // 连接共享内存
shmctl(shmid, IPC_RMID, NULL);  // 删除共享内存
  1. 关系

    一个key可以对应一个shmid

    key是用户层面的标识

    shmid是系统层面的标识

  2. 生命周期

    key:可以重复使用

    shmid:随共享内存段的存在而存在,删除后失效


3.7 systemv消息队列(了解)

消息队列提供了一个从一个进程向另外一个进程发送一块数据的方法

每个数据块都被认为是有一个类型,接收者进程接收的数据块可以有不同的类型值

特性方面:IPC资源必须删除,否则不会自动清除,除非重启,所以system V IPC资源的生命周期随内核

通过消息队列想让AB进行通信那么首先要让不同进程看到同一份资源。

  1. 必须让不同进程看到同一个队列

  2. 允许不同的进程,向内核中发送带类型的数据块(通过类型来区分数据块是属于谁的)

A进程可以把它的数据块放到队列中,B进程可以把它的数据块放到队列中。

A进程就可以从队列中拿B进程给A进程发的数据块,反之亦然。

可以让A进程 <--以数据块的形式发送数据--> B进程。

2afaf5fef54063425c71bea5f3ab0008


3.8 systemv信号量(了解)

信号量主要用于同步和互斥的。什么是同步和互斥?

进程互斥

由于各进程要求共享资源,而且有些资源需要互斥使用,因此各进程间竞争使用这些资源,进程的这种关系为进程的互斥。

系统中某些资源一次只允许一个进程使用,称这样的资源为临界资源或互斥资源。

在进程中涉及到互斥资源的程序段叫临界区。

特性方面:IPC资源必须删除,否则不会自动清除,除非重启,所以system V IPC资源的生命周期随内核


四个问题

当我们的A正在写入,写入了一部分,就被B拿走了,导致双方发和收的数据不完整 – 数据不一致问题

  1. A B看到的同一份资源,共享资源,如果不加保护,会导致数据不一致问题

  2. 我们可以通过“加锁”形成互斥访问 – 任何时刻,只允许一个执行流访问共享资源 – 互斥

  3. 共享的,任何时刻只允许一个执行流访问(就是执行访问代码)的资源我们一般称为:临界资源(一般是操作系统和用户维护的内存空间)(管道也是临界资源)

  4. 举例:100行代码,5~10行代码才在访问临界资源。 我们访问临界资源的代码在:临界区


理解信号量

信号量的本质是一个计数器。

描述临界资源数量的多少。

  1. 申请计数器成功,就表示我具有访问资源的权限了

  2. 申请了计数器资源,我当前访问我要的资源了吗?没有。申请了计数器资源是对资源的预订机制

  3. 计数器可以有效保证进入共享资源的执行流的数量

  4. 所以每一个执行流,想访问共享资源中的一部分的时候,不是直接访问,而是先申请计数器资源。

程序员把这个"计数器",叫做信号量。

申请信号量,本质是对计数器--,P操作

释放资源,释放信号量,本质是对计数器进行++操作,V操作

申请和释放PV操作是原子性操作。

要么不做,要做就做完 — 两态的。没有“正在做”这样的概念。

信号量本质是一把计数器,PV操作,原子的。

执行流申请资源,必须先申请信号量资源,得到信号量之后,才能访问临界资源。

信号量值1,0两态的,二元信号量,就是互斥功能

申请信号量的本质:是对临界资源的预订机制。

信号量凭什么是进程间通信的一种?

  1. 通信不仅仅是通信数据,双方互相协同也是。

  2. 要协同,本质也是通信,信号量首先要被所有的通信进程看到。

mmap函数 – 也是共享内存。(仅作了解)

后面学习的信号和这里的信号量没有任何关系。

相关文章:

【Linux】24.进程间通信(3)

文章目录 3.6 systemv共享内存3.6.1 共享内存函数3.6.3 一个简单的共享内存代码实现3.6.4 一个复杂的共享内存代码实现3.6.4 key和shmid的主要区别: 3.7 systemv消息队列&#xff08;了解&#xff09;3.8 systemv信号量&#xff08;了解&#xff09;进程互斥四个问题理解信号量…...

【自然语言处理】TextRank 算法提取关键词(Python实现)

文章目录 前言PageRank 实现TextRank 简单版源码实现jieba工具包实现TextRank 前言 TextRank 算法是一种基于图的排序算法&#xff0c;主要用于文本处理中的关键词提取和文本摘要。它基于图中节点之间的关系来评估节点的重要性&#xff0c;类似于 Google 的 PageRank 算法。Tex…...

Java-128陷阱、抽象类和接口的区别、为什么 hashCode()需要和equals()一起重写、封装继承多态

128陷阱 Integer a 100; Integer b 100; System.out.println(ab); //true Integer c 1000; Integer d 1000; System.out.println(cd);//false int e 1000; System.out.println(ce);//true 分析以上代码运行的结果 源码&#xff1a; Integer a128; 编译器执行的是&…...

使用 Python 编程语言来实现机器学习小项目教程案例

以下是一个简单的机器学习小项目教程案例,使用 Python 编程语言和 Scikit-learn 库来实现一个分类任务。我们将使用经典的鸢尾花(Iris)数据集来训练一个分类器,预测鸢尾花的种类。 项目目标 使用机器学习算法对鸢尾花数据集进行分类,预测鸢尾花的类别(Setosa、Versicolor…...

如何评价镜头的好坏?光学镜头的一种评价标准

1光学传递函数MTF MTF是什么&#xff1f; 光学传递函数&#xff08;optical transfer function&#xff09;是指以空间频率为变量&#xff0c;表征成像过程中调制度和横向相移的相对变化的函数。光学传递函数是光学系统对空间频谱的滤波变换。一个非相干照明的光学成像系统&a…...

openGauss 3.0 数据库在线实训课程1:学习数据库状态查看

openGauss数据库状态查看 前提 我正在参加21天养成好习惯| 第二届openGauss每日一练活动 课程详见&#xff1a;openGauss 3.0.0数据库在线实训课程 学习目标 学习从操作系统层面和使用openGauss工具查看数据库的状态、版本和数据文件目录。 课程作业 gs_ctl是openGauss提…...

Stable Diffusion的入门介绍和使用教程

Stable Diffusion是一个文本到图像的潜在扩散模型&#xff0c;由CompVis、StabilityAI和LAION的研究人员和工程师创建。它使用来自LAION-5B数据库子集的512x512图像进行训练。使用这个模型&#xff0c;可以生成包括人脸在内的任何图像&#xff0c;因为有开源的预训练模型&#…...

docker安装es及分词器ik

系统是macos&#xff0c;docker是docker-desktop 拉取镜像 docker pull bitnami/elasticsearch 启动docker镜像 docker create -e "discovery.typesingle-node" \ --name elasticsearch1 -p 9200:9200 -p 9300:9300 \ bitnami/elasticsearch:8.17.1 测试是否好…...

记一次框架利用接管学工系统

视频教程在我主页简介或专栏里 链接&#xff1a;观看更多 Springboot actuator &#xff08;1&#xff09;某学院学工管理系统存在Springboot actuator未授权,泄露了很多接口地址&#xff0c;其他接口就不过多介绍了&#xff0c;这里具体讲述这次利用到的httptrace和jolokia两…...

低代码提升交付效率的公式计算

低&#xff08;无&#xff09;代码平台&#xff08;后统称“低代码”&#xff09;能够提升数字化应用建设、交付效率&#xff0c;已经成为IT从业人员的共识。目前&#xff0c;大部分CIO/CDO都能清晰定位和认知低代码的特点和作用。但仍然有人认为&#xff0c;使用了低代码工具软…...

深入解析:如何利用 Python 爬虫获取商品 SKU 详细信息

在电商领域&#xff0c;SKU&#xff08;Stock Keeping Unit&#xff0c;库存单位&#xff09;详细信息是电商运营的核心数据之一。它不仅包含了商品的规格、价格、库存等关键信息&#xff0c;还直接影响到库存管理、价格策略和市场分析等多个方面。本文将详细介绍如何利用 Pyth…...

java后端开发面试常问

面试常问问题 1 spring相关 &#xff08;1&#xff09;Transactional失效的场景 <1> Transactional注解默认只会回滚运行时异常&#xff08;RuntimeException&#xff09;&#xff0c;如果方法中抛出了其他异常&#xff0c;则事务不会回滚&#xff08;数据库数据仍然插…...

第六期:开放银行突围战 - API经济下的跨域经营合规框架

一、监管沙盒中的API兵法 1.1 开放银行接口的军备等级 人行《商业银行应用程序接口管理规范》(2025修订版): 安全分级: L1(查询类):日均调用量≤10万次 (如余额查询) L2(交易类):必须双因素认证 (如转账) L3(决策类):需人工智能审计跟踪 (如授信评估) 实战接口设计…...

全程Kali linux---CTFshow misc入门(25-37)

第二十五题&#xff1a; 提示&#xff1a;flag在图片下面。 直接检查CRC&#xff0c;检测到错误&#xff0c;就直接暴力破解。 暴力破解CRC的python代码。 import binascii import struct def brute_force_ihdr_crc(filename): # 读取文件二进制数据 with open(filen…...

Axure大屏可视化动态交互设计:解锁数据魅力,引领决策新风尚

可视化组件/模板预览&#xff1a;https://8dge09.axshare.com 一、大屏可视化技术概览 在数据驱动决策的时代&#xff0c;大屏可视化技术凭借直观、动态的展示方式&#xff0c;已成为众多行业提升管理效率和优化决策过程的关键工具。它能够将复杂的数据转化为易于理解的图形和…...

《AI “造脸术”:生成对抗网络打造超真实虚拟人脸》

在科技飞速发展的当下&#xff0c;人工智能的浪潮席卷而来&#xff0c;其中生成对抗网络&#xff08;GANs&#xff09;技术以其独特的魅力&#xff0c;成为了生成高度真实感虚拟人脸的强大引擎。无论是影视制作中虚拟角色的塑造&#xff0c;还是游戏领域中多样化角色形象的构建…...

常用工具类——Collections集合框架

常用工具类——Collections集合框架 Collections 是 JDK 提供的一个工具类&#xff0c;提供了一系列静态方法&#xff0c;分类来复习&#xff01; 1.排序操作 reverse(List list) :反转顺序shuffle(List list) &#xff1a; 洗牌&#xff0c;将顺序打乱sort(List list) &…...

Verilog语言学习总结

Verilog语言学习&#xff01; 目录 文章目录 前言 一、Verilog语言是什么&#xff1f; 1.1 Verilog简介 1.2 Verilog 和 C 的区别 1.3 Verilog 学习 二、Verilog基础知识 2.1 Verilog 的逻辑值 2.2 数字进制 2.3 Verilog标识符 2.4 Verilog 的数据类型 2.4.1 寄存器类型 2.4.2 …...

软件工程-数据流图DFD

数据流图&#xff08;DFD&#xff09;是一种图形化技术&#xff0c;它描绘信息流和数据从输入移动到输出的过程中经受的变换。 数据流图是系统逻辑功能和图形表示&#xff0c;即使不是专业的计算机人员也容易理解它&#xff0c;因此是分析员与用户之间极好的通信工具。 设计数…...

为什么需要同时重写equals方法和hashCode方法

在 Java 编程中&#xff0c;equals 和 hashCode 是两个非常重要的方法&#xff0c;它们用于确定对象的相等性和哈希值。这两个方法通常需要同时重写&#xff0c;否则会导致哈希表类&#xff08;如 HashMap、HashSet&#xff09;的行为异常。因此&#xff0c;理解这两个方法的工…...

c++11总结26——std::regex

std::regex 是 C11 引入的 正则表达式库&#xff0c;用于 字符串匹配、搜索和替换。 &#x1f539; 头文件&#xff1a;#include <regex> &#x1f539; 命名空间&#xff1a;std &#x1f539; 支持的匹配模式&#xff1a;ECMAScript&#xff08;默认&#xff09;、POS…...

Linux运维——查看命令帮助信息

查看命令帮助信息 一、查看 Linux 命令帮助信息的要点二、常见命令用法2.1、help2.2、whatis2.3、info2.4、which2.5、whereis2.6、man 一、查看 Linux 命令帮助信息的要点 查看 Shell 内部命令的帮助信息 - 使用 help查看命令的简要说明 - 使用 whatis查看命令的详细说明 - 使…...

应急场景中的数据融合与对齐

1. 概述 在应急管理中,快速、准确地掌握现场状况、实时监控灾情并进行决策至关重要。各类数据(如卫星影像、无人机图像、激光雷达点云、地理信息系统(GIS)数据、传感器数据、社交媒体信息、移动终端数据等)具有来源广泛、格式多样、时空特性不同等特点。如何将这些异构数…...

Java数据结构与算法之“树”

目录 一、什么是树 ​编辑 二、树的相关组成 1. 常用名词 2.需要了解的名词 三、树的分类 &#xff08;一&#xff09;初级树 1.普通树 2.二叉树 &#xff08;二&#xff09;中级树 1.哈夫曼树HuffmanTree 2.二叉搜索树BST 3.平衡二叉树AVL &#xff08;三&#x…...

网络HTTP详细讲解

学习目标 什么是HTTPHTTP的请求和响应常见的HTTP状态码HTTP的安全性 什么是HTTP&#xff1f;HTTP的请求和响应&#xff0c;常见的HTTP状态码&#xff0c;HTTP的安全性 什么是HTTP HTTP&#xff08;HyperText Transfer Protocol&#xff0c;超文本传输协议&#xff09;是一种用…...

基于Python的智能物流路径优化算法研究与应用

基于Python的智能物流路径优化算法研究与应用 摘要 随着电商行业的迅猛发展&#xff0c;物流配送的效率和成本成为影响企业竞争力的关键因素。本论文聚焦于基于Python语言实现智能物流路径优化算法的研究。通过对经典路径优化算法如Dijkstra算法、A*算法等的深入分析&#xff…...

Origin2024 软件安装步骤与百度网盘

软件简介&#xff1a; Origin 2024是一款功能强大的科学绘图与数据分析软件&#xff0c;广泛应用于科研和工程领域&#xff0c;支持多种图形绘制、数据分析功能以及便捷的数据导入和管理。 网盘链接: https://pan.baidu.com/s/1MNQG2pD802LWxuIN40JfeA?pwdc85q 提取码:c85…...

【算法应用】Alpha进化算法求解二维栅格路径规划问题

目录 1.算法原理2.二维路径规划数学模型3.结果展示4.参考文献5.代码获取 1.算法原理 Alpha进化&#xff1a;一种具有进化路径自适应和矩阵生成的高效进化算法 2.二维路径规划数学模型 栅格法模型最早由 W.E. Howden 于 1968 年提出&#xff0c;障碍物的栅格用黑色表示&#…...

PyQt6/PySide6 的 QDialog 类

QDialog 是 PyQt6 或 PySide6 库中用于创建对话框的类。对话框是一种特殊的窗口&#xff0c;通常用于与用户进行短期交互&#xff0c;如输入信息、显示消息或选择选项等。QDialog 提供了丰富的功能和灵活性&#xff0c;使得开发者可以轻松地创建各种类型的对话框。下面我将详细…...

嵌入式八股文面试题(一)C语言部分

1. 变量/函数的声明和定义的区别&#xff1f; &#xff08;1&#xff09;变量 定义不仅告知编译器变量的类型和名字&#xff0c;还会分配内存空间。 int x 10; // 定义并初始化x int x; //同样是定义 声明只是告诉编译器变量的名字和类型&#xff0c;但并不为它分配内存空间…...