Linux進程通信——匿名管道

目錄

1、進程間通信基礎概念

2、管道的工作原理

?2.1 什么是管道文件

3、匿名管道的創建與使用

3.1、pipe 系統調用

3.2??父進程調用 fork() 創建子進程

3.3. 父子進程的文件描述符共享

3.4. 關閉不必要的文件描述符

3.5?父子進程通過管道進行通信

父子進程通信的具體例子

?4.管道的四種場景?

4.1 場景一:父進程不寫,子進程嘗試讀取

4.2 場景二:父進程不斷寫入,直到管道寫滿,子進程不讀取

4.3 場景三:關閉寫端,子進程讀取數據

4.4 場景四:關閉讀端,父進程寫入數據

5、匿名管道實操-進程控制

5.1 邏輯設計


🌇前言

在操作系統中,進程間通信(Interprocess Communication,簡稱IPC)是兩個不同進程之間進行協同工作和信息交換的基礎。IPC 允許不同的進程相互協調,協作完成任務。進程間通信的方式有很多種,而管道則是一種非常經典且常用的方式。本文將詳細探討 匿名管道,它在進程間通信中扮演著重要的角色。

🏙?正文

1、進程間通信基礎概念

在深入探討匿名管道之前,我們先來了解一些基本概念。進程間通信的目的是為了使多個獨立的進程能夠協同工作,進行信息交換。主要有四個目的:

  • 數據傳輸:不同進程之間需要傳輸數據。例如,將數據從客戶端傳送到服務器。

  • 資源共享:多個進程共享系統資源,保證高效使用。

  • 事件通知:某個進程需要通知其他進程某個事件的發生,例如進程的終止。

  • 進程控制:用于進程管理,協調進程的執行和資源的分配。

這些目的的核心是打破進程的獨立性,讓它們能夠共享資源和信息,協同完成任務。?

2、管道的工作原理

管道是一種用于進程間通信的方式,它本質上是一個文件。無論是匿名管道還是命名管道,它們的原理都是通過文件描述符來共享數據。每個管道都有兩個端口:一個是寫端,另一個是讀端。

管道最初是由 Unix 系統引入的,它允許具有“血緣關系”的進程(如父子進程)通過管道進行通信。管道的實現通常會涉及到內核為進程分配文件描述符。父進程在創建管道后,會為其子進程繼承文件描述符,并通過關閉不需要的端口,確保通信的流向。

?2.1 什么是管道文件

