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

目录

一、什么是共享内存

二、共享内存的原理

三、使用共享内存实现进程间通信

3.1 shmget接口

3.1.1 key形参详解

3.2 释放共享内存

3.2.1 ipcs指令

3.2.2 ipcrm指令

3.2.3 shmctl接口

3.3 关联共享内存

3.4 去关联共享内存

3.5 使用共享内存进行进程间通信实例

四、共享内存的特性

4.1 共享内存的优点

4.2 共享内存的缺点

五、实例代码改进


一、什么是共享内存

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

二、共享内存的原理

要实现进程间通信的根本在于让不同的进程看到同一块份数据,共享内存也不例外

现在有两个毫不相干的进程A和B:

它们想要进行通信,就必须要看到同一份数据,所以操作系统会在物理空间上开辟一块空间,供这两个进程共同访问,这一块空间被称为共享内存

共享内存被页表映射到进程地址空间的存储区域在共享区

想要释放共享内存也很简单,先取消两个进程页表对应的映射关系,就可以释放共享内存块了

但是系统中的进程都可以用共享内存进行通信,所以在任何一个时刻,可能有多个共享内存在被用来进行通信,导致系统中一定会存在很多共享内存同时存在

那OS要不要整体管理所有的共享内存呢?

当然是要的,所以共享内存,不仅仅是我们想的那样,只要在内存中开辟空间即可,系统也要为了管理共享内存,构建对应的描述共享内存的结构体对象!

即:共享内存=共享内存的内核数据结构+真正开辟的内存空间

管理共享内存的数据结构struct shmid_ds:

struct shmid_ds
{struct ipc_perm shm_perm; /* Ownership and permissions */size_t shm_segsz;         /* Size of segment (bytes) */time_t shm_atime;         /* Last attach time */time_t shm_dtime;         /* Last detach time */time_t shm_ctime;         /* Last change time */pid_t shm_cpid;           /* PID of creator */pid_t shm_lpid;           /* PID of last shmat(2)/shmdt(2) */shmatt_t shm_nattch;      /* No. of current attaches */...
};
struct ipc_perm
{key_t __key;          /* Key supplied to shmget(2) */uid_t uid;            /* Effective UID of owner */gid_t gid;            /* Effective GID of owner */uid_t cuid;           /* Effective UID of creator */gid_t cgid;           /* Effective GID of creator */unsigned short mode;  /* Permissions + SHM_DEST andSHM_LOCKED flags */unsigned short __seq; /* Sequence number */
};

三、使用共享内存实现进程间通信

废话不多说,我们直接来上操作

3.1 shmget接口

现在有一个系统接口shmget(包含在头文件<sys/ipc.h>和<sys/shm.h>中)来帮我们建立共享内存:

该函数有三个参数:

key:这个共享内存段名字,要具备唯一性(通常使用ftok函数传入)

size:共享内存大小(以字节为单位),但是OS实际开辟的空间大小是以Page页(4KB)为单位的,底层会将我们传入的空间大小向上取整为Page页的倍数并开辟;但是这不代表OS实际开辟了多少空间我们就可以用多少空间,实际使用空间的大小还是依据我们传入数据的大小,防止越界

shmflg:由九个权限标志构成(它们的用法和创建文件时使用的mode模式标志是一样的),常用的标志位有IPC_CREATIPC_EXCL,另外我们还可以通过该参数设置我们所创建出的共享内存的权限(或上八进制方案)

        IPC_CREAT:可以单独使用,单独使用就是创建一个共享内存,如果共享内存不存在,就创建之,如果已经存在,获取已经存在的共享内存并返回

        IPC_EXCL:不能单独使用,一般都要配合IPC_CREAT使用(IPC_CREAT | IPC_EXCL:创建一个共享内存,如果共享内存不存在,就创建之,如果已经存在,则立马出错返回;这样可以保证创建成功时所对应的共享一定是最新的,一定没有被别的进程使用过)

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

3.1.1 key形参详解

操作系统中会存在很多共享内存,那如何让毫不相干的进程识别同一块共享内存空间呢?

