Linux系統編程:通過System V共享內存實現進程間通信

目錄

一. 共享內存實現進程間通信的原理

二.?共享內存相關函數

2.1 共享內存的獲取 shmget / ftok

2.2?共享內存與進程地址空間相關聯 shmat

2.3?取消共享內存與進程地址空間的關聯 shmdt

2.4?刪除共享內存?shmctl

2.5?通信雙方創建共享內存代碼

三.?共享內存實現進程間通信

3.1?實現方法及特性

3.2?為共享內存添加訪問控制

四.?總結


一. 共享內存實現進程間通信的原理

要實現進程間通信,就必須讓相互之間進行通信的進程看到同一份資源(同一塊內存空間),如通過管道實現進程間通信,本質就是讓兩個進程分別以讀和寫的方式打開同一份管道文件,一個進程向管道中寫數據,另一個進程再從管道中將數據讀出,這樣兩個進程就可以看到同一份內存空間,從而實現了進程間通信。

System V共享內存實現進程間通信的方式與管道相同,區別在于管道是基于文件的,而共享內存則是直接申請內存空間,不需用進行文件相關操作通過System V共享內存實現通信的進程,都會使用物理內存中的同一塊空間,這一塊公共的物理內存空間經過通信雙方進程的頁表,映射到進程地址空間的共享區,通信雙方進程在運行期間,拿到共享區虛擬地址,通過頁表映射,就可以看到同一塊物理內存,就可以實現進程間通信。

如果操作系統內有多組通過System V共享內存方式相互通信的進程處于運行狀態,那么就會存在多組共享內存,操作系統需要對這些共享內存空間進行管理,管理方式為:先通過struct結構體進行描述,再利用特定的數據結構組織

可以這樣理解:共享內存 =?共享的物理內存 +?對應的內核級數據結構。

圖1.1?共享內存的實現原理

二.?共享內存相關函數

共享內存實現進程間通信的步驟可以總結為:創建共享內存 ->?共享內存與地址空間相關聯 ->?通信 ->?共享內存與地址空間解綁 ->?銷毀共享內存。

2.1 共享內存的獲取 shmget / ftok

shmget函數:獲取共享內存

頭文件:#include<sys/ipc.h>、#include<sys/shm.h>

函數原型:int shmget(key_t key, size_t size, int shmflg)

函數參數:

? ? ? ? key --?特定共享內存的標識符

? ? ? ? size --?共享內存的大小

????????shmflg --?共享內存獲取的權限參數

返回值:創建成功返回共享內存的編號(稱為shmid),失敗返回-1

共享內存標識符key:OS中可能存在多個共享內存,需要保證通信雙方看到同一塊共享內存,因此,每個共享內存都需要一個特定的key值進行區分,這個key值是多少并不重要,只要保證它在OS中是唯一的即可通信雙方進程(Serve && Client)需要約定相同的算法,保證他們可以使用shmget獲取到同一塊共享內存

ftok函數可以用于獲取key值,只要調用ftok的實參相同,就會返回相同的key值。

ftok函數:獲取共享內存標識符key

頭文件:?#include<sys/ipc.h>、#include<sys/types.h>

函數原型:key_t ftok(const char* pathname, int proj_id);

函數參數:

? ? ? ? pathname:項目(文件)路徑

? ? ? ? proj_id:項目(文件)的id編號

返回值:成功返回特定的key值,否則返回-1。

共享內存大小size:以字節為單位,建議取頁(PAGE:4096bytes)大小的整數倍,因為如果獲取共享內存空間的大小不是頁大小的整數倍,OS就會向上取整申請到頁大小整數倍的內存空間,但是多申請的空間卻不能被用戶所使用。如,申請4097bytes的共享內存,OS會實際申請2*4096bytes的空間,而能被使用的只有4097bytes,剩下的都浪費掉了。

權限參數shmflg:有IPC_CREAT、IPC_EXCL、共享內存起始權限碼、0這幾種選項,他們之間通過豎劃線 |?隔開,每個選項都有其意義。

  • IPC_CREAT:如果key標識的共享內存存在,就直接將其獲取,如果不存在,就創建。
  • IPC_EXCL:單獨使用沒有任何意義,一般配合IPC_CREAT使用,IPC_CREAT | IPC_EXCL表示如果共享內存不存在就將其創建,如果存在直接報錯,這樣可以保證獲取到的共享內存是一塊全新的共享內存。
  • 起始權限碼:用戶對于這塊共享內存的使用權限,如0666就表示擁有者、所屬組、其他人就具有讀寫權限。
  • 0:只能獲取已經存在的共享內存,不能創建新的,不存在就報錯。

