【Linux】七、进程间通信(二)
目录
三、system V(IPC)
3.1 system V共享内存
3.1.1 共享内存的概念
3.1.2 共享内存的原理
3.1.3 创建共享内存(shmget )
3.1.4 ftok函数
3.1.5 查看共享内存资源
3.1.6 创建共享内存测试代码
3.1.7 再次理解共享内存
3.1.8 释放共享内存(shmctl)
3.1.9 关联共享内存(shmat)
3.1.10 去关联共享内存(shmdt)
3.1.11 使用共享内存实现serve&client通信
3.1.12 共享内存的优缺点
3.1.12 共享内存的内核数据结构
3.2 System V消息队列(了解)
3.3 System V信号量(了解)
三、system V(IPC)
system V 用于本地通信,通信的本质就是让不同的进程看到同一份资源
system V IPC提供的通信方式有以下三种:
- system V共享内存
- system V消息队列
- system V信号量
3.1 system V共享内存
3.1.1 共享内存的概念
让不同的进程看到同一块内存块,这块内存块就叫共享内存,并且共享内存区是最快的IPC形式
3.1.2 共享内存的原理
共享内存让不同进程看到同一份资源的方式就是,在物理内存当中申请一块内存空间,然后将这块内存空间分别与各个进程各自的页表之间建立映射,再在虚拟地址空间当中开辟空间并将虚拟地址填充到各自页表的对应位置,使得虚拟地址和物理地址之间建立起对应关系,至此这些进程便看到了同一份物理内存,这块物理内存就叫做共享内存,共享内存申请好之后会把共享内存的起始地址返回给用户
未来不想通信(即关闭通信):
- 去关联:取消进程和内存的映射关系
- 释放共享内存
对共享内存的理解:
- 共享内存,是专门设计的,是用于IPC,与malloc、new出来的内存完全不一样,这个共享内存是进程都可以看得到的,而malloc、new 出来的内存只对自己的进程开放,是私有的,其他进程看不到这块内存
- 共享内存是一种通信方式,所有想通信的进程都可以使用
- OS内一定可能同时存在大量的的共享内存
3.1.3 创建共享内存(shmget )
共享内存的建立大致包括以下两个过程:
- 在物理内存当中申请共享内存空间
- 将申请到的共享内存挂接到地址空间,即建立映射关系
创建共享内存需要用 shmget 函数(系统调用)
man 2 shmget 查看一下
解释:
函数:shmget
shm: shared memory头文件:#include <sys/ipc.h>#include <sys/shm.h>函数声明:
int shmget(key_t key, size_t size, int shmflg);参数:第一个参数key: 这个共享内存段名字,表示待创建共享内存在系统当中的唯一标识第二个参数size: 共享内存大小第三个参数shmflg: 表示创建共享内存的方式,用法和创建文件时使用的mode模式标志是一样的返回值:
成功,返回一个有效的共享内存标识符(用户层标识符)
失败,返回-1,错误码被设置
第三个参数shmflg 可使用的选项:
常用的选项只有两个:IPC_CREAT 和 IPC_CREAT
- IPC_CREAT:创建新的共享内存,如果共享内存已经存在,就获取已经存在的共享内存并返回它
- IPC_CREAT:无法单独使用,通常与 IPC_CREAT 一起使用,即(IPC_CREAT | IPC_CREAT),如果共享内存已经存在则报错,也就是说如果创建成功,这个共享内存一定是新建的
第一个参数key: 表示共享内存的唯一性,这个很重要
如何获取 key? 要使用函数 ftok
3.1.4 ftok函数
ftok函数用于获取keuy
man 3 ftok 查看一下:
解释:
函数:ftok头文件:#include <sys/types.h>#include <sys/ipc.h>函数声明:
key_t ftok(const char *pathname, int proj_id);参数:第一个参数pathname: 路径名第一个参数proj_id: 一个整数标识符
注:这两个参数都可以随便给返回值:
成功key被返回
失败返回-1,错误码被设置
3.1.5 查看共享内存资源
Linux当中,我们可以使用 ipcs 命令查看有关进程间通信的相关资源信息,单独使用 ipcs 命令时,会默认列出消息队列、共享内存以及信号量相关的信息
若只想查看它们之间某一个的相关信息,可以选择携带以下选项:
- q:列出消息队列相关信息
- -m:列出共享内存相关信息
- -s:列出信号量相关信息
比如查看共享内存:ipcs -m
ipcs 命令输出的每列信息的含义如下:
3.1.6 创建共享内存测试代码
代码如下:
#include <iostream>
#include <cerrno>
#include <cstring>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>using namespace std;#define PATHNAME "."
#define PROJ_ID 0x666
#define MAX_SIZE 4096int main()
{//获取keykey_t k = ftok(PATHNAME, PROJ_ID);if(k < 0)//失败{// cin cout cerr -> stdin stdout stderr -> 对应文件描述符:0 1 2cerr << errno << ":" << strerror(errno) << endl;}//打印key值printf("key: %x\n", k);//创建共享内存int shm = shmget(k, MAX_SIZE, IPC_CREAT | IPC_EXCL);if(shm < 0)//创建失败{cerr << errno << ":" << strerror(errno) << endl;exit(-1);}//打印shmprintf("shm: %d\n", shm);return 0;
}
运行结果
ipcs -m 查看一下
再次运行
从这里可以看出:程序结束,共享内存还没有被释放,说明共享内存的声明周期是随OS的,不随进程结束而结束
共享内存既然创建了,也需要释放(去关联),下面讲
3.1.7 再次理解共享内存
共享内存不是普通的内存(即不是malloc、new出来的),共享内存有自己的属性,OS也会存在很多的共享内存,它们都需要被OS管理
怎么管理共享内存?先描述,再组织
共享内存 = 物理内存块 + 共享内存的相关属性
创建共享内存时,如何保证共享内存的唯一性??通过 key
进行通信的时候,两个进程也要看到同一块共享内存,如何保障另一个进程也看到同一块共享内存??只有让另一个进程看到相同的一个 key 就可以保证两个进程可以看到同一块共享内存
key是要被设置进共享内存的属性里面的,用来表示唯一的共享内存
shmget 创建共享内存成功返回一个有效的共享内存标识符,这里暂且叫它 shmid,那这个shimid 和 key 有什么区别呢??
shimid 和 key 有点像 fd 和 inode
shmid -> fd
key -> inode
文件在内核里面是用 inode 标识的,而上层不使用inode,而是使用fd,所以 fd 是给上层使用的,同理shmid 和 key也是如此;
key 是在内核中表示共享内存的,上层也是不使用 key,而是使用 shmid,这么对比可以很好理解
所以,我们尝试删除共享内存
ipcrm -m 命令用于删除共享内存
从测试结果看,使用key删除不了共享内存,必须使用 shmid 才可以,这也反映了上层使用的是shmid,而不是key
ipcrm -m shmid
3.1.8 释放共享内存(shmctl)
共享内存释放,有两个方法,一就是使用命令(ipcrm)释放共享内存,二就是在进程通信完毕后调用释放共享内存的函数进行释放,shnctl 系统调用就是用于控制共享内存(包括控制或设置共享内存的属性,共享内存的释放)
man 2 shmctl 查看一下
解释:
函数: shmctl
shmctl: shared memory control头文件:#include <sys/ipc.h>#include <sys/shm.h>函数原型:
int shmctl(int shmid, int cmd, struct shmid_ds *buf);参数:第一个参数shmid,表示所控制共享内存的用户级标识符第二个参数cmd,表示具体的控制动作第三个参数buf,用于获取或设置所控制共享内存的数据结构返回值:
成功,返回0
失败返回-1,错误码被设置
第二个参数cmd的选项有:
- IPC_STAT:获取共享内存的当前关联值,此时参数buf作为输出型参数
- IPC_SET:在进程有足够权限的前提下,将共享内存的当前关联值设置为buf所指的数据结构中的值
- IPC_RMID:删除共享内存
常用的选项就是第三个:IPC_RMID ,删除共享内存
第三个参数buf 一般不使用,传nullptr 即可,它是用来控制共享内存的数据结构的
共享内存的数据结构下面谈
测试删除共享内存:
#include <iostream>
#include <unistd.h>
#include <cerrno>
#include <cstring>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>using namespace std;#define PATHNAME "."
#define PROJ_ID 0x666
#define MAX_SIZE 4096int main()
{//获取keykey_t k = ftok(PATHNAME, PROJ_ID);if(k < 0)//失败{// cin cout cerr -> stdin stdout stderr -> 对应文件描述符:0 1 2cerr << errno << ":" << strerror(errno) << endl;}//打印key值printf("key: %x\n", k);//创建共享内存int shmid = shmget(k, MAX_SIZE, IPC_CREAT | IPC_EXCL);if(shmid < 0)//创建失败{cerr << errno << ":" << strerror(errno) << endl;exit(-1);}//打印shmprintf("shm: %d\n", shmid);//间隔10s删除共享内存sleep(10);//释放共享内存int ret = shmctl(shmid, IPC_RMID, nullptr);if(ret == -1)//删除失败{cerr << errno << ":" << strerror(errno) << endl;}return 0;
}
监控脚本
while :; do ipcs -m; sleep 2; done
运行结果,共享内存已被删除
上面所做的工作是创建和删除共享内存,但是共享内存还没有与进程关联起来,进程无法使用,所以下面谈共享内存的关联
3.1.9 关联共享内存(shmat)
shmat 函数的功能:将共享内存段连接到进程地址空间,即关联共享内存
man 2 shmat 查看一下:
解释:
函数: shmat
at:attach头文件:
#include <sys/types.h>
#include <sys/shm.h>函数原型:
void *shmat(int shmid, const void *shmaddr, int shmflg);参数:第一个参数shmid,表示待关联共享内存的用户级标识符第二个参数shmaddr,指定共享内存映射到进程地址空间的某一地址,通常设置为NULL,表示让内核自己决定一个合适的地址位置第三个参数shmflg,表示关联共享内存时设置的某些属性返回值:
成功,返回共享内存映射到进程地址空间中的起始地址(成功返回一个指针,指向共享内存第一个节)
败,返回(void*)-1,错误码被设置
第三个参数shmflg跟读写权限有关,通常设置为0,表示默认为读写权限
进行测试:
#include <iostream>
#include <unistd.h>
#include <cerrno>
#include <cstring>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>using namespace std;#define PATHNAME "."
#define PROJ_ID 0x666
#define MAX_SIZE 4096int main()
{//获取keykey_t k = ftok(PATHNAME, PROJ_ID);if(k < 0)//失败{// cin cout cerr -> stdin stdout stderr -> 对应文件描述符:0 1 2cerr << "ftok: " << errno << ":" << strerror(errno) << endl;exit(-1);}//打印key值printf("key: %x\n", k);//创建共享内存int shmid = shmget(k, MAX_SIZE, IPC_CREAT | IPC_EXCL);if(shmid < 0)//创建失败{cerr << "shmget: "<< errno << ":" << strerror(errno) << endl;exit(-1);}//打印shmprintf("shm: %d\n", shmid);//关联共享内存void* men = shmat(shmid, nullptr, 0);if(men == (void*)-1)//关联失败{cerr << "shmat: "<< errno << ":" << strerror(errno) << endl;exit(-1);}//间隔10s删除共享内存sleep(10);//释放共享内存int ret = shmctl(shmid, IPC_RMID, nullptr);if(ret == -1)//删除失败{cerr << "shmctl: "<< errno << ":" << strerror(errno) << endl;exit(-1);}return 0;
}
运行结果,在关联共享内存的时候权限被拒绝
这是为什么??
这是因为我们使用 shmget 函数创建共享内存时,并没有对创建的共享内存设置权限,所以创建出来的共享内存的默认权限为0,即什么权限都没有,因此server进程没有权限关联该共享内存
perme 便是该共享内存的权限,我们创建的共享内存的权限为0,
nattch 是关联共享内存的进程数量,n代表数量,attch代表关联
所以我们需要给创建共享内存时加上权限:
再次编译运行,共享内存关联成功
3.1.10 去关联共享内存(shmdt)
取消共享内存与进程地址空间之间的关联我们需要用 shmdt 函数
man 2 shmdt 查看:
解释:
函数:shmdt
dt: detach头文件与shmat一致函数原型:int shmdt(const void *shmaddr);参数:传入shmat所返回的指针,即共享内存的起始地址返回值
成功,返回0
失败,返回-1,错误码被设置
进行测试:
#include <iostream>
#include <unistd.h>
#include <cerrno>
#include <cstring>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>using namespace std;#define PATHNAME "."
#define PROJ_ID 0x666
#define MAX_SIZE 4096int main()
{//获取keykey_t k = ftok(PATHNAME, PROJ_ID);if(k < 0)//失败{// cin cout cerr -> stdin stdout stderr -> 对应文件描述符:0 1 2cerr << "ftok: " << errno << ":" << strerror(errno) << endl;exit(-1);}//打印key值printf("key: %x\n", k);//创建共享内存int shmid = shmget(k, MAX_SIZE, IPC_CREAT | IPC_EXCL | 0666);if(shmid < 0)//创建失败{cerr << "shmget: "<< errno << ":" << strerror(errno) << endl;exit(-1);}//打印shmprintf("shm: %d\n", shmid);//关联共享内存void* men = shmat(shmid, nullptr, 0);if(men == (void*)-1)//关联失败{cerr << "shmat: "<< errno << ":" << strerror(errno) << endl;exit(-1);}//使用和其他操作sleep(5);//去关联共享内存int n = shmdt(men);if(n == -1){cerr << "shmdt: "<< errno << ":" << strerror(errno) << endl;exit(-1);}//间隔5s删除共享内存sleep(5);//释放共享内存int ret = shmctl(shmid, IPC_RMID, nullptr);if(ret == -1)//删除失败{cerr << "shmctl: "<< errno << ":" << strerror(errno) << endl;exit(-1);}return 0;
}
运行结果,共享内存去关联成功
3.1.11 使用共享内存实现serve&client通信
已经知道共享内存的创建、关联、去关联以及释放后,现在可以尝试让两个进程通过共享内存进行通信
共同的头文件(comm.hpp):
#include <iostream>
#include <unistd.h>
#include <cerrno>
#include <cstring>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>using namespace std;#define PATHNAME "."
#define PROJ_ID 0x666
#define MAX_SIZE 4096//获取key
key_t getKey()
{key_t k = ftok(PATHNAME, PROJ_ID);if(k < 0)//失败{// cin cout cerr -> stdin stdout stderr -> 对应文件描述符:0 1 2cerr << "ftok: " << errno << ":" << strerror(errno) << endl;exit(-1);}
}int getShmHelper(key_t k, int flags)
{int shmid = shmget(k, MAX_SIZE, flags);if(shmid < 0)//创建或获取失败{cerr << "shmget: "<< errno << ":" << strerror(errno) << endl;exit(-1);}
}//创建共享内存
int createShm(key_t k)
{return getShmHelper(k, IPC_CREAT | IPC_EXCL | 0600);
}//获取共享内存
int getShm(key_t k)
{return getShmHelper(k, IPC_CREAT);
}//关联共享内存
void *attachShm(int shmid)
{void* men = shmat(shmid, nullptr, 0);if(men == (void*)-1)//关联失败{cerr << "shmat: "<< errno << ":" << strerror(errno) << endl;exit(-1);}return men;
}//去关联共享内存
void detachShm(void *start)
{if(shmdt(start) == -1){cerr << "shmdt: "<< errno << ":" << strerror(errno) << endl;}
}//释放共享内存void delShm(int shmid){int ret = shmctl(shmid, IPC_RMID, nullptr);if(ret == -1)//删除失败{cerr << "shmctl: "<< errno << ":" << strerror(errno) << endl;}}
服务端负责创建共享内存,创建好后将共享内存和服务端进行关联,获取用户端发送的消息
服务端代码如下(shm_server.cpp):
#include "comm.hpp"//服务端
int main()
{//获取keykey_t k = getKey(); printf("key: 0x%x\n", k);//创建共享内存int shmid = createShm(k);printf("shmid: %d\n", shmid);//关联共享内存char *start = (char*)attachShm(shmid);printf("attach success, address start: %p\n", start);//使用,进行通信while(true){printf("client say : %s\n", start);sleep(1);}//去关联detachShm(start);sleep(5);// //释放共享内存delShm(shmid);return 0;
}
客户端只需要直接和服务端创建的共享内存进行关联即可,客户端给服务端发送消息
客户端代码如下(shm_client.cpp):
#include "comm.hpp"//用户端
int main()
{//获取key,key是与服务端一致的key_t k = getKey(); printf("key: 0x%x\n", k);//获取服务端创建的共享内存int shmid = getShm(k);printf("shmid: %d\n", shmid);//关联共享内存char *start = (char*)attachShm(shmid);printf("attach success, address start: %p\n", start);//使用,与服务端通信const char* message = "hello server, 我是另一个进程,正在和你通信";pid_t id = getpid();int cnt = 1;while(true){snprintf(start, MAX_SIZE, "%s[pid:%d][消息编号:%d]", message, id, cnt++);//snprintf自带 '\n'sleep(1);}//去关联detachShm(start);//donereturn 0;
}
注意:删除shm ipc资源,注意,不是必须通过手动来删除,这里只为演示相关指令,删除IPC资源是进程该做的事情
服务端先运行,然后运行客户端,通信成功
ipcs 查看一下,共享内存的链接数为2
3.1.12 共享内存的优缺点
共享内存的优点:
共享内存是所有进程间通信中速度最快的(无需缓冲区,能大大减少通信数据的拷贝次数)
共享内存的缺点:
共享内存是不进行同步和互斥的,没有对数据进行任何保护。比如:如果服务端读取速度较快,用户端发送数据较慢,就会产生同一段消息被服务端读取多遍
注意:因为系统分配共享内存是以4KB为基本单位(因为内存划分内存块的基本单位Pag),一般建议申请共享内存的大小为4KB的整数倍, 4096字节(4KB), 比如你申请的共享内存的大小为 4097字节,OS会给你申请 8KB,但是你实际上可以使用等待只有 4097字节
3.1.12 共享内存的内核数据结构
共享内存的数据结构如下:
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 */
};
在用户层,OS也暴露了大部分数据结构给用户使用
共享内存唯一性的标识key 也在这个数据结构里面
3.2 System V消息队列(了解)
消息队列实际上就是在系统当中创建了一个队列,队列当中的每个成员都是一个数据块,这些数据块都由类型和信息两部分构成,两个互相通信的进程通过某种方式看到同一个消息队列,消息队列的两端都可以读写数据(简单了解即可)
消息队列的内核数据结构中也是用 key标识队列的唯一性
消息队列使用的接口,与共享内存的接口功能基本一致,函数名字不同而已
//创建消息队列
msgget
int msgget(key_t key, int msgflg);//消息队列的控制
msgctl
int msgctl(int msqid, int cmd, struct msqid_ds *buf);//向消息队列发送数据
msgsnd
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);//从消息队列获取数据
msgrcv
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
3.3 System V信号量(了解)
信号量主要用于同步和互斥的,也是属于通信的范畴
信号量本质是:
信号量的本质是一个计数器,通常用来表示公共资源中,资源数量多少的问题
- 公共资源:被多个进程可以同时访问的资源(比如上面的共享内存,消息队列都是公共资源)
- 访问没有受到保护的公共资源:会产生数据不一致问题(比如上面的共享内存)
- 临界资源:未来将被保护的公共资源我们叫做临界资源
- 临界区:进程有对应的代码来访问对应的临界资源,对应的代码就叫临界区
- 非临界区:进程中不访问临界资源的代码称为非临界区
- 注意:大部分的资源是独立的
- 互斥和同步:这是用来保护公共资源的(互斥:有一个进程对该公共资源进行访问了,就不允许其他进程对该资源进行访问,同步多线程再谈)
- 原子性:要么不做,要做就做完,只有两态,称为原子性
注:上面的概念只是浅谈,有些到后面解释
为什么要有信号量???
以例子进行解释,比如一个电影院,这个电影院看作是公共资源,电影院里面的每一个座位看作是共享资源划分成的一个个子资源;
我只有买了票,这个座位在一定时间内才是归属我的,即便我买了票不去,这个座位也是我的,反过来,你没买票,这个座位就一定不是你的。买的票一定对应着一个座位。
同样道理,我想用某种资源的时候,可以对这种资源进行预订,就相当于买票,买了票我就已经预订了这个座位,我预订了这个资源,它在未来的某一时间一定属于我
假设电影院的座位只有100个,你不能卖出第101张票吧,信号量就相当于这里的票一样,票卖完了就不允许再卖了,再卖就会发生 ‘座位冲突’,也就对应进程访问该资源会发生冲突
进程想要访问某个资源,就要先申请信号量,申请到了信号量就相当于预订了共享资源的某一部分小资源,就允许该进程对该资源的访问
将一块公共的资源拆分成一个个的子资源,不同的进程可以对不同的子资源进程访问,从而实现并发
而申请信号量的前提是,所有进程要看到一个相同的信号量,所以信号量本身就是公共资源
既然信号量是公共资源,是公共资源就要保证自身的安全性,可以把信号量看作是一个整数,这个整数进行 ++ 和 -- 就要保证自身的安全性,所以 ++ 和 -- 操作是原子性的
即信号量本身也是一个临界资源,它能保护其他共享资源的同时,也需要保护自己的安全,信号量内部的加加减减具有原子性
信号量为1时,说明共享资源是一整个整体使用的,提供互斥功能(别的进程不能使用),提供互斥功能的信号量也叫二元信号量
这些浅谈一下,后序详谈
注意:IPC资源必须删除,否则不会自动清除,除非重启,system V IPC资源的生命周期随内核
----------------我是分割线---------------
文章到这里就结束了,下一篇即将更新
相关文章:

【Linux】七、进程间通信(二)
目录 三、system V(IPC) 3.1 system V共享内存 3.1.1 共享内存的概念 3.1.2 共享内存的原理 3.1.3 创建共享内存(shmget ) 3.1.4 ftok函数 3.1.5 查看共享内存资源 3.1.6 创建共享内存测试代码 3.1.7 再次理解共享内存 3.1.8 释放共享内存(shm…...

Synchronized学习大总结
目录 1.synchronized特性 2.synchronized如何使用 3.synchronized的锁机制 1.synchronized特性 synchronized 是乐观锁,也是悲观锁,是轻量级锁(j基于自旋锁实现),也是重量级锁(基于挂起等待锁实现),它不是读写锁,是互斥锁,当一个线程抢到锁之后,其它线程阻塞等待,进入synchr…...

VN5620以太网测试——环境搭建篇
文章目录 前言一、新建以太网工程二、Port Configuration三、Link up四 Trace界面五、添加Ethernet Packet Builder六、添加ARP Packet七、添加Ethernet IG总结前言 CANoe(CAN open environment)VN5620 :是一个紧凑而强大的接口,用于以太网网络的分析、仿真、测试和验证。 …...

redis哨兵和集群部署手册
一、哨兵模式原理及作用 1.原理 哨兵(sentinel): 是一个分布式系统,用于对主从结构中的每台服务器进行监控,当出现 故障时,通过投票机制选择新的master并将所有slave连接到新的master。所以整个运行哨兵的集…...

ctfshow web入门 java 295 298-300
其他没啥好讲的,都是工具就通杀了 web295 漏洞地址 http://ip/S2-048/integration/saveGangster.action 这里我们可以看到他是解析了 尝试使用网上的payload %{(#dmognl.OgnlContextDEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess#dm):((#container#cont…...

SWIG包装器使用指南——(四)C#使用SWIG简介与实践
SWIG系列:http://t.csdn.cn/cIAcr 文章目录一、简介二、全局函数、变量、常量三、继承四、传递指针、引用、数组与值五、基本类型的指针与引用六、基本类型的数组七、基本类型的默认map规则八、常用的typemap方法九、代码插入十、实践10.1 如何映射Foo*&到ref F…...

HashTable, HashMap 和 ConcurrentHashMap
HashTable, HashMap 和 ConcurrentHashMap 都是 Java 集合框架中的类,用于存储和操作键值对。它们之间存在一些关键区别,如下所示: 1.同步性: HashTable:线程安全,所有的方法都是同步的(synchr…...

ToBeWritten之IoT 技战法
也许每个人出生的时候都以为这世界都是为他一个人而存在的,当他发现自己错的时候,他便开始长大 少走了弯路,也就错过了风景,无论如何,感谢经历 转移发布平台通知:将不再在CSDN博客发布新文章,敬…...

基于ASP.NET开发的医院手术麻醉信息管理系统源码 项目源码
系统主要功能介绍: 门诊科室管理系统:手术快速申请、手术申请、手术审核 麻醉科管理系统:手术安排、术后处方、术后小结、PCS实施及管理记录、手术流程 手术护理系统:手术安排、安排临时手术、添加急诊手术、局麻手术护理、整体护…...

伪加密超具体破解办法,直击原理底层,细致演示!!!
前言: 由于我自己目前在misc和取证工作中,也遇到很多压缩包的问题,我个人非常喜欢做压缩包的题目,但也会遇到伪加密问题难以破解,全网ctf教程我都看完了,但是都觉得不够具体,所以我写一篇博客&…...

ChatGPT大规模封锁亚洲地区账号
我是卢松松,点点上面的头像,欢迎关注我哦! 在毫无征兆的情况下,从3月31日开始OpenAI大规模封号,而且主要集中在亚洲地区,特别是ip地址在台湾、日本、香港三地的,命中率目测40%。新注册的账号、…...

脂肪酸脂质Myristic acid PEG NHS,Myristic-acid PEG NHS ester,肉豆蔻酸PEG活性酯,具有优异疏水性
一、基础产品数据: 中文名:肉豆蔻酸PEG N-羟基琥珀酰亚胺,肉豆蔻酸PEG活性酯 英文名:Myristic acid PEG NHS,Myristic-acid PEG NHS ester,Myristic acid PEG SE 结构式(Structural)…...

MFC - CFormView类学习1
CFormView简介 MFC提供了一个名为CFormView的特殊视图类,我们称其为表单视图。表单视图是指用控件来输入和输出数据的视图,用户可以方便地在表单视图中使用控件。表单视图具有对话框和滚动视图的特性,它使程序看起来象是一个具有滚动条的对话…...

图像预处理方法
图像预处理 膨胀腐蚀概述 ⚫ 膨胀、腐蚀属于形态学的操作, 简单来说就是基于形状的一系列图像处理操作 ⚫ 膨胀腐蚀是基于高亮部分(白色)操作的, 膨胀是対高亮部分进行膨胀, 类似“领域扩张”, 腐蚀是高亮部分被腐蚀, 类似“领域被蚕食” ⚫ 膨胀腐蚀的应用和功能: 消除噪声…...

【蓝桥杯C/C++】专题六:动态规划
专题六:动态规划 目录专题六:动态规划导读什么是动态规划解决的问题解题步骤动态规划应该如何debug记忆化搜索斐波那契数题目代码题解爬楼梯题目代码题解使用最小花费爬楼梯题目代码题解不同路径题目题解dfsdp凑硬币题目题解dfsdp滑雪题目代码题解汉罗塔…...

图的定义和基本术语
图的定义和基本术语1.图的定义2.图的基本术语3.图的分类1.图的定义 图是由顶点和有穷非空集合和顶点边的集合吗,表示为G(V,E)。 G表示一个图,V是图G的顶点(数据元素)的集合,E是图G中顶点之间边的集合。在图中…...

041:cesium加载Blue Marble地图
第041个 点击查看专栏目录 本示例的目的是介绍如何在vue+cesium中加载Blue Marble地图。Blue Marble是一个术语,用来描述星球漂浮在浩瀚太空中的形象。早在 1972 年,阿波罗 17 号任务的工作人员就首次捕捉到了地球的标志性卫星图像,并将其称为“Blue Marble”。从那时起,NA…...

【概念梳理】激活函数
一、引言 常用的激活函数如下: 1、Sigmoid函数 2、Tanh函数 3、ReLU函数 4、ELU函数 5、PReLU函数 6、Leaky ReLU函数 7、Maxout函数 8、Mish函数 二、激活函数的定义 多层神经网络中,上层节点的输出和下层节点的输入之间具有一个函数关系,…...

【python】@property 和 @staticmethod
property 和 staticmethod 是 Python 中的两个装饰器,它们分别用于在类中创建属性或静态方法。它们的作用如下: property property:用于将类的一个方法作为属性访问。在 Python 中,使用“getter” 和“setter”方法来实现属性&a…...

Spring题集 - Spring AOP相关面试题总结
文章目录01. Spring AOP 的理解?02. Spring AOP 思想的代码实现03. Spring AOP 的相关术语有哪些?04. Spring AOP 基于注解的切面实现?05. Spring AOP 的通知有哪些类型?06. AOP 有哪些实现方式?07. Spring AOP 和 AspectJ AOP 有…...

分考场
[蓝桥杯 2017 国 C] 分考场(假题:最小色数) 题目描述 nnn 个人参加某项特殊考试。 为了公平,要求任何两个认识的人不能分在同一个考场。 求最少需要分几个考场才能满足条件。 输入格式 第一行,一个整数 n(1<n<100)n(1<n<100…...

BI技巧丨DAX Studio
DAX Studio DAX Studio,作为PowerBI外部插件使用率排名第一的插件,相信各位小伙伴或多或少都听说过,那么DAX Studio具体有哪些功能呢? PS:DAX Studio的下载链接,小伙伴们可以自行搜索,这里就不…...

Java 8常用时间 API
Date: 你不爱我了吗? 🚡本地时间时区相关格式化在Java 8中,Instant类用于表示时间戳,相当于旧的Date类;LocalDateTime类用于表示日期和时间,相当于旧的Calendar类;DateTimeFormatter类用于格式化日期和时间…...

C++运算符
C运算符 运算符是一种告诉编译器执行特定的数学或逻辑操作的符号。C 内置了丰富的运算符,并提供了以下类型的运算符: 算术运算符关系运算符逻辑运算符位运算符赋值运算符杂项运算符 1. 算术运算符 运算符描述实例把两个操作数相加A B 将得到 30-从第…...

低/无代码赋能企业,IT与业务的角色正在悄然改变
现在这个社会,年轻人的压力是真的大,需要会的技能多到数不清。想学习多点技能也不知道去哪学,主要是网络资源太丰富,很难找到一个适合自己的。那接下来推荐4个大神级别的资源网站你可一定得码住,都是年轻人特别 …...

SpringCloud学习2(Spring Cloud Netflix)负载均衡Ribbon、Feign负载均衡、Hystix服务熔断
文章目录负载均衡RibbonRibbon的作用代码实现生产者cloud1_provider实现配置文件在HiController中编写以下代码启动集群消费者cloud1_consumer实现引入依赖编写配置文件编写启动类,并给RestTemplate配置LoadBalanced注解编写RestController来测试Feign负载均衡简介F…...

Spring 源码解析 - @Async 注解下的循环依赖问题原理
一、Async 注解下的循环依赖问题 我们都知道 Spring IOC 单例模式下可以帮助我们解决循环依赖问题,比如下面自己依赖自己循环依赖的场景: Component public class TestAsync {ResourceTestAsync async;public void test() {System.out.println("t…...

8个全球性编程比赛,天才程序员的梦想舞台
很多编程爱好者在学习之初,都渴望与全球的程序员一较高下,以证明自己的实力。 一些全球性的编程竞赛为他们提供了这样的机会,不仅可以与全世界的顶尖程序员们交流,还有机会获得丰厚的奖金和进入顶级公司的机会,更重要…...

2023年中国海洋大学计算机及电子信息考研分析
考研时间跨度: 初试时间: 2022年8月23 海大推免及创新人才计划接收通知。 2022年9月13 海大专业目录及人数,包含推免。 2022年10月18 2022年硕士研究生计划 ,不含推免。 海大2022年硕士研究生计划 网上第一次时间为2022年9月24日…...

【C++笔试强训】第六天
选择题 1. 解析:十进制转换为八进制就是不断的除8,取余数。十进制转换成其他进制的数就是除以进制,取余。 解析:注意printf的转换,%%只会打印一个%,所以选A。 解析:由于()的原因p先和*结合&…...