我们可以给每个共享内存起名字,key该形参就可以表示共享内存段名字,但是名字可不是随便起的,要保证每一块共享内存的名字不相同我们可以使用ftok函数(包含在头文件<sys/types.h>和<sys/ipc.h>中):

该函数有两个形参:

● pathname:传入文件路径

● proj_id:传入工程id

对于这两个形参并没有强制性的要求,该函数会对传入的两个形参进行特定的算法计算后,返回一个冲突几率极小的值,该值可以作为系统接口shmget的key形参

该函数成功后,将返回生成的key_t值。失败时返回-1,并设置errno

这样子只要两个进程都调用ftok函数,传入相同的形参值就可以得到相同的值,以这个值我们就可以标定同一个共享内存,从而建立进程间的通信

3.2 释放共享内存

我们需要注意的是:当进程创建共享内存之后,当进程结束后,其创建的共享内存空间不会被自动释放,该空间的生命周期是随操作系统的(在下面我们来介绍释放共享内存的方法)

3.2.1 ipcs指令

我们在可以使用ipcs指令用于查看Linux进程间通信设施的状态,包括消息列表、共享内存和信号量的信息,下面是其命令选项:

-i,--id ID
    详细显示指定资源 ID 的 IPC 信息。使用时需要指定资源类型,资源包括消息队列(-q)、共享内存(-m)和信号量(-s)
-h,--help
    显示帮助信息
-V,--version
    显示版本信息

IPC 资源类型选项:
-q,--queues
    显示活动的消息队列信息
-m,--shmems
    显示活动的共享内存信息
-s, --semaphores
    显示活动的信号量信息
-a,--all
    显示系统内所有的IPC信息。命令的默认选项

输出格式选项:当指定多个时,以最后一个为准。
-c,--creator
    查看 IPC 的创建者和所有者
-l,--limits
    查看 IPC 资源的限制信息
-p,--pid
    查看 IPC 资源的创建者和最后操作者的进程 ID
-t,--time
    查看最新调用 IPC 资源的详细时间。包括 msgsnd() 和 msgrcv() 对 message queues 的操作,shmat() 和 shmdt() 对shared memory 的操作,以及 semop() 对 semaphores 的操作
-u,--summary
    查看 IPC 资源状态汇总信息

显示大小单位控制选项:只对选项 -l, --limits 生效
-b,--bytes
    以字节为单位显示大小
--human
    以可读的格式显示大小

下面我们显示活动的共享内存信息:

(nattch表示该共享内存被几个进程关联着)

可以看到在我的系统中有一个key为0x1101120d,shmid为0的共享内存块, 下面我们来释放它 :

3.2.2 ipcrm指令

ipcrm可以删除指定 ID 的 IPC(Inter-Process Communication)对象,包括消息队列(message queue)、共享内存(shared memory)和信号量(semaphore),同时将与 IPC 对象关联的数据一并删除,下面是其命令选项:

-a, --all [shm | msg | sem]
    删除所有 IPC 资源。当给定选项参数 shm、msg 或 sem,则只删除指定类型的 IPC 资源。注意:慎用该选项,否则可能会导致某些程序出于不确定状态
-M, --shmem-key SHMKEY
    当没有进程与共享内存段绑定时,通过 SHMKEY 删除共享内存段
-m, --shmem-id SHMID
    当没有进程与共享内存段绑定时,通过 SHMID 删除共享内存段
-Q, --queue-key MSGKEY
    通过 MSGKEY 删除消息队列
-q, --queue-id MSGID
    通过 MSGID 删除消息队列
-S, --semaphore-key SEMKEY
    通过 SEMKEY 删除信号量
-s, --semaphore-id SEMID
    通过 SEMID 删除信号量
-h, --help
    显示帮助信息并退出
-V, --version
    显示版本信息并退出
-v, --verbose
    以冗余模式执行 ipcrm,输出 rpcrm 正在做什么

下面我们来删除上面的key为0x1101120d,shmid为0的共享内存块,我们使用-m选项加上要删除的shmid即可:

3.2.3 shmctl接口

我们在写代码创建共享内存后总不可能使用系统指令去删除,所以我们需要在代码中调用系统接口shmctl来掌控共享内存:

shmctl函数的有三个参数:

● shmid :共享内存标识符,即要控制的共享内存段的标识符。


● cmd :控制命令,用于指定要执行的操作,比如删除共享内存段、修改权限等。常用的命令包括:

        IPC_RMID (删除共享内存段)

        IPC_SET (在进程有足够权限的前提下,把共享内存的当前关联值设置为shmid_ds数据结构中给出的值)

        IPC_STAT(在进程有足够权限的前提下,把shmid ds结构中的数据设置为共享内存的当前关联值)


● buf :指向 struct shmid_ds 结构的指针,用于存储共享内存段的信息。可以为 NULL ,表示不获取共享内存段的信息。

shmctl函数的返回值是一个整型值,表示函数执行的结果。如果函数执行成功,返回值为0;如果出现错误,返回值为-1,并设置 errno 来指示具体的错误原因。

3.3 关联共享内存

我们创建(获取)了共享内存,那该使两个进程联系起来呢?

我们可以将共享内存段关联到进程地址空间,这样子进程就可以使用所获得的进程地址空间中的地址访问共享内存了

我们调用系统接口shmat(包含在头文件<sys/types.h>和<sys/shm.h>中)来让进程挂接共享内存:

该函数需要3个参数:

● shmid:传入shmget返回的标识符。


● shmaddr:如果传入NULL,系统将自动选择一个合适的进程地址空间! 如果shmaddr不是NULL 并且没有指定SHM_RND,则此段连接到addr所指定的地址上,如果shmaddr非0 并且指定了SHM_RND 则此段连接到shmaddr -(shmaddr mod SHMLAB)所表示的地址上(SHM_RND命令的意思是取整,SHMLAB的意思是低边界地址的倍数,它总是2的乘方。该算式是将地址向下取最近一个 SHMLAB的倍数)。除非只计划在一种硬件上运行应用程序(这在当今是不大可能的),否则不用指定共享段所连接到的地址。所以一般应指定shmaddr为NULL,以便由内核选择地址。

● shmflg:传入SHM_RDONLY,以只读方式连接此段;传入0以读写的方式连接此段

shmat返回值是返回创建的进程虚拟空间的地址 如果出错返回-1

3.4 去关联共享内存

那两个进程通过共享内存通信结束后,我们要取消进程与共享内存之间的关联,这里要用到另一个接口shmdt(包含在头文件<sys/types.h>和<sys/shm.h>中):

该接口有一个参数:

● shmaddr: 传入由shmat所返回的指针

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

注意:将共享内存段与当前进程去关联不等于删除共享内存段

3.5 使用共享内存进行进程间通信实例

下面我们使用两个进程:client和server来实现共享内存进行进程间通信:

common.hpp:

#ifndef __COMM_HPP__
#define __COMM_HPP__#include <iostream>
#include <cstring>
#include <string>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>#endif#define PATHNAME "."
#define PROJID 0x1111const int gsize = 4069; // 共享内存大小std::string To_Hex(int x) // 将十进制转为十六进制
{char buffer[64];snprintf(buffer, sizeof buffer, "0x%x", x);return buffer;
}const key_t GetKey() // 获取key值
{key_t k = ftok(PATHNAME, PROJID);if (k == -1){std::cerr << "error: " << errno << ": " << strerror(errno) << std::endl;exit(1);}return k;
}static int getshm(key_t key, int size, int flag) // 获取共享内存
{int k = shmget(key, size, flag);if (k == -1){std::cerr << "error: " << errno << ": " << strerror(errno) << std::endl;exit(1);}return k;
}int Create_shm(key_t key, int size) // 创建共享内存段
{umask(0);return getshm(key, size, IPC_CREAT | IPC_EXCL | 0666); //(IPC_CREAT | IPC_EXCL)以保证创建的共享内存段是最新的,|0666将创建的共享空间的权限让创建者有读写权
}int Obtain_shm(key_t key, int size) // 获取srever进程创建的共享内存段的标识码
{return getshm(key, size, IPC_CREAT);
}int Delete_shm(int shmid) // 删除共享内存
{return shmctl(shmid, IPC_RMID, nullptr);
}void *Attach_shm(int shmid) // 让进程关联上共享内存
{return shmat(shmid, nullptr, 0);
}void Datach_shm(void *addptr) // 解除进程关联的共享内存
{if (shmdt(addptr) == -1){std::cerr << "error: " << errno << ": " << strerror(errno) << std::endl;exit(1);}
}#define SERVER 0 // 服务段进程
#define CLIENT 1 // 客户端进程class Init_shm
{
public:Init_shm(int type): _type(type){_key = GetKey();if (type == SERVER)_shmid = Create_shm(_key, gsize);else_shmid = Obtain_shm(_key, gsize);_addptr = Attach_shm(_shmid);}void Get_shm_data(){struct shmid_ds ds;int ret = shmctl(_shmid, IPC_STAT, &ds);if (ret == -1)std::cerr << "error: " << errno << ": " << strerror(errno) << std::endl;else{std::cout << "create shm process pid: " << ds.shm_cpid << " ,proccess pid:" << getpid() << std::endl;std::cout << "shm shmid: " << To_Hex(ds.shm_perm.__key) << std::endl;}}void *Getptr(){return _addptr;}~Init_shm(){Datach_shm(_addptr);if (_type == SERVER)Delete_shm(_shmid);}private:int _type; // 进程类型key_t _key;int _shmid;void *_addptr; // 关联的共享内存在进程空间中的地址
};

client.cc:

#include "common.hpp"
#include <unistd.h>
int main()
{Init_shm shm(CLIENT);shm.Get_shm_data();void *ptr = shm.Getptr();char c = 'a';std::cout << "Start writing" << std::endl;while (c != 'z' + 1) // 写入数据{((char *)ptr)[c - 'a'] = c;((char *)ptr)[c - 'a' + 1] = '\0';++c;sleep(1);}std::cout << "Write End" << std::endl;return 0;
}

server.cc:

#include "common.hpp"int main()
{Init_shm shm(SERVER);shm.Get_shm_data();void *ptr = shm.Getptr();int n = 30;while (n--) // 读取数据{std::cout << "client message:" << (char *)ptr << std::endl;sleep(1);}return 0;
}

运行效果:

四、共享内存的特性

4.1 共享内存的优点

我们可以看到上面的实例代码,在数据的写入和读取的过程中并没有向管道通信一样调用write、read等系统接口,而是直接向对应的地址空间写入和读取

这样子使共享内存间的进程间的数据不用传送,而是直接访问内存,绕过了Linux的内核,加快了程序的效率。同时,它也不像匿名管道那样要求通信的进程有一定的父子关系

4.2 共享内存的缺点

下面我们单单运行serve进程:

可以看到即使共享内存中没有任何数据,但是该进程还是无脑的进行读取,但是在上期我们介绍的管道通信中,当管道中没有数据时,read函数会阻塞

所以共享内存没有任何保护机制(同步互斥),这使得我们在使用共享内存进行进程间通信时,往往要借助其他的手段来进行进程间的同步工作

五、实例代码改进

现在我们将上面的代码改进一下,让client进程先向共享内存中写入数据,写完了再通知server进程来读取:

对于该功能的实现我们要联系到管道通信,创建一个管道来告知server进程是否完成了写入任务(对于管道不熟悉的同学请看到这里:【Linux】进程间通信——管道):

common.hpp

