IO进程线程day9(2023.8.7)

news2024/11/25 19:48:27

一、Xmind整理:

消息队列的原理:

共享内存的原理:

二、课上练习:

练习1:用信号的方式回收僵尸进程(重点!)

1.子进程退出后,父进程会收到17)SIGCHLD信号

2.父进程中捕获17)SIGCHLD信号,给该信号注册新的处理函数。在该新的处理函数中执行waitpid函数,回收僵尸进程。

3.当在信号A的处理函数内部时,再次触发A信号,会导致信号屏蔽,会造成多个子进程短时间内同时退出,父进程只会处理一个17号信号,导致僵尸进程收不干净的问题

4.解决方式:当成功回收到僵尸进程后,再回收一次。直到没有僵尸进程,结束循环。即判断waitpid(-1, NULL, WNOHANG);的返回值

    ①  =0,有子进程,但是没有僵尸进程

    ②  =-1,没有子进程,也没有僵尸进程

小练1:回收一个子进程

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <head.h>
void handler(int sig)
{
    printf("触发%d信号\n",sig);
    pid_t wpid =waitpid(-1,NULL,WNOHANG);
    printf("wpid =%d\n",wpid);
    return;
}
int main(int argc, const char *argv[])
{
    pid_t cpid = fork();

    if(cpid > 0)
    {
        //捕获2号SIGINT信号
        if(signal(17,handler) == SIG_ERR)
        {
            perror("signal");
            return -1;
        }
        printf("捕获17号信号成功\n");
        while(1)                                                                              
        {
            printf("this id parent: %d %d\n",getpid(),cpid);
            sleep(1);
        }
    }
    else if(0 == cpid)
    {
        for(int i = 0; i<3; i++)
        {
            printf("this is cpid %d %d\n",getppid(),getpid());
            sleep(1);
        }
        printf("子进程 %d 准备退出\n",getpid());
        exit(0);
    }
    else
    {
        perror("fork");
        return -1;
    }

    return 0;
}

小练2:多个子进程短时间内同时退出问题

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <head.h>
#include <sys/wait.h>
int count = 0;
void handler(int sig)
{
    while(1)
    {   //当回收成功后,再收一次,直到回收失败
        //=-1,没有僵尸进程,也没有子进程
        //=0,没有僵尸进程,但是有子进程
        pid_t wpid = waitpid(-1,NULL,WNOHANG);
        if(wpid <= 0)
            break;
        printf("[%d] wpid = %d\n",++count,wpid);
    }
       /*
       也可以这样写
       while(waitpid(-1,NULL,WNOHANG) > 0);
       */                                                           

    return;
}
int main(int argc, const char *argv[])
{
    //捕获17号SIGINT信号
    if(signal(17,handler) == SIG_ERR)
    {
        perror("signal");
        return -1;
    }
    printf("捕获17号信号成功\n");

    int i = 0;
    while(i < 100)
    {
        if(fork() == 0)   //若是子进程
        {
            exit(0);      //退出
        }
        i++;              //只有父进程执行i++
    }
    //能运行到当前位置,则代表是父进程
    while(1)
        sleep(1);

    return 0;
}

练习2:kill

功能:给指定进程或者进程组发送一个信号

原型:

#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);

参数:

pid_t pid:指定要发送的进程或者进程组;
pid > 0,  将信号发送给指定的进程,进程号==pid参数;
pid == 0, 将信号发送给当前进程组下的所有进程;
pid == -1, 将信号发送给当前进程权限所能送达的所有进程,除了1号进程;
pid < -1,  将信号发送给指定进程组下的所有进程。进程组ID == -pid参数;
int sig:指定要发送的信号的编号,可以填对应的宏;
sig==0代表没有信号被发送,但是依然会检测对方进程是否存在,或者是否有权限访问。

返回值:

成功,返回0;
失败,返回-1,更新errno;

练习3:alarm

功能:设置一个定时器,当时间到后,给当前进程发送一个14) SIGALRM 信号

原型:

#include <unistd.h>
unsigned int alarm(unsigned int seconds);

参数:

unsigned int seconds:设置定时器时间,以秒为单位;
seconds == 0, 则取消定时器;

返回值:

>0, 返回上一个定时器没有走完的时间;
=0, 没有未走完的定时器;