管道文件是操作系統中用于實現進程間通信的特殊文件,具有以下幾個顯著特點:

  • 單向通信:管道是 單向 的通信方式,意味著數據只能從一個端流向另一個端。通常情況下,一個進程寫數據到管道,而另一個進程從管道中讀取數據。這種方式被稱為“半雙工通信”,如果需要實現雙向通信,需要兩個管道。

  • 基于文件的設計,管道本質上是內存中的文件; 管道文件并不是磁盤級別的文件,而是內存級別文件,管道文件沒有自己的inode,也沒有名字。過內存中的緩沖區進行存儲,操作系統會將管道作為文件來處理。

  • 管道分為 匿名管道命名管道。匿名管道沒有名字,是由操作系統在內存中創建的,僅限于有血緣關系(如父子進程或兄弟進程)的進程間通信。由于沒有名字,匿名管道無法在進程間直接共享。

    與此不同,命名管道(FIFO)則擁有一個系統中的路徑名,因此它可以被不具備血緣關系的進程之間共享。這使得命名管道的通信更加靈活。

  • 生命周期與進程綁定,管道的生命周期與創建它的進程生命周期緊密相關。當進程結束時,管道也會被操作系統回收。管道文件的生命周期由打開它的進程的生命周期決定,在進程終止時,管道的資源會被釋放。

  • 內存緩沖區,管道的一個重要特點是,它在內存中創建一個緩沖區,用于存儲待傳輸的數據。由于是內存中的緩沖區,管道中的數據并不會被持久化到磁盤中。這使得管道比磁盤文件更為高效,但數據在管道中的存儲是臨時的,不會在系統重啟后保留

  • 阻塞行為與同步機制

    管道的通信遵循 阻塞 和 同步 機制。當讀端嘗試讀取數據時,如果管道為空,進程會阻塞,直到寫端寫入數據。同樣,寫端如果嘗試寫入數據時,如果管道已滿,進程也會阻塞,直到讀端讀取部分數據。

    這種阻塞行為本身提供了一定的同步機制。管道會保證寫入數據的順序,并且數據在被讀取之前不會丟失。這使得進程間的通信是同步的,確保數據完整傳輸。

  • 管道大小限制,管道的大小在不同的操作系統和系統配置中可能有所不同。通常,管道大小會受到系統配置的限制。在 Linux 中,管道大小的默認值通常為 64KB(從 Linux 2.6.11 版本開始),不過在不同的系統或不同的內核版本中,管道的大小也可能有所變化

  • 在管道中,寫入 與 讀取 的次數并不是嚴格匹配的,此時讀寫次數沒有強相關關系,管道是面向字節流讀寫的面向字節流讀寫又稱為 流式服務:數據沒有明確的分割,不分一定的報文段;與之相對應的是 數據報服務:數據有明確的分割,拿數據按報文段拿不論寫端寫入了多少數據,只要寫端停止寫入,讀端都可以將數據讀取。
  • 具有一定的協同能力讓 讀端 和 寫端 能夠按照一定的步驟進行通信(自帶同步機制)當讀端進行從管道中讀取數據時,如果沒有數據,則會阻塞,等待寫端寫入數據;如果讀端正在讀取,那么寫端將會阻塞等待讀端,因此 管道自帶 同步與互斥 機制。

3、匿名管道的創建與使用

具體流程:

父進程創建匿名管道,同時以讀、寫的方式打開匿名管道,此時會分配兩個 fd
fork 創建子進程,子進程擁有自己的進程系統信息,同時會繼承原父進程中的文件系統信息,此時子進程和父進程可以看到同一份資源:匿名管道 pipe
因為子進程繼承了原有關系,因此此時父子進程對于 pipe 都有讀寫權限,需要確定數據流向,關閉不必要的 fd,比如父進程寫、子進程讀,或者父進程讀、子進程寫都可以。

3.1、pipe 系統調用

匿名管道的創建通過 pipe() 系統調用來實現。該函數會創建一個管道,并返回兩個文件描述符:一個用于讀,另一個用于寫。函數原型如下:

#include <unistd.h>int pipe(int pipefd[2]);

?傳入一個大小為2的整型數組作為輸出型參數操作系統就會生成一個管道文件,并且讓進程以讀寫的方式分別打開進程,并且將進程的讀管道文件的文件標識符寫道pipe[1],寫管到文件描述符寫道pipe[1]之中。

int pipefd[2];
pipe(pipefd);  // 創建管道
3.2??父進程調用 fork() 創建子進程

當父進程調用 fork() 時,操作系統會創建一個新的子進程。子進程會繼承父進程的文件描述符表,因此,父子進程可以共享父進程所創建的管道文件描述符。也就是說,父進程和子進程都會擁有相同的管道讀端和寫端。

pid_t pid = fork();
3.3. 父子進程的文件描述符共享

父進程和子進程共享管道的讀寫端口意味著:

  • 父進程子進程 都可以操作 pipefd[0]pipefd[1]但它們之間的角色(讀或寫)通常是根據進程的需求來確定的。

  • 父進程和子進程在創建時各自擁有自己的 進程資源但文件描述符表會被子進程繼承,指向相同的管道內存資源。

3.4. 關閉不必要的文件描述符

由于管道是單向通信的,所以為了避免數據混亂,父進程和子進程通常會關閉不必要的文件描述符。例如,如果父進程要寫數據到管道而子進程讀取數據,父進程應該關閉管道的讀端,子進程應該關閉管道的寫端

