IO进程线程(十)进程间通信 消息队列 共享内存 信号灯集

news2024/11/17 23:59:15

文章目录

  • 一、IPC(Inter-Process Communication)进程间通信相关命令 :
    • (一)ipcs --- 查看IPC对象
    • (二)获取IPC键值
    • (三)删除IPC对象的命令
    • (四)获取IPC键值的函数
      • 1. 函数定义
      • 2. 使用示例
  • 二、消息队列
    • (一) 特点
    • (二) 相关API
      • 1. 创建或获取一个消息队列
      • 2. 向消息队列中写消息
      • 3. 在消息队列中读取一条消息
      • 4. 控制消息队列
    • (三) 不关注消息类型
    • (四)关注消息类型
    • (五)消息队列属性结构体
  • 三、共享内存 shared memory(shm)
    • (一)特点
    • (二) 相关API
      • 1. 创建共享内存
      • 2. 映射共享内存到当前的进程空间
      • 3. 取消地址映射
      • 4. 共享内存控制
    • (三)使用示例
    • (四) 属性
  • 四、信号灯集---控制进程间同步
    • (一)特点
    • (二) 相关API
    • (三) 原生函数使用示例
    • (四)封装函数

一、IPC(Inter-Process Communication)进程间通信相关命令 :

(一)ipcs — 查看IPC对象

在这里插入图片描述

(二)获取IPC键值

ipcs -q 查看消息队列
在这里插入图片描述

ipcs -m 查看共享内存
在这里插入图片描述

ipcs -s 查看信号灯集
在这里插入图片描述

(三)删除IPC对象的命令

ipcrm -q id 删除消息队列
ipcrm -m id 删除共享内存
ipcrm -s id 删除信号灯集

(四)获取IPC键值的函数

1. 函数定义

#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
功能:
    使用给定的文件和字符来生成一个IPC通信的key值
参数:
    pathname:路径和文件名(必须是已存在的可访问的)
    proj_id:[0-255]的值  一般我们传一个字符即可 如 'A'  'm'
返回值:
    成功  key值
    失败  -1  重置错误码
  • 注:
  • typedef int key_t key_t 即 int 类型
  • pathname 要求必须是一个已存在的文件,因为key值的是由proj_id的后八位,设备号的后8位以及inode号的后16位组成。因此这种机制并不保证key值一定不重复
  • proj_id 只是用了int的一个字节,即取值范围是[0-255],一般使用时传一个字符,字符ASCII码是0-127。

2. 使用示例

生成并打印出key,分析key值的由来

验证代码

#include <my_head.h>

int main(int argc, char const *argv[])
{
    key_t key=0;
    key = ftok("/home/linux/05work",'A');
    printf("my_key = %#x\n",key);

    struct stat file_stat= {0};
    stat("/home/linux/05work",&file_stat);
    printf("proj_id=%#x  dev=%#lx inode=%#lx\n",'A', file_stat.st_dev, file_stat.st_ino);

    return 0;
}

输出结果
在这里插入图片描述

二、消息队列

(一) 特点

  1. 消息队列也是基于内核实现的,存放在内存上(而非硬盘上)。
  2. 消息队列的大小,默认是 16K。
  3. 消息队列中的消息有类型和正文。
    A进程将消息写入消息队列;
    B进程可以根据消息的类型从消息队列中将对应类型的消息取走。
  4. 半双工通信

(二) 相关API

1. 创建或获取一个消息队列

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgget(key_t key, int msgflg);
功能:
    创建或者获取一个消息队列
参数:
    key:键值
        key 通过ftok获取的
        IPC_PRIVATE 表示只有亲缘进程间能用
    msgflg:消息队列的标志位
        IPC_CREAT|0666 消息队列不存在则创建,权限0666 
        或者  
        IPC_CREAT|IPC_EXCL|0666 消息不存在则创建,存在则但会-1,置错误码为EEXIST
