Linux高级系统编程- 消息队列 与 内存共享

消息队列

消息队列是消息的链表,存放在内存中,由内核维护
特点:
        1、消息队列中的消息是有类型的。
        2、消息队列中的消息是有格式的。
        3、消息队列可以实现消息的随机查询。消息不一定要以先进先出的次序读取,编程时 可以按消息的类型读取。
        4、消息队列允许一个或多个进程向它写入或者读取消息。
        5、与无名管道、命名管道一样,从消息队列中读出消息,消息队列中对应的数据都会被删除。
        6、每个消息队列都有消息队列标识符,消息队列的标识符在整个系统中是唯一的。
        7、只有内核重启或人工删除消息队列时,该消息队列才会被删除。若不人工删除消息队列,消息队列会一直存在于系统中
注意 :
        在 ubuntu 某些版本中消息队列限制值如下 :
        每个消息内容最多为8K 字节
        每个消息队列容量最多为16K 字节
        系统中消息队列个数最多为1609
        系统中消息个数最多为16384

消息队列操作命令

ipcs -q 查看消息队列
ipcrm -q msgid 删除消息队列 id msgid 的消息队列

获取Key

System V 提供的进程间通讯机制 (IPC 通信机制 ) 需要一个 key
key 值可以是人为指定的,也可以通过 ftok 函数获得。

ftok函数

所需头文件 :
        #include <sys/types.h>
        #include <sys/ipc.h>
函数 :
        key_t ftok(const char *pathname, int proj_id);
参数:
        pathname:路径名 , 这个路径必须是存在的而且可以访问的
        //~
        ///
        //./
        proj_id:项目 ID ,非 0 整数 ( 只有低 8 位有效 )
返回值:
        成功返回 key
        失败返回 -1
注意 :
        当调用该函数时传入的参数一致获取到的key 值也将相同
        多进程通讯时要保证路径名相同,id 也相同
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
int main(int argc, char const *argv[])
{
    key_t key01 = ftok("./",2023);
    key_t key02 = ftok("./",2023);
    key_t key03 = ftok("./",2022);
    printf("key1=%u\n",key01);
    printf("key2=%u\n",key02);
    printf("key3=%u\n",key03);
    return 0;
}

创建或获取消息队列--msgget函数

        创建一个新的或打开一个已经存在的消息队列。不同的进程调用此函数,只要用相同的 key 值就能得到同一个消息队列的标识符。
所需头文件
        #include <sys/msg.h>
函数
        int msgget(key_t key, int msgflg);
参数:
        key: IPC 键值。
        msgflg:标识函数的行为及消息队列的权限。
        msgflg 的取值:
                IPC_CREAT:创建消息队列。
                IPC_EXCL:检测消息队列是否存在。
        位或权限位:消息队列位或权限位后可以设置消息队列的访问权限,格式和 open 函数的 mode_t 一样,但可执行权限未使用。
返回值:
        成功:消息队列的标识符(msgid)
        失败:返回-1
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int main(int argc, char const *argv[])
{
    //获取key值
    //./表示当前路径
    key_t key = ftok("./",2023);
    printf("key=%u\n",key);
    //创建消息队列,权限为可读可写
    //返回值为消息队列id
    int msgid = msgget(key,IPC_CREAT | 0666);
    printf("msgid=%d\n",msgid);
    //判断消息队列是否存在
    key_t key02 = ftok("/",2022);
    int msgid02 = msgget(key02,IPC_EXCL);
    printf("msgid02=%d\n",msgid02);
    return 0;
}

消息的格式

typedef struct _msg
{
        long mtype ; /* 消息类型 , 必须是第一个成员 , 必须是 long , 就是该消息的 id*/
        char mtext [ 100 ]; /* 消息正文 , 用户自定义 */
        ... /* 消息的正文可以有多个成员 */
} MSG ;

发送消息--msgsnd函数

作用 : 将新消息添加到消息队列。
函数 :
所需头文件 :
        #include <sys/msg.h>
函数 :
        int msgsnd(int msqid, const void *msgp,size_t msgsz, int msgflg);
