当前位置: 首页 > news >正文

day25

一、进程间通信(IPC)


1.1    进程间通信的引入


1>    对于多个线程之间通信,我们可以使用临界资源来完成,通过一个线程任务对临界资源进行修改,另一个线程也可以使用已经修改过的临界资源,但是要注意使用同步互斥机制完成对临界资源的保护
2>    多个进程之间能否使用全局变量来进行进程间通信?
答:不能,多个进程之间的用户空间是相互独立的,每个进程的全局变量是各个进程独立拥有的,更改一个进程的全局变量,另一个进程不会受到影响,故而不能使用全局变量来完成通信
3>    有同学说:使用文件来完成进程间通信?
答:可行,但是要求写进程先完成向文件中写入数据,然后读进程才能读取数据,必须要加入进程的同步操作
4>    多个进程之间内核空间是共享的,我们可以通过将数据放入到内核空间,来完成两个进程之间信息的交流

5>    进程间通信方式:

1、内核提供的原始通信方式
        无名管道
        有名管道
        信号
2、system V提供三种
        消息队列
        共享内存
        信号量集
 


1.2    无名管道


1>    顾名思义就是没有名字的管道文件,所以只能适用于亲缘进程间的通信
2>    原理:通过内核,在内存中创建出一个管道文件,存放在内存空间,并不在文件系统中真实存在。当打开该文件时,会返回该文件的两个文件描述符,分别对应读端和写端。可以通过读端从管道中读取数据,从写端向管道中写入数据。当该文件的读写两端全部被关闭后,该管道文件会在内存中消失
3>    注意:由于打开管道时返回的是文件描述符,所以,对该文件的操作,只能使用文件IO
对于存入管道中的数据的读取是一次性的,当数据读取结束后,数据就不存在于管道文件中了
4>    无名管道的API接口函数

       #include <unistd.h>
 
       int pipe(int pipefd[2]);
       功能:在通过内核在内存中创建一个无名管道,并通过参数将该管道文件的两个文件描述符返回
       参数:接收文件描述符的数组,pipefd[0]表示管道文件的读端,pipefd[1]表示管道的写端
       返回值:成功返回0,失败返回-1并置位错误码
 

#include<myhead.h>
 
int main(int argc, const char *argv[])
{
 
    //创建用于通信的管道文件,并返回该文件对应的文件描述符
    int pipefd[2] ;
    if(pipe(pipefd)==-1)
    {
        perror("pipe error");
        return -1;
    }
 
 
    //创建父子进程
    pid_t pid = fork();
    if(pid < 0)
    {
        perror("fork error");
        return -1;
    }else if(pid  == 0)
    {
        //子进程
        close(pipefd[1]);          //关闭写端
    
        //接受父进程发来的消息
        char rbuf[128] = "";
 
        while(1)
        {
            bzero(rbuf, sizeof(rbuf));
 
            //从管道中读取数据
            read(pipefd[0], rbuf, sizeof(rbuf));
 
            if(strcmp(rbuf, "quit")==0)
            {
                break;
            }
 
            //输出数据
            printf("收到父进程的来信:%s\n", rbuf);
        }
 
        //关闭读段
        close(pipefd[0]);
 
 
        //退出进程
        exit(EXIT_SUCCESS);
    }
 
    //父进程
    char sbuf[128] = "";
     //关闭读端
    close(pipefd[0]);
    while(1)
    {        
 
        usleep(10);
        printf("父进程中输入>>>");
        fgets(sbuf, sizeof(sbuf), stdin);          //从终端获取字符串
        sbuf[strlen(sbuf)-1] = 0;
 
        //发送给子进程,将数据通过写端写入管道 pipefd[1]
        write(pipefd[1], sbuf, strlen(sbuf));
 
        //判断终端获取的数据
        if(strcmp(sbuf, "quit") == 0)
        {
            break;
        }
 
        //关闭写端
        close(pipefd[1]);
    }
 
    
 
    //回收子进程资源
    wait(NULL);
 
    return 0;
}
 
5>    总结管道通信的特点

1、管道通信是半双工的通信方式
    单工:任意时刻只能A向B发送消息,B不能向A发送消息
    半双工:同一时刻,只能A向B发送消息或者B向A发消息
    全双工:任意时刻,AB可以互相发送消息