一般而言,通信雙方分別以?IPC_CREAT | IPC_EXCL?和 0?的方式獲取共享內存,確保一方創建全新的共享內存,另一方只能獲取到該共享內存(傳0阻斷不存在創建新共享內存的可能)。

代碼2.1以?IPC_CREAT | IPC_EXCL | 0666?的方式獲取共享內存,運行代碼,就可以成功獲取共享內存,但是當第二次運行代碼,卻發現運行出錯了(見圖2.1),這是因為該共享內存再第一次程序運行后被創建,存在于操作系統中,IPC_CREAT | IPC_EXCL獲取的共享內存一定是全新的,因此第二次運行程序會失敗,刪除該共享內存之后才可以再次成功運行。

結論:共享內存的生命周期是隨OS內核的,而不是隨進程的。

代碼2.1:獲取共享內存

// common.hpp -- 頭文件
#pragma once#include <iostream>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>#define PATH_NAME "."
#define PROJ_ID 0x66
#define SIZE 4096// shmServe.cc -- 客戶端代碼源文件(用于接收信息)
#include "common.hpp"int main()
{// 獲取共享內存key值key_t k = ftok(PATH_NAME, PROJ_ID);   if(k == -1){perror("ftok");exit(1);}// 創建共享內存int shmid = shmget(k, SIZE, IPC_CREAT | IPC_EXCL | 0666);if(shmid == -1){perror("shmget");exit(2);}printf("Serve# 共享內存獲取成功,shmid:%d\n", shmid);return 0;
}
圖2.1?代碼2.1的兩次運行結果

這里介紹兩條指令,分別用于查看共享內存信息和刪除共享內存:

  • ipcs -m 指令:查看系統中所有共享內存的詳細信息。
  • ipcrm -m [shmid]:通過指定共享內存的shmid來刪除指定的共享內存。

當然,也可以通過代碼刪除共享內存,本文后面會講解。

圖2.2?通過指令查看共享內存的屬性信息和刪除共享內存

2.2?共享內存與進程地址空間相關聯 shmat

shmat函數:將共享內存關聯到進程地址空間

頭文件:#include<sys/types.h>、#include<sys/shm.h>

函數原型:void* shmat(int shmid, const void* shmaddr, int shmflg)

函數參數:

? ? ? ? shmid:進行掛接的共享內存的shmid

? ? ? ? shmaddr:指定掛接的虛擬地址(傳NULL表示讓OS自動選擇掛接地址)

? ? ? ? shmflg:掛接權限相關參數

返回值:若成功返回掛接到的虛擬地址,失敗返回nullptr

掛接地址shmaddr參數:由于我們并不可知虛擬地址的具體使用情況,所以這個參數基本都是傳NULL/nullptr來讓OS自動選擇虛擬地址進行關聯。?

掛接權限shmflg:如果傳SHM_RDONLY,這表示對應共享內存空間只有讀權限,傳其他都是讀寫權限,一般shmflg都傳實參0。

當共享內存與虛擬地址關聯期間,使用ipcs -m指令查看共享內存屬性信息,nattch就會變為1,如果通信雙方都與共享內存進行了關聯,那么nattch就是2。

2.3?取消共享內存與進程地址空間的關聯 shmdt

shmdt函數:讓共享內存與當前進程脫離

頭文件:#include<sys/types.h>、#include<sys/shm.h>

函數原型:int shmdt(const char* shmaddr)

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

2.4?刪除共享內存?shmctl

通過共享內存控制shmctl函數(共享內存控制函數),可以刪除共享內存。

刪除共享內存的操作只要通信雙方有一方指向即可,否則會造成重復刪除。一般而言,讀取信息的進程創建新的共享內存,也負責刪除共享內存,遵循誰創建、誰刪除的原則。

shmctl函數:控制共享內存

頭文件:#include<sys/ipc.h>? #include<sys/shm.h>