// 父進程關閉管道的讀端,子進程關閉管道的寫端
close(pipefd[0]);  // 父進程關閉讀端
close(pipefd[1]);  // 子進程關閉寫端
3.5?父子進程通過管道進行通信
  • 父進程寫數據:父進程通過管道的寫端 pipefd[1] 向管道中寫入數據。

    write(pipefd[1], "Hello from parent", 17);
    

    子進程讀數據:子進程通過管道的讀端 pipefd[0] 從管道中讀取數據。

    char buf[128];
    read(pipefd[0], buf, sizeof(buf));
    
  • 父進程寫入的數據會通過管道傳遞給子進程。

  • 子進程從管道中讀取數據,通常會按順序接收父進程寫入的數據。

父子進程通信的具體例子

下面是一個完整的示例代碼,展示了父子進程如何通過管道進行通信:

#include <iostream>
#include <cassert>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>using namespace std;int main()
{// 1、創建匿名管道int pipefd[2]; // 數組int ret = pipe(pipefd);assert(ret == 0);(void)ret; // 防止 release 模式中報警告// 2、創建子進程pid_t id = fork();if (id == 0){// 子進程內close(pipefd[1]); // 3、子進程關閉寫端// 4、開始通信char buff[64]; // 緩沖區while (true){int n = read(pipefd[0], buff, sizeof(buff) - 1);    //注意預留一個位置存儲 '\0'buff[n] = '\0';if (n >= 5 && n < 64){// 讀取到了信息cout << "子進程成功讀取到信息:" << buff << endl;}else{// 未讀取到信息if (n == 0)cout << "子進程沒有讀取到信息,通信結束!" << endl;// 讀取異常(消息過短)elsecout << "子進程讀取數據量為:" << n << " 消息過短,通信結束!" << endl;break;}}close(pipefd[0]); // 關閉剩下的讀端exit(0);          // 子進程退出}// 父進程內close(pipefd[0]); // 3、父進程關閉讀端char buff[64];// 4、開始通信srand((size_t)time(NULL)); // 隨機數種子while (true){int n = rand() % 26;for (int i = 0; i < n; i++)buff[i] = (rand() % 26) + 'A'; // 形成隨機消息buff[n] = '\0';                    // 結束標志cout << "=============================" << endl;cout << "父進程想對子進程說: " << buff << endl;write(pipefd[1], buff, strlen(buff)); // 寫入數據if (n < 5)break; // 消息過短時,不寫入sleep(1);}close(pipefd[1]); // 關閉剩下的寫端// 父進程等待子進程結束int status = 0;waitpid(id, &status, 0);// 通過 status 判斷子進程運行情況if ((status & 0x7F)){printf("子進程異常退出,core dump: %d   退出信號:%d\n", (status >> 7) & 1, (status & 0x7F));}else{printf("子進程正常退出,退出碼:%d\n", (status >> 8) & 0xFF);}return 0;
}

?

站在?文件描述符?的角度理解上述代碼:

?

?所以,看待?管道?,就如同看待?文件?一樣!管道?的使用和?文件?一致,迎合?Linux一切皆文件思想。

?4.管道的四種場景?

4.1 場景一:父進程不寫,子進程嘗試讀取

情況描述:

  • 父進程沒有寫入數據到管道。

  • 子進程嘗試從管道中讀取數據。

結果:

  • 由于管道為空,子進程在嘗試讀取時會進入阻塞狀態。

  • 只有當父進程開始向管道中寫入數據后,子進程才會成功讀取數據。

形象化理解:

  • 這就像一個垃圾桶,子進程是倒垃圾的工作人員,而父進程是往垃圾桶里扔垃圾。如果垃圾桶為空,子進程(倒垃圾的人)就無法工作,必須等待父進程(扔垃圾的人)開始丟垃圾,才能開始工作。

4.2 場景二:父進程不斷寫入,直到管道寫滿,子進程不讀取

情況描述:

  • 父進程持續向管道寫入數據,直到管道被寫滿。

  • 子進程不進行讀取操作。

結果:

  • 當管道的緩沖區滿了,父進程會被阻塞,無法繼續寫入數據,直到子進程讀取數據。

  • 這是因為管道有大小限制,管道滿時,寫端無法繼續寫入,必須等待管道中有空間才能繼續寫入。