参数 :
        msqid:消息队列的标识符。
        msgp:待发送消息结构体的地址。
        msgsz:消息正文的字节数。
        msgflg:函数的控制属性
        0: msgsnd 调用阻塞直到条件满足为止。 ( 推荐 )
        IPC_NOWAIT: 若消息没有立即发送则调用该函数的进程会立即返回。
返回值 :
        成功:0
        失败:-1

接收消息--msgrcv函数

作用 : 从标识符为 msqid 的消息队列中接收一个消息。一旦接收消息成功,则消息 在消息 队列中被删除。
函数
所需头文件
        #include <sys/msg.h>
函数 :
        ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
参数 :
        msqid:消息队列的标识符,代表要从哪个消息列中获取消息。
        msgp:存放消息结构体的地址。
        msgsz:消息正文的字节数。
        msgtyp:消息的类型、可以有以下几种类型
        msgtyp=0:返回队列中的第一个消息
        msgtyp>0:返回队列中消息类型为 msgtyp 的消息
        msgtyp<0:返回队列中消息类型值小于或等于 msgtyp 绝对值的消息 , 如果这种 消息有若干个, 则取类型值最小的消息。
注意:
        若消息队列中有多种类型的消息,msgrcv 获取消息的时候按消息类型获取, 不是先进先出的。
在获取某类型消息的时候 , 若队列中有多条此类型的消息 , 则获取最先添加的消息, 即先进先出原则
        msgflg:函数的控制属性
        0:msgrcv调用阻塞直到接收消息成功为止。
        MSG_NOERROR:若返回的消息字节数比 nbytes 字节数多 , 则消息就会截短到 nbytes字节 , 且不通知消息发送进程
        IPC_NOWAIT:调用进程会立即返回。若没有收到消息则立即返回 -1
返回值 :
        成功返回读取消息的长度
        失败返回-1

案例1:简单的消息收发

张三发出消息类型为 10 20 的消息
李四接收消息类型为 10 的消息
王五接收消息类型为 20 的消息
代码:zs.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
typedef struct struct_msg{
    long msgType;
    char name[20];
    char text[128];
}Msg;
int main(int argc, char const *argv[])
{
    //获取key值
        key_t key = ftok("./",258);
    //创建或获取消息对象
    int msgid = msgget(key,IPC_CREAT| 0666);
    //准备要发送的消息
    Msg msg01 = {10,"张三","你好 李四"};
    Msg msg02;
    msg02.msgType=20;
    strcpy(msg02.name,"张三");
    strcpy(msg02.text,"你好 王五");
    //发送消息给李四
    msgsnd(msgid,&msg01,sizeof(msg01) - sizeof(long),0);
    //发现消息给王五
    msgsnd(msgid,&msg02,sizeof(msg01) - sizeof(long),0);
    return 0;
}

ls.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
typedef struct struct_msg{
    long msgType;
    char name[20];
    char text[128];
}Msg;
int main(int argc, char const *argv[])
{
    key_t key = ftok("./",258);
    int msqid = msgget(key,IPC_CREAT|0666);
    Msg msg;
    msgrcv(msqid,&msg,sizeof(msg)-sizeof(long),10,0);
    printf("李四接收到的消息为\n");
    printf("姓名:%s说%s\n",msg.name,msg.text);
    return 0;
}

ww.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
typedef struct struct_msg{
    long msgType;
    char name[20];
    char text[128];
}Msg;
int main(int argc, char const *argv[])
{
    key_t key = ftok("./",258);
    int msqid = msgget(key,IPC_CREAT|0666);
    Msg msg;
    msgrcv(msqid,&msg,sizeof(msg)-sizeof(long),20,0);
    printf("王五接收到的消息为\n");
    printf("姓名:%s说%s\n",msg.name,msg.text);
    return 0;
}

案例2:多人聊天程序

