『 Linux 』 进程间通信 - 匿名管道 (万字)

文章目录

    • 什么是管道
    • 匿名管道的直接原理
    • pipe( )系统调用接口
    • 匿名管道代码示例
    • 匿名管道的特征
    • 总结


什么是管道

请添加图片描述

管道(Pipe) 是一种基本的进程间通信(IPC)机制,允许一个进程与另一个进程之间进行数据传输;

管道工作方式类似于生活中的水管因此命名为管道,数据从一端流入另一段流出,数据流为单向;

Linux中可以使用who | wc -l查看当前登入系统的用户数;

who命令用与显示当前登入系统的用户信息,其中一条会话代表一个用户;

wc -l命令统计当前行数;

两条命令通过管道符|进行连接,即将显示的信息通过管道符传输给wc命令再进行统计行数;

|符号即为一种管道;

管道存在两种基本类型:

  • 匿名管道(Anonymous Pipes)

    不存在命名的管道,用于有亲缘关系的进程之间的通信(如父子进程或兄弟进程等);

    匿名管道通常用于单个系统内部的进程通信;

  • 命名管道(Named Pipes)

    也称为FIFO(First In First Out),拥有命名并存在于文件系统中;

    命名管道允许没有亲缘关系的进程之间进行通信;


匿名管道的直接原理

请添加图片描述

每个进程需要维护其task_struct结构体,对应的内核数据结构中存在一个struct file_struct*指针指向一个file_struct结构体,这个结构体中存在一个struct file* fd_array[]指针数组,数组的下标为文件描述符;

对应的struct file结构体存放打开的文件的基本信息;

这些信息包括但不限于:

  • Inode

    文件的Inode编号;

  • file_operators

    提供给上层的读写接口方法集;

  • 缓冲区

    对于普通文件而言这个缓冲区通常为 页缓冲区 ;

    缓冲区与文件系统配合实现数据的写入与读取;

匿名管道是一种区别于普通文件的内存级文件;

不存在于磁盘中不基于文件系统,操作系统不会为匿名管道文件分配Inode与对应的数据块;

对应的读写方法file_operators是针对于缓冲区的读写;

当进程创建子进程时子进程为父进程的一个拷贝;

操作系统会为子进程单独维护一个task_struct结构体以及其对应的内容包括file_struct结构体与文件描述符表;

文件系统与进程管理之间为并列关系,文件不会因为创建子进程而单独为其拷贝一份新文件;

子进程文件描述符对应的结构体指针所指向的文件与父进程相同;

进程间通信的本质是 “让不同的进程看到同一份资源” ;

在创建子进程时即可实现两个不同的进程看到同一份"资源",即管道文件的缓冲区;

在打开文件时通常会记录打开文件的方式 (读/写),在创建子进程时打开方式也会连同一起拷贝,这意味着单独以读或是写的方式打开文件不能使得两个不同进程进行通信;

在进行匿名管道通信时进程将占用两个文件描述符分别以读和写的方式打开管道文件,在创建子进程后根据需求关闭另一个文件描述符从而实现单向通信;

系统并不会在使用管道时为用户关闭某个文件描述符,该操作由用户自行决定;

为确保管道的正常使用与进程间通信,用户需要手动关闭不需要的文件描述符;

单个管道只能进行单向通信,若是使单个管道进行双向通信可能会因为读写位置不同或数据覆盖,数据碎片等问题造成通信错误;

需要利用管道进行双向通信时可采用两个管道的方式,其中每个管道负责一个方向的通信;


pipe( )系统调用接口

请添加图片描述

使用 open()等系统调用接口创建的文件是一种磁盘级文件,在文件系统中存在自身的文件名,Inode与数据块且将被文件系统管理;

管道是一种内存级文件,不存在对应的文件名与Inode,数据块分配,内存级文件不被文件系统所管理,不可使用open()等接口函数创建;

匿名管道文件的创建需要通过系统调用接口pipe()进行创建;