小练: 设置一个3s的定时器

 #include <stdio.h>
 #include <unistd.h>
 #include <signal.h>
 
 void callback(int sig)
 {
     printf("alarm...\n");
     alarm(3);     //设置一个3s的定时器
     return;
 }
 
 int main(int argc, const char *argv[])
 {
     //捕获14号信号
     if(signal(14,callback) == SIG_ERR)              
     {
         perror("signal");
         return -1;
     }
 
     alarm(3);    //设置一个3s的定时器
 
     while(1)
     {
         printf("signal...\n");
         sleep(1);
     }
     return 0;
 }
                                                     

练习4:ftok

功能:该函数通过pathname提供的id,以及proj_id提供的8bit的值,计算key值(键值),给msgget shmget semget函数使用。只要pathname和proj_id一致,则计算的key值就一致。那么通过相同key值找到的IPC对象就是同一个。

原型:

#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);

参数:

char *pathname:文件的路径以及名字; 该文件必须存在且可访问
int proj_id:传入一个非0参数;

返回值:

成功,返回计算得到的key值;
失败,返回-1,更新errno;

小练: 创建消息队列

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <head.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int main(int argc, const char *argv[])
{
    //创建key值
    key_t key =ftok("/",1);
    if(key < 0)
    {
        perror("ftok");
        return -1;
    }
    printf("key = %#x\n",key);

    //创建消息队列
    int msqid = msgget(key,IPC_CREAT|0664);
    if(msqid < 0)
    {
        perror("msgget");
        return -1;
    }
    printf("msqid = %d\n",msqid);
    system("ipcs -q");               //让代码执行shell指令

    return 0;
}                                                                  

练习5:msgget

功能:通过key值到内核内存中找对应的消息队列,并返回消息队列的id ---> msqid

原型:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);

参数:

key_t key:ftok函数返回出来的key值;
int msgflg:
        IPC_CREAT:若消息队列不存在,则创建消息队列。若消息队列存在,则忽略该选项;
        IPC_CREAT|0664:创建的同时指定消息队列的权限。
        IPC_CREAT|IPC_EXCL:若消息队列不存在,则创建消息队列。若消息队列存在,则报错;

返回值:

>=0, 成功返回消息队列的id号  msqid;
=-1, 函数运行失败,更新errno;

练习6:msgsnd

功能:将数据打包后发送到消息队列中

原型:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

参数:

int msqid:指定要发送到哪个消息队列中;
void *msgp:指定要发送的消息包的首地址; 通用格式如下:
struct msgbuf {
long mtype;       /* message type, must be > 0 */    消息类型,必须大于0;
char mtext[1];    /* message data */  消息内容,类型根据需求修改,想要发什么类型就填什么类型。
                                      大小与下一个参数msgsz指定的一致
              };   
size_t msgsz:消息内容的大小,以字节为单位。   
int msgflg:
0:阻塞方式发送,当消息队列满了,则当前函数阻塞;
IPC_NOWAIT:非阻塞方式,当消息队列满了,该函数不阻塞,且函数运行失败,errno == EAGAIN.

返回值:

=0, 函数运行成功;
=-1, 函数运行失败,更新errno;

小练: 

 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 #include <head.h>
 #include <sys/ipc.h>
 #include <sys/msg.h>
 
 struct msgbuf
 {
     long mtype;
     char mtext[128];
 };
 
 int main(int argc, const char *argv[])
 {
     //创建key值
     key_t key =ftok("/",1);
     if(key < 0)
     {
         perror("ftok");
         return -1;
     }
     printf("key = %#x\n",key);
 
     //创建消息队列
     int msqid = msgget(key,IPC_CREAT|0664);
     if(msqid < 0)
     {
         perror("msgget");
         return -1;
     }
     printf("msqid = %d\n",msqid);
 
     struct msgbuf sndbuf;
     while(1)
     {
         printf("请输入消息类型:");
         scanf("%ld",&sndbuf.mtype);
         getchar();
 
         if(0 == sndbuf.mtype)   //若终端输入0,则跳出循环
             break;
         printf("请输入消息内容:");
         fgets(sndbuf.mtext,sizeof(sndbuf.mtext),stdin);
         sndbuf.mtext[strlen(sndbuf.mtext)-1] = 0;
 
         //向消息队列中发送数据
         if(msgsnd(msqid,&sndbuf,sizeof(sndbuf.mtext),0)< 0)
         {
             perror("msgsnd");
             return -1;
         }
         printf("发送成功\n");
         system("ipcs -q");               //让代码执行shell指令
     }
     return 0;
 }