#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <unistd.h>
typedef struct info{
    long type;
    char name[30];
    char text[128];
}INF;
int main(int argc, char const *argv[])
{
    key_t key = ftok("./",1);
    printf("请输入姓名\n");
    int name[30]={0};
    scanf("%s",&name);
    printf("请输入接收的类型\n");
    int rtype;
    scanf("%d",&rtype);
    printf("请输入发送的类型\n");
    int stype;
    scanf("%d",&stype);
    int msgid = msgget(key,IPC_CREAT|0666);
    int i = 0;
    for(i = 0; i<2; i++){
        pid_t pid = fork();
        if (pid == 0)
        {
            break;
        }
        
    }
    if(i==0){
        //接收
        INF inf;
        while (1)
        {
            msgrcv(msgid,&inf,sizeof(INF)-sizeof(long),rtype,0);
            printf("%s:%s\n",inf.name,inf.text);
            if(strcmp("886",inf.text)==0){
                break;
            }
        }
        _exit(0);
    }else if(i==1){
        //发送
        INF inf;
        while (1)
        {
            strcpy(inf.name,name);
            scanf("%s",inf.text);
            inf.type = stype;
            msgsnd(msgid,&inf,sizeof(INF)-sizeof(long),0);
            // printf("%s:%s\n",info.name,info.text);
            if(strcmp("886",inf.text)==0){
                break;
            }
        }
        _exit(0);
    }else if(i==2){
        while (1)
        {
           int id = waitpid(-1,NULL,WNOHANG);
            if (id == -1)
            {
                break;
                /* code */
            }
        }
        
        
        
    }
    return 0;
}

消息队列控制(了解)

作用 : 对消息队列删除 , 获取 , 修改等操作
语法
所需头文件 :
        #include <sys/msg.h>
函数 :
                int msgctl(int msqid, int cmd, struct msqid_ds *buf);
参数:
        msqid:消息队列的标识符。
        cmd:函数功能的控制。
        IPC_RMID:删除由 msqid 指示的消息队列,将它从系统中删除并破坏相关 数据结构。
        IPC_STAT:将 msqid 相关的数据结构中各个元素的当前值存入到由 buf 指 向的结构中。
        IPC_SET:将 msqid 相关的数据结构中的元素设置为由 buf 指向的结构中 的对应值。
        buf:msqid_ds 数据类型的地址,用来存放或更改消息队列的属性。
返回值:
        成功: 返回 0
        失败: 返回 -1
struct msqid_ds 类型:
struct msqid_ds {
        struct ipc_perm msg_perm; /* 所有者与权限 */
        time_t msg_stime; /* 最后一条消息发送的时间 */
        time_t msg_rtime; /* 最后一条消息接收的时间 */
        time_t msg_ctime; /* 最后一次更改的时间 */
        unsigned long __msg_cbytes; /* 队列中的当前字节数(非标准) */
        msgqnum_t msg_qnum; /* 队列中的当前消息数 */
        msglen_t msg_qbytes; /* 队列中允许的最大字节数 */
        pid_t msg_lspid; /* 最后一次发送的进程 id */
        pid_t msg_lrpid; /* 最后一次接收的进程 id */
};
struct ipc_perm 类型 :
struct ipc_perm {
        key_t __key; /* 提供给 msgget 的密钥 (2) */
        uid_t uid; /* 所有者的有效 UID */
        gid_t gid; /* 所有者的有效 GID */
        uid_t cuid; /* 创建者的有效 UID */
        gid_t cgid; /* 创建者的有效 GID */
        unsigned short mode; /* 权限 */
        unsigned short __seq; /* 序列号 */
};

磁盘映射

磁盘映射MMAP

        存储映射 I/O (Memory-mapped I/O) 使一个磁盘文件与存储空间中的一个缓冲区相 映射。于是当从缓冲区中取数据,就相当于读文件中的相应字节。于此类似,将数据存 入缓冲区,则相应的字节就自动写入文件。这样,就可在不适用 read write 函数 的情况下,使用地址(指针)完成 I/O 操作。 使用存储映射这种方法,首先应通知内 核,将一个指定文件映射到存储区域中。这个映射工作可以通过mmap 函数来实现。

与文件读取的区别(了解)

        首先简单的回顾一下常规文件系统操作(调用read/fread 等类函数)中,函数的调用