2、无名管道只适用于亲缘进程间通信
3、无名管道也能完成自己跟自己的通信
4、无名管道文件的大小:64KB
6>    管道的读写特点(笔试面试)

1、当管道读端存在时,写管道有多少写多少,直到写满64K为止
2、当管道读端不存在时,写管道写入数据时,会出现管道破裂,此时内核空间会向用户空间发送一个SIGPIPE信号
3、当写端存在时,读管道有多少读多少,没有数据会在read处阻塞
4、当写端不存在时,读管道有多少读多少,没有数据也不会在read处阻塞
 

#include<myhead.h>
 
int main(int argc, const char *argv[])
{
    //创建一个五名管道
    int fds[2];
 
    if(pipe(fds) == -1)
    {
        perror("pipe error");
        return -1;
    }
 
    //向管道中写入数据
    char buf = 'A';
    int count = 0;       //记录写入的大小
    while(1)
    {
        write(fds[1], &buf, 1);
        count ++;
        printf("count = %d\n", count);
        //关闭读端
        close(fds[0]);
    }
 
 
    while(1);
 
    return 0;
}
 
 

#include<myhead.h>
 
int main(int argc, const char *argv[])
{
 
    //创建用于通信的管道文件,并返回该文件对应的文件描述符
    int pipefd[2] ;
    if(pipe(pipefd)==-1)
    {
        perror("pipe error");
        return -1;
    }
 
 
    //创建父子进程
    pid_t pid = fork();
    if(pid < 0)
    {
        perror("fork error");
        return -1;
    }else if(pid  == 0)
    {
        //子进程
        close(pipefd[1]);          //关闭写端
    
        //接受父进程发来的消息
        char rbuf[128] = "";
 
        while(1)
        {
            bzero(rbuf, sizeof(rbuf));
 
            //从管道中读取数据
            read(pipefd[0], rbuf, sizeof(rbuf));
 
            if(strcmp(rbuf, "quit")==0)
            {
                break;
            }
 
            //输出数据
            printf("收到父进程的来信:%s\n", rbuf);
        }
 
        //关闭读段
        close(pipefd[0]);
 
 
        //退出进程
        exit(EXIT_SUCCESS);
    }
 
    //父进程
    char sbuf[128] = "";
    //关闭读端
    close(pipefd[0]);
    while(1)
    {
 
 
        usleep(10);
        printf("父进程中输入>>>");
        fgets(sbuf, sizeof(sbuf), stdin);          //从终端获取字符串
        sbuf[strlen(sbuf)-1] = 0;
 
        //发送给子进程,将数据通过写端写入管道 pipefd[1]
        write(pipefd[1], sbuf, strlen(sbuf));
 
        //判断终端获取的数据
        if(strcmp(sbuf, "quit") == 0)
        {
            break;
        }
 
        //关闭写端
        close(pipefd[1]);
 
 
    }
 
    //关闭写端
    close(pipefd[1]);
 
    //回收子进程资源
    wait(NULL);
 
    return 0;
}
 
 


1.3    有名管道


1>    顾名思义就是有名字的管道文件,会在文件系统中创建一个有名字的管道文件
2>    可以用于亲缘进程间的通信,也可以用于非亲缘进程间的通信
3>    有名管道的API函数

       #include <sys/types.h>
       #include <sys/stat.h>
 
       int mkfifo(const char *pathname, mode_t mode);
    功能:在文件系统中创建一个有名管道,但是并没有打开该文件
    参数1:有名管道的名称
    参数2:管道文件的权限
    返回值:成功返回0,失败返回-1并置位错误码
create.c

#include<myhead.h>
 
int main(int argc, const char *argv[])
{
    //创建一个有名管道文件
    if(mkfifo("./linux", 0664)==-1)
    {
        perror("mkfifo error");
        return -1;
    }
 
    getchar();
 
    system("rm linux");
    
    return 0;
}
 
snd.c

#include<myhead.h>
 