返回值:
    成功 消息队列的id
    失败 -1 重置错误码

2. 向消息队列中写消息

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
功能:向消息队列中写入一条消息
参数:
    msqid:消息队列的id
    msgp: 要写入的数据的首地址
        struct msgbuf {
           long mtype;      /* 消息的类型 必须大于 0 */
           char mtext[1];   /* 消息正文 可以自定义 */
       };
    msgsz:消息正文的大小
    msgflg:标志位 0 阻塞发送  IPC_NOWAIT 非阻塞发送
返回值:
    成功 0
    失败 -1  重置错误码
  • 注:
  • 关于void *msgp参数,第一个long类型大小的空间必须用来存放消息的类型,消息的正文可以自定义
  • size_t msgsz参数,只包含正文数据的大小(sizeof(struct msgbuf)-sizeof(mtype))
  • 阻塞发送的情况下,如果消息队列满了,A进程还想向消息队列中写入消息,此时A进程将会阻塞。

3. 在消息队列中读取一条消息

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, 
        long msgtyp, int msgflg);
功能:在消息队列中读取一条消息
参数:
    msqid:消息队列的id
    msgp: 用来保存接收的数据的缓冲区的首地址
        struct msgbuf {
           long mtype;     /* 消息的类型 必须大于 0 */
           char mtext[1];  /* 消息正文 可以自定义 */
       };
    msgsz:消息正文的大小
    msgtyp:要接受的消息的类型
        0 :接收消息队列中第一条消息
        >0 : 接收指定类型的第一条消息
        <0 :一般不使用,了解即可,表示接收消息队列中第一条类型最小的小于msgtyp的绝对值的消息
            3-2-5-500-200-8
            读取时,类型传 -200
            读取的顺序  2-3-5 
    msgflg:标志位 0 阻塞接收  IPC_NOWAIT 非阻塞接收
返回值:
    成功 实际读到的正文的字节数
    失败 -1  重置错误码
  • 注:读消息队列和写消息队列中的void *msgp结构体的内部成员要尽量对应。

4. 控制消息队列

int msgctl(int msqid, int cmd, struct msqid_ds *buf);
功能:控制消息队列
参数:
    msqid:消息队列id
    cmd:指令
        IPC_STAT:获取消息队列的属性
        IPC_SET:设置消息队列的属性
        IPC_RMID:立即删除消息队列
            只有消息队列的创建者和所有者以及root用户可以删除消息队列
            msgctl函数的第三个参数被忽略
    buff:属性结构体的地址
返回值:
    成功 0
    失败 -1  重置错误码

(三) 不关注消息类型

此时进程间通信不关心消息类型,按顺序接收第一条消息。
注意,当其中一个进程关闭消息队列后,另一个进程再试图关闭,就会报错,错误码EINVAL

read.c

#include <my_head.h>

typedef struct msgbuf{
    long type;
    char name[20];
}msg_t;

int main(int argc, char const *argv[])
{
    //获取key值
    key_t key=ftok("/home/linux/05work",'A');

    //创建消息队列
    int msgid = msgget(key,IPC_CREAT|0666);
    if(-1 == msgid)
        ERR_LOG("msgget error");
    //定义消息结构体
    msg_t msg={0};
    int type=0;
    while(1){
        if(-1 == msgrcv(msgid,&msg,sizeof(msg_t)-sizeof(msg.type),type,0)){
            ERR_LOG("msgrcv error");
        }
        if(!strcmp(msg.name,"quit")){
            break;
        }
        printf("%ld:%s\n",msg.type,msg.name);
    }
    if(-1 == msgctl(msgid,IPC_RMID,NULL)){
        if(EINVAL == errno){
            return 0;
        }
        ERR_LOG("msgctl error");
    }
    return 0;
}

write.c

#include <my_head.h>

typedef struct msgbuf{
    long type;
    char name[20];
}msg_t;