过程:
        1、进程发起读文件请求。
        2、内核通过查找进程文件符表,定位到内核已打开文件集上的文件信息,从而找到此 文件的inode( 存储文件元信息的区域 , 中文名索引节点 )
        3、 inode address_space( 地址空间 ) 上查找要请求的文件页是否已经缓存在页缓存 中。如果存在,则直接返回这片文件页的内容。
        4、如果不存在,则通过 inode 定位到文件磁盘地址,将数据从磁盘复制到页缓存。之后 再次发起读页面过程,进而将页缓存中的数据发给用户进程。
        总结来说,常规文件操作为了提高读写效率和保护磁盘,使用了页缓存机制。这样造成 读文件时需要先将文件页从磁盘拷贝到页缓存中,由于页缓存处在内核空间,不能被用 户进程直接寻址,所以还需要将页缓存中数据页再次拷贝到内存对应的用户空间中。这 样,通过了两次数据拷贝过程,才能完成进程对文件内容的获取任务。写操作也是一 样,待写入的buffer 在内核空间不能直接访问,必须要先拷贝至内核空间对应的主存, 再写回磁盘中(延迟写回),也是需要两次数据拷贝。
        而使用mmap 操作文件中,创建新的虚拟内存区域和建立文件磁盘地址和虚拟内存区域映 射这两步,没有任何文件拷贝操作。而之后访问数据时发现内存中并无数据而发起的缺 页异常过程,可以通过已经建立好的映射关系,只使用一次数据拷贝,就从磁盘中将数 据传入内存的用户空间中,供进程使用。
        总而言之,常规文件操作需要从磁盘到页缓存再到用户主存的两次数据拷贝。而mmap 操 控文件,只需要从磁盘到用户主存的一次数据拷贝过程。
mmap 效率更高

mmap函数

作用:建立映射区
所需头文件
        #include <sys/mman.h>
函数
        void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
参数 :
        addr 内核创建映射的地址,填 NULL, 由内核自己选取地址
        length 长度 要申请的映射区的长度
        prot 权限
        PROT_READ 可读
        PROT_WRITE 可写
        flags 标志位
        MAP_SHARED 共享的 -- 对映射区的修改会影响源文件
        MAP_PRIVATE 私有的
        fd 文件描述符 需要打开一个文件
        offset 指定一个偏移位置 ,从该位置开始映射
返回值
        成功 返回映射区的首地址
        失败 返回 MAP_FAILED ((void *) -1)

munmap函数

作用:

释放映射区
所需头文件
        #include <sys/mman.h>
函数
        int munmap(void *addr, size_t length)
参数 :
        addr 映射区的首地址
        length 映射区的长度
返回值
        成功 返回 0
        失败 返回 -1

truncate函数

作用:

        会将参数path 指定的文件大小改为参数 length 指定的大小 . 如果原来的文件大小比 参数length , 则超过的部分会被删去 .
所需头文件
        #include <unistd.h>
        #include <sys/types.h>
函数
        int truncate(const char *path, off_t length);
参数 :
        path 要拓展的文件
        length 要拓展的长度

使用步骤