int main(int argc, const char *argv[])
{
    //以写的形式打开管道文件
    int wfd = open("./linux", O_WRONLY);
    if(wfd == -1)
    {
        perror("open error");
        return -1;
    }
    printf("管道文件已经打开\n");
 
    //发送数据
    char wbuf[128] = "";
    while(1)
    {
        printf("请输入>>>>");
        fgets(wbuf, sizeof(wbuf), stdin);
        wbuf[strlen(wbuf)-1] = 0;
 
        //将数据发送给到管道中
        write(wfd, wbuf, strlen(wbuf));
 
        //判断数据
        if(strcmp(wbuf, "quit") == 0)
        {
            break;
        }
    }
 
    //关闭文件描述符
    close(wfd);
 
    
    return 0;
}
 
recv.c

#include<myhead.h>
 
int main(int argc, const char *argv[])
{
    //以读的形式打开文件
    int rfd = open("./linux", O_RDONLY);
    if(rfd == -1)
    {
        perror("open error");
        return -1;
    }
    printf("管道文件读端打开\n");
 
    //定义接受容器
    char rbuf[128] = "";
    while(1)
    {
        bzero(rbuf, sizeof(rbuf));
        //读取数据
        read(rfd, rbuf, sizeof(rbuf));
        if(strcmp(rbuf, "quit") == 0)
        {
            break;
        }
        printf("收到消息为:%s\n", rbuf);
    }
 
    //关闭文件描述符
    close(rfd);
    
    return 0;
}
 
 


1.4    信号通信


1>    信号通信原理图:两个异步通信的进程之间,通过发送相关信号,完成任务间的通信

2>    信号是linux中软件模拟硬件的“中断”的一种方式。信号是软件部分的名词,中断是硬件部分的名词
3>    能够发送的信号可以通过指令:kill -l进行查看

 1) SIGHUP     2) SIGINT     3) SIGQUIT     4) SIGILL     5) SIGTRAP
 6) SIGABRT     7) SIGBUS     8) SIGFPE     9) SIGKILL    10) SIGUSR1
11) SIGSEGV    12) SIGUSR2    13) SIGPIPE    14) SIGALRM    15) SIGTERM
16) SIGSTKFLT    17) SIGCHLD    18) SIGCONT    19) SIGSTOP    20) SIGTSTP
21) SIGTTIN    22) SIGTTOU    23) SIGURG    24) SIGXCPU    25) SIGXFSZ
26) SIGVTALRM    27) SIGPROF    28) SIGWINCH    29) SIGIO    30) SIGPWR
31) SIGSYS    34) SIGRTMIN    35) SIGRTMIN+1    36) SIGRTMIN+2    37) SIGRTMIN+3
38) SIGRTMIN+4    39) SIGRTMIN+5    40) SIGRTMIN+6    41) SIGRTMIN+7    42) SIGRTMIN+8
43) SIGRTMIN+9    44) SIGRTMIN+10    45) SIGRTMIN+11    46) SIGRTMIN+12    47) SIGRTMIN+13
48) SIGRTMIN+14    49) SIGRTMIN+15    50) SIGRTMAX-14    51) SIGRTMAX-13    52) SIGRTMAX-12
53) SIGRTMAX-11    54) SIGRTMAX-10    55) SIGRTMAX-9    56) SIGRTMAX-8    57) SIGRTMAX-7
58) SIGRTMAX-6    59) SIGRTMAX-5    60) SIGRTMAX-4    61) SIGRTMAX-3    62) SIGRTMAX-2
63) SIGRTMAX-1    64) SIGRTMAX    
4>    对于信号的处理方式有三种:默认、捕获、忽略
有两个信号既不能被捕获,也不能被忽略:SIGKILL、SIGSTOP
5>    上面的信号触发条件以及默认处理方式可以通过指令 man 7 signal进行查看

       Signal     Value     Action   Comment
       ──────────────────────────────────────────────────────────────────────
       SIGHUP        1       Term    Hangup detected on controlling terminal
                                     or death of controlling process
       SIGINT        2       Term    Interrupt from keyboard
       SIGQUIT       3       Core    Quit from keyboard
       SIGILL        4       Core    Illegal Instruction
       SIGABRT       6       Core    Abort signal from abort(3)
       SIGFPE        8       Core    Floating-point exception
       SIGKILL       9       Term    Kill signal
       SIGSEGV      11       Core    Invalid memory reference
       SIGPIPE      13       Term    Broken pipe: write to pipe with no
                                     readers; see pipe(7)
       SIGALRM      14       Term    Timer signal from alarm(2)
       SIGTERM      15       Term    Termination signal
       SIGUSR1   30,10,16    Term    User-defined signal 1
       SIGUSR2   31,12,17    Term    User-defined signal 2
       SIGCHLD   20,17,18    Ign     Child stopped or terminated
       SIGCONT   19,18,25    Cont    Continue if stopped
       SIGSTOP   17,19,23    Stop    Stop process
       SIGTSTP   18,20,24    Stop    Stop typed at terminal
       SIGTTIN   21,21,26    Stop    Terminal input for background process
       SIGTTOU   22,22,27    Stop    Terminal output for background process
 
