共享內存實現進程通信

目錄

system V共享內存

共享內存示意圖

共享內存函數

shmget函數

shmat函數

shmdt函數

shmctl函數

代碼示例

shm頭文件

構造函數

獲取key值

創建者的構造方式

GetShmHelper?函數

GetShmUseCreate?函數

使用者的構造方式

GetShmForUse?函數

分離附加操作

DetachShm?函數

AttachShm?函數

RoleToString

析構函數

客戶端

服務端

命名管道與共享內存的區別

一、定義與工作原理

二、性能與效率

三、使用場景與限制

四、實現與操作


system V共享內存

共享內存區是最快的IPC形式。一旦這樣的內存映射到共享它的進程的地址空間,這些進程間數據傳遞不再涉及到內核,換句話說是進程不再通過執行進入內核的系統調用來傳遞彼此的數據。

共享內存示意圖

共享內存在我們的虛擬地址空間的共享區當中,這意味著它一旦通信建立,一有數據就能一下被拿到。

共享內存函數

shmget函數

功能:用來創建共享內存
原型int shmget(key_t key, size_t size, int shmflg);
參數key:這個共享內存段名字size:共享內存大小shmflg:由九個權限標志構成,它們的用法和創建文件時使用的mode模式標志是一樣的
返回值:成功返回一個非負整數,即該共享內存段的標識碼;失敗返回-1

shmat函數

功能:將共享內存段連接到進程地址空間
原型void *shmat(int shmid, const void *shmaddr, int shmflg);
參數shmid: 共享內存標識shmaddr:指定連接的地址shmflg:它的兩個可能取值是SHM_RND和SHM_RDONLY
返回值:成功返回一個指針,指向共享內存第一個節;失敗返回-1

說明:

shmaddr為NULL,核心自動選擇一個地址 shmaddr不為NULL且shmflg無SHM_RND標記,則以shmaddr為連接地址。 shmaddr不為NULL且shmflg設置了SHM_RND標記,則連接的地址會自動向下調整為SHMLBA的整數倍。公式:shmaddr - (shmaddr % SHMLBA) shmflg=SHM_RDONLY,表示連接操作用來只讀共享內存

shmdt函數

功能:將共享內存段與當前進程脫離
原型int shmdt(const void *shmaddr);
參數shmaddr: 由shmat所返回的指針
返回值:成功返回0;失敗返回-1
注意:將共享內存段與當前進程脫離不等于刪除共享內存段

shmctl函數

功能:用于控制共享內存
原型int shmctl(int shmid, int cmd, struct shmid_ds *buf);
參數shmid:由shmget返回的共享內存標識碼cmd:將要采取的動作(有三個可取值)buf:指向一個保存著共享內存的模式狀態和訪問權限的數據結構
返回值:成功返回0;失敗返回-1

代碼示例

shm頭文件