int main(int argc, char const *argv[])
{
    //获取key值
    key_t key=ftok("/home/linux/05work",'A');

    //创建消息队列
    int msgid = msgget(key,IPC_CREAT|0666);
    if(-1 == msgid)
        ERR_LOG("msgget error");
    //定义消息结构体
    msg_t msg={0};
    while(1){
        printf("请输入消息:<类型> <正文>:");
        scanf("%ld %s",&msg.type,msg.name);
        msgsnd(msgid,&msg,sizeof(msg_t)-sizeof(msg.type),0);
        if(!strcmp(msg.name,"quit")){
            break;
        }
    }

    //销毁消息队列
    if(-1 == msgctl(msgid,IPC_RMID,NULL)){
        if(EINVAL == errno){
            return 0;
        }
        ERR_LOG("msgctl error");
    }
    return 0;
}

(四)关注消息类型

此时进程间通信关心消息类型,按顺序接收第一条符合类型的消息,如果消息队列中没有同类型消息,会阻塞等待。
在这里插入图片描述
read.c

#include <my_head.h>

typedef struct msgbuf{
    long type;
    char name[20];
}msg_t;

int main(int argc, char const *argv[])
{
    //获取key值
    key_t key=ftok("/home/linux/05work",'A');

    //创建消息队列
    int msgid = msgget(key,IPC_CREAT|0666);
    if(-1 == msgid)
        ERR_LOG("msgget error");
    //定义消息结构体
    msg_t msg={0};
    int type=0;
    while(1){
        printf("请输入想要接收的消息类型:");
        scanf("%d",&type);
        if(-1 == msgrcv(msgid,&msg,sizeof(msg_t)-sizeof(msg.type),type,0)){
            ERR_LOG("msgrcv error");
        }
        if(!strcmp(msg.name,"quit")){
            break;
        }
        printf("%ld:%s\n",msg.type,msg.name);
    }
    if(-1 == msgctl(msgid,IPC_RMID,NULL)){
        if(EINVAL == errno){
            return 0;
        }
        ERR_LOG("msgctl error");
    }
    return 0;
}

write.c

#include <my_head.h>

typedef struct msgbuf{
    long type;
    char name[20];
}msg_t;

int main(int argc, char const *argv[])
{
    //获取key值
    key_t key=ftok("/home/linux/05work",'A');

    //创建消息队列
    int msgid = msgget(key,IPC_CREAT|0666);
    if(-1 == msgid)
        ERR_LOG("msgget error");
    //定义消息结构体
    msg_t msg={0};
    while(1){
        printf("请输入消息:<类型> <正文>:");
        scanf("%ld %s",&msg.type,msg.name);
        msgsnd(msgid,&msg,sizeof(msg_t)-sizeof(msg.type),0);
        if(!strcmp(msg.name,"quit")){
            break;
        }
    }

    //销毁消息队列
    if(-1 == msgctl(msgid,IPC_RMID,NULL)){
        if(EINVAL == errno){
            return 0;
        }
        ERR_LOG("msgctl error");
    }
    return 0;
}

(五)消息队列属性结构体

struct msqid_ds {
    struct ipc_perm msg_perm;     /* IPC权限结构体 */
    time_t          msg_stime;    /* 最后一次执行msgsnd的时间 */
    time_t          msg_rtime;    /* 最后一次执行msgrcv的时间 */
    time_t          msg_ctime;    /* 最后一次被修改的时间 */
    unsigned long   __msg_cbytes; /* 当前消息队列中的字节数 */
    msgqnum_t       msg_qnum;     /* 当前消息队列中的消息数 */
    msglen_t        msg_qbytes;   /* 允许的最大字节数 */
    pid_t           msg_lspid;    /* 最后一次执行msgsnd的进程的PID */
    pid_t           msg_lrpid;    /* 最后一次执行msgrcv的进程的PID */
};