PIPE(2)                               Linux Programmer's Manual                               PIPE(2)NAMEpipe, pipe2 - create pipeSYNOPSIS#include <unistd.h>int pipe(int pipefd[2]);#define _GNU_SOURCE             /* See feature_test_macros(7) */#include <fcntl.h>              /* Obtain O_* constant definitions */#include <unistd.h>RETURN VALUEOn success, zero is returned.  On error, -1 is returned, and errno is set appropriately.
  • 头文件

    pipe()系统调用接口存在于<unistd.h>头文件中;

  • 参数

    int pipefd[2]参数代表使用该系统调用接口时需要传递一个数组,数组中只需要包含两个int类型的元素;

    该参数是一种输出型参数;

    • 传入型参数

      这类参数用于向函数提供需要的数据或信息,函数通过这些参数读取传入的值,但不会修改它们;

    • 输出型参数

      输出参数则用于从函数内部向外部返回额外的数据,函数通过修改这些参数的值来传递数据给调用者;

    pipefd[0]pipefd[1]分别代表创建的管道文件对应的文件描述符,其中 0号下标对应的int类型数据代表读,1号下标代表写;

  • 返回值

    函数调用成功时返回0;

    函数调用失败时返回-1并设置errno;

匿名管道的使用方式一般为:

  • 父进程调用pipe()系统调用接口创建管道文件

  • 父进程调用fork()系统调用接口创建子进程(或间接创建具有亲缘关系的进程)

  • 判断数据流方向( 父流向子/子流向父 )

  • 根据需求(数据流向)关闭父子进程的另一个文件描述符

  • 进行通信

    通信一般采用write()系统调用接口与read()系统调用接口;

  • 关闭剩余文件描述符

    在通信过后可以根据条件关闭通信过的文件描述符,也可不关闭(最终管道文件将会自动被操作系统回收);


匿名管道代码示例