函數原型:int shmctl(int shmid, int cmd, struct shmid_ds* buf)

函數參數:

? ? ? ? shmid --?共享內存的shmid

? ? ? ? cmd --?控制指令,選擇操作

? ? ? ? buf --?指向描述共享內存屬性信息的結構體指針

返回值:成功返回非負數,失敗返回-1

形參cmd可以選擇具體的控制策略:

  • IPC_STAT --?以buf為輸出型參數,獲取共享內存的屬性信息。
  • IPC_SET --?設置共享內存的屬性為buf指向的內容。
  • IPC_RMID --?刪除共享內存,此時buf傳空指針NULL。

2.5?通信雙方創建共享內存代碼

代碼2.2:頭文件common.hpp --?由通信雙方共同包含

#pragma once#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>#define PATH_NAME "."
#define PROJ_ID 0x66
#define SIZE 4096

代碼2.3:服務端代碼shmServe.cc --?用于數據讀取

#include "common.hpp"int main()
{// 獲取共享內存key值key_t k = ftok(PATH_NAME, PROJ_ID);   if(k == -1){perror("Serve ftok");exit(1);}printf("Serve# 成功獲取key值,key:%d\n", k);// 創建共享內存int shmid = shmget(k, SIZE, IPC_CREAT | IPC_EXCL | 0666);if(shmid == -1){perror("Sreve shmget");exit(2);}printf("Serve# 共享內存獲取成功,shmid:%d\n", shmid);// 將共享內存與進程相關聯char* shmaddr = (char*)shmat(shmid, NULL, 0);if(shmaddr == nullptr){perror("Serve shmat");exit(3);}printf("Serve# 共享內存與進程成功關聯,shmid:%d\n", shmid);// 通信代碼// ... ...// 讓共享內存脫離當前進程int n = shmdt(shmaddr);if(n == -1){perror("Serve shmdt");exit(4);}printf("Serve# 共享內存成功脫離進程,shmid:%d\n", shmid);// 刪除共享內存n = shmctl(shmid, IPC_RMID, NULL);if(n == -1){perror("Serve shmctl");exit(5);}printf("Serve# 共享內存刪除成功,shmid:%d\n", shmid);return 0;
}

代碼2.4:客戶端代碼shmClient.cc --?用于數據發送

#include "common.hpp"int main()
{// 獲取共享內存key值key_t k = ftok(PATH_NAME, PROJ_ID);   if(k == -1){perror("Client ftok");exit(1);}printf("Client# 成功獲取key值,key:%d\n", k);// 創建共享內存int shmid = shmget(k, SIZE, 0);if(shmid == -1){perror("Client shmget");exit(2);}printf("Client# 共享內存獲取成功,shmid:%d\n", shmid);// 將共享內存與進程相關聯char* shmaddr = (char*)shmat(shmid, NULL, 0);if(shmaddr == nullptr){perror("Client shmat");exit(3);}printf("Client# 共享內存與進程成功關聯,shmid:%d\n", shmid);// 通信代碼// ... ...// 讓共享內存脫離當前進程int n = shmdt(shmaddr);if(n == -1){perror("Client shmdt");exit(4);}printf("Client# 共享內存成功脫離進程,shmid:%d\n", shmid);return 0;
}

三.?共享內存實現進程間通信

3.1?實現方法及特性

在數據輸入端(shmClient),我們可以將共享內存視為一塊通過malloc得來的char*指向的一段動態內存空,可以使用printf系列函數向這塊空間寫數據,或者將共享內存空間視為數組,使用下標的形式給每個位置賦值,這樣就實現了將數據寫入共享內存。

在數據讀取端(shmServe),可以將共享內存視為一個大字符串,通過特定的方式,從這個大字符串中獲取數據即可。

代碼3.1和代碼3.2實現了共享內存進程間通信的簡單邏輯,在shmClient端,通過下標訪問的方式,每隔3s寫一次數據,在shmServe端,每隔1s讀取一次數據。先運行shmServe端代碼,間隔幾秒后運行shmClient端代碼,根據圖3.1展示的運行結果,shmServe端在shmClient端開始運行之前就開始讀取共享內存中的內容,在shmClient運行起來后,由于讀快寫慢,shmClient寫入的內容在shmServe端被多次讀取,可見,共享內存,沒有訪問控制。