struct ipc_perm {
    key_t          __key;       /* 键值 */
    uid_t          uid;         /* 所属用户的id */
    gid_t          gid;         /* 所属用户的组id */
    uid_t          cuid;        /* 创建者的id */
    gid_t          cgid;        /* 创建者的组id */
    unsigned short mode;        /* 权限 */
};
  • 注:
    qbytes可以改小,改大的话需要sudo权限

三、共享内存 shared memory(shm)

(一)特点

在内核中创建共享内存,让进程A和进程B都能够访问到,通过这段内存进行数据的传递。
共享内存是所有进程间通信方式中效率最高的(不需要来回进行数据的拷贝)
在这里插入图片描述

(二) 相关API

1. 创建共享内存

#include <sys/ipc.h>
#include <sys/shm.h>

int shmget(key_t key, size_t size, int shmflg);
功能:
    创建共享内存
参数:
    key:键值
        key 通过ftok获取
        IPC_PRIVATE:只能用于亲缘进程间的通信
    size:共享内存的大小  PAGE_SIZE(4k)的整数倍
    shmflg:共享的标志位
        IPC_CREAT|0666 
        或 
        IPC_CREAT|IPC_EXCL|0666
返回值:
    成功 共享内存编号
    失败 -1 重置错误码
  • 注:
  • 共享内存大小必须要4k的整数倍,因为一页是4k。如果申请时不要求4
    的整数倍,分配时也是分配4k的整数倍。
  • 内核空间越界会直接段错误
  • 同一个key值可以同时用于消息队列,共享内存,信号灯集

2. 映射共享内存到当前的进程空间

#include <sys/ipc.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
功能:
    映射共享内存到当前的进程空间
参数:
    shmid:共享内存编号
    shmaddr:指定共享内存出现在进程内存地址的什么位置,直接指定为NULL,让系统自动分配
    shmflg:共享内存操作方式
        0    读写
        SHM_RDONLY    只读
返回值:
    成功 指向共享内存的地址
    失败 (void *)-1 重置错误码

3. 取消地址映射

#include <sys/ipc.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
功能:
    取消地址映射
参数:
    shmaddr:指向共享内存的指针
返回值:
    成功 0
    失败 -1 重置错误码

4. 共享内存控制

int shmctl(int shmid, int cmd, struct shmid_ds *buf);
功能:
    共享内存控制的函数
参数:
    shmid:共享内存编号
    cmd:操作的命令码
        IPC_STAT:获取
        IPC_SET:设置
        IPC_RMID:删除共享内存
            标记要销毁的段。实际上,只有在最后一个进程将其分离之后 
            (也就是说,关联结构shmid_ds的shm_nattch成员为零时), 
            段才会被销毁。
            调用者必须是段的所有者或创建者,或具有特权。buf参数被忽略。
    buf:共享内存属性结构体指针
返回值:
    成功 0
    失败 -1 重置错误码

(三)使用示例

read.c

#include <my_head.h>

#define SHM_PAGE 1024*4
int main(int argc, char const *argv[])
{
    //获取键值
    key_t key = ftok("/home/linux/05work",'A');
    if(-1 == key)
        ERR_LOG("ftok error");

    //创建共享内存
    int shmid = shmget(key, SHM_PAGE,IPC_CREAT|0666);
    if(-1 == shmid)
        ERR_LOG("shmget error");
    
    //映射共享内存
    char *addr = (char *)shmat(shmid,NULL,SHM_RDONLY);

    while(1){
        getchar();
        printf("%s",addr);
    }

    return 0;
}

write.c

#include <my_head.h>

#define SHM_PAGE 1024*4
int main(int argc, char const *argv[])
{
    //获取键值
    key_t key = ftok("/home/linux/05work",'A');
    if(-1 == key)
        ERR_LOG("ftok error");

    //创建共享内存
    int shmid = shmget(key, SHM_PAGE,IPC_CREAT|0666);
    if(-1 == shmid)
        ERR_LOG("shmget error");
    
    //映射共享内存
    char *addr = (char *)shmat(shmid,NULL,0);

    while(1){
        printf("请输入要发送的内容:");
        scanf("%s",addr);
    }

    return 0;
}

