【Linux】Linux 進程間通訊-管道

參考博客:https://blog.csdn.net/sjsjnsjnn/article/details/125864580

一、進程間通訊介紹

1.1 進程間通訊的概念

進程通信(Interprocess communication),簡稱:IPC

  • 本來進程之間是相互獨立的。但是由于不同的進程之間可能要共享某些信息,所以就必須要有通訊來實現進程間的互斥和同步。比如說共享同一塊內存、管道、消息隊列、信號量等等就是實現這一過程的手段,相當于移動公司在打電話的作用。

1.2 進程間通訊的目的

  • 數據傳輸:一個進程需要將它的數據發送給另一個進程
  • 資源共享:多個進程之間共享同樣的資源。
  • 通知事件:一個進程需要向另一個或一組進程發送消息,通知它(它們)發生了某種事件(如進程終止時要通知父進程)。
  • 進程控制:有些進程希望完全控制另一個進程的執行(如Debug進程),此時控制進程希望能夠攔截另一個進程的所有陷入和異常,并能夠及時知道它的狀態改變

1.3 進程間通信的前提

  • 進程間通信的前提本質:由操作系統參與,提供一份所有通信進行都能看到的公共資源;
  • 兩個或多個進程相互通信,必須先看到一份公共的資源,這里的所謂的資源是屬于操作系統的,就是一段內存(可能以文件的方式提供、可能以隊列的方式提供,也有可能提供的就是原始內存塊),這也就是通信方式有很多種的原因;

1.4 進程間通信的分類

管道

  • 匿名管道pipe
  • 命名管道

System V IPC

  • System V 消息隊列
  • System V 共享內存
  • System V 信號量

POSIX IPC

  • 消息隊列
  • 共享內存
  • 信號量
  • 互斥量
  • 條件變量
  • 讀寫鎖

二、管道通訊

2.1 管道的概念

  • 管道是Unix中最古老的進程間通信的形式。
  • 我們把從一個進程連接到另一個進程的一個數據流稱為一個“管道”

比如下面的命令,我們通過管道連接了cat test.cwc -l兩個命令,本質是兩個進程

cat test.c | wc -l
  • 運行的結果如下,統計了test.c文件的行數(wc),并且將對應的結果輸出了出來(cat)
  • 這里執行的順序為從右到左,先執行wc -l

在這里插入圖片描述

在這里插入圖片描述

2.2 匿名管道

2.2.1 基本原理

  • 匿名管道用于進程間通信,且僅限于父子進程之間的通信。

在這里插入圖片描述

  • 我們知道進程的PCB中包含了一個指針數組 struct file_struct,它是用來描述并組織文件的。父進程和子進程均有這個指針數組,因為子進程是父進程的模板,其代碼和數據是一樣的;

  • 打開一個文件時,其實是將文件加載到內核中,內核將會以結構體(struct file)的形式將文件的相關屬性、文件操作的指針集合(即對應的底層IO設備的調用方法)等;

  • 當父進程進行數據寫入時(例如:寫入“hello Linux”),數據是先被寫入到用戶級緩沖區,經由系統調用函數,又寫入到了內核緩沖區,在進程結束或其他的操作下才被寫到了對應的設備中;

  • 如果數據在寫入設備之前,“hello Linux”是在內核緩沖區的,因為子進程和父進程是同時指向這個文件的,所以子進程是能夠看到這個數據的,并且可以對其操作;

  • 簡單來說,父進程向文件寫入數據時,不直接寫入對應的設備中,而是將數據暫存在內核緩沖區中,交給子進程來處理;

所以這種基于文件的方式就叫做管道;

2.2.2 管道的創建步驟

  • 在創建匿名管道實現父子進程間通信的過程中,需要pipe函數和fork函數搭配使用,具體步驟如下:

在這里插入圖片描述

  • 匿名管道屬于單向通信,意味著父子進程只有一個端是打開的,實現父子通信的時候就需要根據自己的想要實現的情況,關閉對應的文件描述符;
pipe函數
#include <unistd.h>
int pipe(int pipefd[2]);