結論1:共享內存沒有訪問控制。

代碼3.1:shmClient端發送數據

    // 通信代碼char ch = 'a';int count = 0;for(; ch <= 'c'; ++ch){shmaddr[count++] = ch;printf("write succsee# %s\n", shmaddr);sleep(3);}snprintf(shmaddr, SIZE, "quit");

代碼3.2:shmServe端讀取數據?

    // 通信代碼while(true){printf("[Client say]# %s\n", shmaddr);if(strcmp(shmaddr, "quit") == 0) break;sleep(1);}
圖3.1?共享內存通信讀寫雙方代碼執行結果

通過觀察上面的代碼我們發現,用戶可以直接向共享內存中寫數據和從共享內存中讀取數據,不需要經過用戶級緩沖區,共享內存的讀或寫操作最少只需要一次拷貝即可完成。而通過管道進行讀寫,則需要將數據預先寫入或讀入緩沖區,才可以寫入管道文件或讀出。圖3.2為使用管道和共享內存的方法進行進程間通信時,讀和寫操作涉及的數據拷貝情況,管道通信至少要進行兩次數據拷貝,而共享內存可以只進行一次數據拷貝,因此共享內存是一種高效的進程間通信手段。

結論2:共享內存進行進程間通信,通信的一方向共享內存中寫入數據,通信的另一方馬上就能讀取到數據,不需要向操作系統中拷貝數據,共享內存是所有進程間通信方法中效率最高的。

圖3.2?管道和共享內存實現進程間通信的資源拷貝情況

管道通信的特性總結:

  1. 不具有訪問控制,存在并發問題。
  2. 不需要向OS內核中拷貝數據,通信效率高。

3.2?為共享內存添加訪問控制

通過使用命名管道加以輔助,就可以為共享內存添加訪問控制,具體的實現方法和原理為:

  • 在讀端(shmServe)程序開始運行時創建命名管道文件,程序運行結束后管道文件銷毀。
  • 在寫端(shmClient)向共享內存中寫入數據后,向管道文件中寫入任意的、少量的數據,在讀端(shmServe)獲取共享內存內容之前,先讀取管道中的資源,如果寫端沒有將期望的數據全部寫入共享內存,那么就不會向管道中寫數據,讀端就必須阻塞等待管道中被寫入數據,也就無法獲取共享內存中的數據。只有當寫端完成向共享內存中寫入一次數據,然后向管道文件中寫入數據讓讀端讀到了管道資源后,讀端代碼才可以繼續運行,獲取到共享內存中的資源。

代碼3.3 ~ 3.5,為通過管道為共享內存添加訪問控制的實現代碼。

代碼3.3:common.hpp頭文件 --?被通信雙方源文件包含

#pragma once#include <iostream>
#include <cstring>
#include <cassert>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/shm.h>#define PATH_NAME "."
#define PROJ_ID 0x66
#define SIZE 4096#define FIFO_NAME "fifo.ipc"
#define MODE 0666// 定義類,其構造和析構函數可以創建和銷毀管道文件
class Init
{
public:Init(){int n = mkfifo(FIFO_NAME, MODE);if(n == -1) perror("mkfifo");assert(n != -1);(void)n;}~Init(){int n = unlink(FIFO_NAME);assert(n != -1);(void)n;}
};#define READ O_RDONLY 
#define WRITE O_WRONLY// 管道文件打開函數
int OpenFifo(const char* pathname, int flags)
{int fd = open(pathname, flags);assert(fd != -1);return fd;
}// 等待函數 -- 用于讀端訪問控制
// 管道內沒有資源時就阻塞
void Wait(int fd)
{uint32_t temp = 0;ssize_t sz = read(fd, &temp, sizeof(uint32_t));assert(sz == sizeof(uint32_t));(void)sz;
}// 喚醒函數 -- 用于寫端進程控制
// 向管道內寫數據,終止讀端進程的阻塞等待
void WakeUp(int fd)
{uint32_t temp = 1;ssize_t sz = write(fd, &temp, sizeof(uint32_t));assert(sz == sizeof(uint32_t));(void)sz;
}// 管道關閉函數
void CloseFifo(int fd)
{close(fd);
}

