【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提供的通信方式有以下三种:

  1. system V共享内存
  2. system V消息队列
  3. system V信号量

3.1 system V共享内存

3.1.1 共享内存的概念

让不同的进程看到同一块内存块,这块内存块就叫共享内存,并且共享内存区是最快的IPC形式

3.1.2 共享内存的原理

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

未来不想通信(即关闭通信):

  1. 去关联:取消进程和内存的映射关系
  2. 释放共享内存

对共享内存的理解:

  1. 共享内存,是专门设计的,是用于IPC,与malloc、new出来的内存完全不一样,这个共享内存是进程都可以看得到的,而malloc、new 出来的内存只对自己的进程开放,是私有的,其他进程看不到这块内存
  2. 共享内存是一种通信方式,所有想通信的进程都可以使用
  3. OS内一定可能同时存在大量的的共享内存

3.1.3 创建共享内存(shmget )

共享内存的建立大致包括以下两个过程:

  1. 在物理内存当中申请共享内存空间
  2. 将申请到的共享内存挂接到地址空间,即建立映射关系

创建共享内存需要用 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

  1. IPC_CREAT:创建新的共享内存,如果共享内存已经存在,就获取已经存在的共享内存并返回它
  2. 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 4096

int main()
{
    //获取key
    key_t k = ftok(PATHNAME, PROJ_ID);
    if(k < 0)//失败
    {
        // cin cout cerr  ->  stdin stdout stderr  ->  对应文件描述符:0 1 2
        cerr << 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);
    }

    //打印shm
     printf("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 4096

int main()
{
    //获取key
    key_t k = ftok(PATHNAME, PROJ_ID);
    if(k < 0)//失败
    {
        // cin cout cerr  ->  stdin stdout stderr  ->  对应文件描述符:0 1 2
        cerr << 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);
    }

    //打印shm
     printf("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 4096

int main()
{
    //获取key
    key_t k = ftok(PATHNAME, PROJ_ID);
    if(k < 0)//失败
    {
        // cin cout cerr  ->  stdin stdout stderr  ->  对应文件描述符:0 1 2
        cerr << "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);
    }

    //打印shm
     printf("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 4096

int main()
{
    //获取key
    key_t k = ftok(PATHNAME, PROJ_ID);
    if(k < 0)//失败
    {
        // cin cout cerr  ->  stdin stdout stderr  ->  对应文件描述符:0 1 2
        cerr << "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);
    }

    //打印shm
     printf("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 2
        cerr << "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()
{
    //获取key
    key_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);

    //done

    return 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资源的生命周期随内核

----------------我是分割线---------------

文章到这里就结束了,下一篇即将更新

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Redis7搭建主从+哨兵通俗易懂

背景前提–用到的命令 ps -ef |grep redis redis服务器启动(精确启动配置文件位置) redis-server redis6379.conf redis-server redis6380.conf redis-server redis6381.conf redis客户端登录 redis-cli -a 123456 -p 6379 redis-cli -a 123456 -p 6380 redis-cli -a 12345…

蓝桥杯刷题冲刺 | 倒计时1天

作者&#xff1a;指针不指南吗 专栏&#xff1a;蓝桥杯倒计时冲刺 &#x1f43e;蓝桥杯加油&#xff0c;大家一定可以&#x1f43e; 文章目录我是菜菜&#xff0c;最近容易我犯的错误总结 一些tips 各位蓝桥杯加油加油 当输入输出数据不超过 1e6 时&#xff0c;scanf printf 和…

【Vue】初识Vue(一)

&#x1f697;Vue学习扬帆起航~ &#x1f6a9;本文已收录至专栏&#xff1a;Vue框架 &#x1f44d;由于Vue2与Vue3存在许多相似之处&#xff0c;先开始Vue2学习再进阶到Vue3 我们知道技术的兴起与流行一般都是为了帮助我们解决一类问题使得我们开发体验更加舒适&#xff0c;那么…

C++之多态

文章目录前言一、多态的概念二、多态的定义及实现1.多态的构成条件2.虚函数3.虚函数的重写&#xff08;覆盖&#xff09;4.虚函数重写的两个例外4.C11中的override和final关键字三、重载、重定义&#xff08;隐藏&#xff09;、重写&#xff08;覆盖&#xff09;的区分四、抽象…

【美赛】2023年ICM问题Z:奥运会的未来(思路、代码)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

【面试】MySQL面试题

文章目录数据库基础知识为什么要使用数据库什么是SQL&#xff1f;什么是MySQL?MySql, Oracle&#xff0c;Sql Service的区别数据库三大范式是什么mysql有关权限的表都有哪几个MySQL的binlog有有几种录入格式&#xff1f;分别有什么区别&#xff1f;数据库经常使用的函数数据类…

C++设置动态库的版本号(软链接)

1,动态库版本命名规则 假设有一个动态库&#xff1a;libfooSdk.so.1.1.0&#xff0c;其对应的三个名称如下。 realname&#xff1a;libfooSdk.so.1.1.0 soname&#xff1a;libfooSdk.so.1 linkname&#xff1a;libfooSdk.solinux的动态库的命名格式是libfooSdk.so.x.y.z 版本…

大数据概述及其软件生态

一、大数据的诞生 &#xff08;1&#xff09;当全球互联网逐步建成&#xff08;2000年左右&#xff09;&#xff0c;各大企业或政府单位拥有了海量的数据亟待处理。 &#xff08;2&#xff09; 基于这个前提逐步诞生了以分布式的形式&#xff08;即多台服务器集群&#xff09;…

PCB生产工艺流程三:生产PCB的内层线路有哪7步

PCB生产工艺流程三&#xff1a;生产PCB的内层线路有哪7步 在我们的PCB生产工艺流程的第一步就是内层线路&#xff0c;那么它的流程又有哪些步骤呢&#xff1f;接下来我们就以内层线路的流程为主题&#xff0c;进行详细的分析。 由半固化片和铜箔压合而成&#xff0c;用于…

Vue|计算属性

1. 计算属性1.1 差值语法1.2 methods1.3 计算属性1. 计算属性 1.1 差值语法 开始前分别在项目目录创建文件夹及页面如下 需求1&#xff1a;在两个文本框中分别输入姓和名的同时需要在下方将数据进行拼接组装&#xff0c;效果如下图 如果用传统的方式来实现的话&#xff0c;需要…

投屏软件:ApowerMirror Crack

一个软件&#xff0c;两个系统 ApowerMirror是一个跨平台的屏幕镜像应用程序&#xff0c;可用于iOS和Android设备&#xff0c;与Windows和Mac兼容。对于运行支持 Chromecast 的 Android 5.0 或更高版本的手机&#xff0c;用户可以使用此程序镜像屏幕。而对于支持AirPlay的iOS设…

bfs与dfs详解(经典例题 + 模板c-代码)

文章首发于&#xff1a;My Blog 欢迎大佬们前来逛逛 文章目录模板解析dfsbfs1562. 微博转发3502. 不同路径数165. 小猫爬山模板解析 DFS&#xff08;深度优先搜索&#xff09;和BFS&#xff08;广度优先搜索&#xff09;是图论中两个重要的算法。 dfs 其中DFS是一种用于遍历…

spring源码之扫描前设置

扫描前设置 &#x1f6f9;源码源码说明总结启动一个springboot项目源码 org.springframework.context.annotation.ComponentScanAnnotationParser#parse public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, String declaringClass) {// 创建C…

清明-微信小程序

# 云开发接入 初始化云开发 环境保密

深度学习部署(十三): CUDA RunTime API thread_layout线程布局

1. 知识点 在.vscode/settings.json中配置"*.cu": "cuda-cpp"可以实现对cuda的语法解析 layout是设置核函数执行的线程数&#xff0c;要明白最大值、block最大线程数、warpsize取值 maxGridSize对应gridDim的取值最大值maxThreadsDim对应blockDim的取值最…

酷炫的青蛇探针serverMmon

本文软件由网友 114514 推荐&#xff1b; 什么是 serverMmon &#xff1f; serverMmon (青蛇探针)是 nodeJs 开发的一个酷炫高逼格的云探针、云监控、服务器云监控、多服务器探针。 主要功能介绍&#xff1a; 全球服务器分布世界地图服务器&#xff08;控制端&#xff09; ping…

简单3招教你设置电脑时间

案例&#xff1a;电脑时间怎么设置&#xff1f; 【我使用电脑时&#xff0c;电脑显示的时间一直不对&#xff0c;这导致我非常不方便&#xff0c;想问下大家平常使用电脑时有什么设置电脑时间比较简单的方法吗&#xff1f;】 电脑的时间设置很重要&#xff0c;不仅可以保证电…

Java单例模式、阻塞队列、定时器、线程池

目录1. 单例模式1.1 饿汉模式实现单例1.2 懒汉模式实现单例1.2.1 加锁实现懒汉模式线程安全1.2.2 volatile实现懒汉模式线程安全1.3 饿汉模式和懒汉模式小结&#xff08;面试题&#xff09;2. 阻塞队列2.1 单线程下阻塞队列2.2 多线程下阻塞队列——生产者消费者模型2.3 模拟写…

【蓝桥集训18】二分图(2 / 2)

二分图定义&#xff1a;将所有点分成两个集合&#xff0c;使得所有边只出现在集合之间&#xff0c;就是二分图 目录 860. 染色法判定二分图 1、dfs 2、bfs 861. 二分图的最大匹配 - 匈牙利算法ntr算法 860. 染色法判定二分图 活动 - AcWing 1、dfs 思路&#xff1a; 对每…

白农:Imagination将继续致力于推进车规半导体IP技术创新和应用

4月2日Imagination中国董事长白农在中国电动汽车百人论坛上发表演讲&#xff0c;探讨了车规半导体核心IP技术如何推动汽车智能化发展&#xff0c;并接受了媒体采访。本次论坛上&#xff0c;他强调了IP技术在汽车产业链中日益重要的地位和供应商的位置前移。类比手机行业的发展&…