形象化理解:

  • 就像垃圾桶滿了后,不能繼續往里面丟垃圾,必須等到垃圾桶被清空(子進程讀取數據)之后,才能繼續丟垃圾。

4.3 場景三:關閉寫端,子進程讀取數據

情況描述:

  • 父進程寫入數據到管道,并關閉寫端。

  • 子進程從管道中讀取數據,并在讀取到末尾時判斷寫端是否關閉。

結果:

  • 當父進程關閉寫端后,子進程可以繼續讀取管道中的數據,直到數據讀取完。

  • 子進程在讀取到數據末尾時會收到 read的,表示已經沒有更多數據可讀取,且寫端已關閉。

形象化理解:

  • 這類似于垃圾桶的垃圾已經被倒空,子進程(倒垃圾的人)會看到垃圾桶已經沒有垃圾了。即使它繼續嘗試“倒垃圾”,也不會有新的垃圾,顯示讀取到了文件末尾。

4.4 場景四:關閉讀端,父進程寫入數據

情況描述:

  • 父進程是寫端,子進程是讀端。父進程寫入數據。

  • 父進程在讀取五次后關閉讀端。

結果:

  • 當關閉讀端后,寫端(父進程)會收到 SIGPIPE 信號,通常導致進程終止。

  • 因為操作系統會發現,寫端已沒有可用的讀取端(讀端關閉了),它會強制終止寫端進程以防止資源浪費。

形象化理解:

這就像垃圾桶的“倒垃圾的人”(寫端)發現沒有“垃圾桶”(讀端)可以丟垃圾,因此操作系統會終止寫端,避免無意義的行為繼續發生。


5、匿名管道實操-進程控制

匿名管道作為 IPC 的其中一種解決方案,那么肯定有它的實戰價值

場景:父進程創建了一批子進程,并通過多條匿名管道與它們鏈接,父進程選擇某個子進程,并通過匿名管道與子進程通信,并下達指定的任務讓其執行

5.1 邏輯設計

首先創建一批子進程及匿名管道 -> 子進程(讀端)阻塞,等待寫端寫入數據 -> 選擇相應的進程,并對其寫入任務編號(數據)-> 子進程拿到數據后,執行相應任務

1.創建一批進程及管道

首先需要先創建一個包含進程信息的類,最主要的就是子進程的寫端 fd,這樣父進程才能通過此 fd 進行數據寫入
循環創建管道、子進程,進行相應的管道鏈接操作,然后子進程進入任務等待狀態,父進程將創建好的子進程信息注冊
假設子進程獲取了任務代號,那么應該根據任務代號,去執行相應的任務,否則阻塞等待
注意: 因為是創建子進程,所以存在關系重復繼承的情況,此時應該統計當前子進程的寫端 fd,在創建下一個進程時,關閉無關的 fd

具體體現為:每次都把 寫端 fd 存儲起來,在確定關系前 “清理” 干凈

關于上述操作的危害,需要在編寫完進程等待函數后,才能演示其作用?。

完整代碼如下:

Task.hpp

#pragma once 
#include<iostream>
#include<vector>typedef void (*task_t)();void task1()
{std::cout << "lol 刷新日志" << std::endl;
}
void task2()
{std::cout << "lol 更新野區,刷新出來野怪" << std::endl;
}
void task3()
{std::cout << "lol 檢測軟件是否更新,如果需要,就提示用戶" << std::endl;
}
void task4()
{std::cout << "lol 用戶釋放技能,更新用的血量和藍量" << std::endl;
}
void LoadTask(std::vector<task_t>& task)
{task.push_back(task1);task.push_back(task2);task.push_back(task3);task.push_back(task4);
}

?processpool.cc