代碼3.4:讀端源文件(shmServe.cc)代碼

#include "common.hpp"// 全局類對象
// 構造和析構函數分別負責管道文件的創建和銷毀
Init init;int main()
{// 獲取共享內存key值key_t k = ftok(PATH_NAME, PROJ_ID);   if(k == -1){perror("Serve ftok");exit(1);}printf("Serve# 成功獲取key值,key:%d\n", k);// 創建共享內存int shmid = shmget(k, SIZE, IPC_CREAT | IPC_EXCL | 0666);if(shmid == -1){perror("Sreve shmget");exit(2);}printf("Serve# 共享內存獲取成功,shmid:%d\n", shmid);// 將共享內存與進程相關聯char* shmaddr = (char*)shmat(shmid, NULL, 0);if(shmaddr == nullptr){perror("Serve shmat");exit(3);}printf("Serve# 共享內存與進程成功關聯,shmid:%d\n", shmid);// 通信代碼int fd = OpenFifo(FIFO_NAME, READ);   // 只讀方式打開管道文件while(true){Wait(fd);   // 等待讀取printf("[Client say]# %s\n", shmaddr);if(strcmp(shmaddr, "quit") == 0) break;}// while(true)// {//     printf("[Client say]# %s\n", shmaddr);//     if(strcmp(shmaddr, "quit") == 0) break;//     sleep(1);// }// 讓共享內存脫離當前進程int n = shmdt(shmaddr);if(n == -1){perror("Serve shmdt");exit(4);}printf("Serve# 共享內存成功脫離進程,shmid:%d\n", shmid);// 刪除共享內存n = shmctl(shmid, IPC_RMID, NULL);if(n == -1){perror("Serve shmctl");exit(5);}printf("Serve# 共享內存刪除成功,shmid:%d\n", shmid);CloseFifo(fd);return 0;
}

代碼3.5:寫端源文件(shmClient.cc)代碼

#include "common.hpp"int main()
{// 獲取共享內存key值key_t k = ftok(PATH_NAME, PROJ_ID);   if(k == -1){perror("Client ftok");exit(1);}printf("Client# 成功獲取key值,key:%d\n", k);// 創建共享內存int shmid = shmget(k, SIZE, 0);if(shmid == -1){perror("Client shmget");exit(2);}printf("Client# 共享內存獲取成功,shmid:%d\n", shmid);// 將共享內存與進程相關聯char* shmaddr = (char*)shmat(shmid, NULL, 0);if(shmaddr == nullptr){perror("Client shmat");exit(3);}printf("Client# 共享內存與進程成功關聯,shmid:%d\n", shmid);// 通信代碼int fd = OpenFifo(FIFO_NAME, WRITE);while(true){ssize_t sz = read(0, shmaddr, SIZE);    // 共享內存從鍵盤中讀入數據(換行符也被寫入)assert(sz >= 0);shmaddr[sz - 1] = '\0';   //末尾添加'\0'表示終止WakeUp(fd);    // 喚醒讀端進程if(strcmp(shmaddr, "quit") == 0) break;}// char ch = 'a';// int count = 0;// for(; ch <= 'c'; ++ch)// {//     shmaddr[count++] = ch;//     printf("write succsee# %s\n", shmaddr);//     sleep(3);// }// snprintf(shmaddr, SIZE, "quit");// 讓共享內存脫離當前進程int n = shmdt(shmaddr);if(n == -1){perror("Client shmdt");exit(4);}printf("Client# 共享內存成功脫離進程,shmid:%d\n", shmid);CloseFifo(fd);return 0;
}

四.?總結

  • System V共享內存實現進程間通信的底層原理是通信雙方進程看到同一塊內存,位于物理內存上的共享內存塊,通過頁表映射到通信雙方的進程地址空間的共享區,通信雙方拿到共享區的虛擬地址,通過頁表映射,訪問到同一塊物理內存。
  • 使用System V共享內存實現進程間通信的操作流程為:通過ftok函數獲取唯一的共享內存標識符key ->?通過shmget函數獲取共享內存 ->?通過shmat函數讓共享內存和進程綁定 -> 【進行進程通信】->?通過shmdt函數讓共享內存和進程脫離 ->?通過shmctl刪除共享內存。
  • System V共享內存 進程間通信的特點為:(1)不需要向操作系統內核中拷貝數據,是所有進程間通信的方法中效率最高的。(2)沒有訪問控制。
  • 通過管道的輔助,可以為?System V共享內存 進程間通信添加訪問控制。

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

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