#pragma once#ifndef __COMM_HPP__
#define __COMM_HPP__#include <iostream>
#include <cstring>
#include <string>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>#endif#define PATHNAME "."
#define PROJID 0x1111const int gsize = 4069; // 共享内存大小std::string To_Hex(int x) // 将十进制转为十六进制
{char buffer[64];snprintf(buffer, sizeof buffer, "0x%x", x);return buffer;
}const key_t GetKey() // 获取key值
{key_t k = ftok(PATHNAME, PROJID);if (k == -1){std::cerr << "error: " << errno << ": " << strerror(errno) << std::endl;exit(1);}return k;
}static int getshm(key_t key, int size, int flag) // 获取共享内存
{int k = shmget(key, size, flag);if (k == -1){std::cerr << "error: " << errno << ": " << strerror(errno) << std::endl;exit(1);}return k;
}int Create_shm(key_t key, int size) // 创建共享内存段
{umask(0);return getshm(key, size, IPC_CREAT | IPC_EXCL | 0666); //(IPC_CREAT | IPC_EXCL)以保证创建的共享内存段是最新的,|0666将创建的共享空间的权限让创建者有读写权
}int Obtain_shm(key_t key, int size) // 获取srever进程创建的共享内存段的标识码
{return getshm(key, size, IPC_CREAT);
}int Delete_shm(int shmid) // 删除共享内存
{return shmctl(shmid, IPC_RMID, nullptr);
}void *Attach_shm(int shmid) // 让进程关联上共享内存
{return shmat(shmid, nullptr, 0);
}void Datach_shm(void *addptr) // 解除进程关联的共享内存
{if (shmdt(addptr) == -1){std::cerr << "error: " << errno << ": " << strerror(errno) << std::endl;exit(1);}
}#define SERVER 0 // 服务端进程
#define CLIENT 1 // 客户端进程class Init_shm
{
public:Init_shm(int type): _type(type){_key = GetKey();if (type == SERVER)_shmid = Create_shm(_key, gsize);else_shmid = Obtain_shm(_key, gsize);_addptr = Attach_shm(_shmid);}void Get_shm_data(){struct shmid_ds ds;int ret = shmctl(_shmid, IPC_STAT, &ds);if (ret == -1)std::cerr << "error: " << errno << ": " << strerror(errno) << std::endl;else{std::cout << "create shm process pid: " << ds.shm_cpid << " ,proccess pid:" << getpid() << std::endl;std::cout << "shm shmid: " << To_Hex(ds.shm_perm.__key) << std::endl;}}void *Getptr(){return _addptr;}~Init_shm(){Datach_shm(_addptr);if (_type == SERVER)Delete_shm(_shmid);}private:int _type; // 进程类型key_t _key;int _shmid;void *_addptr; // 关联的共享内存在进程空间中的地址
};#define NUM 1024std::string file_name = "./serve_fifo";class Init_fifo
{
public:Init_fifo(){umask(0);int ret;ret = mkfifo(file_name.c_str(), 0666); // 创建管道文件if (ret == -1){std::cout << "mkfifo error ,errno:" << strerror(errno) << std::endl;exit(1);}elsestd::cout << "created fifo successfully" << std::endl;}~Init_fifo(){unlink(file_name.c_str()); // 删除管道文件}
};

client.cc:

#include "common.hpp"int main()
{Init_shm shm(CLIENT);shm.Get_shm_data();void *ptr = shm.Getptr();char c = 'a';// 开始写入数据std::cout << "Start writing" << std::endl;while (c != 'z' + 1){((char *)ptr)[c - 'a'] = c;((char *)ptr)[c - 'a' + 1] = '\0';++c;sleep(1);}std::cout << "Write End" << std::endl;// 写入完毕int wfd;wfd = open(file_name.c_str(), O_WRONLY);if (wfd == -1){std::cout << "open error ,errno:" << strerror(errno) << std::endl;exit(1);}char buffer[NUM];buffer[0] = '1';ssize_t n = write(wfd, buffer, strlen(buffer)); // 向管道文件中传入信号if (n < 0){std::cout << "write error ,errno:" << strerror(errno) << std::endl;close(wfd);exit(1);}close(wfd);return 0;
}

 server.cc:

#include "common.hpp"int main()
{Init_shm shm(SERVER);shm.Get_shm_data();Init_fifo client;int wfd;wfd = open(file_name.c_str(), O_RDONLY);if (wfd == -1){std::cout << "open error ,errno:" << strerror(errno) << std::endl;exit(1);}elsestd::cout << "open success" << std::endl;char buffer[NUM];ssize_t n = read(wfd, buffer, sizeof(buffer) - 1); // 等待client进程的信号if (n > 0){buffer[n] = '\0';if (buffer[0] == '1'){std::cout << "client write end" << std::endl;void *ptr = shm.Getptr();std::cout << "client message:" << (char *)ptr << std::endl;sleep(1);}}else if (n == 0){std::cout << "client quit" << std::endl;exit(1);}else{std::cout << "read error ,errno:" << strerror(errno) << std::endl;close(wfd);exit(1);}close(wfd);return 0;
}

运行效果:

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

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

相关文章

Java基础-IO流

目录 1 File 类的使用 1.1 File类的概念 1.2 构造方法 1.3 常用方法 1.4 课后练习 2 IO流原理及流的分类 2.1 IO原理 2.2 流的分类 2.3 IO流体系 2.4 接口方法 2.4.1 InputStream & Reader相同点 2.4.2 InputStream方法详解 2.4.3 Reader方法详解 2.4.4 Outp…

ant javac任务的fork和executable属性

ant javac任务是用于编译源文件的。 它的fork属性表示是否用JDK编译器在外部执行javac&#xff0c;取值可以为"yes"、“no”&#xff0c;默认值为"no"。 当fork属性的取值为"yes"时&#xff0c;可以用executable属性指明javac可执行文件的完全…

sql高级教程-索引

文章目录 架构简介1.连接层2.服务层3.引擎层4.存储层 索引优化背景目的劣势分类基本语法索引结构和适用场景 性能分析MySq| Query Optimizerexplain 索引优化单表优化两表优化三表优化 索引失效原因 架构简介 1.连接层 最上层是一些客户端和连接服务&#xff0c;包含本地sock通…

汽车屏类产品(五):中控IVI车载信息娱乐系统

前言: 车载信息娱乐系统(IVI)的起源可以追溯到20世纪,按钮调幅收音机被认为是第一个功能。从那以后,IVI系统在创造壮观的车内体验方面变得不可或缺,以至于汽车被称为“车轮上的智能手机”。但随着包括自动驾驶汽车在内的汽车技术的进步,以及对个性化体验的需求不断增长…

Leetcode1839. 所有元音按顺序排布的最长子字符串

Every day a Leetcode 题目来源&#xff1a;1839. 所有元音按顺序排布的最长子字符串 解法1&#xff1a;滑动窗口 要找的是最长美丽子字符串的长度&#xff0c;我们可以用滑动窗口解决。 设窗口内的子字符串为 window&#xff0c;每当 word[right] > window.back() 时&…

最短路相关笔记

Floyd Floyd 算法&#xff0c;是一种在图中求任意两点间最短路径的算法。 Floyd 算法适用于求解无负边权回路的图。 时间复杂度为 O ( n 3 ) O(n^3) O(n3)&#xff0c;空间复杂度 O ( n 2 ) O(n^2) O(n2)。 对于两点 ( i , j ) (i,j) (i,j) 之间的最短路径&#xff0c;有…

算法刷题-链表

算法刷题-链表 203. 移除链表元素 给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链表中所有满足 Node.val val 的节点&#xff0c;并返回 新的头节点 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,6,3,4,5,6], val 6 输出&#xff1a;[1,2,3,4,5]…

asp.net社区医疗辅助诊断网站系统VS开发sqlserver数据库web结构c#编程

一、源码特点 asp.net社区医疗辅助诊断网站系统 是一套完善的web设计管理系统&#xff0c;系统采用mvc模式&#xff08;BLLDALENTITY&#xff09;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为vs2010&#xff0c;数据库为sqlserver200…

基于白鲸优化的BP神经网络(分类应用) - 附代码

基于白鲸优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于白鲸优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.白鲸优化BP神经网络3.1 BP神经网络参数设置3.2 白鲸算法应用 4.测试结果&#xff1a;5.M…

C语言指针

指针 文章目录 指针1.指针概念2.指针变量2.1 定义指针变量2.2 引用指针变量2.3 指针变量作为函数参数 3.通过指针引用数组3.1数组元素的指针3.2 在引用数组元素时指针的运算3.3通过指针引用数组元素3.4用数组名作函数参数3.5 通过指针引用多维数组 4.通过指针引用字符串4.1字符…

超详细 | 差分进化算法原理及其实现(Matlab/Python)

差分进化(Differential Evolution&#xff0c;DE)算法是由美国学者Storn和 Price在1995年为求解Chebyshev多项式拟合问题而提出的。算法主要通过基于差分形式的变异操作和基于概率选择的交叉操作进行优化搜索&#xff0c;虽然其操作名称和遗传算法相同&#xff0c;但实现方法有…

最新Tuxera NTFS2024破解版mac读写NTFS磁盘工具

Tuxera NTFS for Mac是一款Mac系统NTFS磁盘读写软件。在系统默认状态下&#xff0c;MacOSX只能实现对NTFS的读取功能&#xff0c;Tuxera NTFS可以帮助MacOS 系统的电脑顺利实现对NTFS分区的读/写功能。Tuxera NTFS 2024完美兼容最新版本的MacOS 11 Big Sur&#xff0c;在M1芯片…

Prometheus接入AlterManager配置邮件告警(基于K8S环境部署)

文章目录 一、配置AlterManager告警发送至邮箱二、Prometheus接入AlterManager配置三、部署PrometheusAlterManager(放到一个Pod中)四、测试告警 注意&#xff1a;请基于 PrometheusGrafana监控K8S集群(基于K8S环境部署)文章之上做本次实验。 一、配置AlterManager告警发送至邮…

EF执行迁移时提示provider: SSL Provider, error: 0 - 证书链是由不受信任的颁发机构颁发的

ef在执行时提示provider: SSL Provider, error: 0 - 证书链是由不受信任的颁发机构颁发的。 只需要在数据库链接字符串后增加EncryptTrue;TrustServerCertificateTrue;即可 再次执行

好用的办公软件有哪些

日常的工作难免和各种各样的软件打交道&#xff0c;除了传统的Office三件套&#xff0c;小编日常还在用着其他的办公软件&#xff0c;借此跟各位分享其中比较好用、堪称办公神器的8款软件&#xff01; 1.WPS office 2.office2007 3.EasyConnect 4.ToDesk 5.Photoshop 6.A…

​CUDA学习笔记(五)GPU架构

本篇博文转载于https://www.cnblogs.com/1024incn/tag/CUDA/&#xff0c;仅用于学习。 GPU架构 SM&#xff08;Streaming Multiprocessors&#xff09;是GPU架构中非常重要的部分&#xff0c;GPU硬件的并行性就是由SM决定的。 以Fermi架构为例&#xff0c;其包含以下主要组成…

Git 安装和基础命令、IDEA 基础操作

目录 总结命令&#xff1a;1、安装&#xff1a;1、安装2、配置环境变量&#xff1a; 2、Git操作&#xff1a;1、初始化&#xff1a;1、姓名邮箱&#xff1a;2、初始化仓库&#xff1a;3、工作区和暂存区分析 2、提交文件3、查看版本库状态4、安装小乌龟git不显示图标 5、查看提…

H3C SecParh堡垒机 get_detail_view.php 任意用户登录漏洞

与齐治堡垒机出现的漏洞不能说毫不相关&#xff0c;只能说一模一样 POC验证的url为&#xff1a; /audit/gui_detail_view.php?token1&id%5C&uid%2Cchr(97))%20or%201:%20print%20chr(121)%2bchr(101)%2bchr(115)%0d%0a%23&loginadmin成功获取admin权限 文笔生疏…

智慧公厕系列产品:为您提供更便捷、更卫生的厕所体验

智慧公厕系列产品致力于改善公共厕所的管理和使用体验&#xff0c;通过引入先进的科技和智能设备&#xff0c;提升厕所的安全、卫生、舒适性。这些产品涵盖了从厕位监测到环境调控&#xff0c;从安全防范到能耗监测的各个方面&#xff0c;为用户提供了一个更加方便、舒适、卫生…

【excel】列转行

列转行 工作中有一些数据是列表&#xff0c;现在需要转行 选表格内容&#xff1a;在excel表格中选中表格数据区域。点击复制&#xff1a;在选中表格区域处右击点击复制。点击选择性粘贴&#xff1a;在表格中鼠标右击点击选择性粘贴。勾选转置&#xff1a;在选择性粘勾选转置选…