(四) 属性

struct shmid_ds{
    struct ipc_perm shm_perm;    //权限结构体
    size_t shm_segsz;             //共享内存大小,单位是字节 
    __time_t shm_atime;         //最后一次映射的时间 
    __pid_t shm_cpid;             //创建共享内存进程的pid 
    __pid_t shm_lpid;             //最后一次操作共享内存进程的pid 
    shmatt_t shm_nattch;         //共享内存映射的次数
};
struct ipc_perm{
    __key_t __key;                //ftok获取的key
    __uid_t uid;                 //用户的ID
    __gid_t gid;                 //组ID
    __uid_t cuid;                //创建共享内存的用户的ID
    __gid_t cgid;                //创建共享内存的组的ID
    unsigned short int mode;     //消息队列的权限
};

四、信号灯集—控制进程间同步

(一)特点

进程间同步的机制

(二) 相关API

#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
功能:
    创建一个信号灯集
参数:
    key:键值
        IPC_PRIVATE  key
    nsems:信号灯集合中信号灯的个数
    semflag:创建的标志位
        IPC_CREAT|0666 或 IPC_CREAT|IPC_EXCL|0666
返回值:
    成功 semid
    失败 -1  重置错误码


int semctl(int semid, int semnum, int cmd, ...);
功能:信号灯集的控制函数
参数:
    semid信号灯集的ID
    senum:信号灯的编号 从0开始
    cmd:命令码
        SETVAL:设置信号灯的值 --->第四个参数val选项
        GETVAL:获取信号灯的值 --->不需要第四个参数
        IPC_STAT:获取信号灯集的属性--->第二参数被忽略,第四个参数buf选项
        IPC_SET :设置信号灯集的属性--->第二参数被忽略,第四个参数buf选项
        IPC_RMID:删除信号灯集 第二参数被忽略,第4个参数不用填写 
    @...:可变参
    
    union semun{
        int val; /* Value for SETVAL */
        struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
    };
返回值:
    成功:
        GETVAL:成功返回信号灯的值
        其余的命令码成功返回0
    失败 -1  重置错误码


int semop(int semid, struct sembuf *sops, size_t nsops);
功能:
    信号灯集中信号灯的操作函数
参数:
    semid:信号灯集的编号
    sops:操作方式
        struct sembuf{
            unsigned short sem_num; //信号灯的编号
            short sem_op; //操作方式(PV)-1:P操作,申请资源 1:V操作,释放资源
            short sem_flg; //操作的标志位 0:阻塞 IPC_NOWAIT:非阻塞方式操作
        }
    nsops:本次操作信号灯的个数
返回值:
    成功 0
    失败 -1  重置错误码
  • 注:
  • 初始化操作必须只能一次,如果多次可能会将其他进程的值初始化
  • 同时对多个信号灯进行操作时,可以定义一个结构体数组

(三) 原生函数使用示例

(四)封装函数

原生函数直接使用会比较繁琐,因此会做二次封装。

初始化(解决可能重复初始化的问题):


P操作


V操作


销毁


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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CorelDRAW2024最新版本有哪些功能?揭秘设计界最新神器!

“设计”一词最早来源于拉丁语“designare”&#xff0c;意为计划&#xff0c;构思。随着时代的发展&#xff0c;人们将“设计”理解为一种创造性活动&#xff0c;通过这种活动&#xff0c;人们可以创造出新的产品、新的场景以及新的体验。 「CorelDRAW汉化版下载」&#xff0c…

讯方618代表有话说 | 行业大咖详解鸿蒙,全程在线答疑