相關文章

承接各種設計

小弟985研究生畢業&#xff0c;目前攻讀讀博士&#xff0c;可做各種設計&#xff0c;包括但不限于Matlab 電力電子/電氣工程&#xff0c;matlab/simulink 電氣專業仿真MATLAB 電氣工程專業&#xff0c;matlab建模 電力電子&#xff0c;電氣工程&#xff0c;電力系統&#xff0c…

vue echarts macd指標 完整代碼

1 邏輯 給指定的series兩個對象 兩個對象有相同的xAxisIndex: 2,yAxisIndex: 2, 不同的data {name: "",type: "line",data: data1,xAxisIndex: 2,yAxisIndex: 2,},{name: "",type: "bar",data: data2,xAxisIndex: 2,yAxisIndex: 2,},…

Mac M2 Pro安裝使用Cocoapods

Mac Pro M2安裝使用Cocoapods 在新公司要做iOS開發&#xff0c;所以在新電腦上安裝Cocoapods 在升級gem&#xff0c;sudo gem update --system&#xff0c;和安裝cocoapods時都遇到如下的提示&#xff1a; ERROR: While executing gem ... (Errno::EPERM)Operation not per…

Linux下安裝nodejs

1、下載nodejs 點擊前往&#xff1a;Download | Node.js 2、解壓 tar -xvf node-v18.16.0-linux-x64.tar.xz mv node-v18.16.0-linux-x64/ /usr/local/nodejs 3、 建立軟鏈接 此時的bin文件夾中已經存在node以及npm&#xff0c;如果你進入到對應文件的中執行命令行一點問題…

現代C++:使用 shared_from_this 防止 this 提前被釋放

首先概括一下shared_from_this的作用&#xff1a;可以在類的成員函數中直接通過this得到指向當前所在對象的shared_ptr的智能指針&#xff0c;具體操作如下。 使用方法 設需要提供shared_from_this方法的類為C0定義為類&#xff0c;首先需要將C0定義為 std::enable_shared_fr…

mysql 02 數據庫的約束

為防止錯誤的數據被插入到數據表&#xff0c;MySQL中定義了一些維護數據庫完整性的規則&#xff1b;這些規則常稱為表的約束。常見約束如下&#xff1a; 主鍵約束 主鍵約束即primary key用于唯一的標識表中的每一行。被標識為主鍵的數據在表中是唯一的且其值不能為空。這點類似…

前后端分離------后端創建筆記(10)用戶修改

本文章轉載于【SpringBootVue】全網最簡單但實用的前后端分離項目實戰筆記 - 前端_大菜007的博客-CSDN博客 僅用于學習和討論&#xff0c;如有侵權請聯系 源碼&#xff1a;https://gitee.com/green_vegetables/x-admin-project.git 素材&#xff1a;https://pan.baidu.com/s/…

Spring Boot實現第一次啟動時自動初始化數據庫流程詳解

隨著互聯網的發展項目中的業務功能越來越復雜&#xff0c;有一些基礎服務我們不可避免的會去調用一些第三方的接口或者公司內其他項目中提供的服務&#xff0c;但是遠程服務的健壯性和網絡穩定性都是不可控因素。 在測試階段可能沒有什么異常情況&#xff0c;但上線后可能會出…

https證書獲取的方法及好處

我們常說的https證書其實就是ssl證書&#xff0c;眼下為網站部署https證書是保障網站安全必不可少的一步。而https證書該如何獲取呢&#xff1f;下面就簡單介紹一下https證書獲取的方法。 https證書獲取途徑有兩種&#xff1a;自己簽發和由受信任的CA機構簽發。 自己給自己簽…

全國三網優惠話費充值接口開發指南

一、文檔綜述 近期想做項目的看過來~三網&#xff08;全國移動、聯通、電信&#xff09;話費、電費充值接口能夠實現將接口接入到小程序或者app上面&#xff0c;通過接口提交號碼和金額進行充值&#xff0c;可以幫助相關人員快速完成接口對接與聯調&#xff0c;平臺用戶可以通…