请添加图片描述

  • 头文件与宏定义

    #include <sys/types.h>
    #include <sys/wait.h>
    #include <unistd.h>#include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <string>#define N 2#define NUM 1024
    using namespace std;
    
    • Npipe()系统调用接口参数的数组大小
    • NUM为用户层缓冲区的大小
  • main函数

    int main() {int pipefd[N] = {0};int n = pipe(pipefd);if (n < 0) {cerr << "pipe error" << endl;exit(-1);}pid_t id = fork();if (id < 0) {cerr << "fork error" << endl;exit(-1);}/*child -> parentpipefd[0] -- 'r'pipefd[1] -- 'w'*/if (id == 0) {// child - `w`close(pipefd[0]);// IPC codePIC::Write(pipefd[1]);exit(0);}// parent - `r`close(pipefd[1]);// IPC codePIC::Read(pipefd[0]);pid_t rid = waitpid(id, nullptr, 0);if (rid < 0) return 3;return 0;
    }
    

    该示例为子进程向父进程进行单向通信;

    设置int类型数组pipefd[N]并调用pipe()系统调用接口创建匿名管道文件;

    利用fork()系统调用接口创建子进程并根据需求调用close()系统调用接口关闭父子进程中不需要的文件描述符;

    父进程调用waitpid()系统调用接口进行进程等待;

    Read()Write()接口用来完成具体的通信过程;

    子进程调用Write(),父进程调用Read()完成通信;

  • Read()Write()实现

    namespace PIC {void Write(int wfd) {char buff[NUM];pid_t self = getpid();string s = "I am a child";int number = 0;while (true) {buff[0] = 0;  // 字符串清空 (为读者展示该数组将被视为一个字符串)snprintf(buff, sizeof(buff), "%s-%d-%d", s.c_str(), self, number++);// cout << buff << endl;// 向父进程进行通信write(wfd, buff, strlen(buff));  // 进行写入时数据不作为一个字符串sleep(1);}
    }
    void Read(int rfd) {char buff[NUM];while (true) {buff[0] = 0;  // 字符串清空 (为读者展示该数组将被视为一个字符串)ssize_t n = read(rfd, buff, sizeof(buff));if (n > 0) {buff[n] = 0;  // 需要打印时将需要称为一个字符串 需要添加'\0'cout << "parent process get a massage [" << getpid() << "]# " << buff<< endl;}//忽略read()调用失败}
    }}  // namespace PIC
    

    创建命名空间PIC避免出现命名冲突;

    • Write()

      创建用户层缓冲区buff[NUM]用于存储子进程需要向父进程写入的内容;

      调用snprintf()C标准接口用于将字符串格式化后写入用户层缓冲区buff[](snprintf()具体调用查看手册);

      调用write()系统调用接口将用户层缓冲区内容buff[]写入至内核缓冲区(匿名管道文件的缓冲区,写入过程中文件数据不当做字符串看待);

      调用sleep()使每向内核缓冲区写入一条数据后休眠1s;

    • Read()

      创建用户层缓冲区buff[NUM]用于存储接收的由子进程写给父进程的内容;

      调用read()系统调用接口从内核缓冲区(匿名管道文件的缓冲区)读取内容并写入至用户层缓冲区buff[]中并用n接收返回值;

      子进程在向父进程写入时将数据以字符串形式看待,此处需要打印需要将数据以字符串看待需要buff[n]处添加字符串结束符\0;

      利用std::cout打印接收的内容;

缓冲区为内核的空间,用户层必须通过系统调用接口才能间接对内核缓冲区进行操作;


匿名管道的特征

请添加图片描述

  • 匿名管道只能为具有血缘关系的进程间进行通信

    不具有血缘关系的进程无法利用匿名管道进行通信;

  • 管道只能单向通信

    单个管道进行双向通信将会因为读写位置不同或数据覆盖,数据碎片等问题造成通信错误;

  • 父子进程通信时会进行协同

    在上段代码中子进程向父进程写入数据时利用sleep()每隔1s进行一次写入;

    父进程在读取子进程数据时不进行休眠;

    运行上段代码并在另一个会话窗口利用脚本观察父子进程情况;

    $ while :; 
    do ps axj | head -1 && ps axj | grep mytest | grep -v grep ; 
    echo"#####################" ; 
    sleep 1 ;
    done
    

    脚本结果如下:

    #####################PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
    29927 30127 30127 29927 pts/2    30127 S+    1002   0:00 ./mytest
    30127 30128 30127 29927 pts/2    30127 S+    1002   0:00 ./mytest
    #####################PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
    29927 30127 30127 29927 pts/2    30127 S+    1002   0:00 ./mytest
    30127 30128 30127 29927 pts/2    30127 S+    1002   0:00 ./mytest
    

    观察结果父子进程都进行了休眠;

    当子进程向父进程传输数据时父进程会直接接收;

    若是子进程未向父进程传输数据时为了避免读取"脏数据"(即错误或无效数据),父进程将会等待子进程进行下一次的通信;

    进程间通信的本质是 “使不同进程看到同一个资源” ,这意味着这份资源将被多个执行流共享;

    因此可能会出现 访问冲突临界资源竞争 等问题;

    父子进程间协同的方式一般采用 同步与互斥 ,主要保护管道文件资源的数据安全;

  • 匿名管道通信时的四种情况

    • 管道为空时读端将进行阻塞

      参考上文 父子进程通信时会进行协同 ;

    • 管道为满时写端将进行阻塞

      在原代码基础上取消子进程的sleep()并在父进程开头处sleep(5),结尾处sleep(100)进行等待(只进行一次读取,sleep(100)为与一次读取进行分割避免第二次read());

      在两个会话中分别运行程序与监控脚本;

      结果如下:

      • 监控脚本

        $ while :; do ps axj | head -1 && ps axj | grep mytest | grep -v grep ; echo "#####################" ; sleep 1 ;donePPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
        #####################PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
        29927 32142 32142 29927 pts/2    32142 S+    1002   0:00 ./mytest
        32142 32143 32142 29927 pts/2    32142 S+    1002   0:00 ./mytest
        #####################PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
        29927 32142 32142 29927 pts/2    32142 S+    1002   0:00 ./mytest
        32142 32143 32142 29927 pts/2    32142 S+    1002   0:00 ./mytest#...
      • 程序结果

        $ ./mytest 
        parent process get a massage [32142] # I am a child-32143-0I am a child-32143-1I am a child-32143-2I am a child-32143-3I am a child-32143-4I am a child-32143-5I am a child-32143-6I am a child-32143-7I am a child-32143-8I am a child-32143-9I am a child-32143-10I am a child-32143-11I am a child-32143-12I am a child-32143-13I am a child-32143-14I am a child-32143-15I am a child-32143-16I am a child-32143-17I am a child-32143-18I am a child-32143-19I am a child-32143-20I am a child-32143-21I am a child-32143-22I am a child-32143-23I am a child-32143-24I am a child-32143-25I am a child-32143-26I am a child-32143-27I am a child-32143-28I am a child-32143-29I am a child-32143-30I am a child-32143-31I am a child-32143-32I am a child-32143-33I am a child-32143-34I am a child-32143-35I am a child-32143-36I am a child-32143-37I am a child-32143-38I am a child-32143-39I am a child-32143-40I am a child-32143-41I am a child-32143-42I am a child-32143-43I am a child-32143-44I am a child-32143-45I am a child-32143-46I am a child-32143-47I am a child-32143-48I am 
        

      结果来看在运行程序时父进程因sleep(5)并未接收到子进程传输的数据;

      当休眠结束时将管道内的所有数据进行一次性读取;

      从读取的数据来看最终写入的数据停留在了I am a child-32143-48I am ;

      这意味着管道此时已经被写满了;

      当管道满了时写端将进行阻塞,等待读端读取数据后才能进行下一次写入;

    • 读端正常,写端关闭

      利用man查看read()系统调用接口返回值;

      RETURN VALUEOn  success, the number of bytes read is returned (zero indicates endof file), and the file position is advanced by this  number.  ...
      

      读端正常写端关闭时当管道中的数据被读端读完后读端将会读取到0表示已经读到文件(管道Pipe)结尾且不会被阻塞;

      故为防止该种情况需要在父进程中进行特殊处理(上述原文件并未对该情况进行处理);

    • 写端正常,读端关闭

      当一个进程尝试向管道的写端写入数据,而管道的读端已经被所有相关进程关闭时,该进程会收到SIGPIPE信号;

      默认情况下SIGPIPE信号会终止该进程;

      这是因为如果没有任何进程能够从管道读取数据,继续写入数据就没有意义;

      操作系统通过发送SIGPIPE信号来通知这一点;

      修改上文原代码,使写端持续每隔一秒对管道文件缓冲区进行写入(原代码中保持不变);

      读端读取3次后退出,即读端被关闭:

      void Read(int rfd) {int cnt = 0;char buff[NUM];buff[0] = 0;while (true) {buff[0] = 0;  // 字符串清空 (为读者展示该数组将被视为一个字符串)ssize_t n = read(rfd, buff, sizeof(buff));if (n > 0) {buff[n] = 0;  // 需要打印时将需要称为一个字符串 需要添加'\0'cout << "parent process get a massage [" << getpid() << "]# " << buff<< endl;}if (cnt++ > 5) break;}
      }
      

      子进程若被信号杀死父进程可以看到其对应状态;

      修改原代码main函数中父进程的操作,使其在子进程结束后sleep(3),并调用宏WTERMSIG观察子进程被哪个信号杀死;

        // parent - `r`close(pipefd[1]);// IPC codePIC::Read(pipefd[0]);close(pipefd[0]);sleep(3);//观察僵尸状态int status = 0;pid_t rid = waitpid(id, &status, 0);cout << "Parent process [" << getpid()<< "]@ waited for child process sucessed " << endl<< "Child process terminated by signal " << WTERMSIG(status) << endl;if (rid < 0) return 3;
      

      在两个会话中调用监控脚本与运行程序;

      结果如下:

      • 运行结果:

        $ ./mytest 
        parent process get a massage [2381]# I am a child-2382-0
        parent process get a massage [2381]# I am a child-2382-1
        parent process get a massage [2381]# I am a child-2382-2
        parent process [2381]@ waited for child process sucess the status is 0  , Child process terminated by signal 13
        
      • 脚本结果

        #######################################PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
        31962  4575  4575 31962 pts/0     4575 S+    1002   0:00 ./mytest4575  4576  4575 31962 pts/0     4575 S+    1002   0:00 ./mytest
        #######################################PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
        31962  4575  4575 31962 pts/0     4575 S+    1002   0:00 ./mytest4575  4576  4575 31962 pts/0     4575 S+    1002   0:00 ./mytest
        #######################################PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
        31962  4575  4575 31962 pts/0     4575 S+    1002   0:00 ./mytest4575  4576  4575 31962 pts/0     4575 S+    1002   0:00 ./mytest
        #######################################PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
        31962  4575  4575 31962 pts/0     4575 S+    1002   0:00 ./mytest4575  4576  4575 31962 pts/0     4575 Z+    1002   0:00 [mytest] <defunct>
        #######################################PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
        31962  4575  4575 31962 pts/0     4575 S+    1002   0:00 ./mytest4575  4576  4575 31962 pts/0     4575 Z+    1002   0:00 [mytest] <defunct>
        

        读端(父进程)读取两次后关闭读端并sleep(3);

        读端被关闭时写端(子进程)立马为僵尸状态Z+;

        而后读端阻塞sleep(3)结束后两个进程退出;

        打印结果中子进程(写端)被13号信号杀死,kill -l命令查看信号集:

      $ kill -l1) SIGHUP	 2) SIGINT	 3) SIGQUIT	 4) SIGILL	 5) SIGTRAP6) 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
      ...
      

      13号信号对应信号SIGPIPE;

  • 匿名管道具有固定大小

    利用ulimit命令带-a选项查看操作系统的限制;

    $ ulimit -a
    core file size          (blocks, -c) 0
    data seg size           (kbytes, -d) unlimited
    scheduling priority             (-e) 0
    file size               (blocks, -f) unlimited
    pending signals                 (-i) 7269
    max locked memory       (kbytes, -l) 64
    max memory size         (kbytes, -m) unlimited
    open files                      (-n) 65535
    pipe size            (512 bytes, -p) 8
    POSIX message queues     (bytes, -q) 819200
    real-time priority              (-r) 0
    stack size              (kbytes, -s) 8192
    cpu time               (seconds, -t) unlimited
    max user processes              (-u) 4096
    virtual memory          (kbytes, -v) unlimited
    file locks                      (-x) unlimited
    

    其中pipe size大小为512bytes * 84kb;

    修改原代码中Write()为:

    void Write(int wfd) {int number = 0;while (true) {char c = 'c';write(wfd, &c, 1);  number++;cout << number << endl;}
    }//读端对应进行阻塞(该测试中用不到读端)
    

    即每次向管道文件内核缓冲区中写入一个字节;

    重新编译运行代码结果为:

    ...
    65534
    65535
    65536
    ^C #进行阻塞时 Ctrl+C 停止继续执行
    

    最终结果为65536字节,约为64kbulimit中结果不符;

    • 原因是匿名管道的缓冲区大小是固定的,但这个大小由操作系统决定,不同的操作系统和配置可能会有不同的管道缓冲区大小;
  • 管道是面向字节流的

    参考上文 “管道的四种情况 > 管道为满时写端将进行阻塞” ,写端向读端进行数据传输时只按照字节进行写入;

    最终的格式将取决于读端采用什么方式对写端的内容进行接收;

    写端可以连续写入任意数量的字节,而读端可以根据需要读取任意数量的字节;

    故写端不需要考虑读端采用何种方式进行接收,读端不需要考虑写端采用何种方式进行写入;

  • 匿名管道是基于文件的

    匿名管道不属于文件系统(不存在文件名,Inode与对应的数据块),但匿名管道是基于文件的,其必须通过系统调用接口与文件描述符的配合才能进行使用;

    同时匿名管道的生命周期是由进程决定的(引用计数);