讯方618“代表有话说” 系列专场直播活动来啦 6月11日&#xff08;周二&#xff09;19:30 本期直播特邀华为、学校、讯方代表 与大家畅聊鸿蒙奥秘 共同开启未来技术之门&#xff01; 行业大咖将带大家 了解鸿蒙概况和岗位需求 解析鸿蒙系统强势崛起带来的影响 解读高校…

Netty中的ByteBuf使用介绍

ByteBuf有三类&#xff1a; 堆缓存区&#xff1a;JVM堆内存分配直接缓冲区&#xff1a;有计算机内存分配&#xff0c;JVM只是保留分配内存的地址信息&#xff0c;相对于堆内存方式较为昂贵&#xff1b;复合缓冲区&#xff1a;复合缓冲区CompositeByteBuf&#xff0c;它为多个B…

【算法专题--栈】最小栈--高频面试题(图文详解,小白一看就会!!)

目录 一、前言 二、题目描述 三、解题方法 ⭐解题方法--1 ⭐解题方法--2 四、总结 五、共勉 一、前言 最小栈这道题&#xff0c;可以说是--栈专题--&#xff0c;比较经典的一道题&#xff0c;也是在面试中频率较高的一道题目&#xff0c;通常在面试中&#xff0c;面试官可…

python - DataFrame查询数据操作