設計HTML5文本

網頁文本內容豐富、形式多樣&#xff0c;通過不同的版式顯示在頁面中&#xff0c;為用戶提供最直接、最豐富的信息。HTML5新增了很多文本標簽&#xff0c;它們都有特殊的語義&#xff0c;正確使用這些標簽&#xff0c;可以讓網頁文本更嚴謹、更符合語義。 1、通用文本 1.1、標…

算法競賽備賽之搜索與圖論訓練提升,暑期集訓營培訓

目錄 1.DFS和BFS 1.1.DFS深度優先搜索 1.2.BFS廣度優先搜索 2.樹與圖的遍歷&#xff1a;拓撲排序 3.最短路 3.1.迪杰斯特拉算法 3.2.貝爾曼算法 3.3.SPFA算法 3.4.多源匯最短路Floy算法 4.最小生成樹 4.1.普利姆算法 4.2.克魯斯卡爾算法 5.二分圖&#xff1a;染色法…

7. CSS(四)

目錄 一、浮動 &#xff08;一&#xff09;傳統網頁布局的三種方式 &#xff08;二&#xff09;標準流&#xff08;普通流/文檔流&#xff09; &#xff08;三&#xff09;為什么需要浮動&#xff1f; &#xff08;四&#xff09;什么是浮動 &#xff08;五&#xff09;浮…

OpenAI全球招外包大軍,手把手訓練ChatGPT取代碼農 ; 碼農:我自己「殺」自己

目錄 前言 OpenAI招了一千多名外包人員&#xff0c;訓練AI學會像人類一樣一步步思考。如果ChatGPT「學成歸來」&#xff0c;碼農恐怕真的危了&#xff1f; 碼農真的危了&#xff01; 當時OpenAI也說&#xff0c;ChatGPT最合適的定位&#xff0c;應該是編碼輔助工具。 用Cha…

常用的Elasticsearch查詢DSL

1.基本查詢 GET /index_name/_search {"query": {"match": {"dispatchClass": "1"}} }2.多條件查詢 GET /index_name/_search {"query": {"bool": {"must": [{"match": {"createUser&…

計算機競賽 opencv 圖像識別 指紋識別 - python

0 前言 &#x1f525; 優質競賽項目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于機器視覺的指紋識別系統 &#x1f947;學長這里給一個題目綜合評分(每項滿分5分) 難度系數&#xff1a;3分工作量&#xff1a;3分創新點&#xff1a;4分 該項目較為新穎&#xff0c;適…

Vue引入Echarts報錯 import * as echarts from “echarts“;

項目場景&#xff1a; 已經下載好echarts cnpm i echarts Vue引入Echarts import echarts from echarts mounted() {this.myChart echarts.init(document.querySelector(.right))this.myChart.setOption({title: {text: 消費列表,left: center},...問題描述 原因分析&#…

【100天精通python】Day38:GUI界面編程_PyQT從入門到實戰(中)

目錄 專欄導讀 4 數據庫操作 4.1 連接數據庫 4.2 執行 SQL 查詢和更新&#xff1a; 4.3 使用模型和視圖顯示數據 5 多線程編程 5.1 多線程編程的概念和優勢 5.2 在 PyQt 中使用多線程 5.3 處理多線程間的同步和通信問題 5.3.1 信號槽機制 5.3.2 線程安全的數據訪問 Q…

日常BUG——通過命令行創建vue項目報錯

&#x1f61c;作 者&#xff1a;是江迪呀??本文關鍵詞&#xff1a;日常BUG、BUG、問題分析??每日 一言 &#xff1a;存在錯誤說明你在進步&#xff01; 一、問題描述 在使用vue命令行創建一個vue項目時&#xff0c;出現一下的錯誤&#xff1a; vue create my…

UDP數據報結構分析(面試重點)

在傳輸層中有UDP和TCP兩個重要的協議&#xff0c;下面將針對UDP數據報的結構進行分析 UDP結構圖示 UDP報頭結構的分析 UDP報頭有4個屬性&#xff0c;分別是源端口&#xff0c;目的端口&#xff0c;UDP報文長度&#xff0c;校驗和&#xff0c;它們都占16位2個字節&#xff0c;所…