练习7:msgrcv

功能:从消息队列中读取数据

原型:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);

参数:

int msqid: 指定要从哪个消息队列中读取消息;
void *msgp:指定将读取到的数据存储到什么位置,填对应空间的地址; 以什么形式写入,就以什么形式读取。通用格式如下:
struct msgbuf {
long mtype;       /* message type, must be > 0 */    消息类型,必须大于0;
char mtext[1];    /* message data */  消息内容,类型根据需求修改,想要发什么类型就填什么类型。
                                      大小与下一个参数msgsz指定的一致
              };   
size_t msgsz:指定要读取多少个字节大小的消息内容,以字节为单位。   
long msgtyp:指定要读取的消息类型; 
msgtyp == 0, 读取消息队列中的第一条消息; 先进先出;
msgtyp > 0,  指定消息类型读取,读取消息队列中第一条消息类型为 msgtyp参数指定的消息;
msgflg指定了MSG_EXCEPT,读取消息队列中第一条消息类型 不等于 msgtyp参数指定的消息;
vi -t MSG_EXCEPT;   #define MSG_EXCEPT      020000 
msgtyp < 0,  读取消息队列中第一条最小的,且类型小于等于 msgtyp参数绝对值的消息。                  
int msgflg:
0:阻塞方式,当消息队列中没有消息了,该函数阻塞;
IPC_NOWAIT:非阻塞方式运行,当消息队列中没有消息了,该函数不阻塞,运行失败,errno == ENOMSG;

返回值:

>0, 成功读取到的字节数;
=-1, 函数运行失败,更新errno;

代码示例: 

若消息队列中有消息:

                                   mtype     100      101      99      100      101

                                   mtext       aaa     bbb     ccc     ddd     eee

 i. msgtyp == 0

while(1)                                                                   
    {
        //阻塞方式读取消息队列中第一条消息,先进先出的原则
        //res = msgrcv(msqid, &rcv, sizeof(rcv.mtext), 0, 0);
        
        //非阻塞方式读取消息队列中第一条消息,先进先出的原则
        res = msgrcv(msqid, &rcv, sizeof(rcv.mtext), 0, IPC_NOWAIT);
        if(res < 0)
        {
            perror("msgrcv");
            return -1;
        }
        printf("res=%ld : %ld %s\n", res, rcv.mtype, rcv.mtext);
    }
​
输出顺序:
    100 aaa   101 bbb   99 ccc   100 ddd    101 eee

ii. msgtyp > 0 

while(1)
    {
        //1.阻塞方式读取消息队列中第一条消息类型 == 101 的消息
        //res = msgrcv(msqid, &rcv, sizeof(rcv.mtext), 101, 0);
​
        //2.非阻塞方式读取消息队列中第一条消息 == 101 的消息
        //res = msgrcv(msqid, &rcv, sizeof(rcv.mtext), 101, IPC_NOWAIT);
                                                                                  
        //3.非阻塞方式读取消息队列中第一条消息类型 != 101的消息 
        res = msgrcv(msqid, &rcv, sizeof(rcv.mtext), 101, IPC_NOWAIT|020000);
        if(res < 0)
        {
            perror("msgrcv");
            return -1;
        }
        printf("res=%ld : %ld %s\n", res, rcv.mtype, rcv.mtext);
    }
注释1,2的现象:
    101 bbb     101 eee
第3个的现象:
    100 aaa    99 ccc   100 ddd    

iii.msgtyp < 0

 while(1)
    {
        //阻塞方式读取消息队列中<=msgtyp参数指定的消息类型中最小的那条消息;
        res = msgrcv(msqid, &rcv, sizeof(rcv.mtext), -100, 0);
       
        //非阻塞方式读取消息队列中<=msgtyp参数指定的消息类型中最小的那条消息;
        //res = msgrcv(msqid, &rcv, sizeof(rcv.mtext), -100, IPC_NOWAIT);
​
        if(res < 0)
        {
            perror("msgrcv");
            return -1;
        }
        printf("res=%ld : %ld %s\n", res, rcv.mtype, rcv.mtext);
    }