1, 打开文件
2, 指定文件大小
3, 建立映射
4, 释放映射区
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char const *argv[])
{
    // 1、通过open打开文件
    int fd = open("tmp", O_RDWR | O_CREAT, 0666);
    if (fd < 0)
    {
        perror("open");
        return 0;
    }
    // 2、拓展文件大小
    truncate("tmp", 16);
    // 3、mmap建立映射
    char *buf = (char *)mmap(NULL, 16, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
    // 4、使用内存区域,读
    printf("buf=%s\n", buf);
    printf("buf=%s\n", buf);
    // 4、使用内存区域,写
    //strcpy(buf, "hello mmap");
    // 5、断开映射
    munmap(buf, 16);
    close(fd);
    return 0;
}

共享内存

        共享内存允许两个或者多个进程共享给定的存储区域。
        物理内存: 电脑物理内存就是指的内存条
        虚拟内存: 是系统默认在 C 盘划分的一部分磁盘空间临时存储数据用的
特点:
        1、共享内存是进程间共享数据的一种最快的方法。 一个进程向共享的内存区域写入了 数据,共享这个内存区域的所有进程就可以立刻看到其中的内容.
        2、使用共享内存要注意的是多个进程之间对一个给定存储区访问的互斥。 若一个进程 正在向共享内存区写数据,则在它做完这一步操作前,别的进程不应当去读、写这些数据

ubuntu 部分版本中共享内存限制值如下

共享存储区的最小字节数: 1
共享存储区的最大字节数: 32M
共享存储区的最大个数: 4096
每个进程最多能映射的共享存储区的个数: 4096

共享内存命令

查看共享内存 ipcs -m
删除共享内存 ipcrm -m shmid

shmget函数

作用:

创建或打开一块共享内存区 , 获得一个共享存储标识符
所需头文件
        #include <sys/ipc.h>
        #include <sys/shm.h>
函数
        int shmget(key_t key, size_t size, int shmflg);
参数
        key:IPC 键值 ( 进程间通讯键值 )
        size:该共享存储段的长度 ( 字节 )
        shmflg:标识函数的行为及共享内存的权限。
        IPC_CREAT:创建 , 如果不存在就创建
        IPC_EXCL:如果已经存在则返回失败
        位或权限位: 共享内存位或权限位后可以设置共享内存的访问权限,格式和 open函数的 mode_t 一样 , 但可执行权限未使用。
返回值 :
        成功:返回共享内存标识符。
        失败:返回-1

shmat函数

作用:

建立进程和物理内存的映射
头文件:
        #include <sys/types.h>
        #include <sys/shm.h>
        void *shmat(int shmid, const void *shmaddr,int shmflg);
参数 :
        shmid:共享内存标识符。
        shmaddr:共享内存映射地址 ( 若为 NULL 则由系 统自动指定 ) ,推荐使用 NULL。
        shmflg:共享内存段的访问权限和映射条件
        0:共享内存具有可读可写权限。
        SHM_RDONLY:只读。
        SHM_RND:( shmaddr 非空时才有效)没有指定 SHM_RND 则此段连接到
        shmaddr 所指定的地址上 (shmaddr 必需页对齐 )
        指定了 SHM_RND 则此段连接到 shmaddr- shmaddr%SHMLBA 所表示的地址 上。
返回值:
        成功:返回共享内存段映射地址
        失败:返回 -1
注意 :
        shmat函数使用的时候第二个和第三个参数一般设为 NULL 0, 即系统自动指定共享 内存地址, 并且共享内存可读可写

shmdt函数

作用:

        将共享内存和当前进程分离( 仅仅是断开本进程与共享内存的联系 , 并不删除共享内存)
所需头文件
        #include <sys/types.h>
        #include <sys/shm.h>
函数
        int shmdt(const void *shmaddr);
参数:
        shmaddr:共享内存映射地址。
返回值:
        成功返回 0
        失败返回 -1

shmctl函数

作用:

共享内存空间的控制。
        #include <sys/ipc.h>
        #include <sys/shm.h>
        int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数:
        shmid:共享内存标识符。
        cmd:函数功能的控制。
        IPC_RMID:删除。
        IPC_SET:设置 shmid_ds 参数。
        IPC_STAT:保存 shmid_ds 参数。
        SHM_LOCK:锁定共享内存段 ( 超级用户 )
        SHM_UNLOCK:解锁共享内存段。
        buf: shmid_ds 数据类型的地址,用来存放或修改共享内存的属性。
返回值:
        成功返回 0
        失败返回 -1
注意:
        SHM_LOCK 用于锁定内存,禁止内存交换。并不代表共享内存被锁定后禁止其它进程访问。其真正的意义是:被锁定的内存不允许被交换到虚拟内存中。这样做的优势在 于让共享内存一直处于内存中,从而提高程序性能

使用步骤

1, 获取唯一 key
2, 获取共享内存标识符
3, 建立共享内存映射
4, 操作映射
5, 关闭映射
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
int main(int argc, char const *argv[])
{
    // 1、获取唯一的key值
    key_t key = ftok("/", 2023);
    // 2、根据唯一的key的共享内存标识(分配物理内存)
    int shm_id = shmget(key, 32, IPC_CREAT | 0666);
    printf("shm_id=%d\n", shm_id);
    // 3、建立进程和物理内存的映射
    char *p = (char *)shmat(shm_id, NULL, 0);
    //4,操作映射,写
    strcpy(p, "hello shm");
    //4,操作映射,读
    // printf("%s\n",p);
    // 5、断开进程和物理内存的映射
    shmdt(p);
    return 0;
}

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

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

相关文章

Python中的并发编程(3)线程池、锁

concurrent.futures 提供的线程池 concurrent.futures模块提供了线程池和进程池简化了多线程/进程操作。 线程池原理是用一个任务队列让多个线程从中获取任务执行&#xff0c;然后返回结果。 常见的用法是创建线程池&#xff0c;提交任务&#xff0c;等待完成并获取结果&…

Nginx正则表达式

目录 1.nginx常用的正则表达式 2.location location 大致可以分为三类 location 常用的匹配规则 location 优先级 location 示例说明 优先级总结 3.rewrite rewrite功能 rewrite跳转实现 rewrite执行顺序 语法格式 rewrite示例 实例1&#xff1a; 实例2&#xf…

2023年阿里云云栖大会-核心PPT资料下载

一、峰会简介 历经14届的云栖大会&#xff0c;是云计算产业的建设者、推动者、见证者。2023云栖大会以“科技、国际、年轻”为基调&#xff0c;以“计算&#xff0c;为了无法计算的价值”为主题&#xff0c;发挥科技平台汇聚作用&#xff0c;与云计算全产业链上下游的先锋代表…

网线市场现状与发展趋势预测

随着物联网、5G、云计算等技术的迅速发展&#xff0c;全球对于高速、稳定的网络需求急剧增长&#xff0c;这进一步推动了网线市场的发展。各种网络应用场景&#xff0c;从家庭到企业、数据中心到智能城市&#xff0c;都需要大量的高质量网线来支持数据传输和通信需求。本文将对…

LinuxBasicsForHackers笔记 -- 管理用户环境变量

查看和修改环境变量 env – 您可以通过从任何目录在终端中输入 env 来查看所有默认环境变量。环境变量的名称始终为大写&#xff0c;如 HOME、PATH、SHELL 等。 查看所有环境变量 set – 查看所有环境变量&#xff0c;包括 shell 变量、局部变量和 shell 函数&#xff08;例…

Axure的安装及基本功能介绍

目录 一. Axure概述 二. Axure安装 2.1 安装包下载 2.2 安装步骤 三. Axure功能介绍​ 3.1 工具栏介绍 3.1.1 复制&#xff0c;剪切及粘贴 3.1.2 选择模式和连接 3.1.3 插入形状 3.1.4 点&#xff08;编辑控点&#xff09; 3.1.5 置顶和置底 3.1.6 组合和取消组合 …

利用Rclone将阿里云对象存储迁移至雨云对象存储的教程,对象存储数据迁移教程

使用Rclone将阿里云对象存储(OSS)的文件全部迁移至雨云对象存储(ROS)的教程&#xff0c;其他的对象存储也可以参照本教程。 Rclone简介 Rclone 是一个用于和同步云平台同步文件和目录命令行工具。采用 Go 语言开发。 它允许在文件系统和云存储服务之间或在多个云存储服务之间…

RE2文本匹配调优实战

引言 在RE2文本匹配实战的最后&#xff0c;博主说过会结合词向量以及其他技巧来对效果进行调优&#xff0c;本篇文章对整个过程进行详细记录。其他文本匹配系列实战后续也会进行类似的调优&#xff0c;方法是一样的&#xff0c;不再赘述。 本文所用到的词向量可以在Gensim训练…

如何用CHAT写方案?

问CHAT&#xff1a;帮我写一份航空无动力乐园的可执行方案 CHAT回复&#xff1a; 方案一&#xff1a;概念及地点筛选 航空无动力乐园是指以航空运动为主题&#xff0c;利用自然地形与风力进行滑翔、跳伞等无动力航空运动的户外休闲娱乐乐园。鉴于此&#xff0c;首需要确定乐园…

Java入门项目--蚂蚁爱购

简介 这是一个靠谱的Java入门项目实战&#xff0c;名字叫蚂蚁爱购。 从零开发项目&#xff0c;视频加文档&#xff0c;十天就能学会开发JavaWeb项目&#xff0c;教程路线是&#xff1a;搭建环境> 安装软件> 创建项目> 添加依赖和配置> 通过表生成代码> 编写Ja…

力扣111. 二叉树的最小深度

给定一个二叉树&#xff0c;找出其最小深度。 最小深度是从根节点到最近叶子节点的最短路径上的节点数量。 说明&#xff1a;叶子节点是指没有子节点的节点。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;2 示例 2&#xff1a; 输入…

这样的Python自动化测试面试题,测开来了都不一定都会把!

十、接口自动化 10.1 接口自动化怎么测试 ( Python requestspytest 版本) 原来我们接口自动化是用 python request pytest 执行 接口自动化其实主要就是接口测试的基础上填加了断言&#xff0c;参数化&#xff0c;动态关联 做接口自动化之前&#xff0c;我们也会划分模块&#…

【数据结构】C语言实现堆(附完整运行代码)

&#x1f984;个人主页:修修修也 &#x1f38f;所属专栏:数据结构 ⚙️操作环境:Visual Studio 2022 目录 一.了解项目功能 二.项目功能演示(以大堆为例) 三.逐步实现项目功能模块及其逻辑详解 1.实现堆程序主函数 2.创建堆结构 3.堆的初始化 4.数据元素入堆 5.数据元素…

Linux上编译和测试V8引擎源码

介绍 V8引擎是一款高性能的JavaScript引擎&#xff0c;广泛应用于Chrome浏览器和Node.js等项目中。在本篇博客中&#xff0c;我们将介绍如何在Linux系统上使用depot_tools工具编译和测试V8引擎源码。 步骤一&#xff1a;安装depot_tools depot_tools是一个用于Chromium开发…

边缘智能网关如何应对环境污染难题

随着我国工业化、城镇化的深入推进&#xff0c;包括大气污染在内的环境污染防治压力继续加大。为应对环境污染防治难题&#xff0c;佰马综合边缘计算、物联网、智能感知等技术&#xff0c;基于边缘智能网关打造环境污染实时监测、预警及智能干预方案&#xff0c;可应用于大气保…

【华为OD题库-076】执行时长/GPU算力-Java

题目 为了充分发挥GPU算力&#xff0c;需要尽可能多的将任务交给GPU执行&#xff0c;现在有一个任务数组&#xff0c;数组元素表示在这1秒内新增的任务个数且每秒都有新增任务。 假设GPU最多一次执行n个任务&#xff0c;一次执行耗时1秒&#xff0c;在保证GPU不空闲情况下&…

ELK综合案例

综合案例 ELKfilebeatnginxjson nginx配置 1,在nginx服务器上安装nginx # yum install epel-release # yum install nginx 2,将nginx日志改成json格式,这样各个字段就方便最终在kibana进行画图统计了 # vim /etc/nginx/nginx.conf ​ http {log_format main $remote_ad…

解决Git提交错误分支

如果 Git 提交到错误的分支&#xff0c;可以通过以下步骤将其转移到正确的分支上&#xff1a; 1.检查当前所在的分支&#xff0c;可以通过 git branch 命令查看。 git branch2.切换到正确的分支&#xff0c;可以通过 git checkout <正确的分支名> 命令进行切换。 git …

windows系统proteus中Ardunio Mega 2560和虚拟机上Ubuntu系统CuteCom进行串口通信

在文章利用proteus实现串口助手和arduino Mega 2560的串口通信-CSDN博客 中&#xff0c;实现了windows系统的proteus中Ardunio Mega 2560和SSCOM通过虚拟串口进行通信。虚拟串口的连接示意图如下图所示。 在文章windows系统和虚拟机上ubuntu系统通过虚拟串口进行通信-CSDN博客…

高级网工在Linux服务器抓包,少不了这几条常用的tcpdump命令。

Linux 的命令太多&#xff0c;tcpdump 是一个非常强大的抓包命令。有时候想看线上发生的一些问题&#xff1a; nginx 有没有客户端连接过来…… 客户端连接过来的时候 Post 上来的数据对不对…… 我的 Redis 实例到底是哪些业务在使用…… tcpdump 作为网络分析神器就派上用场…