总结

请添加图片描述

  • 管道是一种基于文件描述符的进程间通信(IPC)机制,允许单向数据传输;

  • 有两种基本类型的管道

    匿名管道用于有亲缘关系的进程间通信;

    命名管道(FIFO) 允许没有亲缘关系的进程间通信;

  • 管道存在固定大小的缓冲区,由操作系统决定,面向字节流,支持连续字节的读写操作;

  • 匿名管道的生命周期与创建它的进程相关,不属于文件系统,但通过文件描述符进行操作;

  • 进程间通信通过管道需要正确管理文件描述符,如关闭不需要的端以避免阻塞和SIGPIPE信号;

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

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

相关文章

NSSCTF-Web题目16

目录 [GDOUCTF 2023]受不了一点 1、题目 2、知识点 3、思路 [UUCTF 2022 新生赛]ez_upload 1、题目 2、知识点 3、思路 [GDOUCTF 2023]受不了一点 1、题目 2、知识点 php代码审计、数组绕过、弱比较绕过 3、思路 打开题目&#xff0c;出现代码&#xff0c;我们进行代…

【STM32】江科大STM32学习笔记汇总(已完结)

00. 目录 文章目录 00. 目录01. STM32学习笔记汇总02. 相关资料下载03. 打赏04. 附录 01. STM32学习笔记汇总 【STM32】STM32学习笔记-课程简介(01) 【STM32】STM32学习笔记-STM32简介(02) 【STM32】STM32学习笔记-软件安装(03) 【STM32】STM32学习笔记-新建工程(04) 【ST…