6>    信号的发送者可以是内核、其他进程、用户自己
7>    将信号与信号处理方式连接函数

       #include <signal.h>
 
       typedef void (*sighandler_t)(int);
 
       sighandler_t signal(int signum, sighandler_t handler);
       功能:将信号与信号处理函数绑定到一起
       参数1:要绑定的信号
       参数2:信号处理函数
               SIG_IGN:表示忽略信号
               SIG_DFL:表示默认处理
               填自定义函数的入口地址
        返回值:成功返回处理方式的起始地址,失败返回 SIG_ERR
 
 

#include<myhead.h>
//自定义处理信号的函数
void handler(int signo)
{
    //判断传过来的信号是哪个
    if(signo == SIGINT)
    {
        printf("当前进程收到了ctrl + c,但是就是关不掉\n");
    }
 
    //判断是否为SIGKILL到位
    if(signo == SIGKILL)
    {
        printf("捕获了SIGKILL\n");
    }
    
}
 
 
/**************************主程序*******************/
int main(int argc, const char *argv[])
{
 
    //将2号信号与对应的信号处理函数进行绑定
    /*尝试忽略2号信号
    if(signal(2, SIG_IGN) == SIG_ERR)
    {
        perror("signal error");
        return -1;
    }
    */
 
    //尝试捕获2号信号
    if(signal(2, handler) == SIG_ERR)
    {
        perror("signal error");
        return -1;
    }
 
    /*尝试忽略SIGKILL信号:报错,参数不合法
        if(signal(SIGKILL, handler) == SIG_ERR)
    {
        perror("signal error");
        return -1;
    }*/
 
    /*尝试捕获SIGKILL信号:报错,参数不合法
        if(signal(SIGKILL, handler) == SIG_ERR)
    {
        perror("signal error");
        return -1;
    }
    */
 
    /*尝试默认处理:报错,参数不合法
    if(signal(SIGKILL, SIG_DFL) == SIG_ERR)
    {
        perror("signal error");
        return -1;
    }*/
 
 
    while(1)
    {
        printf("啦啦啦啦,我是卖报的小行家\n");
        sleep(1);
    }
    
 
    return 0;
}
 
 


 
1.5    特殊的信号处理


1>    SIGCHLD信号:以非阻塞的形式回收僵尸进程

#include<myhead.h>
 
//自定义信号处理函数
void handler(int signo)
{
    if(signo == SIGCHLD)
    {
        while(1)
        {
            //判断是否将僵尸进程全部回收了
            if(waitpid(-1, NULL, WNOHANG) == 0)
            {
                break;
            }
        }
    }
}
 
/******************主程序**********************/
int main(int argc, const char *argv[])
{
    //将SIGCHLD信号与信号处理函数绑定到一起
    if(signal(SIGCHLD, handler) == SIG_ERR)
    {
        perror("signal error");
        return -1;
    }
 
 
    for(int i=0; i<10; i++)
    {
        if(fork() == 0)
        {
            exit(EXIT_SUCCESS);
        }
    
    }
 
    while(1);
 
    /*
    for(;;)
    {
        printf("我是小妖怪,逍遥游自在\n");
        sleep(1);
    }*/
 
    return 0;
}
 
2>    SIGALRM:定时器信号
程序允许启动一个定时器,当所定的时间到位后,会发送一个SIGALRM信号,我们可以将该信号绑定到对应的信号处理函数中,从而导致,给定时间后,处理自定义函数。该信号需要使用一个函数来发送超时信号:alarm闹钟函数


       #include <unistd.h>
 
       unsigned alarm(unsigned seconds);
    功能:给进程设置一个定时器,以秒为单位,当定时器到位后,后向该进程发送一个SIGALRM的信号
    参数:秒数,如果参数设置成0,表示删除定时器
    返回值:>0:表示返回的上一个定时器剩余的秒数,并且重置上一个定时器
            0:表示之前没有设置定时器
 