学习目标 掌握获取df一列或多列数据的方法 知道loc和iloc的区别以及使用方法 知道df的query函数的使用方法 知道isin函数的作用和使用方法 获取DataFrame子集的基本方法 1.1 从前从后获取多行数据 案例中用到的数据集在文章顶部 LJdata.csv 前景回顾 head() & tail(…

西门子PLC学习之数据块的单个实例,多重实例与参数实例间的区别

首先介绍下函数&#xff0c;函数块与数据块这三个概念。 数据块 数据块里可以存储各种类型的参数。有人可能会问&#xff0c;m寄存器不是可以存储布尔值&#xff0c;8位&#xff0c;16位&#xff0c;32位变量吗&#xff0c;为什么要多此一举&#xff1f;因为虽然m寄存器能存储以…

超详细的java Comparable,Comparator接口解析

前言 Hello大家好呀&#xff0c;在java中我们常常涉及到对象的比较&#xff0c;不同于基本数据类型&#xff0c;对于我们的自定义对象&#xff0c;需要我们自己去建立比较标准&#xff0c;例如我们自定义一个People类&#xff0c;这个类有name和age两个属性&#xff0c;那么问…

QT creator c动态链接库的创建与调用

QT creator c动态链接库的创建与调用 QT5.15.2 1.创建dll项目 确保两类型选择正确 2.选择MinGW 64-bit 3.点击完成 pro文件参考&#xff1a; QT - guiTEMPLATE lib DEFINES QT_DLL_DEMO_LIBRARYCONFIG c17# You can make your code fail to compile if it uses deprecat…

计算机组成结构—IO系统概述

目录 一、I/O 系统的发展 1. 早期阶段 2. 接口模块和 DMA 阶段 3. 通道结构阶段 4. 处理机阶段 二、I/O 系统的组成 1. I/O 软件 2. I/O 硬件 三、I/O 设备 1. I/O 设备分类 2. I/O 设备的组成 在计算机中&#xff0c;除 CPU 和主存两大模块之外&#xff0c;第三个重…

Vue项目安装axios报错npm error code ERESOLVE npm error ERESOLVE could not resolve解决方法

在Vue项目中安装axios时报错 解决方法&#xff1a;在npm命令后面加--legacy-peer-deps 例如&#xff1a;npm install axios --save --legacy-peer-deps 因为别的需求我把node版本重装到了最新版&#xff08;不知道是不是这个原因&#xff09;&#xff0c;后来在项目中安装axi…

STM32作业实现(四)光敏传感器

目录 STM32作业设计 STM32作业实现(一)串口通信 STM32作业实现(二)串口控制led STM32作业实现(三)串口控制有源蜂鸣器 STM32作业实现(四)光敏传感器 STM32作业实现(五)温湿度传感器dht11 STM32作业实现(六)闪存保存数据 STM32作业实现(七)OLED显示数据 STM32作业实现(八)触摸按…

【Python爬虫单点登录实战】PyExecJS破解慧职教:过河源技术学院单点登录统一身份认证

目录 前言大致分析PyExecJS 使用案例pip 安装:Demo:输出:案例1.访问目标网站的登录页面并查看源码2.将js放到和py脚本同一级目录下3. 编写Python脚本来调用js破解单点登录实战提取密钥参数清洗数据登陆测试单点登录获取ticket获取jsessionid获取token成功我的专栏前言 博主提供…

Python 知识图谱补全,Knowledge Graph Completion,基于大模型的知识图谱补全,基于LLMs的KGC任务

今天讲一篇文章《Exploring Large Language Models for Knowledge Graph Completion》 &#xff0c;这篇文章主题&#xff1a;基于大模型做知识图谱补全 1.文章主要思想&#xff1a; 本章描述知识图谱补全中的三个任务&#xff1a;三元组分类、关系预测和实体(链接)预测&…

微信如何防止被对方拉黑删除?一招教你解决!文末附软件!

你一定不知道&#xff0c;微信可以防止被对方拉黑删除&#xff0c;秒变无敌。只需一招就能解决&#xff01;赶快来学&#xff01;文末有惊喜&#xff01; 惹到某些重要人物&#xff08;比如女朋友&#xff09;&#xff0c;被删除拉黑一条龙&#xff0c;那真的是太令人沮丧了&a…

Ubuntu server 24 (Linux) AdGuard Home +SmartDNS 安装配置 搭建去广告快速DNS

一 SmartDNS 安装 &#xff0c;可参考&#xff1a;Ubuntu server 24 (Linux) 安装部署smartdns 搭建智能DNS服务器-CSDN博客 二 安装AdGuard 1 下载地址&#xff1a;GitHub - AdguardTeam/AdGuardHome: Network-wide ads & trackers blocking DNS server 2 解压安装 #下…

路由器重启真的好吗?多久重启一次更好?

前言 小白前段时间发现自己家的OpenWRT软路由上网特别慢&#xff0c;有时候通话还有点卡顿。 然而有个朋友用的普通路由器也有类似的问题&#xff0c;而且有时候根本上不去网。 解决的办法很简单&#xff1a;重启路由器。 重启路由器&#xff1f; 但路由器重启是真的好吗&a…

链表反转--理解链表指针的基本操作

链表反转--理解链表指针的基本操作 链表反转的方法--主要是理解链表指针链表心得类节点是对象和指针区别&#xff1a; 链表反转的方法–主要是理解链表指针 根据值创建新列表 用一个链表指针代替整个新链表 两个链表的赋值 递归求解反向链表 用一个链表代替前后链表数…

将div渲染成textarea框,类似于ant design 的TextArea

一 先看效果 原始效果 输入时效果 二 代码如下 1. html 代码 <div className{style.divTextArea} contentEditable"true"></div> 2. Css(Less)代码 .divTextArea {width: 90%;margin-top: 10px;line-height: 28px;min-height: 60px;border: 1px solid …

优雅谈大模型10:MoE

大模型技术论文不断&#xff0c;每个月总会新增上千篇。本专栏精选论文重点解读&#xff0c;主题还是围绕着行业实践和工程量产。若在某个环节出现卡点&#xff0c;可以回到大模型必备腔调或者LLM背后的基础模型新阅读。而最新科技&#xff08;Mamba,xLSTM,KAN&#xff09;则提…

牛客java基础(一)

A 解析 : java源程序只允许一个public类存在 &#xff0c;且与文件名同名 ; D hashCode方法本质就是一个哈希函数&#xff0c;这是Object类的作者说明的。Object类的作者在注释的最后一段的括号中写道&#xff1a;将对象的地址值映射为integer类型的哈希值。但hashCode()并不…