VUE div的右上角的角标/标签

一、效果图 二、代码 <div class"comp-overview"><div class"overview-item" v-for"(item,index) in overviewInfoList" :key"index"><div class"angle_mark"><span>{{item.label}}</span>&…

2024上海初中生古诗文大会倒计时4个月:单选题真题示例和独家解析

现在距离2024年初中生古诗文大会还有4个多月时间&#xff0c;我们继续来看10道选择题真题和详细解析&#xff0c;以下题目截取自我独家制作的在线真题集&#xff0c;都是来自于历届真题&#xff0c;去重、合并后&#xff0c;每道题都有参考答案和解析。 为帮助孩子自测和练习&…

Qt 信号与槽的使用详解 - 多种绑定形式、同步异步、Lambda表达式等

Qt 信号与槽的使用详解 - 多种绑定形式、同步异步、Lambda表达式等 引言一、信号与槽常见的绑定形式二、信号与槽的连接方式 - 同步异步 引言 在Qt框架中&#xff0c;信号与槽&#xff08;Signals and Slots&#xff09;机制是一种强大的通信方式&#xff0c;它允许对象之间进…

Linux常用命令、基本配置、shell基本语法整合

Linux常用命令的使用 Linux文件系统 和 Windows文件系统目录&#xff1a; Windows是分C盘、D盘、E盘…的&#xff0c;但是在Linux中是有一个最大的目录&#xff0c;称之为根目录&#xff0c;用 / 表示&#xff0c;根目录下面会有很多子目录&#xff0c;这些子目录其实可以理解…