#ifndef _SHM_HPP_
#define _SHM_HPP_#include <iostream>
#include <cstdio>
#include <cerrno>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>#define Creater 1
#define User 2
const int gShmSize = 4096;
const std::string gpathname = "/home/lsf/lesson24/shm";
const int gproj_id = 0x66;class Shm
{
private:key_t GetCommKey(){key_t k = ftok(_pathname.c_str(), _proj_id);if (k < 0){perror("ftok");}return k;}int GetShmHelper(key_t key, int size, int flag){int shmid = shmget(key, size, flag);if (shmid < 0){perror("shmget");}return shmid;}std::string RoleToString(int who){if (who == Creater)return "Creater";else if (who == User)return "User";elsereturn "None";}void *AttachShm(){if (_addrshm != nullptr)DetachShm(_addrshm);void *shmaddr = shmat(_shmid, nullptr, 0);if (shmaddr == nullptr){perror("shmat");}std::cout << "who: " << RoleToString(_who) << " attach shm..." << std::endl;return shmaddr;}void DetachShm(void *shmaddr){if (shmaddr == nullptr)return;shmdt(shmaddr);std::cout << "who: " << RoleToString(_who) << " detach shm..." << std::endl;}public:Shm(const std::string &pathname, int proj_id, int who): _pathname(pathname), _proj_id(proj_id), _who(who), _addrshm(nullptr){_key = GetCommKey();if (_who == Creater)GetShmUseCreate();else if (_who == User)GetShmForUse();_addrshm = AttachShm();std::cout << "shmid: " << _shmid << std::endl;std::cout << "_key: " << ToHex(_key) << std::endl;}std::string ToHex(key_t key){char buffer[128];snprintf(buffer, sizeof(buffer), "0x%x", key);return buffer;}bool GetShmForUse(){if (_who == User){_shmid = GetShmHelper(_key, gShmSize, IPC_CREAT);// sleep(10);if (_shmid >= 0){std::cout << "shm get done..." << std::endl;return true;}}return false;}bool GetShmUseCreate(){if (_who == Creater){_shmid = GetShmHelper(_key, gShmSize, IPC_CREAT | IPC_EXCL | 0666);// sleep(10);if (_shmid >= 0){std::cout << "shm create done..." << std::endl;return true;}}return false;}void *Addr(){return _addrshm;}void Zero(){if (_addrshm){memset(_addrshm, 0, gShmSize);}}~Shm(){if (_who == Creater){int res = shmctl(_shmid, IPC_RMID, nullptr);}std::cout << "shm remove done..." << std::endl;}private:key_t _key;int _shmid;std::string _pathname;int _proj_id;int _who;void *_addrshm;
};#endif

成員變量中除了_pathname和_who一個表示路徑名一個表示身份外,其余的都是我們上文提到的函數的參數,我們遇到時一一講解。

構造函數

Shm(const std::string &pathname, int proj_id, int who): _pathname(pathname), _proj_id(proj_id), _who(who), _addrshm(nullptr){_key = GetCommKey();if (_who == Creater)GetShmUseCreate();else if (_who == User)GetShmForUse();_addrshm = AttachShm();std::cout << "shmid: " << _shmid << std::endl;std::cout << "_key: " << ToHex(_key) << std::endl;}

我們分別將我們的路徑,_proj_id(后面說),身份,_addeshm這些能設的初始值先設置好。然后我們就可以先獲得一個我們共享內存的key標識值,為什么不直接設置,而是要寫個函數呢?這是為了達到我們唯一性的目的,我們程序員自己設置的話難免會設置到重復的,為了避免它我們就可以用一個ftok函數,它會根據我們的路徑和_proj_id用一種算法設計出一個key值,我們采用它能達到我們心目中的要求。

它的返回值就是我們要的key值。

項目標識符(proj_id):除了文件路徑外,ftok()還需要一個項目標識符(proj_id)。這個標識符的最低有效8位將被用于生成鍵值。proj_id必須是非零的,以確保生成的鍵值不是全零(全零的鍵值在System V IPC中有特殊含義,通常表示無效的鍵值)。

獲取key值
key_t GetCommKey(){key_t k = ftok(_pathname.c_str(), _proj_id);if (k < 0){perror("ftok");}return k;}

這就是我們的獲取key值的函數,我們只是對ftok簡單的封裝了一下。

我們回到構造函數,在我們完成了key值的設置之后我們就要對創建者和使用則進行不同的構造方式了。

創建者的構造方式
int GetShmHelper(key_t key, int size, int flag){int shmid = shmget(key, size, flag);if (shmid < 0){perror("shmget");}return shmid;}bool GetShmUseCreate(){if (_who == Creater){_shmid = GetShmHelper(_key, gShmSize, IPC_CREAT | IPC_EXCL | 0666);// sleep(10);if (_shmid >= 0){std::cout << "shm create done..." << std::endl;return true;}}return false;}

GetShmHelper函數就是對我們的shmget函數做個簡單封裝,

GetShmHelper?函數

這個函數根據給定的鍵值(key)、大小(size)和標志(flag)來獲取或創建一個共享內存段。

  • 參數
    • key_t key:共享內存段的鍵值,由ftok()函數生成。
    • int size:共享內存段的大小(以字節為單位)。
    • int flag:控制shmget()行為的標志。可以是IPC_CREAT(如果鍵不存在,則創建新的共享內存段)、IPC_EXCL(與IPC_CREAT一起使用時,如果鍵已存在,則調用失敗)等標志的組合。
  • 返回值
    • 成功時返回共享內存段的標識符(shmid)。
    • 失敗時返回-1,并通過perror()函數打印錯誤信息。
GetShmUseCreate?函數

這個函數檢查某個條件(這里是通過_who變量與Creater比較),如果條件滿足,則嘗試創建一個新的共享內存段。

  • 變量
    • _who:用于指示當前進程的角色。
    • _key:共享內存段的鍵值。
    • gShmSize:共享內存段的大小。
    • _shmid:用于存儲共享內存段標識符的變量。
  • 邏輯
    • 如果_who等于Creater,則調用GetShmHelper函數嘗試創建新的共享內存段。
    • GetShmHelper的調用使用了IPC_CREAT | IPC_EXCL | 0666作為標志,這意味著如果鍵值已存在,則調用將失敗(因為IPC_EXCL標志的存在)。0666是權限設置,但由于IPC_CREATIPC_EXCL的存在,它實際上只在創建新段時有效。
    • 如果GetShmHelper返回非負值(即成功創建了共享內存段或獲取了已存在的共享內存段),則函數返回true
    • 如果GetShmHelper返回-1(即失敗),則函數返回false,并且不會打印"shm create done..."消息。
使用者的構造方式
bool GetShmForUse(){if (_who == User){_shmid = GetShmHelper(_key, gShmSize, IPC_CREAT);// sleep(10);if (_shmid >= 0){std::cout << "shm get done..." << std::endl;return true;}}return false;}
GetShmForUse?函數
  • 目的:嘗試獲取一個共享內存段以供使用。當前角色是用戶(_who == User),嘗試獲取或創建共享內存段。

  • 參數

    • 隱式參數(全局變量):
      • _who:當前進程的角色。
      • _key:共享內存段的鍵值。
      • gShmSize:共享內存段的大小。
      • _shmid:用于存儲共享內存段標識符的變量。
  • 邏輯

    • 如果_who等于User,則調用GetShmHelper函數嘗試獲取或創建共享內存段。
    • GetShmHelper的調用使用了IPC_CREAT作為標志。這意味著:
      • 如果鍵值對應的共享內存段已存在,則shmget將返回該段的標識符。
      • 如果鍵值對應的共享內存段不存在,則shmget將創建一個新的共享內存段,并返回其標識符。
    • 如果GetShmHelper返回非負值(即成功獲取了已存在的段或創建了新段),則函數返回true,打印"shm get done..."。
    • 如果GetShmHelper返回-1(即失敗,這通常不應該發生,因為IPC_CREAT允許創建新段)。
    • 如果_who不是User,則函數直接返回false
分離附加操作
void DetachShm(void *shmaddr){if (shmaddr == nullptr)return;shmdt(shmaddr);std::cout << "who: " << RoleToString(_who) << " detach shm..." << std::endl;}void *AttachShm(){if (_addrshm != nullptr)DetachShm(_addrshm);void *shmaddr = shmat(_shmid, nullptr, 0);if (shmaddr == nullptr){perror("shmat");}std::cout << "who: " << RoleToString(_who) << " attach shm..." << std::endl;return shmaddr;}

DetachShm?函數

  • 目的:分離(detach)之前附加(attach)的共享內存段。

  • 參數

    • shmaddr:指向之前附加的共享內存段的指針。
  • 邏輯

    • 如果傳入的shmaddrnullptr,則函數直接返回,不執行任何操作。這是一種防御性編程實踐,用于防止對空指針進行解引用。
    • 使用shmdt函數分離共享內存段。shmdt接受一個指向共享內存段的指針,并分離該進程與該內存段之間的關聯。
    • 打印一條消息,指示當前角色(通過RoleToString(_who)獲取)正在分離共享內存段。RoleToString是一個將角色轉換為字符串的函數。

AttachShm?函數

  • 目的:附加(attach)到一個已存在的共享內存段。

  • 參數:無。

  • 邏輯

    • 如果全局變量(或類成員變量)_addrshm不是nullptr,則首先調用DetachShm函數分離當前附加的共享內存段(如果有的話)。這是一種清理操作,確保在附加新的共享內存段之前不會泄漏舊的內存段。
    • 使用shmat函數附加到共享內存段。shmat接受共享內存段的標識符、一個指向期望附加地址的指針(這里傳入nullptr,讓系統選擇地址)、以及一組標志(這里傳入0,表示使用默認行為)。
    • 如果shmat返回nullptr,則使用perror函數打印一條錯誤消息。這意味著附加操作失敗,可能是由于資源不足、權限問題或其他原因。
    • 打印一條消息,指示當前角色(通過RoleToString(_who)獲取)正在附加共享內存段。
    • 返回shmat返回的指針,即指向附加的共享內存段的指針。如果shmat失敗,則返回nullptr
RoleToString
std::string RoleToString(int who){if (who == Creater)return "Creater";else if (who == User)return "User";elsereturn "None";}

將_who轉換成字符串。

接下來我們可以打印一下共享內存標識符和我們的key值,我們將key值轉換成十六進制,方便觀察。

std::string ToHex(key_t key){char buffer[128];snprintf(buffer, sizeof(buffer), "0x%x", key);return buffer;}
void *Addr(){return _addrshm;}

我們可以使用這個函數讓上層獲取共享內存的地址。

void Zero(){if (_addrshm){memset(_addrshm, 0, gShmSize);}}

我們可以用memset將這段共享內存清空。

析構函數

~Shm(){if (_who == Creater){int res = shmctl(_shmid, IPC_RMID, nullptr);}std::cout << "shm remove done..." << std::endl;}

這里我們也是讓創建者把共享內存移除。

int res = shmctl(_shmid, IPC_RMID, nullptr);:這行代碼調用?shmctl?函數嘗試刪除共享內存。_shmid?是共享內存的標識符,IPC_RMID?是刪除共享內存的命令,nullptr?是指向?shmid_ds?結構的指針(在這個命令中不需要,因此傳遞?nullptr)。函數返回的結果存儲在?res?變量中。

客戶端

#include "Shm.hpp"
#include "namedPipe.hpp"int main()
{//1.先創建共享內存Shm shm(gpathname,gproj_id,User);shm.Zero();char *shmaddr = (char*)shm.Addr();sleep(3);//2.打開管道NamePiped fifo(comm_path,User);fifo.OpenForWrite();char ch = 'A';while(ch <= 'Z'){shmaddr[ch-'A'] = ch;ch++;std::string temp = "weakup";std::cout<< "add" <<ch<<" into shm, "<<"weakup reader"<<std::endl;fifo.WriteNamedPipe(temp);sleep(2);}return 0;
}

我們創建共享內存并清空原先的數據,獲取一下共享內存地址,然后我們使用之前的管道,管道在這里的作用就是同步,因為我們的共享內存是直接內存訪問,這意味著我們的客戶端一發送服務端就能看到。所以我們需要借助管道的特性達到同步的效果,管道在這里發送什么信息并不重要。

服務端

#include "Shm.hpp"
#include "namedPipe.hpp"int main()
{//1.先創建共享內存Shm shm(gpathname,gproj_id,Creater);char *shmaddr = (char*)shm.Addr();//2.創建管道NamePiped fifo(comm_path,Creater);fifo.OpenForRead();while(true){std::string temp;fifo.ReadNamedPipe(&temp);std::cout<<"shm memory content: " << shmaddr << std::endl;sleep(1);}sleep(5);return 0;
}

服務端的邏輯也是一樣的,不同的是服務端是只讀。

代碼效果:

命名管道與共享內存的區別

命名管道與共享內存是兩種常見的進程間通信(IPC)機制,它們各自具有獨特的特點和適用場景。以下是兩者的主要區別:

一、定義與工作原理

  1. 命名管道(Named Pipe)

    • 也稱為FIFO(First In First Out)管道。
    • 它允許無親緣關系的進程間進行通信,通過一個在文件系統中存在的名字來標識,進程可以通過這個名字來訪問和通信。
    • 數據流動是單向的,即數據只能從一個進程流向另一個進程。如果需要雙向通信,通常需要創建兩個管道。
    • 命名管道常用于跨進程通信,并且允許數據以流的方式從一個進程傳輸到另一個進程。
  2. 共享內存(SharedMemory)

    • 它是映射一段能被其他進程所訪問的內存,這段共享內存由一個進程創建,但多個進程都可以訪問。
    • 它是針對其他進程間通信方式運行效率低而專門設計的,允許兩個或多個進程共享一個給定的存儲區。
    • 一個進程寫入共享內存的信息,可以被其他使用這個共享內存的進程通過一個簡單的內存讀取讀出,從而實現了進程間的通信。

二、性能與效率

  1. 命名管道

    • 數據傳輸通過內核緩沖區進行,存在一定的開銷。
    • 相比其他IPC機制(如信號、消息隊列等),命名管道的性能處于中等水平。
    • 它支持阻塞和非阻塞操作,提供了靈活的通信方式。
  2. 共享內存

    • 是最快的IPC方式之一,因為它允許進程直接訪問共享的內存區域,減少了數據的拷貝次數。
    • 相比命名管道等機制,共享內存具有更高的數據傳輸效率和更低的延遲。
    • 但是,共享內存需要額外的同步機制(如信號量)來避免數據競爭和不一致性問題。

三、使用場景與限制

  1. 命名管道

    • 適用于需要在不同進程間傳遞流式數據的場景。
    • 由于其半雙工特性和需要創建兩個管道以實現雙向通信的限制,命名管道在某些復雜通信場景中可能不夠靈活。
  2. 共享內存

    • 適用于需要高效數據傳輸和大量數據共享的場景。
    • 由于允許多個進程直接訪問同一塊內存區域,因此需要注意同步和互斥問題,以避免數據競爭和訪問沖突。

四、實現與操作

  1. 命名管道

    • 在類Unix系統中,可以通過mkfifo函數創建命名管道,并使用open、read、write等系統調用進行讀寫操作。
    • 在Windows系統中,命名管道通常通過特定的API進行創建和管理。
  2. 共享內存

    • 在類Unix系統中,可以使用shmget、shmat、shmdt、shmctl等系統調用進行共享內存的創建、掛接、去關聯和控制操作。
    • 在Windows系統中,也有相應的API用于共享內存的創建和管理。

綜上所述,命名管道和共享內存是兩種各具特色的進程間通信機制。命名管道適用于需要在不同進程間傳遞流式數據的場景,而共享內存則適用于需要高效數據傳輸和大量數據共享的場景。在選擇使用哪種IPC機制時,需要根據具體的應用需求和場景進行權衡和選擇。

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

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

相關文章

6月15日星期日早報簡報微語報早讀

6月15日星期日&#xff0c;農歷五月二十&#xff0c;早報#微語早讀。 1、證監會擬修訂期貨公司分類評價&#xff1a;明確扣分標準&#xff0c;優化加分標準&#xff1b; 2、國家考古遺址公園再添10家&#xff0c;全國已評定65家&#xff1b; 3、北京多所高校禁用羅馬仕充電寶…

破解關鍵領域軟件測試“三重難題”:安全、復雜性、保密性

在國家關鍵領域&#xff0c;軟件系統正成為核心戰斗力的一部分。相比通用軟件&#xff0c;關鍵領域軟件在 安全性、復雜性、實時性、保密性 等方面要求極高。如何保障安全合規前提下提升測試效率&#xff0c;確保系統穩定&#xff0c;已成為軟件質量保障的核心挑戰。 關鍵領域…

記錄一次 Oracle DG 異常停庫問題解決過程

記錄一次 Oracle DG 異常停庫問題解決過程 某醫院有以下架構的雙節點 Oracle 集群&#xff1a; 節點1:172.16.20.2 節點2:172.16.20.3 SCAN IP&#xff1a;172.16.20.1 DG&#xff1a;172.16.20.1206月12日&#xff0c;醫院信息科用戶反映無法連接 DG 服務器。 登錄 DG 服務…

MySQL使用EXPLAIN命令查看SQL的執行計劃

1?、EXPLAIN 的語法 MySQL 中的 EXPLAIN 命令是用于分析 SQL 查詢執行計劃的關鍵工具,它能幫助開發者理解查詢的執行方式并找出性能瓶頸??。 語法格式: EXPLAIN <sql語句> 【示例】查詢學生表關聯班級表的執行計劃。 (1)創建班級信息表和學生信息表,并創建索…

Go語言2個協程交替打印

WaitGroup 無緩沖channel waitgroup 用來控制2個協程 Add() 、Done()、Wait() channel用來實現信號的傳遞和信號的打印 ch1: 用來記錄打印的信號 ch2:用來實現信號的傳遞&#xff0c;實現2個協程的順序打印 package mainimport ("fmt""sync" )func ma…

微信小程序 路由跳轉

路由方式 官方參考文檔 wx.switchTab 實現底部導航欄 1.配置信息 app.json"tabBar": {"custom": true,"list": [{"pagePath": "pages/home/index","text": "首頁"},{"pagePath": "p…

[Java 基礎]正則表達式

正則表達式是一種強大的文本模式匹配工具&#xff0c;它使用一種特殊的語法來描述要搜索或操作的字符串模式。在 Java 中&#xff0c;我們可以使用 java.util.regex包提供的類來處理正則表達式。 :::color3 正則表達式不止 Java 語言提供了相應的功能&#xff0c;很多其他語言…

ArcGIS安裝出現1606錯誤解決辦法

問題背景&#xff1a; 由于最近Arcgis10.2打是有些功能不正常退出&#xff0c;比如arctoolbox中的&#xff0c;table to excel 功能&#xff0c;只要一點擊&#xff0c;arcgis就報錯退出&#xff0c;平常在使用過程中&#xff0c;也經常出現一些莫名其妙的崩潰現象&#xff0c…

wpf 解決DataGridTemplateColumn中width綁定失效問題

感謝酪酪烤奶 提供的Solution 文章目錄 感謝酪酪烤奶 提供的Solution使用示例示例代碼分析各類交互流程 WPF DataGrid 列寬綁定機制分析整體架構數據流分析1. ViewModel到Slider的綁定2. ViewModel到DataGrid列的綁定a. 綁定代理(BindingProxy)b. 列寬綁定c. 數據流 關鍵機制詳…

語音轉文本ASR、文本轉語音TTS

ASR Automatic Speech Recognition&#xff0c;語音轉文本。 技術難點&#xff1a; 聲學多樣性 口音、方言、語速、背景噪聲會影響識別準確性&#xff1b;多人對話場景&#xff08;如會議錄音&#xff09;需要區分說話人并分離語音。 語言模型適配 專業術語或網絡新詞需要動…

通用embedding模型和通用reranker模型,觀測調研

調研Qwen3-Embedding和Qwen3-Reranker 現在有一個的問答庫&#xff0c;包括150個QA-pair&#xff0c;用10個query去同時檢索問答庫的300個questionanswer Embedding模型&#xff0c;query-question的匹配分數 普遍高于 query-answer的匹配分數。比如對于10個query&#xff0c…

基于YOLOv8+Deepface的人臉檢測與識別系統

摘要 人臉檢測與識別系統是一個集成了先進計算機視覺技術的應用&#xff0c;通過深度學習模型實現人臉檢測、識別和管理功能。系統采用雙模式架構&#xff1a; ??注冊模式??&#xff1a;檢測新人臉并添加到數據庫??刪除模式??&#xff1a;識別數據庫中的人臉并移除匹…

Grdle版本與Android Gradle Plugin版本, Android Studio對應關系

Grdle版本與Android Gradle Plugin版本&#xff0c; Android Studio對應關系 各個 Android Gradle 插件版本所需的 Gradle 版本&#xff1a; https://developer.android.com/build/releases/gradle-plugin?hlzh-cn Maven上發布的Android Gradle Plugin&#xff08;AGP&#x…

用c語言實現簡易c語言掃雷游戲

void test(void) {int input 0;do{menu();printf("請選擇&#xff1a; >");scanf("%d", &input);switch (input){menu();case 1:printf("掃雷\n");game();break;case 2:printf("退出游戲\n");break;default:printf("輸入…

系統辨識的研究生水平讀書報告期末作業參考

這是一份關于系統辨識的研究生水平讀書報告&#xff0c;內容系統完整、邏輯性強&#xff0c;并深入探討了理論、方法與實際應用。報告字數超過6000字 從理論到實踐&#xff1a;系統辨識的核心思想、方法論與前沿挑戰 摘要 系統辨識作為連接理論模型與客觀世界的橋梁&#xff…

開源、免費、美觀的 Vue 后臺管理系統模板

隨著前端技術的不斷發展&#xff0c;Vue.js 憑借其輕量、高效、易上手的特性&#xff0c;成為國內外開發者最喜愛的前端框架之一。在構建后臺管理系統時&#xff0c;Vue 提供了以下優勢&#xff1a; 響應式數據綁定&#xff1a;讓頁面和數據保持同步&#xff0c;開發效率高。 …

適合 Acrobat DC 文件類型解析

文件類型 (File Type)ProgID (Continuous)ProgID (Classic)主要用途.pdfAcroExch.Document.DCAcroExch.Document.20XX (版本特定)Adobe PDF文檔格式&#xff0c;用于存儲文檔內容和格式.pdfxmlAcroExch.pdfxmlAcroExch.pdfxmlPDF與XML結合的格式&#xff0c;可能用于結構化數據…

C/C++數據結構之漫談

概述 在當今的數字化時代&#xff0c;無論是刷短視頻、社交聊天&#xff0c;還是使用導航軟件、網絡購物&#xff0c;背后都離不開計算機技術的支持。但你是否想過&#xff1a;為什么同樣的功能&#xff0c;有的軟件運行得飛快&#xff0c;有的卻嚴重卡頓&#xff0c;半天沒有響…

4步使用 vue3 路由

路由的基本使用步驟分為以下4步 第一步&#xff1a;定義路由組件&#xff1a;略 第二步&#xff1a;定義路由鏈接和路由視圖&#xff1a; <template><div class"app-container"><h1>App根組件</h1><router-link to"/home">…

VScode使用npm啟動項目以及npm install ,npm start報錯問題處理

安裝啟動步驟 打開cmd 輸入指令 npm -v 查看npm是否安裝&#xff0c;需要先安裝node.js node.js安裝&#xff1a;node.js安裝 安裝包下載后&#xff0c;一直點擊next &#xff0c;安裝完成&#xff0c;打開cmd 輸入 node -v 查看安裝是否成功 使用VScode 打開項目&#xf…