#include<myhead.h>
 
//定义信号处理函数
void handler(int signo)
{
    if(signo == SIGALRM)
    {
        printf("阎王叫你三更死,怎可留人到五更\n");
        sleep(2);
        exit(EXIT_SUCCESS);
    }
}
 
 
/********************主程序******************/
int main(int argc, const char *argv[])
{
 
    //将SIGALRM信号与信号处理函数绑定
    if(signal(SIGALRM, handler) == SIG_ERR)
    {
        perror("signal error");
        return -1;
    }
 
 
    printf("%d\n", alarm(10));          //0
 
    sleep(5);
 
 
    printf("%d\n", alarm(10));          //5
 
 
    int count = 0;
    while(1)
    {
        printf("能活一秒是一秒\n");
        sleep(1);
        /*
        count++;
        if(count == 3)
        {
            alarm(0);           //删除定时器
        }
        */
    }
 
    return 0;
}
 
使用SIGALRM模拟斗地主出牌

#include<myhead.h>
 
//定义信号处理函数
void handler(int signo)
{
    if(signo == SIGALRM)
    {
        printf("系统已经自动帮您出了一张牌\n");
        alarm(5);
    }
}
 
 
/*******************主程序**********************/
int main(int argc, const char *argv[])
{
 
    //将信号与信号处理函数绑定
    if(signal(SIGALRM, handler) == SIG_ERR)
    {
        perror("signal error");
        return -1;
    }
 
 
    char ch;        //要出的牌
 
    while(1)
    {
        //启动一个定时器
        alarm(5);
 
        printf("请出牌>>");
        scanf("%c", &ch);
        getchar();
 
        printf("您出的牌为:%c\n", ch);
    }
 
    return 0;
}
 
 
 
 
 
 
 
 
 
 
 
 



1>    使用有名管道实现,一个进程用于给另一个进程发消息,另一个进程收到消息后,展示到终端上,并且将消息保存到文件上一份

xs.c

#include<myhead.h>
 
int main(int argc, const char *argv[])
{
    //创建一个有名管道文件
    if(mkfifo("./linux", 0664)==-1)
    {
        perror("mkfifo error");
        return -1;
    }
 
    getchar();
     //删除管道文件
    system("rm linux");
    return 0;
}
 cb.c

#include<myhead.h>
 
int main(int argc, const char *argv[])
{
    //以写的形式打开管道文件
    int wfd = open("./linux", O_WRONLY);
    if(wfd == -1)
    {
        perror("open error");
        return -1;
    }
    printf("管道文件已经打开\n");
 
    //发送数据
    char wbuf[128] = "";
    while(1)
    {
        printf("请输入>>>>");
        fgets(wbuf, sizeof(wbuf), stdin);
        wbuf[strlen(wbuf)-1] = '\0';
 
        //将数据发送给到管道中
        write(wfd, wbuf, strlen(wbuf));
 
        //判断数据
        if(strcmp(wbuf, "quit") == 0)
        {
            break;
        }
    }
 
    //关闭文件描述符
    close(wfd);
 
    
    return 0;
}

cd.c

#include<myhead.h>
 
int main(int argc, const char *argv[])
{
    //以读的形式打开文件
    int rfd = open("./linux", O_RDONLY);
    if(rfd == -1)
    {
        perror("open error");
        return -1;
    }
    printf("管道文件读端打开\n");
     int tid = open("./1.txt", O_RDWR);
    if(rfd == -1)
    {
        perror("open error");
        return -1;
    }
    //定义接受容器
    char rbuf[128] = "";
    while(1)
    {
        bzero(rbuf, sizeof(rbuf));
        //读取数据
        read(rfd, rbuf, sizeof(rbuf));
        if(strcmp(rbuf, "quit") == 0)
        {
            break;
        }
        printf("收到消息为:%s\n", rbuf);
        write(tid,rbuf,sizeof(rbuf));
    }
 
    //关闭文件描述符
    close(rfd);
    close(tid);
    return 0;
}
 