​
现象:
res=128 : 99 ccc
res=128 : 100 aaa
res=128 : 100 ddd

练习8:msgctl

功能:控制消息队列,常用于删除消息队列

原型:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);

参数:

int msqid:指定要控制的消息队列的id号;
int cmd:
        IPC_STAT:获取消息队列的属性,属性存储在第三个参数中;
        IPC_SET:设置消息队列属性,属性存储在第三个参数中;
        IPC_RMID:删除消息队列,第三个参数无效,填NULL即可;

返回值:

成功,返回0;
失败,返回-1,更新errno;
//删除消息队列
    if(msgctl(msqid, IPC_RMID, NULL) < 0)
    {
        perror("msgctl");
        return -1;
    }
    printf("删除消息队列成功\n");

练习9:共享内存函数   ftok

功能:该函数通过pathname提供的id,以及proj_id提供的8bit的值,计算key值(键值),给msgget shmget semget函数使用。只要pathname和proj_id一致,则计算的key值就一致。那么通过相同key值找到的IPC对象就是同一个。

原型:

#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);

参数:

char *pathname:文件的路径以及名字; 该文件必须存在且可访问
int proj_id:传入一个非0参数;

返回值:

成功,返回计算得到的key值;
失败,返回-1,更新errno;

练习10:shmget

功能:通过key值到内核内存中找对应的共享内存,并返回共享内存的id ---> shmid

原型:

#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);

参数:

key_t key:ftok函数返回出来的key值;
size_t size:指定要申请多少个字节的共享内存;
int shmflg:
        IPC_CREAT:若消息队列不存在,则创建共享内存。若共享内存存在,则忽略该选项;
        IPC_CREAT|0664:创建的同时指定共享内存的权限。
        IPC_CREAT|IPC_EXCL:若共享内存不存在,则创建共享内存。若共享内存存在,则报错;

返回值:

>=0, 成功返回共享内存的id号  shmid;
=-1, 函数运行失败,更新errno;

练习11:shmat

功能:将共享内存映射到用户空间中

原型:

#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);

参数:

int shmid:指定要映射的共享内存id号 , 
const void *shmaddr:指定共享内存要映射到用户空间的位置,填对应空间的首地址; 例如:(void*)0x10
                     填NULL,代表让操作系统自动映射;
int shmflg :
        0:默认方式映射,进程对共享内存可读可写;
        SHM_RDONLY:只读,进程对共享内存只读;

返回值:

成功,返回共享内存映射到用户空间的首地址;
失败,返回 (void *) -1,更新errno;
注意:获取到的映射空间的首地址的指向不允许修改,若修改后会导致首地址找不到,导致内存泄漏,与堆空间首地址不能改变的概念一致

小练: 

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/shm.h>
#include <head.h>
#include <sys/ipc.h>
int main(int argc, const char *argv[])
{
    //创建key值
    key_t key = ftok("./",10);
    if(key < 0)
    {
        perror("ftok");
        return -1;
    }
    printf("key = %#x\n",key);

    //创建共享内存,获得shmid号
    int shmid =shmget(key, 32, IPC_CREAT|0664);
    if(shmid < 0)
    {
        perror("shmget");
        return -1;                                 
    }
    printf("shmid = %d\n",shmid);

    //映射共享内存到用户空间
    void* addr = shmat(shmid,NULL,0);
    if((void*)-1 == addr)
    {
        perror("shmat");
        return -1;
    }
    printf("addr = %p\n",addr);
    system("ipcs -m");

    return 0;
}

练习12:shmdt

功能:将共享内存与进程的用户空间断开映射; 当进程不想操作共享内存的时候,就可以断开映射

原型:

#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);

参数:

void *shmaddr:指定要断开映射的用户空间的首地址

返回值:

成功,返回0;
失败,返回-1,更新errno;

练习13:shmctl

功能:控制共享内存,常用于删除共享内存

原型:

#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

参数:

int shmid:指定要控制的共享内存;
int cmd:
        IPC_STAT:获取共享内存的属性,属性存储在第三个参数中;
        IPC_SET:设置共享内存属性,属性存储在第三个参数中;
        IPC_RMID:删除共享内存,第三个参数无效,填NULL即可;

返回值:

成功,返回0;
失败,返回-1,更新errno;
//删除共享内存
    if(shmctl(shmid, IPC_RMID, NULL) < 0)
    {
        perror("shmctl");
        return -1;
    }
    printf("删除共享内存成功\n");

三、课后作业:

1.要求用消息队列实现AB进程对话

   A进程先发送一句话给B进程,B进程接收后打印

   B进程再回复一句话给A进程,A进程接收后打印

   重复1.2步骤,当收到quit后,要结束AB进程

   实现随时收发:用多进程 多线程。

A进程:

#include <stdio.h>
#include <string.h>
#include <head.h>

struct msgbuf
{
    long mtype;
    char mtext[128];
};
int main(int argc, const char *argv[])
{
    //创建key值
    key_t key =ftok("./",1);

    if(key < 0)
    {
        perror("ftok");
        return -1;
    }
    printf("key = %#x\n",key);

    //创建消息队列                                                   
    int msqid = msgget(key,IPC_CREAT|0664);
    if(msqid < 0)
    {
        perror("msgget");
        return -1;
    }
    printf("msqid = %d\n",msqid);

    struct msgbuf sndbuf;
    //创建一个子进程
    pid_t cpid = fork();
    if(cpid > 0)
    {
        while(1)
        {
            sndbuf.mtype = 1;
            scanf("%s",sndbuf.mtext);
            msgsnd(msqid,&sndbuf,sizeof(sndbuf.mtype),0);
            if(strcmp(sndbuf.mtext,"quit") == 0)
                break;
        }
    }
    else if(0 == cpid)
    {
        while(1)
        {
            msgrcv(msqid,&sndbuf,sizeof(sndbuf.mtext),2,0);
            if(strcmp(sndbuf.mtext,"quit") == 0)
                break;
            printf("%s\n",sndbuf.mtext);
        }
        kill(getppid(),2);
    }
    else
    {
        perror("fork");
        return -1;
    }
    return 0;
}

B进程:

#include <stdio.h>
#include <string.h>
#include <head.h>
struct msgbuf
{
    long mtype;
    char mtext[128];
};
int main(int argc, const char *argv[])
{
    key_t key = ftok("./",1);
    if(ftok < 0)
    {
        perror("ftok");
        return -1;                                                       
    }
    printf("key = %#x\n",key);

    int msqid = msgget(key,IPC_CREAT|0664);
    if(msqid < 0)
    {
        perror("msgget");
        return -1;
    }

    struct msgbuf sndbuf;
    pid_t cpid = fork();
    if(cpid > 0)
    {
        while(1)
        {
            sndbuf.mtype = 2;
            scanf("%s",sndbuf.mtext);
            msgsnd(msqid,&sndbuf,sizeof(sndbuf.mtype),0);
            if(strcmp(sndbuf.mtext,"quit") == 0)
                break;
        }
    }

    else if(0 == cpid)
    {
        while(1)
        {
            msgrcv(msqid,&sndbuf,sizeof(sndbuf.mtext),1,0);
            if(strcmp(sndbuf.mtext,"quit") == 0)
                break;
            printf("%s\n",sndbuf.mtext);
        }
        kill(getppid(),2);
    }
    else
    {
        perror("fork");
        return -1;
    }

    return 0;
}

2.要求在共享内存中存入字符串 “1234567”。A进程循环打印字符串,B进程循环倒置字符串,要求结果不允许出现乱序:

提示:共享内存中存储 flag + string.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <head.h>
#include <sys/shm.h>
int main(int argc, const char *argv[])
{
    //创建key值
    key_t key = ftok("./", 3);
    if(key < 0)
    {
        perror("ftok");
        return -1;
    }
    printf("key = %#x\n", key);

    //通过key值获取shmid号
    int shmid = shmget(key, 32, IPC_CREAT|0777);
    if(shmid < 0)
    {
        perror("shmget");
        return -1;
    }
    printf("shmid = %d\n", shmid);

    //将共享内存映射到用户空间
    void* addr = shmat(shmid, NULL, 0);
    if((void*)-1 == addr)
    {
        perror("shmat");
        return -1;
    }                                                                            
    printf("addr = %p\n", addr);

    //存储一个str
    *(int *)addr = 0;
    char *str = (char *)addr+4;
    //存储一个字符串
    strcpy(str,"1234567");
    pid_t cpid = fork();
    if(cpid > 0)
    {
        while(1)
        {
            if(*(int *)addr == 0)
            {
                printf("%s\n",(char *)addr+4);
                *(int *)addr = 1;
            }
        }
    }
    else if(0 == cpid)
    {
        while(1)
        {
            if(*(int*)addr == 1)
            {
                char *star = (char *)addr+4;
                char *end  = (char*)addr+4+strlen(str)-1;
                while(star<end)
                {
                    char temp = *star;
                    *star = *end;
                    *end  = temp;
                    star++;
                    end--;
                }
                *(int*)addr = 0;
            }
        }
    }
    else
    {
        perror("fork");
        return -1;
    }

    system("ipcs -m");

    return 0;
}

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

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