百度comate 专业版免费试用90天

我发现一个编码效率提升好帮手——Baidu Comate&#xff0c;结合文心大模型和百度编程大数据&#xff0c;为你生成优质编程代码。现在通过我的链接注册&#xff0c;立得90天专业版体验卡&#xff0c;来吧&#xff0c;让我们一起释放“十倍”软件生产力&#xff01; https://com…

如何利用自助式商业智能(BI)打破组织中的数据孤岛?

前言 许多组织都存在数据问题。当许多员工远程工作&#xff08;或在混合环境中&#xff09;并在多个位置使用多个设备访问公司数据时&#xff0c;他们正在处理信息过载问题。这只会加剧数据孤岛的问题。 数据孤岛正是它听起来的样子&#xff1a;孤立在一个孤立的用户/环境中的…

[深度学习]循环神经网络RNN

RNN&#xff08;Recurrent Neural Network&#xff0c;即循环神经网络&#xff09;是一类用于处理序列数据的神经网络&#xff0c;广泛应用于自然语言处理&#xff08;NLP&#xff09;、时间序列预测、语音识别等领域。与传统的前馈神经网络不同&#xff0c;RNN具有循环结构&am…

【详述】BP神经网络建模流程一步一步详述

本文来自《老饼讲解-BP神经网络》https://www.bbbdata.com/ 目录 一、BP神经网络的建模流程二、BP神经网络的建模分步讲解2.1.数据归一化2.2.数据划分2.3.网络结构设置2.4.网络训练2.5.训练效果评估 本文梳理BP神经网络的建模流程&#xff0c;供大家建模时进行借鉴。 一、BP神经…

SPECweb2009调优指南