2>    使用有名管道实现两个进程间相互通信

day1.c

#include <myhead.h>

int main(int argc, const char *argv[])
{
    //创建一个有名管道文件
    if (mkfifo("./linux", 0664) == -1)
    {
        perror("mkfifo error");
        return -1;
    }
        //创建一个有名管道文件
    if (mkfifo("./text", 0664) == -1)
    {
        perror("mkfifo error");
        return -1;
    }

    getchar();
    //删除管道文件
    system("rm linux");
    system("rm text");
    return 0;
}

dday1.c

#include <myhead.h>
int main(int argc, const char *argv[])
{
    //以写的形式打开管道文件
    int wfd = open("./linux", O_WRONLY);
    if (wfd == -1)
    {
        perror("open error");
        return -1;
    }
    printf("管道文件已经打开\n");
    int rid = open("./text", O_RDONLY);
    if (rid == -1)
    {
        perror("open error");
        return -1;
    }
    printf("管道文件已经打开\n");
    int rp = -1;
    if ((rp = fork()) == -1)
    {
        perror("fork error\n");
    }
    if (rp > 0)
    {
        //发送数据
        char wbuf[128] = "";
        while (1)
        {
            printf("请输入>>>>\n");
            fgets(wbuf, sizeof(wbuf), stdin);
            wbuf[strlen(wbuf) - 1] = 0;

            //将数据发送给到管道中
            write(wfd, wbuf, strlen(wbuf));

            //判断数据
            if (strcmp(wbuf, "quit") == 0)
            {
                break;
            }
            //关闭文件描述符
        }
        close(wfd);
   
    }
    else
    {
        char sv[128] = "";
        while (1)
        {
            bzero(sv, sizeof(sv));
            read(rid, sv, sizeof(sv));
            if (strcmp(sv, "quit") == 0)
            {
                break;
            }
            printf("接收到的数据为%s\n", sv);
        }

   
        close(rid);
 

         exit(0);
    }

    wait(NULL);

    return 0;
}
day2.c

#include <myhead.h>
int main(int argc, const char *argv[])
{
    //以写的形式打开管道文件
    int wfd = open("./linux", O_RDONLY);
    if (wfd == -1)
    {
        perror("open error");
        return -1;
    }
    printf("管道文件已经打开\n");
    int rid = open("./text", O_WRONLY);
    if (rid == -1)
    {
        perror("open error");
        return -1;
    }
    printf("管道文件已经打开\n");
    int rp = -1;
    if ((rp = fork()) == -1)
    {
        perror("fork error\n");
    }
    if (rp > 0)
    {
        char sv[128] = "";
        while (1)
        {
            bzero(sv, sizeof(sv));
            read(wfd, sv, sizeof(sv));
            if (strcmp(sv, "quit") == 0)
            {
                break;
            }
            printf("接受到的数据为%s\n", sv);
        }
  
        close(wfd);
    }
    else
    {
        //发送数据
        char wbuf[128] = "";
        while (1)
        {
            printf("请输入>>>>\n");
            fgets(wbuf, sizeof(wbuf), stdin);
            wbuf[strlen(wbuf) - 1] = 0;

            //将数据发送给到管道中
            write(rid, wbuf, strlen(wbuf));

            //判断数据
            if (strcmp(wbuf, "quit") == 0)
            {
                break;
            }
            //关闭文件描述符
        }

   
    
        close(rid);

        exit(0);
    }

    wait(NULL);

    return 0;
}
 

相关文章:

day25

一、进程间通信&#xff08;IPC&#xff09; 1.1 进程间通信的引入 1> 对于多个线程之间通信&#xff0c;我们可以使用临界资源来完成&#xff0c;通过一个线程任务对临界资源进行修改&#xff0c;另一个线程也可以使用已经修改过的临界资源&#xff0c;但是要注意使用…...

红黑树的概念和模拟实现[C++]

文章目录 红黑树的概念一、红黑树的性质红黑树原理二、红黑树的优势和比较 红黑树的模拟实现构建红黑树的数据结构定义节点的基本结构和初始化方式插入新节点插入新节点的颜色调整颜色和结构以满足红黑树性质 红黑树的应用场景 红黑树的概念 一、红黑树的性质 红黑树是一种自平…...

