【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 有…...
Ubuntu系统下交叉编译openssl
一、参考资料 OpenSSL&&libcurl库的交叉编译 - hesetone - 博客园 二、准备工作 1. 编译环境 宿主机:Ubuntu 20.04.6 LTSHost:ARM32位交叉编译器:arm-linux-gnueabihf-gcc-11.1.0 2. 设置交叉编译工具链 在交叉编译之前&#x…...
SkyWalking 10.2.0 SWCK 配置过程
SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外,K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案,全安装在K8S群集中。 具体可参…...

DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI
前一阵子在百度 AI 开发者大会上,看到基于小智 AI DIY 玩具的演示,感觉有点意思,想着自己也来试试。 如果只是想烧录现成的固件,乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外,还提供了基于网页版的 ESP LA…...

Module Federation 和 Native Federation 的比较
前言 Module Federation 是 Webpack 5 引入的微前端架构方案,允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...
06 Deep learning神经网络编程基础 激活函数 --吴恩达
深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...

Mysql中select查询语句的执行过程
目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...

【JVM面试篇】高频八股汇总——类加载和类加载器
目录 1. 讲一下类加载过程? 2. Java创建对象的过程? 3. 对象的生命周期? 4. 类加载器有哪些? 5. 双亲委派模型的作用(好处)? 6. 讲一下类的加载和双亲委派原则? 7. 双亲委派模…...
Webpack性能优化:构建速度与体积优化策略
一、构建速度优化 1、升级Webpack和Node.js 优化效果:Webpack 4比Webpack 3构建时间降低60%-98%。原因: V8引擎优化(for of替代forEach、Map/Set替代Object)。默认使用更快的md4哈希算法。AST直接从Loa…...
MySQL 索引底层结构揭秘:B-Tree 与 B+Tree 的区别与应用
文章目录 一、背景知识:什么是 B-Tree 和 BTree? B-Tree(平衡多路查找树) BTree(B-Tree 的变种) 二、结构对比:一张图看懂 三、为什么 MySQL InnoDB 选择 BTree? 1. 范围查询更快 2…...

android RelativeLayout布局
<?xml version"1.0" encoding"utf-8"?> <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_height"match_parent"android:gravity&…...