相关文章

04-1_Qt 5.9 C++开发指南_常用界面设计组件_字符串QString

本章主要介绍Qt中的常用界面设计组件&#xff0c;因为更多的是涉及如何使用&#xff0c;因此会强调使用&#xff0c;也就是更多针对实例&#xff0c;而对于一些细节问题&#xff0c;需要参考《Qt5.9 c开发指南》进行学习。 文章目录 1. 字符串与普通转换、进制转换1.1 可视化U…

用i18n 实现vue2+element UI的国际化多语言切换详细步骤及代码

一、i18n的安装 这个地方要注意自己的vue版本和i1n8的匹配程度&#xff0c;如果是vue2点几&#xff0c;记得安装i18n的8版本&#xff0c;不然会自动安装的最新版本&#xff0c;后面会报错哦&#xff0c;查询了下资料&#xff0c;好像最新版本是适配的vue3。 npm install vue-…

2023年天猫除湿器行业数据分析(天猫数据分析软件)

除湿器是小家电的一种&#xff0c;随着人们生活品质的提升及健康意识的增强&#xff0c;人们对于除湿产品的观念也在不断改变&#xff0c;除湿器这一小家电也走入了越来越多消费者的家中。特别是在南方地区&#xff0c;全年的空气湿度都处于较高的水平&#xff0c;尤其是回南天…

PHP最简单自定义自己的框架创建目录结构(二)

1、mvc目录结构 2、目录解释 KJ&#xff1a;项目名称 core&#xff1a;框架核心目录 KJ.php 框架运行入口 index: 框架模块 controller:模块控制器 model:模块模型数据库操作 view:页面显示html index.php:index模块框架入口 3、index.php框架入口文件引入框架 <?php r…

FPGA学习——Altera IP核调用之PLL篇

文章目录 一、IP核1.1 IP核简介1.2 FPGA中IP核的分类1.3 IP核的缺陷 二、PLL简介2.1 什么是PLL2.2 PLL结构图2.3 C4开发板上PLL的位置 三、IP核调用步骤四、编写测试代码五、总结 一、IP核 1.1 IP核简介 IP核&#xff08;知识产权核&#xff09;&#xff0c;是在集成电路的可…

ReSharper C++ 2023 Crack

ReSharper C 2023 Crack ReSharper的AI助手会考虑项目中使用的语言和技术。这种上下文感知可以一开始就调整其响应&#xff0c;为您节省时间和精力。 您可以在查询中包含部分源代码。ReSharper将检测你发送或粘贴到聊天中的代码&#xff0c;并正确格式化&#xff0c;而人工智能…

Redis 加入服务列表自启动

1、下载reids windows版本&#xff0c;选择zip格式下载 2、解压zip&#xff0c;并进入路径&#xff1b; 3、命令提示符&#xff08;cmd&#xff09; 进入解压后的路径后&#xff0c;输入指令&#xff1a;redis-server --service-install redis.windows.conf&#xff1b; 4、如…

Gumbel-Softmax简介

一、Gumbel Softmax trick的使用场景 1. argmax简介 在NLP领域的强化学习或者对抗学习中&#xff0c;token的生成是离散的。比如&#xff0c;一个token的产生是一个大小为vocab size的one-hot向量。比如&#xff0c;对于character level的token&#xff1a; [ 1 , 0 , 0 , 0 …

阻抗是什么?什么时候要考虑阻抗匹配?