【写在前面】 飞腾开发者平台是基于飞腾自身强大的技术基础和开放能力&#xff0c;聚合行业内优秀资源而打造的。该平台覆盖了操作系统、算法、数据库、安全、平台工具、虚拟化、存储、网络、固件等多个前沿技术领域&#xff0c;包含了应用使能套件、软件仓库、软件支持、软件适…

计算机网络-BGP路由负载分担

在大型网络中&#xff0c;到达同一目的地通常会存在多条有效BGP路由&#xff0c;设备只会优选一条最优的BGP路由&#xff0c;将该路由加载到路由表中使用&#xff0c;这一特点往往会造成很多流量负载不均衡的情况。 通过配置BGP负载分担&#xff0c;可以使得设备同时将多条等代…

spring boot整合WebSocket实现群聊功能 通俗易懂教程

首先获取消息的内容&#xff0c;即payload。将payload转换为JSONObject对象&#xff0c;并从中获取消息类型type。如果消息类型是"join"&#xff0c;则从消息中获取群组ID&#xff0c;并将当前WebSocketSession的ID加入到该群组对应的Set中。如果消息类型是"lea…

Python爬取中国福彩网彩票数据并以图表形式显示

网页分析 首先打开中国福彩网&#xff0c;点击双色球&#xff0c;选择往期开奖栏目 进入栏目后&#xff0c;选定往期的奖金数目作为我们想要爬取的目标内容 明确目标后&#xff0c;开始寻找数据所在的位置 鼠标右击页面&#xff0c;打开网页源代码&#xff0c;在源代码中搜索…

vue3-openlayers 轨迹回放(历史轨迹)(ol-animation-path实现)

本篇介绍一下使用vue3-openlayers轨迹回放&#xff08;历史轨迹&#xff09;&#xff08;ol-animation-path实现&#xff09; 1 需求 轨迹回放&#xff08;历史轨迹&#xff09;实时轨迹 2 分析 轨迹回放&#xff08;历史轨迹&#xff09;&#xff0c;一般是一次性拿到所有…

功能测试【测试用例模板、Bug模板、手机App测试★】

功能测试 Day01 web项目环境与测试流程、业务流程测试一、【了解】web项目环境说明1.1 环境的定义&#xff1a;项目运行所需要的所有的软件和硬件组合1.2 环境(服务器)的组成&#xff1a;操作系统数据库web应用程序项目代码1.3 面试题&#xff1a;你们公司有几套环境&#xff1…

淘宝用户行为分析大数据可视化

文章目录 1. 项目概述2. 技术栈3. 目录结构4. 数据处理流程5. 前端部分5.1 HTML (index.html)5.2 CSS (layer.css)5.3 JavaScript (chart.js) 6. 后端部分7. 数据可视化7.1 ECharts 图表 8. 主要功能模块9. 代码解析10. 数据接口11. 项目功能描述12. 代码功能实现12.1 HTML (in…

Kubernetes 中 ElasticSearch 中的 MinIO 审核日志

无论您是在本地还是在云中&#xff0c;您都希望确保以同构的方式设置工具和流程。无论在何处访问基础结构&#xff0c;您都希望确保用于与各种基础结构进行交互的工具与其他区域相似。 考虑到这一点&#xff0c;在部署您自己的 MinIO 对象存储基础架构时&#xff0c;深入了解您…

无需 AU 专业工具,简单有效的人声分离操作图文教程来了

音乐制作与音频编辑已成为众多创意工作者不可或缺的一部分。无论是制作混音、进行音乐分析&#xff0c;还是为视频内容寻找完美的配音&#xff0c;人声分离技术都扮演着至关重要的角色。 传统上&#xff0c;这样的任务可能需要专业的音频编辑软件如 Adobe Audition&#xff08…

【算法面试】在排序数组中查找元素的第一个和最后一个位置:详细题解

目录 题目描述 示例 示例 1&#xff1a; 示例 2&#xff1a; 示例 3&#xff1a; 问题分析 详细步骤 解决方法 方法 1&#xff1a;标准二分查找&#xff08;分开查找第一个和最后一个&#xff09; 方法 2&#xff1a;优化版二分查找&#xff08;合并查找逻辑&#xf…