#include "Task.hpp"
#include <string>
#include <vector>
#include <cstdlib>
#include <ctime>
#include <cassert>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>using namespace std;const int processnum =4;
vector<task_t> tasks; //把所有的任務,裝進去class channel
{public:channel(int &cmdfd ,int &mypid,string &name):_name(name),_cmdfd(cmdfd),_mypid(mypid){}public:string _name;//子進程的名字int _cmdfd;//發信號的文件描述符  int _mypid;//我的PID
};void Menu()
{std::cout << "################################################" << std::endl;std::cout << "# 1. 刷新日志             2. 刷新出來野怪        #" << std::endl;std::cout << "# 3. 檢測軟件是否更新      4. 更新用的血量和藍量  #" << std::endl;std::cout << "#                         0. 退出               #" << std::endl;std::cout << "#################################################" << std::endl;
}void slaver(int pool)
{while(true){int cmdcode=0;//通過調用碼,去領任務int n=read(pool,&cmdcode,sizeof(int));cout <<"slaver say@ get a command: "<< getpid() << " : cmdcode: " <<  cmdcode << endl;if(cmdcode>0&&cmdcode<=tasks.size()) tasks[cmdcode-1]();if(n==0) break;}
}void InitProcesspool(vector<channel>&channels)
{vector<int> d;//把父進程的所有打開的寫管道存儲到里面for(int i=1;i<=processnum;i++){int pipeid[2];int n=pipe(pipeid);assert(!n);int pid=fork();if(pid==0){cout<<"創建的"<< i<<"號子進程"<<endl;for(auto &t:d){close(t);//關掉所有不相關的管道}close(pipeid[1]);//關閉寫管道slaver(pipeid[0]);//讀操作// close(pipeid[0]);//多此一舉cout<<"關閉的"<< i<<"號子進程"<<endl;exit(0);}close(pipeid[0]);string name="創建的子進程"+to_string(i);channels.push_back({pipeid[1],pid,name});//那個管道發數據記錄下來,d.push_back(pipeid[1]);//把一會發數據的管道號記下來sleep(1);}
}void setslaver(vector<channel>&channels)
{int which=0;int cnt=4;while(cnt--){int slect=0;Menu();cin>>slect;     if(!slect) break;   // rand((void)time(nullptr));// int i=srand()%5;cout<<"farher message"<<channels[which]._name<<endl;write(channels[which]._cmdfd,&slect,sizeof(int));     which++;which%=channels.size();sleep(1);}
}void Quitpool(vector<channel>&channels)
{for(auto& t:channels)//關閉所有的寫管道{close(t._cmdfd);waitpid(t._mypid,nullptr,0);}
}int main()
{vector<channel> channels;//把打開的子進程裝進來LoadTask(tasks);InitProcesspool(channels);//創建子進程setslaver(channels);//發配任務,采用輪詢Quitpool(channels);//關閉寫管道,等到read=0子進程退出,全部關閉return 0;
}

總體來說,在使用這個小程序時,以下關鍵點還是值得多注意的

注冊子進程信息時,存儲的是 寫端 fd,目的是為了通過此 fd 向對應的子進程寫數據,即使用不同的匿名管道

創建管道后,需要關閉父、子進程中不必要的 fd

需要特別注意父進程寫端 fd 被多次繼承的問題,避免因寫端沒有關干凈,而導致讀端持續阻塞關閉讀端對應的寫端后,讀端會讀到 0,可以借助此特性結束子進程的運行

在選擇進程 / 任務 時,要做好越界檢查

等待子進程退出時,需要先關閉寫端,子進程才會退出,然后才能正常等待。

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

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

相關文章

sql:sql在office中的應用有哪些?

在Office軟件套件中&#xff0c;主要是Access和Excel會用到SQL&#xff08;結構化查詢語言&#xff09;&#xff0c;以下是它們在這兩款軟件中的具體應用&#xff1a; 在Access中的應用 創建和管理數據庫對象&#xff1a; 創建表&#xff1a;使用CREATE TABLE語句可以創建新的數…

零基礎完全理解視覺語言模型(VLM):從理論到代碼實踐

本文是《從LLM到VLM&#xff1a;視覺語言模型的核心技術與Python實現》的姊妹篇&#xff0c;主要面向零基礎的讀者&#xff0c;希望用更通俗易懂的語言帶領大家入門VLM。本教程的完整代碼可以在GitHub上找到&#xff0c;如果你有任何問題或建議&#xff0c;歡迎交流討論。 寫在…

數據結構 Map和Set

文章目錄&#x1f4d5;1. 二叉搜索樹??1.1 查找操作??1.2 插入操作??1.3 刪除操作&#x1f4d5;2. Map的使用??2.1 Map的常用方法??2.2 TreeMap和HashMap的區別??2.3 HashMap的底層實現&#x1f4d5;3. Set的使用??3.1 Set的常用方法??3.2 TreeSet和HashSet的區…

樹莓派5-系統 Debian 12 開啟VNC遠程訪問踩坑記錄

簡單記錄一下踩坑&#xff0c;安裝vnc遠程訪問服務并設置開機自啟1.查看系統版本&#xff0c;我這里的系統版本是 12cat /etc/os-release2.安裝VNC服務sudo apt install realvnc-vnc-server realvnc-vnc-viewer -y3.創建服務單元文件&#xff1a;sudo nano /etc/systemd/system…

TASK2 夏令營:用AI做帶貨視頻評論分析

TASK2 夏令營&#xff1a;用AI做帶貨視頻評論分析**電商評論洞察賽題&#xff1a;從Baseline到LLM進階優化學習筆記**一、 賽題核心解讀1.1. 任務鏈條與目標1.2. 關鍵挑戰與評分機制二、 Baseline方案回顧與瓶頸分析2.1. Baseline技術棧2.2. 核心瓶頸三、 進階優化策略&#xf…

Docker:安裝命令筆記

目錄 零、安裝&#xff1a;略 一、鏡像 1.0、獲取鏡像&#xff1a; 1.1、查看鏡像&#xff1a; 1.2、刪除鏡像&#xff1a; 二、容器 2.0、創建并啟動容器 2.1、tomcat和jdk9的“創建并啟動容器”的命令 2.2、容器操作 2.3、容器日志操作 零、安裝&#xff1a;略 略 …

Python七彩花朵

系列文章 序號直達鏈接Tkinter1Python李峋同款可寫字版跳動的愛心2Python跳動的雙愛心3Python藍色跳動的愛心4Python動漫煙花5Python粒子煙花Turtle1Python滿屏飄字2Python藍色流星雨3Python金色流星雨4Python漂浮愛心5Python愛心光波①6Python愛心光波②7Python滿天繁星8Pytho…

【保姆級圖文詳解】MCP架構(客戶端-服務端)、三種方式使用MCP服務、Spring AI MCP客戶端和服務端開發、MCP部署方案、MCP安全性

文章目錄前言一、MCP(model context protocol)1.1、概念描述1.2、MCP作用與意義1.3、MCP架構二、使用MCP(model context protocol)2.1、云平臺使用MCP2.2、軟件客戶端使用MCP2.3、Spring AI程序中使用MCP三、Spring AI MCP(model context protocol)開發過程3.1、MCP服務端開發3…

Linux的 iproute2 配置:以太網(Ethernet)、綁定(Bond)、虛擬局域網(VLAN)、網橋(Bridge)筆記250713

Linux的 iproute2 配置:以太網(Ethernet)、綁定(Bond)、虛擬局域網(VLAN)、網橋(Bridge&#xff09;筆記250713 在 Linux 中使用 iproute2 工具集配置網絡是現代且推薦的方法&#xff0c;它取代了舊的 ifconfig、route、brctl、vconfig 等命令。iproute2 提供了統一的接口 ip …

當信任上鏈解碼區塊鏈溯源系統開發邏輯與產業變革

當信任上鏈&#xff1a;解碼區塊鏈溯源系統的開發邏輯與產業變革在上海某高端超市的進口水果區&#xff0c;消費者王女士拿起一盒車厘子&#xff0c;用手機掃描包裝上的二維碼&#xff0c;屏幕立刻彈出一串動態信息&#xff1a;智利瓦爾帕萊索港口的裝船時間、海關清關的具體日…

可視化DIY小程序工具!開源拖拽式源碼系統,自由搭建,完整的源代碼包分享

溫馨提示&#xff1a;文末有資源獲取方式傳統的小程序開發對技術要求較高&#xff0c;這使得許多非技術人員望而卻步。可視化DIY小程序工具應運而生&#xff0c;它通過拖拽式操作和開源代碼系統&#xff0c;極大地降低了開發門檻&#xff0c;讓更多人能夠快速構建個性化小程序。…

【MLLM】多模態理解GLM-4.1V-Thinking模型

note GLM-4.1V-Thinking模型引入 課程采樣強化學習&#xff08;RLCS, Reinforcement Learning with Curriculum Sampling&#xff09; 策略&#xff0c;在多個復雜推理任務中實現能力突破&#xff0c;整體性能達到 10B 級別視覺語言模型的領先水平。GLM-4.1V-9B-Thinking 通過…

【C++詳解】STL-priority_queue使用與模擬實現,仿函數詳解

文章目錄一、priority_queue使用仿函數控制優先級sort算法里的仿函數二、手撕優先級隊列優先級隊列的容器適配器入堆出堆top/size/empty迭代器區間構造初始化(解耦)三、仿函數仿函數控制冒泡排序仿函數控制priority_queue比較邏輯仿函數使用場景仿函數的其他使用場景源碼一、pr…

在mac m1基于ollama運行deepseek r1

1 下載和安裝 在ollama的官網下載mac m1版本的ollama https://ollama.com/ 最終獲得如下所示的下載地址 https://github.com/ollama/ollama/releases/latest/download/Ollama.dmg 然后點擊安裝&#xff0c;然后測試 ollama list 2 運行deepseek r1 deepseek-r1:8b 比較適…

TCP與UDP協議詳解:網絡世界的可靠信使與高速快遞

> 互聯網的骨架由傳輸層協議支撐,而TCP與UDP如同血管中的紅細胞與血小板,各司其職卻又缺一不可 ### 一、初識傳輸層雙雄:網絡通信的基石 想象你要給朋友寄送重要文件: - **TCP** 如同順豐快遞:**簽收確認+物流追蹤**,確保文件完整送達 - **UDP** 如同普通信件:**直接…

Datawhale AI 夏令營【更新中】

Datawhale AI 夏令營【更新中】夏令營簡介大模型技術&#xff08;文本&#xff09;方向&#xff1a;用AI做帶貨視頻評論分析機器學習&#xff08;數據挖掘&#xff09;方向&#xff1a;用AI預測新增用戶夏令營簡介 本次AI夏令營是Datawhale在暑期發起的大規模AI學習活動&#…

AutoDL掛載阿里云OSS

文章目錄前言AutoDL 設置阿里OSS設置OSS配置相關key 相關競猜時間前言 最近&#xff0c;AutoDL提示北京A區網盤功能要下架&#xff0c;然后需要對網盤中數據進行轉移等操作&#xff0c;我想網盤中數據下載到本地&#xff0c;大概16G&#xff1b;直接在網盤那里下載&#xff0c…

java 基本數據類型所對應的包裝類

一,對應列舉Java 中有 8 種基本數據類型&#xff0c;每種基本數據類型都有對應的包裝類&#xff0c;它們分別是&#xff1a;二,包裝類的作用1. 滿足面向對象編程需求Java 是面向對象的編程語言&#xff0c;基本數據類型不是對象&#xff0c;無法使用面向對象的特性&#xff08;…

牛客網50題-10

1.小苯的數字權值#include <iostream> #include <algorithm> using namespace std;const int max_n 2000000; int d[max_n 1]; int f[max_n 1];int main() {for(int i 1; i<max_n;i){for(int j i; j<max_n;ji){d[j];}}for(int i1; i<max_n;i){f[i] d…

基于springboot的大學公文收發管理系統

博主介紹&#xff1a;java高級開發&#xff0c;從事互聯網行業多年&#xff0c;熟悉各種主流語言&#xff0c;精通java、python、php、爬蟲、web開發&#xff0c;已經做了多年的畢業設計程序開發&#xff0c;開發過上千套畢業設計程序&#xff0c;沒有什么華麗的語言&#xff0…