在电路设计中&#xff0c;我们常常碰到跟阻抗有关的问题&#xff0c;那么到底什么是阻抗&#xff1f; 在具有电阻、电感和电容的电路里&#xff0c;对电路中电流所起的阻碍作用叫做阻抗。常用Z来表示&#xff0c;它的值由交流电的频率、电阻R、电感L、电容C相互作用来决定。由…

Mybatis异常Invalid bound statement (not found)原因之Mapper文件配置不匹配

模拟登录操作 $.post("/admin/login", {aname, pwd }, rt > {if (rt.code 200) {location.href "manager/index.html";return;}alert(rt.msg)});网页提示服务器代码错误 POST http://localhost:8888/admin/login 500后端显示无法找到Mapper中对应的…

ros tf

欢迎访问我的博客首页。 tf 1. tf 命令行工具1.1 发布 tf1.2 查看 tf 2.参考 1. tf 命令行工具 1.1 发布 tf 我们根据 cartographer_ros 的 launch 文件 backpack_2d.launch 写一个 tf.launch&#xff0c;并使用命令 roslaunch cartographer_ros tf.launch 启动。该 launch 文件…

wpf 项目中使用 Prism + MaterialDesign

1.通过nuget安装MaterialDesign 2.通过nuget安装Prism 3.修改App.xmal <prism:PrismApplication x:Class"VisionMeasureGlue.App"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/…

简单易懂的Transformer学习笔记

1. 整体概述 2. Encoder 2.1 Embedding 2.2 位置编码 2.2.1 为什么需要位置编码 2.2.2 位置编码公式 2.2.3 为什么位置编码可行 2.3 注意力机制 2.3.1 基本注意力机制 2.3.2 在Trm中是如何操作的 2.3.3 多头注意力机制 2.4 残差网络 2.5 Batch Normal & Layer Narmal 2.…

C++入门篇5---模板

相信大家都遇到过这么一种情况&#xff0c;为了满足不同类型的需求&#xff0c;我们要写多个功能相同&#xff0c;参数类型不同的代码&#xff0c;为此&#xff0c;C引入了泛型编程这一概念&#xff0c;而模板就是实现泛型编程的基础&#xff0c;其实本质就是我们写一个类似”模…

JVM、JRE、JDK三者之间的关系

JVM、JRE和JDK是与Java开发和运行相关的三个重要概念。 再了解三者之前让我们先来了解下java源文件的执行顺序&#xff1a; 使用编辑器或IDE(集成开发环境)编写Java源文件.即demo.java程序必须编译为字节码文件&#xff0c;javac(Java编译器)编译源文件为demo.class文件.类文…

JavaScript + GO 通过 AES + RSA 进行数据加解密

浏览器端搞些小儿科的加密&#xff0c;就好比在黑暗夜空中&#xff0c;点缀了几颗星星&#xff0c;告诉黑客「这里有宝贵信息&#xff0c;快来翻牌」 浏览器端的加密&#xff0c;都是相对安全的。 它的具体安危&#xff0c;取决于里面存在的信息价值&#xff0c;是否值得破解者…

GO学习之 网络通信(Net/Http)

GO系列 1、GO学习之Hello World 2、GO学习之入门语法 3、GO学习之切片操作 4、GO学习之 Map 操作 5、GO学习之 结构体 操作 6、GO学习之 通道(Channel) 7、GO学习之 多线程(goroutine) 8、GO学习之 函数(Function) 9、GO学习之 接口(Interface) 10、 文章目录 GO系列前言一、H…

CAPL - XML和TestModule结合实现测试项可选(续)

二、xml文件编写 1、设置xml文件版本号 这个方便我们对xml文件进行文件管理,对于后续工作有进一步帮助。 <?xml version="1.0" ?> 2、设置xml根元素 在CANoe中使用的xml文件根元素我统一都会设置为testmodule,这也是我们在CANoe软件中选择测试用例的最大…

微服务间消息传递

微服务间消息传递 微服务是一种软件开发架构&#xff0c;它将一个大型应用程序拆分为一系列小型、独立的服务。每个服务都可以独立开发、部署和扩展&#xff0c;并通过轻量级的通信机制进行交互。 应用开发 common模块中包含服务提供者和服务消费者共享的内容provider模块是…

七、ESP32 4位数码管显示数字

1. 运行后的效果 可以显示0~9999之间的任何数字 2. 4位数码管与ESP32链接方式 3. 代码</