网络安全应急响应概述

前言 在网络安全领域&#xff0c;有一句广为人知的话&#xff1a;“没有绝对的安全”。这意味着任何系统都有可能被攻破。安全攻击的发生并不可怕&#xff0c;可怕的是从头到尾都毫无察觉。当系统遭遇攻击时&#xff0c;企业的安全人员需要立即进行应急响应&#xff0c;以将影响…...

【C++】链表操作技巧综合:重排链表(带你理顺链表的做题思路)

1.题目 2.算法思路 这是一道关于链表的综合题&#xff0c;一共涉及到三个步骤&#xff0c;其中每个步骤单拎出来就可以当一道单独的题目。所以需要大家对链表的操作十分熟悉&#xff0c;否则可能需要大量的时间做这道题目&#xff0c;而且还要很多的bug。 第一个步骤&#xf…...

行为型设计模式2:观察者/职责链/中介者/访问者

设计模式&#xff1a;观察者/职责链/中介者/访问者 (qq.com)...

叛逆,批判

1、对以往说法的批判之一&#xff08;第一次这么公开批判是2004-2005年&#xff09;&#xff1a; 这部英文版的《数学百科全书》似乎是从俄语版翻译过来的&#xff1f;我查了三本引用的图书文献&#xff0c;都没有关于“nonsingular”和“singular”的类似下面的说法&#xff…...

Linux 命令,mkdir说明与使用

1&#xff1a;mkdir命令功用&#xff1a; 用于创建一个或多个目录&#xff0c;创建目录&#xff0c;必须在父目录中写上权限。 新目录的默认模式为0777&#xff0c;可以由系统或用的umask来修改。 2&#xff1a;命令构件: mkdir [options] directories 3:参数选项: -m&#x…...

24. 两两交换链表中的节点(Java)

目录 题目描述&#xff1a;示例 &#xff1a;代码实现&#xff1a; 题目描述&#xff1a; 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0c;只能进行节点交换&am…...

linux虚拟机设置固定ip

修改/etc/sysconfig/network-scripts目录下ifcfg-eth0文件&#xff0c;各虚拟机这个文件名不一致&#xff0c;ifcfg-XX格式 vim /etc/sysconfig/network-scripts/ifcfg-eth0BOOTPROTO设置为static&#xff0c;然后在最后添加固定IP地址和默认网关、DNS等配置&#xff0c;IP地址…...

mysql问题解决

1.etl数据同步时&#xff0c;发现连接不上要同步的数据库 解决方法&#xff1a;关闭mysql的ssl&#xff0c;步骤如下&#xff1a; 在MySQL中禁用SSL连接涉及修改服务器的配置文件&#xff08;通常是my.cnf或my.ini&#xff0c;取决于你的操作系统和MySQL版本&#xff09;。以…...

类和对象(下)C++

1.初始化列表 1.为什么有初始化列表&#xff0c;它的作用&#xff1f; ->初始化列表&#xff0c;是构造函数初始化的另一种形式。 ->在语法上面理解&#xff0c;初始化列表可以认定为是每个成员变量定义初始化的地方. ->引用成员变量&#xff0c;const成员变量&am…...

常用在线 Webshell 查杀工具推荐

一、简介 这篇文章将介绍几款常用的在线 Webshell 查杀工具&#xff0c;包括长亭牧云、微步在线云沙箱、河马和VirusTotal。每个工具都有其独特的特点和优势&#xff0c;用于帮助用户有效检测和清除各类恶意 Webshell&#xff0c;保障网站和服务器的安全。文章将深入探讨它们的…...

RPC远程调用框架Dubbo

一、分布式服务调用_什么是RPC RPC(Remote Procedure Call)远程过程调用&#xff0c;它是一种通过网络从远程计算机程序上请求服务。 大白话理解就是&#xff1a;RPC让你用别人家的东西就像自己家的一样。 RPC两个作用&#xff1a; 屏蔽远程调用跟本地调用的区别&#xff0c…...

基于STM32的智能灌溉系统