函數的參數是兩個文件的描述符,是輸出型參數:

  • pipefd[0]:讀管道 — 對應的文件描述符是3
  • pipefd[1]:寫管道 — 對應的文件描述符是4

返回值:成功返回0,失敗返回-1;

2.2.3 匿名管道通訊

  • 下面的代碼通過使用forkpipe函數實現父子進程之間的通訊
  • 其中,父進程用于讀取數據,子進程用于寫入數據
  • 由于管道是單向通訊的,因此需要關閉管道的另一端,即父進程關閉寫端,子進程關閉讀端
void test1(){int pipe_fd[2];memset(pipe_fd,0,sizeof(pipe_fd));int ret = pipe(pipe_fd);if(ret < 0 ){std::cout << "error:" << strerror(ret) << std::endl;return;}std::cout << "pipe_fd[0] = " << pipe_fd[0] << std::endl;std::cout << "pipe_fd[1] = " << pipe_fd[1] << std::endl;ret = fork();if(ret == 0){ //子進程close(pipe_fd[0]); //關閉子進程讀端for(int i =1;i<=10;++i){std::string msg = "hello from child process :" + std::to_string(i) +" times";write(pipe_fd[1],msg.c_str(),msg.size());sleep(1);}exit(0);}close(pipe_fd[1]); //關閉父進程寫端char buffer[1024];memset(buffer,0,sizeof(buffer));while(1){ssize_t s = read(pipe_fd[0],buffer,sizeof(buffer));if(s <= 0){std::cout << "read finished !" << std::endl;break; }else{buffer[s] = '\0';std::cout << "read from child process : " << buffer << std::endl;}}
}
  • 可以發現,管道的讀寫端的文件描述符為34,其中0,12通常是輸入流、輸出流和錯誤流
  • 通過打印結果,可以發現父子進程成功通訊了

在這里插入圖片描述

2.2.4 匿名管道通訊的特點

五個特點

  1. 管道僅限父子通訊,只能單向通訊
  2. 管道提供流式服務
  3. 管道自帶同步與互斥機制
  4. 進程退出,管道隨之釋放,因此管道的生命周期隨進程
  5. 如果需要雙向通訊,則需要建立兩個管道

四個情況

  1. 讀端不讀或者讀得慢,寫端要等待讀端
  2. 讀端關閉,寫端收到SIGPIPE信號后終止
  3. 寫端不寫或者寫得慢,讀端要等待寫端
  4. 寫端關閉,讀端讀到EOF后退出

2.2.5 字節流通訊

  • 字節流的特征就是沒有邊界,每次讀取指定的字節
  • 我們發送數據的時候是先把數據寫到內核緩沖區中,讀取的時候也是從內核緩沖區讀取指定的字節
  • 因此,如果寫端慢了,那么讀取的數據會重合在一起,如下面的程序所示
void test1(){int pipe_fd[2];memset(pipe_fd,0,sizeof(pipe_fd));int ret = pipe(pipe_fd);if(ret < 0 ){std::cout << "error:" << strerror(ret) << std::endl;return;}std::cout << "pipe_fd[0] = " << pipe_fd[0] << std::endl;std::cout << "pipe_fd[1] = " << pipe_fd[1] << std::endl;ret = fork();if(ret == 0){ //子進程close(pipe_fd[0]); //關閉子進程讀端for(int i =1;i<=10;++i){std::string msg = "hello from child process :" + std::to_string(i) +" times";write(pipe_fd[1],msg.c_str(),msg.size());sleep(1);}exit(0);}close(pipe_fd[1]); //關閉父進程寫端char buffer[1024];memset(buffer,0,sizeof(buffer));while(1){sleep(10);ssize_t s = read(pipe_fd[0],buffer,sizeof(buffer));if(s <= 0){std::cout << "read finished !" << std::endl;break; }else{buffer[s] = '\0';std::cout << "read from child process : " << buffer << std::endl;}}
}
  • 可以發現,讀取的數據全都合并在一起了,因為我們指定讀取的字節數較大

在這里插入圖片描述

2.2.6 同步機制

  • 內核的緩沖區是有大小限制的,下面我們不斷發送數據到內核緩沖區,到了65536字節后,就發送不了了,此時阻塞了進程
void test2(){int pipe_fd[2];memset(pipe_fd,0,sizeof(pipe_fd));int ret = pipe(pipe_fd);if(ret < 0 ){std::cout << "error:" << strerror(ret) << std::endl;return;}std::cout << "pipe_fd[0] = " << pipe_fd[0] << std::endl;std::cout << "pipe_fd[1] = " << pipe_fd[1] << std::endl;ret = fork();if(ret == 0){ //子進程close(pipe_fd[0]); //關閉子進程讀端int writedBytes = 0;for(int i =1;i<=10000000;++i){write(pipe_fd[1],"a",1);writedBytes ++;std::cout << "child process send msg: " << "a" <<",writed Bytes = " << writedBytes<< std::endl;}exit(0);}close(pipe_fd[1]); //關閉父進程寫端char buffer[1024];memset(buffer,0,sizeof(buffer));while(1){sleep(1);}
}

在這里插入圖片描述

  • 管道通訊自帶同步機制和互斥機制,也就是發送端和接收端看到的數據是一致的,并且同時只有一段可以讀或者寫
  • 下面的程序在內核緩沖區寫滿了以后,嘗試讀取數據,發現只有讀取了一些數據之后,才能繼續往內核寫入數據,而不是讀取一個字節可以寫入一個字節
void test3()
{int pipe_fd[2];memset(pipe_fd,0,sizeof(pipe_fd));int ret = pipe(pipe_fd);if(ret < 0 ){std::cout << "error:" << strerror(ret) << std::endl;return;}std::cout << "pipe_fd[0] = " << pipe_fd[0] << std::endl;std::cout << "pipe_fd[1] = " << pipe_fd[1] << std::endl;ret = fork();if(ret == 0){ //子進程close(pipe_fd[0]); //關閉子進程讀端int writedBytes = 0;for(int i =1;i<=10000000;++i){write(pipe_fd[1],"a",1);writedBytes ++;std::cout << "child process send msg: " << "a" <<",writed Bytes = " << writedBytes<< std::endl;}exit(0);}close(pipe_fd[1]); //關閉父進程寫端sleep(5);while(1){char c = 0;read(pipe_fd[0],&c,1);std::cout << "read :" << c << std::endl;sleep(1);}
}

在這里插入圖片描述

  • 讀取部分數據后,才會繼續寫入
  • 讀端太慢,會導致寫端等待讀端
void test3()
{int pipe_fd[2];memset(pipe_fd,0,sizeof(pipe_fd));int ret = pipe(pipe_fd);if(ret < 0 ){std::cout << "error:" << strerror(ret) << std::endl;return;}std::cout << "pipe_fd[0] = " << pipe_fd[0] << std::endl;std::cout << "pipe_fd[1] = " << pipe_fd[1] << std::endl;ret = fork();if(ret == 0){ //子進程close(pipe_fd[0]); //關閉子進程讀端int writedBytes = 0;for(int i =1;i<=10000000;++i){write(pipe_fd[1],"a",1);writedBytes ++;std::cout << "child process send msg: " << "a" <<",writed Bytes = " << writedBytes<< std::endl;}exit(0);}close(pipe_fd[1]); //關閉父進程寫端sleep(5);char buffer[1024];memset(buffer,0,sizeof(buffer));int readBytes = 0;while(1){char c = 0;ssize_t s = read(pipe_fd[0],buffer,sizeof(buffer));buffer[s] = '\0';std::cout << "read :" << buffer << std::endl;std::cout << "read bytes = " << readBytes << std::endl;sleep(1);readBytes += s;}
}

在這里插入圖片描述

在這里插入圖片描述

2.2.7 寫端關閉

  • 寫端關閉,那么讀端會讀到EOF后自動退出
  • 比如下面的程序,我們讓讀進程先休眠一會,然后寫進程寫了一些數據后退出,那么讀進程讀到EOF后也就退出了
void test4()
{int pipe_fd[2];memset(pipe_fd, 0, sizeof(pipe_fd));int ret = pipe(pipe_fd);if (ret < 0){std::cout << "error:" << strerror(ret) << std::endl;return;}std::cout << "pipe_fd[0] = " << pipe_fd[0] << std::endl;std::cout << "pipe_fd[1] = " << pipe_fd[1] << std::endl;ret = fork();if (ret == 0){                      // 子進程close(pipe_fd[0]); // 關閉子進程讀端for (int i = 1; i <= 10; ++i){write(pipe_fd[1], "abcdefg", 7);}exit(0);}sleep(5);close(pipe_fd[1]); // 關閉父進程寫端char buffer[1024];memset(buffer, 0, sizeof(buffer));int readBytes = 0;while (1){char c = 0;ssize_t s = read(pipe_fd[0], buffer, sizeof(buffer));if (s <= 0){std::cout << "read finished !" << std::endl;break;}buffer[s] = '\0';readBytes += s;std::cout << "read :" << buffer << std::endl;std::cout << "read bytes = " << readBytes << std::endl;sleep(1);}
}

在這里插入圖片描述

2.2.8 讀端關閉

  • 讀端關閉,寫段會收到SIGPIPE信號,然后中斷進程
  • 當我們的讀端關閉,寫端還在寫入,在操作系統的層面上,嚴重不合理;這本質上就是在浪費操作系統的資源,所以操作系統在遇到這樣的情況下,會將子進程殺掉(發送13號信號—SIGPIPE)

下面的shell腳本用于持續跟蹤測試進程

while :; do ps axj | grep pipe_process | grep -v grep; sleep 1; echo "####################";
done;
  • 可以發現,子進程退出后,父進程隨之也退出了

在這里插入圖片描述

  • 這里我們添加上父進程等待子進程,也就是waitpid,然后輸出對應的信號值
  • 可以發現,退出后的信號值為13,對應的是SIGPIPE

void test5()
{int pipe_fd[2];memset(pipe_fd, 0, sizeof(pipe_fd));int ret = pipe(pipe_fd);if (ret < 0){std::cout << "error:" << strerror(ret) << std::endl;return;}std::cout << "pipe_fd[0] = " << pipe_fd[0] << std::endl;std::cout << "pipe_fd[1] = " << pipe_fd[1] << std::endl;ret = fork();if (ret == 0){                      // 子進程close(pipe_fd[0]); // 關閉子進程讀端for (int i = 1; i <= 10; ++i){write(pipe_fd[1], "abcdefg", 7);sleep(1);}exit(0);}close(pipe_fd[1]); // 關閉父進程寫端char buffer[7];memset(buffer, 0, sizeof(buffer));int readBytes = 0;while (1){char c = 0;ssize_t s = read(pipe_fd[0], buffer, sizeof(buffer));if (s <= 0){std::cout << "read finished !" << std::endl;break;}buffer[s] = '\0';std::cout << "read :" << buffer << std::endl;readBytes += s;std::cout << "read bytes = " << readBytes << std::endl;sleep(2);close(pipe_fd[0]);int status = 0;waitpid(-1, &status, 0);printf("exit code: %d\n",(status >> 8)& 0xFF);printf("exit signal: %d\n",status& 0x7F);}
}

在這里插入圖片描述

  • 查詢對應的信號,符合預期
kill -l

在這里插入圖片描述

2.2.9 非阻塞管道

int pipe2(int pipefd[2], int flags);
  • 可以通過pip2函數,設置管道通訊的阻塞與非阻塞
  • 可以通過設置O_NONBLOCK標志為非阻塞,默認為阻塞,或者傳入0

當沒有數據可讀時

  • O_NONBLOCK disable:read調用阻塞,即進程暫停執行,一直等到有數據來到為止。
  • O_NONBLOCK enable:read調用返回-1,errno值為EAGAIN。

當管道滿的時候

  • O_NONBLOCK disable: write調用阻塞,直到有進程讀走數據
  • O_NONBLOCK enable:調用返回-1,errno值為EAGAIN
  • 如果所有管道寫端對應的文件描述符被關閉,則read返回0
  • 如果所有管道讀端對應的文件描述符被關閉,則write操作會產生信號SIGPIPE,進而可能導致write進程退出
  • 當要寫入的數據量不大于PIPE_BUF時,linux將保證寫入的原子性。
  • 當要寫入的數據量大于PIPE_BUF時,linux將不再保證寫入的原子性。

在這里插入圖片描述

2.2.9.1 非阻塞寫入滿了
  • 下面的程序演示非阻塞管道寫端,在內核寫入滿了以后的返回值
void test6(){int pipe_fd[2];memset(pipe_fd, 0, sizeof(pipe_fd));int ret = pipe2(pipe_fd,O_NONBLOCK);if (ret < 0){std::cout << "error:" << strerror(ret) << std::endl;return;}std::cout << "pipe_fd[0] = " << pipe_fd[0] << std::endl;std::cout << "pipe_fd[1] = " << pipe_fd[1] << std::endl;ret = fork();if (ret == 0){                      // 子進程close(pipe_fd[0]); // 關閉子進程讀端int writedBytes = 0;for (int i = 1; i <= 10000000; ++i){int ret = write(pipe_fd[1], "a", 1);if(ret == -1 && errno == EAGAIN){std::cout << "errno: EAGAIN !" << std::endl;sleep(1);continue;}writedBytes++;std::cout << "child process send msg: " << "a" << ",writed Bytes = " << writedBytes << std::endl;}exit(0);}close(pipe_fd[1]); // 關閉父進程寫端int readBytes = 0;while (1){sleep(1);}
}

在這里插入圖片描述

2.2.9.2 非阻塞無數據可讀
  • 下面的程序演示非阻塞管道讀取,無數據可讀的返回值

void test7(){int pipe_fd[2];memset(pipe_fd, 0, sizeof(pipe_fd));int ret = pipe2(pipe_fd,O_NONBLOCK);if (ret < 0){std::cout << "error:" << strerror(ret) << std::endl;return;}std::cout << "pipe_fd[0] = " << pipe_fd[0] << std::endl;std::cout << "pipe_fd[1] = " << pipe_fd[1] << std::endl;ret = fork();if (ret == 0){                      // 子進程close(pipe_fd[0]); // 關閉子進程讀端sleep(60);}close(pipe_fd[1]); // 關閉父進程寫端int readBytes = 0;char buffer[1024] = {0};while (1){ssize_t s = read(pipe_fd[0],buffer, sizeof(buffer));if(s == -1 && errno == EAGAIN){std::cout << "errno = " << "EAGAIN !" << std::endl;sleep(1);continue;}}
}

在這里插入圖片描述

2.3 命名管道

2.3.1 命名管道的概念

  • 匿名管道應用的一個限制就是只能在具有共同祖先(具有親緣關系)的進程間通信。
  • 如果我們想在不相關的進程之間交換數據,可以使用FIFO文件來做這項工作,它經常被稱為命名管道。 命名管道是一種特殊類型的文件

2.3.2 命名管道的創建

2.3.2.1 命令行創建

可以通過命令行創建命名管道,使用mkfifo指令

在這里插入圖片描述

創建管道之后,可以使用cat指令讀取數據,使用echo指令寫入數據

在這里插入圖片描述

2.3.2.2 代碼創建
  • 使用mkfifo函數可以創建一個命名管道

函數原型

int mkfifo(const char *pathname, mode_t mode);

pathname:表示你要創建的命名管道文件

  • 如果pathname是以文件的方式給出,默認在當前的路徑下創建;
  • 如果pathname是以某個路徑的方式給出,將會在這個路徑下創建;

mode:表示給創建的命名管道設置權限

  • 我們在設置權限時,例如0666權限,它會受到系統的umask(文件默認掩碼)的影響,實際創建出來是(mode & ~umask)0664;
  • 所以想要正確的得到自己設置的權限(0666),我們需要將文件默認掩碼設置為0;

返回值:命名管道創建成功返回0,失敗返回-1

#define MY_FIFO "myfifo"int main(int argc,char* argv[])
{umask(0);int ret = mkfifo(MY_FIFO,0666);if(ret < 0){perror("mkfifo");return 1;}return 0;
}

2.3.3 使用命名管道通訊

  • 我們使用CS模型,在服務端創建命名管道,同時服務端不斷讀取數據,客戶端發送數據
  • 使用阻塞管道,當沒有數據可讀,服務端持續阻塞等待客戶端的數據

在這里插入圖片描述

server

#include<iostream>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<cstring>#define MY_FIFO "fifo"
int main(){umask(0);int ret = mkfifo(MY_FIFO,0666);if(ret < 0){perror("mkfifo");return 1;}int fd = open(MY_FIFO,O_RDONLY);//只讀模式if(fd < 0){perror("open");return 1;}while(1){char buffer[1024];memset(buffer,0,sizeof(buffer));ssize_t len = read(fd,buffer,sizeof(buffer) - 1);if(len == 0){std::cout << "read fifo finished !" << std::endl;break;}else if(len > 0){buffer[len] = '\0';std::cout << "read from client : " << buffer << std::endl;}else{perror("open");break;}}close(fd);return 0;
}

client

#include<iostream>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<cstring>#define MY_FIFO "fifo"
int main(){int fd = open(MY_FIFO,O_WRONLY);//寫入模式if(fd < 0){perror("open");return 1;}while(1){std::string str;std::cout << "Please enter message :";std::cin >> str;ssize_t len = write(fd,str.c_str(),str.size());if(len <= 0){perror("write");break;}}close(fd);return 0;
}

在這里插入圖片描述

在這里插入圖片描述

2.4 管道的總結

管道:

  • 管道分為匿名管道和命名管道;

  • 管道通信方式的中間介質是文件,通常稱這種文件為管道文件;

  • 匿名管道:管道是半雙工的,數據只能單向通信;需要雙方通信時,需要建立起兩個管道;只能用于父子進程或者兄弟進程之間(具有親緣關系的進程)。

  • 命名管道:不同于匿名管道之處在于它提供一個路徑名與之關聯,以FIFO的文件形式存在于文件系統中。這樣,即使與FIFO的創建進程不存在親緣關系的進程,只要可以訪問該路徑,就能夠彼此通過FIFO相互通信

  • 利用系統調用pipe()創建一個無名管道文件,通常稱為無名管道或PIPE;利用系統調用mkfifo()創建一個命名管道文件,通常稱為有名管道或FIFO。

  • PIPE是一種非永久性的管道通信機構,當它訪問的進程全部終止時,它也將隨之被撤消。

  • FIFO是一種永久的管道通信機構,它可以彌補PIPE的不足。管道文件被創建后,使用open()將文件進行打開,然后便可對它進行讀寫操作,通過系統調用write()和read()來實現。通信完畢后,可使用close()將管道文件關閉。

  • 匿名管道的文件是內存中的特殊文件,而且是不可見的,命名管道的文件是硬盤上的設備文件,是可見的。

更多資料:https://github.com/0voice

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/908353.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/908353.shtml
英文地址,請注明出處:http://en.pswp.cn/news/908353.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

深度剖析 DeepSeek 開源模型部署與應用:策略、權衡與未來走向

在人工智能技術呈指數級發展的當下&#xff0c;大模型已然成為推動各行業變革的核心驅動力。DeepSeek 開源模型以其卓越的性能和靈活的開源特性&#xff0c;吸引了眾多企業與開發者的目光。如何高效且合理地部署與運用 DeepSeek 模型&#xff0c;成為釋放其巨大潛力的關鍵所在&…

第34次CCF-CSP認證真題解析(目標300分做法)

第34次CCF-CSP認證 矩陣重塑&#xff08;其一&#xff09;AC代碼及解析矩陣重塑&#xff08;其二&#xff09;AC代碼及解析貨物調度AC代碼及解析 矩陣重塑&#xff08;其一&#xff09; 輸入輸出及樣例&#xff1a; AC代碼及解析 1.線性化原矩陣 &#xff1a;由于cin的特性我們…

智能制造數字孿生全要素交付一張網:智造中樞,孿生領航,共建智造生態共同體

在制造業轉型升級的浪潮中&#xff0c;數字孿生技術正成為推動行業變革的核心引擎。從特斯拉通過數字孿生體實現車輛全生命周期優化&#xff0c;到海爾卡奧斯工業互聯網平臺賦能千行百業&#xff0c;數字孿生技術已從概念驗證走向規模化落地。通過構建覆蓋全國的交付網絡&#…

【技術】跨設備鏈路聚合的技術——M-LAG

原創&#xff1a;廈門微思網絡 M-LAG&#xff08;Multichassis Link Aggregation Group&#xff09;提供一種跨設備鏈路聚合的技術。M-LAG通過將兩臺接入交換機以同一個狀態和用戶側設備或服務器進行跨設備的鏈路聚合&#xff0c;把鏈路的可靠性從單板級提升到設備級。同時&…

AI健康小屋+微高壓氧艙:科技如何重構我們的健康防線?

目前&#xff0c;隨著科技和社會的不斷發展&#xff0c;人們的生活水平和方式有了翻天覆地的變化。 從吃飽穿暖到吃好喝好再到健康生活&#xff0c;觀念也在逐漸發生改變。 尤其是在21世紀&#xff0c;大家對健康越來越重視&#xff0c;這就不得不提AI健康小屋和氧艙。 一、A…

Python訓練營---Day44

DAY 44 預訓練模型 知識點回顧&#xff1a; 預訓練的概念常見的分類預訓練模型圖像預訓練模型的發展史預訓練的策略預訓練代碼實戰&#xff1a;resnet18 作業&#xff1a; 嘗試在cifar10對比如下其他的預訓練模型&#xff0c;觀察差異&#xff0c;盡可能和他人選擇的不同嘗試通…

1.文件操作相關的庫

一、filesystem(C17) 和 fstream 1.std::filesystem::path - cppreference.cn - C參考手冊 std::filesystem::path 表示路徑 構造函數&#xff1a; path( string_type&& source, format fmt auto_format ); 可以用string進行構造&#xff0c;也可以用string進行隱式類…

【 java 集合知識 第二篇 】

目錄 1.Map集合 1.1.快速遍歷Map 1.2.HashMap實現原理 1.3.HashMap的擴容機制 1.4.HashMap在多線程下的問題 1.5.解決哈希沖突的方法 1.6.HashMap的put過程 1.7.HashMap的key使用什么類型 1.8.HashMapkey可以為null的原因 1.9.HashMap為什么不采用平衡二叉樹 1.10.Hash…

【Dify 知識庫 API】“根據文本更新文檔” 真的是差異更新嗎?一文講透真實機制!

在使用 Dify 知識庫 API 過程中,很多開發者在調用 /datasets/{dataset_id}/document/update-by-text 接口時,常常會產生一個疑問: ?? 這個接口到底是 “智能差異更新” 還是 “純覆蓋更新”? 網上的資料并不多,很多人根據接口名誤以為是增量更新。今天我結合官方源碼 …

大模型如何革新用戶價值、內容匹配與ROI預估

寫在前面 在數字營銷的戰場上,理解用戶、精準觸達、高效轉化是永恒的追求。傳統方法依賴結構化數據和機器學習模型,在用戶價值評估、人群素材匹配以及策略ROI預估等核心問題上取得了顯著成就。然而,隨著數據維度日益復雜,用戶行為愈發多變,傳統方法也面臨著特征工程繁瑣、…

基于端到端深度學習模型的語音控制人機交互系統

基于端到端深度學習模型的語音控制人機交互系統 摘要 本文設計并實現了一個基于端到端深度學習模型的人機交互系統,通過語音指令控制其他設備的程序運行,并將程序運行結果通過語音合成方式反饋給用戶。系統采用Python語言開發,使用PyTorch框架實現端到端的語音識別(ASR)…

【2025年】解決Burpsuite抓不到https包的問題

環境&#xff1a;windows11 burpsuite:2025.5 在抓取https網站時&#xff0c;burpsuite抓取不到https數據包&#xff0c;只顯示&#xff1a; 解決該問題只需如下三個步驟&#xff1a; 1、瀏覽器中訪問 http://burp 2、下載 CA certificate 證書 3、在設置--隱私與安全--…

Jenkins 工作流程

1. 觸發構建 Jenkins 的工作流程從觸發構建開始。構建可以由以下幾種方式觸發&#xff1a; 代碼提交觸發&#xff1a;通過與版本控制系統&#xff08;如 Git、SVN&#xff09;集成&#xff0c;當代碼倉庫有新的提交時&#xff0c;Jenkins 會自動觸發構建。 定時觸發&#xff…

Jmeter如何進行多服務器遠程測試?

&#x1f345; 點擊文末小卡片 &#xff0c;免費獲取軟件測試全套資料&#xff0c;資料在手&#xff0c;漲薪更快 JMeter是Apache軟件基金會的開源項目&#xff0c;主要來做功能和性能測試&#xff0c;用Java編寫。 我們一般都會用JMeter在本地進行測試&#xff0c;但是受到…

Kafka入門-生產者

生產者 生產者發送流程&#xff1a; 延遲時間為0ms時&#xff0c;也就意味著每當有數據就會直接發送 異步發送API 異步發送和同步發送的不同在于&#xff1a;異步發送不需要等待結果&#xff0c;同步發送必須等待結果才能進行下一步發送。 普通異步發送 首先導入所需的k…

分類預測 | Matlab實現CNN-LSTM-Attention高光譜數據分類

分類預測 | Matlab實現CNN-LSTM-Attention高光譜數據分類 目錄 分類預測 | Matlab實現CNN-LSTM-Attention高光譜數據分類分類效果功能概述程序設計參考資料 分類效果 功能概述 代碼功能 該MATLAB代碼實現了一個結合CNN、LSTM和注意力機制的高光譜數據分類模型&#xff0c;核心…

gemini和chatgpt數據對比:誰在卷性能、價格和場景?

先把結論“劇透”給趕時間的朋友&#xff1a;頂配 Gemini Ultra/2.5 Pro 在紙面成績上普遍領先&#xff0c;而 ChatGPT 家族&#xff08;GPT-4o / o3 / 4.1&#xff09;則在延遲、生態和穩定性上占優。下面把核心數據拆開講&#xff0c;方便你對號入座。附帶參考來源&#xff0…

代碼訓練LeetCode(23)隨機訪問元素

代碼訓練(23)LeetCode之隨機訪問元素 Author: Once Day Date: 2025年6月5日 漫漫長路&#xff0c;才剛剛開始… 全系列文章可參考專欄: 十年代碼訓練_Once-Day的博客-CSDN博客 參考文章: 380. O(1) 時間插入、刪除和獲取隨機元素 - 力扣&#xff08;LeetCode&#xff09;力…

C++面試5——對象存儲區域詳解

C++對象存儲區域詳解 核心觀點:內存是程序員的戰場,存儲區域決定對象的生殺大權!棧對象自動赴死,堆對象生死由你,全局對象永生不死,常量區對象只讀不滅。 一、四大地域生死簿 棧區(Stack) ? 特點:自動分配釋放,速度極快(類似高鐵進出站) ? 生存期:函數大括號{}就…

STM32 智能小車項目 L298N 電機驅動模塊

今天開始著手做智能小車的項目了 在智能小車或機器人項目中&#xff0c;我們經常會聽到一個詞叫 “H 橋電機驅動”&#xff0c;尤其是常見的 L298N 模塊&#xff0c;就是基于“雙 H 橋”原理設計的。那么&#xff0c;“H 橋”到底是什么&#xff1f;為什么要用“雙 H 橋”來驅動…