目录 引言环境准备工作 硬件准备软件安装与配置系统设计 系统架构硬件连接代码实现 初始化代码传感器读取和控制代码应用场景 农业灌溉花园自动灌溉常见问题及解决方案 常见问题解决方案结论 1. 引言 智能灌溉系统通过实时监测土壤湿度和环境温度&#xff0c;自动控制灌溉设…...

Datawhale AI 夏令营 Task3(半成品,仍在学习理解

课程链接 / 知识点整理 &#xff08;一&#xff09;...

细腻呵护静音生活缓冲器,家具中的隐形侍者

在忙碌的生活节奏中&#xff0c;家是我们寻找宁静与放松的避风港。而家具缓冲器&#xff0c;就像一位隐形的侍者&#xff0c;在不经意间为我们营造出温馨、宁静的居住环境。它们静静地工作&#xff0c;细腻地呵护着每一处细节&#xff0c;让家的每一次触碰成为一次尊享体验。 细…...

【MATLAB源码-第243期】基于simulink的CUK斩波电路仿真,输出各节点波形。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 CUK电路是一种高效的直流-直流转换器&#xff0c;它以其独特的能量传递方式和高效的电压转换能力&#xff0c;在许多电力电子应用中得到了广泛的使用。下面将详细描述CUK电路的工作原理、各个组成部分以及其在实际应用中的优…...

springboot项目不能同时跑junit4和junit5的解决方法

springboot项目的maven test只会跑junit4 RunWith注解的测试类&#xff0c;而不会跑junit5 ExtendWith的测试类 解决方法&#xff1a;pom加上以下plugin&#xff0c;版本号需要3.0.0-M5及以上 <plugin><groupId>org.apache.maven.plugins</groupId><art…...

【IO】使用消息队列完成两个进程之间相互通信

目录 1、使用消息队列完成两个进程之间相互通信 2、共享内存实现两个进程之间的通信 3、思维导图 1、使用消息队列完成两个进程之间相互通信 //msgsnd.c #include <myhead.h>// 要发送的消息类型 struct msgbuf {long mtype;char mtext[1024]; };// 定义一个宏&#…...

Web开发:用C#的逻辑理解VUE语法(VUE + Webapi小白开发笔记)

适用阅读对象&#xff1a;需要兼顾前端的C#后端开发人员&#xff08;基础笔记&#xff09; 目录 一、后端交互-获取实体数据 二、变量 1.声明 2.作用域 三、字符串的处理 四、数组(列表)的处理 1.数组中的SELECT语法&#xff08;提取特定字段到新数组&#xff09; 2.数…...

7.4.分块查找

一.分块查找的算法思想&#xff1a; 1.实例&#xff1a; 以上述图片的顺序表为例&#xff0c; 该顺序表的数据元素从整体来看是乱序的&#xff0c;但如果把这些数据元素分成一块一块的小区间&#xff0c; 第一个区间[0,1]索引上的数据元素都是小于等于10的&#xff0c; 第二…...

在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:

在 HarmonyOS 应用开发中&#xff0c;手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力&#xff0c;既支持点击、长按、拖拽等基础单一手势的精细控制&#xff0c;也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档&#xff0c…...

前端倒计时误差!

提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...

循环冗余码校验CRC码 算法步骤+详细实例计算

通信过程&#xff1a;&#xff08;白话解释&#xff09; 我们将原始待发送的消息称为 M M M&#xff0c;依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)&#xff08;意思就是 G &#xff08; x ) G&#xff08;x) G&#xff08;x) 是已知的&#xff09;&#xff0…...

STM32+rt-thread判断是否联网

一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...

多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验

一、多模态商品数据接口的技术架构 &#xff08;一&#xff09;多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如&#xff0c;当用户上传一张“蓝色连衣裙”的图片时&#xff0c;接口可自动提取图像中的颜色&#xff08;RGB值&…...

[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...

优选算法第十二讲:队列 + 宽搜 优先级队列

优选算法第十二讲&#xff1a;队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...

【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)

本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...

关于uniapp展示PDF的解决方案

在 UniApp 的 H5 环境中使用 pdf-vue3 组件可以实现完整的 PDF 预览功能。以下是详细实现步骤和注意事项&#xff1a; 一、安装依赖 安装 pdf-vue3 和 PDF.js 核心库&#xff1a; npm install pdf-vue3 pdfjs-dist二、基本使用示例 <template><view class"con…...