WebServer -- 定時器處理非活動連接(上)

目錄

🍍函數指針

🌼基礎知識

🐙整體概述

🎂基礎API

sigaction 結構體

sigaction()

sigfillset()

SIGALRM, SIGTERM 信號

alarm()

socketpair()

send()

📕信號通知流程

統一事件源

信號處理機制

🌼源碼分析

信號處理函數

信號通知邏輯

代碼


🍍函數指針

函數指針是指向函數的指針變量。在C和C++中,函數被存儲在內存中的某個位置,函數指針可以指向這個內存位置,從而允許通過指針間接調用函數

函數指針的聲明方式👇

返回類型 (*指針變量名)(參數列表);

例如,如果有一個函數 void myFunction(int x)

void (*funcPtr)(int); // 聲明一個指向函數的指針變量 funcPtr
funcPtr = &myFunction; // 將函數的地址賦給指針變量
// 通過函數指針調用函數
funcPtr(10); // 調用 myFunction(10)

🌼基礎知識

非活躍?

客戶端(即瀏覽器)與服務器建立連接后,長時間不交換數據,一直占用服務器的文件描述符,導致連接資源浪費

定時事件

固定一段時間后觸發某段代碼,由該代碼處理一個事件

eg: 從內核表刪除事件,并關閉文件描述符,釋放連接資源

定時器

利用結構體 / 其他形式,將多種 定時事件 封裝。

具體的,這里只涉及一種定時事件,即 -- 定期檢測非活躍連接,

這里將該定時事件與連接資源,封裝為一個 結構體定時器

定時器容器

使用某種容器類 數據結構,將上述多個定時器組合起來,便于對定時事件統一管理

比如,項目中使用 升序鏈表 ,將所有定時器串聯起來

🐙整體概述

簡介

  • 定時器處理非活動連接是一種機制,用于檢測和關閉長時間沒有活動的客戶端連接
  • 定時器會周期性地檢查連接的活動狀態,并在連接超過一定時間沒有任何數據傳輸時,將其標記為非活動連接并關閉
  1. 具體來說,當客戶端與服務器建立連接后,服務器會啟動一個定時器來監視該連接的活動狀態
  2. 每當服務器接收到客戶端發送的數據或發送數據給客戶端時,定時器會被重置,表示該連接是活動的
  3. 如果在一段時間內沒有任何數據傳輸,定時器將超時并關閉該連接

TinyWebServer 中,服務器 主循環 為每一個連接創建一個 定時器,并對每個連接進行定時

此外,升序事件鏈表容器,將所有定時器串聯起來

若 主循環 收到定時通知,則在鏈表中依次執行 定時任務

Linux提供 3 種定時方法👇

  • socket 選項 SO_RECVTIMEO 和 SO_SNDTIMEO
  • SIGALRM信號
  • I / O 復用系統調用的超市參數

TinyWebServer 使用 SIGALRM 信號

具體的,利用 alarm() 函數周期性地觸發 SIGALRM 信號,信號處理函數利用 管道 通知 主循環

主循環 接收到信號后,處理 升序鏈表 的所有定時器

若這段時間內,沒有交換數據,則將該連接關閉,釋放占用的資源

由此可見,定時器處理 非活動連接模塊,分 2 部分:

1)定時方法 與 信號通知流程

2) 定時器 及其 容器設計與定時任務 的處理

總覽

定時方法 + 信號通知流程

涉及 基礎API,信號通知流程,代碼實現

基礎API

sigaction 結構體,SIGALRM 信號, SIGTERM 信號

函數👇

sigaction(),sigfillset(),alarm(),socketpair(),send()

信號通知流程

?統一事件源 + 信號處理機制

🎂基礎API

更好的源碼閱讀體驗~

sigaction 結構體

  • sa_handler()?-- 函數指針,指向信號處理函數
  • sa_sigaction() -- 信號處理函數,3個參數,獲得關于信號更詳細的信息
  • sa_mask -- 信號處理函數執行期間,需要被屏蔽的信號
  • sa_falgs -- 信號處理行為
    • SA_RESTART:使被信號打斷的系統調用,重新自動發起
    • SA_NOCLDSTOP:使父進程,在其子進程 暫停/繼續運行 時,不會收到 SIGCHLD 信號
    • SA_NOCLDWAIT:使父進程,在其子進程 退出 時,不會收到 SIGCHLD 信號,此時 子進程 退出不會成為僵尸進程
    • SA_NODEFER:使對信號的屏蔽無效,即,在信號處理函數期間,仍能發出這個信號
    • SA_RESETHAND:信號處理之后,重新設置為默認的處理方式
    • SA_SIGINFO:使用 sa_sigaction 成員,而不是 sa_handler 作為信號處理函數
  • sa_restorer -- 一般不使用
// 定義一個結構體sigaction,用于處理信號
struct sigaction {// 指向處理信號的函數指針,接受一個int參數void (*sa_handler)(int);// 指向處理信號的函數指針,接受3個參數void (*sa_sigaction)(int, siginfo_t*, void*);// 信號掩碼sigset_t sa_mask;// 標志位int sa_flags;// 恢復處理程序的函數指針,不接受參數,不返回任何值void (*sa_restorer)(void);
};

sigaction()

  • signum? ? 操作的信號
  • act? ? 對信號設置新的處理方式
  • oldact? ? 信號舊的處理方式
  • 返回值,0 成功,-1 有錯誤發生
#include<signal.h>int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);

sigfillset()

信號集

👆在 Linux 系統中,通常使用?sigset_t類型來表示信號集。這個類型通常是一個整數數組,每個元素對應一個信號,用來表示該信號是否被設置

👇將參數 set 信號集 初始化,然后把所有信號加入此信號集

#include<signal.h>int sigfillset(sigset_t *set);

SIGALRM, SIGTERM 信號

#define SIGALRM 14 // alarm 系統調用 產生timer時鐘信號
#define SIGTERM 15 // 終端發送的終止信號

alarm()

設置信號傳送鬧鐘,即,設置信號 SIGALRM,經過參數 seconds 秒后,發送給目前進程

如果未設置信號 SIGALRM 的處理函數,那么 alarm() 默認處理終止進程

#include<unistd.h>unsigned int alarm(unsigned int seconds);

socketpair()

Linux 下,使用 socketpair() 函數,創建一對套接字進行通信,TinyWebServer 使用管道通信

  • domain -- 協議族(PF_UNIX 或 AF_UNIX)
  • type -- 協議(SOCK_STREAM 或 SOCK_DGRAM),SOCK_STREAM 基于 TCP,SOCK_DGRAM 基于 UDP
  • protocol -- 類型(只能為 0)
  • sv[2] -- 套接字柄對,兩個句柄作用相同,均可獨寫雙向操作
  • 返回結果,0 成功,-1 創建失敗
#include<sys/types.h>
#include<sys/socket.h>
int socketpair(int domain, int type, int protocol, int sv[2]);

send()

套接字 發送緩沖區 變滿時,send 通常會阻塞,除非 套接字 設置成 非阻塞模式

當緩沖區變滿,返回 EAGAIN 或 EWOULDBLOCK 錯誤,此時可調用 select() 函數,來監視何時可以發送數據

#include<sys/types.h>
#include<sys/socket.h>ssize_t send(int sockfd, const void *buf, size_t len, int flags);

📕信號通知流程

Linux 下信號,采用 異步處理機制,信號處理函數 和 當前進程 是 2 條不同的執行路線

異步處理機制👇解釋

在Linux系統中,信號就像是一種突然發生的事件通知,比如按下Ctrl+C鍵發送的中斷信號。當這個事件發生時,操作系統會中斷當前正在進行的工作,去執行與之對應的處理函數,處理完后再回到原來的工作。

這個處理過程是異步的,也就是說,處理信號的函數和當前正在執行的程序是兩條不同的路線。處理函數負責響應信號事件,而當前程序則會在收到信號時被中斷,等待處理完畢后再繼續執行。這樣可以保證及時響應各種突發事件,確保系統的穩定和安全。

當進程收到信號時,操作系統會中斷當前的正常流程,轉而進入信號處理函數執行操作,完成后再返回中斷的地方繼續執行

為了避免 信號競態 的發生,信號處理期間,系統不會再次觸發它

所以,為確保該信號不被屏蔽太久,信號處理函數,需要盡可能快地執行完畢

信號處理函數,需要處理該信號對應的邏輯,當該邏輯較復雜,信號處理函數執行時間過長,會導致信號屏蔽太久

解決方案

信號處理函數,僅發送信號,通知程序主循環,將信號對應的處理邏輯,放在主循環中

由主循環執行信號對應的邏輯代碼

統一事件源

指的是,將 信號事件 與 其他事件 一樣被處理

eg:信號處理函數使用? 管道? 將信號傳遞給? 主循環

信號處理函數往? 管道的寫端? 寫入信號值

主循環則從? 管道的讀端? 讀出信號值

使用 I / O 復用系統調用來監聽? 管道讀端? 的可讀事件

此時,信號事件 與 其他文件描述符 都可以通過 epoll 來監測,從而實現統一處理

信號處理機制

每個進程中,都存在一個表,里面存著每種信號所代表的含義,內核通過設置表項中每一個位,來標識對應的信號類型

  • 信號接收
    • 接收信號的任務是由 內核 代理的,當內核接收到信號后,會將其放到對應進程的信號隊列中,同時向進程發送一個中斷,使其陷入內核態。注意,此時信號還只是在隊列中,對進程來說,暫時不知道信號到來了
  • 信號檢測
    • 進程從內核態返回到用戶態前,進行信號檢測
    • 進程在內核態中,從睡眠被喚醒時,進行信號檢測
    • 進程陷入內核態后,有 2 種場景對信號進行檢測
    • 當發現新信號時,會進入下一步,信號的處理
  • 信號處理
    • (內核)信號處理函數,運行在 用戶態,調用處理函數前,內核會將當前的內核棧的內容備份,拷貝到用戶棧上,并修改指令寄存器(eip),將其指向信號處理函數
    • (用戶)接下來,進程返回用戶態,執行相應的信號處理函數
    • (內核)信號處理函數 執行完畢后,還要返回內核態,檢查是否還有其他信號未處理
    • (用戶)所有信號處理完后,內核棧就會恢復(從用戶棧的備份拷貝),同時恢復指令寄存器(eip),將其指向中斷前的運行位置,最后返回用戶態,繼續執行進程

到此,一個完整的 信號處理流程 就結束了

如果同時有多個信號到達,上面的處理流程,會在第 2 步和第 3 步間循環

🌼源碼分析

信號處理函數

自定義信號處理函數,創建 sigaction 結構體變量,設置信號函數

// 信號處理函數
void sig_handler(int sig)
{// 為保證函數的可重入性,保留原來的errno// 可重入性:中斷后,再次進入該函數,環境變量與之前相同// 不會丟失數據int save_errno = errno;int msg = sig;// 信號值從 管道寫端 寫入,傳輸字符類型,而非 整型send(pipefd[1], (char*)&msg, 1, 0);// 原來的 errno 賦值為當前的 errnoerrno = save_errno;
}

?信號處理函數中,僅通過管道發送信號值,不處理信號對應的邏輯,縮短異步執行時間,減少對主程序影響

void addsig(int sig, void(handler)(int), bool restart = true);

👆解釋

  1. sig:要注冊的信號。
  2. handler:信號處理函數。
  3. restart:標志,指示是否在中斷系統調用后自動重啟該調用。

函數聲明中使用了函數指針作為參數,這意味著 handler 參數必須是一個指向函數的指針,該函數接受一個整數參數并返回 void 類型

// 設置信號函數
void addsig(int sig, void(handler)(int), bool restart = true)
{// 創建 sigaction 結構體變量struct sigaction sa;// 起始地址, 初值,字節數memeset(&sa, '\0', sizeof(sa));// 信號處理函數中僅發送 信號值,不做對應邏輯處理sa.sa_handler = handler; // 函數指針// 對結構體變量 按位或,SA_RESTART 標志位變1// 表示需要自動重啟if (restart)sa.sa_flags |= SA_RESTART;// 所有信號添加到 信號集 中sigfillset(&sa.sa_mask);// 執行 sigaction() 函數assert(sigaction(sig, &sa, NULL) != -1);
}

項目中設置信號函數,僅關注 SIGTERM 和 SIGALRM 兩個信號

信號通知邏輯

  • 創建管道,管道寫端 寫入信號值,管道讀端 通過 I / O 復用系統 檢測讀事件
  • 設置信號處理函數 SIGALRM(時間到了觸發)和 SIGTERM(kill 會觸發,ctrl + c)
    • 通過 struct sigaction 結構體 和 sigaction() 函數,注冊信號捕捉函數(結構體和函數進行關聯)
    • 在結構體的 handler 參數,設置信號處理函數,具體的,從 管道寫端 寫入信號名字
  • 利用 I /O 復用系統,監聽 管道讀端 文件描述符的可讀事件
  • 信息值 傳遞給主循環,主循環再根據接收到的 信號值,執行目標信號對應的邏輯代碼

代碼

// 創建管道套接字
ret = socketpair(PF_UNIX, SOCK_STREAM, 0, pipefd);
assert(ret != -1);// 設置管道寫端非阻塞,為什么寫端要非阻塞?
setnonblocking(pipefd[1]);// 設置管道讀端為 ET 非阻塞
addfd(epollfd, pipefd[0], false);// 傳遞給主循環的信號值,這里只關注 SIGALRM 和 SIGTERM
addsig(SIGALRM, sig_handler, false);
addsig(SIGTERM, sig_handler, false);// 循環條件
bool stop__server = false;// 超時標志
bool timeout = false;// 每隔 TIMESLOT 時間,觸發 SIGALRM 信號
alarm(TIMESLOT);while (!stop_server)
{// 監測發生事件的文件描述符int number = epoll_wait(epollfd, events, MAX_EVENT_NUMBER, -1);if (number < 0 && errno != EINTR)break;// 輪詢文件描述符for (int i = 0; i < number; i++) {int sockfd = events[i].data.fd;// 管道讀端 對應的 文件描述符 發生讀事件/*按位與 & 運算符判斷某個特定的標志位是否在 events[i].events 中被設置(是否發生了讀事件)如果結果為真,表示發生了讀事件程序將會執行相應的邏輯來處理該事件*/if ( (sockfd == pipefd[0]) &&  (events[i].events & EPOLLIN) ){int sig;char signals[1024];// 從 管道讀端 讀出信號值,成功-返回字節數,失敗-返回-1// 一般,這里的ret返回值總是 1// 只有 14,15 兩個 ASCII碼 對應的字符ret = recv(pipefd[0], signals, sizeof(signals), 0);if (ret == -1) // handle the errorcontinue;else if (ret == 0)continue;else {// 處理 信號值 對應的邏輯for (int i = 0; i < ret; ++i) {switch (signals[i]) { // charcase SIGALRM: // int{timeout = true;break;}case SIGTERM:stop_server = true;}}}}}
}

補充解釋

ret = recv(pipefd[0], signals, sizeof(signals), 0);
  • pipefd[0]?是管道的讀端文件描述符
  • signals?是一個緩沖區,用于存儲接收到的數據
  • sizeof(signals)?表示?signals?緩沖區的大小,即要接收的數據的最大字節數
  • 0?是可選的參數,用于指定接收數據時的額外標志。在此處,它表示沒有任何特殊的處理要求

函數執行過程👇

  1. recv()?函數阻塞等待,直到管道讀端文件描述符 (pipefd[0]) 中有數據可讀
  2. 一旦有數據可讀,recv()?函數將讀取數據并將其存儲在?signals?緩沖區中
  3. recv()?函數返回讀取的字節數,并將其賦值給變量?ret,以便后續處理

問題1:為什么 管道寫端 要 非阻塞?

send() 將信息發送給套接字緩沖區,如果緩沖區滿了,就會阻塞

這時會進一步增加 信號處理函數 的執行時間,為此,將其修改為 非阻塞

👆補充解釋

  • 代碼中的 setnonblocking(pipefd[1]) 調用將管道寫端設置為非阻塞模式,這意味著寫入管道時不會被阻塞,而是立即返回。這是因為在主循環中,當有事件發生時,程序會將相應的數據寫入管道以觸發 SIGALRM 或 SIGTERM 信號,從而實現定時和停止服務器的功能。如果管道寫端是阻塞的,則當數據無法立即寫入管道時,程序會被阻塞等待,直到數據寫入成功或出現錯誤。這可能會導致定時事件失效或無法及時停止服務器。因此,將管道寫端設置為非阻塞模式必要的?

問題2:沒有對 非阻塞返回值 處理,如果 阻塞 是不是意味著這一次 定時事件 失效 了?

對,但 定時事件 不是必須立即處理的事件,可以允許這樣的情況發生

👆補充解釋

  • 代碼中的管道寫端是非阻塞的,因此如果寫入的數據無法立即發送,則會立即返回,并且不會阻塞等待。如果沒有處理非阻塞返回值,則會導致寫入失敗而沒有得到及時處理。如果這種情況經常發生,則可能會導致定時事件失效,因為無法在規定的時間內將數據寫入管道
  • 定時事件不是必須立即處理的事件。在這個例子中,定時事件是通過 SIGALRM 信號實現的,每隔 TIMESLOT 時間就會觸發一次該信號。即使寫入管道失敗,也不會導致 SIGALRM 信號失效,因為下一次定時事件仍然會在規定的時間內觸發。因此,在這種情況下,可以允許寫入管道失敗并且不處理非阻塞返回值

問題3管道 傳遞的是什么類型?switch-case 的變量沖突?

信號 本身是 整型,管道中傳遞的是 ASCII碼 表中整型對應的字符

switch 的變量,一般為 字符或整型,當 switch 變量為字符,case 中可以為字符,也可以是字符對應的 ASCII 碼

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

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

相關文章

2024全球網絡安全展望|構建協同生態,護航數字經濟

2024年1月&#xff0c;世界經濟論壇發布《2024全球網絡安全展望》報告&#xff0c;指出在科技快速發展的背景下&#xff0c;網絡安全不均衡問題加劇&#xff0c;需加強公共部門、企業組織和個人的合作。 報告強調&#xff0c;面對地緣政治動蕩、技術不確定性和全球經濟波動&am…

基于springboot+vue的美發門店管理系統(前后端分離)

博主主頁&#xff1a;貓頭鷹源碼 博主簡介&#xff1a;Java領域優質創作者、CSDN博客專家、阿里云專家博主、公司架構師、全網粉絲5萬、專注Java技術領域和畢業設計項目實戰&#xff0c;歡迎高校老師\講師\同行交流合作 ?主要內容&#xff1a;畢業設計(Javaweb項目|小程序|Pyt…

Python 高級語法:一切皆對象

1 “一切皆對象”是一種核心設計哲學 在編程領域&#xff0c;特別是面向對象編程&#xff08;OOP&#xff09;中&#xff0c;“一切皆對象”是一種核心設計哲學。這種哲學主張&#xff0c;無論是數據、函數、還是更復雜的結構&#xff0c;都可以被視為對象&#xff0c;并賦予…

信息安全基本概念匯總

目錄 一、安全加密算法相關 二、信息安全需求規范相關 三、安全啟動 四、安全更新 五、安全通信SecOC 六、HSM安全固件整體架構 一、安全加密算法相關 基于Autosar的網絡安全理解_搜狐汽車_搜狐網 基于AES的CMAC算法、MAC、Hash、數字簽名之間的關系_aes cmac-CSDN博客…

Cartographer框架簡述

catographer框架分為前端和后端 前端包括雷達數據處理&#xff1b;位姿預測&#xff1b;掃描匹配和柵格地圖更新。 后端包括后端&#xff1a;線程池任務與調度&#xff1b;向位姿圖添加節點&#xff0c;計算節點的子圖內約束和子圖間約束&#xff08;回環檢測&#xff09;&…

C++之Easyx——圖形庫的基本功能(1):界面操作

最近&#xff0c;我覺得使用控制臺編寫游戲太沒意思了&#xff01;&#xff01; 所以我開始研究圖形庫了~ 一、setinitmode 函數定義 void EGEAPI setinitmode(int mode, int x CW_USEDEFAULT, int y CW_USEDEFAULT); //設置初始化模式&#xff0c;mode0為普通&#xff0c…

Spark中寫parquet文件是怎么實現的

背景 本文基于 Spark 3.5.0 寫本篇文章的目的是在于能夠配合spark.sql.maxConcurrentOutputFileWriters參數來加速寫parquet文件的速度&#xff0c;為此研究一下Spark寫parquet的時候會占用內存的大小&#xff0c;便于配置spark.sql.maxConcurrentOutputFileWriters的值&#…

Javascript怎么輸出內容?兩種常見方式以及控制臺介紹

javascript是一種非常重要的編程語言&#xff0c;在許多網頁中它被廣泛使用&#xff0c;可以實現許多交互效果和動態效果。輸出是javascript中最基本的操作之一&#xff0c;下面將介紹兩種常見的輸出方式。 一、使用console.log()函數輸出 console.log()函數是常用的輸出函數…

Jmeter實現階梯式線程增加的壓測

安裝相應jmeter 插件 1&#xff1a;安裝jmeter 管理插件&#xff1a; 下載地址&#xff1a;https://jmeter-plugins.org/install/Install/&#xff0c;將下載下來的jar包放到jmeter文件夾下的lib/ext路徑下&#xff0c;然后重啟jmeter。 2&#xff1a;接著打開 選項-Plugins Ma…

在Linux上安裝Docker: 一站式指南

Docker 是一款強大的容器化平臺&#xff0c;為開發者提供了一種輕松打包、發布和運行應用的方式。在本文中&#xff0c;我們將探討如何在Linux操作系統上安裝Docker&#xff0c;為你提供一站式指南。 步驟1: 卸載舊版本 在安裝新版Docker之前&#xff0c;建議先卸載舊版本&am…

三十年一個大輪回!日股突破“泡沫時期”歷史高點

2月22日周四&#xff0c;英偉達四季報業績超預期&#xff0c;而且本季度業績指引非常樂觀&#xff0c;提振美股股指期貨并成為芯片股和AI概念股情緒的重要催化劑。今日亞洲芯片股和AI股起飛&#xff0c;日本在芯片股的帶動下突破1989年泡沫時期以來的歷史最高收盤價。 美股方面…

我之前炒股虧麻了,找百融云AI Agent談了談心

春節之前&#xff0c;A股和H股都跌麻了&#xff0c;但是機構的路演和調研反而多了。因為&#xff1a;寫不完的安撫、說不完的陪伴、聽不完的客戶指責、以及撿不完的AH股便宜貨。 有一位血液里流淌著美式咖啡的職場白領&#xff0c;雖然這些年在股市過得很不如意&#xff0c;但…

C語言---鏈表

一.定義 鏈表是由一系列節點組成&#xff0c;每個結點包含兩個域&#xff0c;一個是數據域&#xff0c;數據域用來保存用戶數據&#xff0c;另一個是指針域&#xff0c;保存下一個節點的地址。鏈表在內存中是非連續的。 二.分類 靜態鏈表 動態鏈表 單向鏈表 雙向鏈表 循環鏈…

maven使用問題及解決辦法匯總

文章目錄 1、maven clean后打包出現Cannot create resource output directory2、把已有jar包打包進本地maven倉庫 1、maven clean后打包出現Cannot create resource output directory 主要原因是target目錄被別的程序占用了&#xff0c;最笨的辦法是重啟電腦&#xff0c;當然也…

C++跨模塊釋放內存

linux一個進程只有一個堆&#xff0c;不要考慮這些問題&#xff0c;但是windows一個進程可能有多個堆&#xff0c;要在對應的堆上釋放。 一&#xff0c; MT改MD 一個進程的地址空間是由一個可執行模塊和多個DLL模塊構成的&#xff0c;這些模塊中&#xff0c;有些可能會鏈接到…

代碼隨想錄訓練營第29天| 491.遞增子序列、46.全排列、47.全排列 II

491.遞增子序列 題目鏈接&#xff1a;491. 非遞減子序列 - 力扣&#xff08;LeetCode&#xff09; class Solution {List<List<Integer>> ans new ArrayList<>();public List<List<Integer>> findSubsequences(int[] nums) {backtrack(nums, …

(十三)【Jmeter】線程(Threads(Users))之tearDown 線程組

簡述 操作路徑如下: 作用:在正式測試結束后執行清理操作,如關閉連接、釋放資源等。配置:設置清理操作的采樣器、執行順序等參數。使用場景:確保在測試結束后應用程序恢復到正常狀態,避免資源泄漏或對其他測試的影響。優點:提供清理操作,確保測試環境的整潔和可重復性…

租用海外服務器,自己部署ChatGPT-Next-Web,實現ChatGPT聊天自由,還可以分享給朋友用

前言 如果有好幾個人需要使用ChatGPT&#xff0c;又沒有魔法上網環境&#xff0c;最好就是自己搭建一個海外的服務器環境&#xff0c;然后很多人就可以同時直接用了。 大概是情況是要花80元租一個一年的海外服務器&#xff0c;花15元租一個一年的域名&#xff0c;然后openai 的…

centos安裝擴展

centos下安裝php擴展時遇到的問題php 1.imapgit cd /root/php-5.6.27/ext/imap /usr/local/php/bin/phpize ./configure --prefix/usr/local/imap 錯誤1github configure: error: utf8_mime2text() has new signature, but U8T_CANONICAL is missing. This should not happe…

一 些有代表性的相位解包裹算法

Itoh首先給出了傳統解包裹算法的數學描述!。傳統的相位解包裹操作是通過對空間相鄰點相位值的比較來完成的。根據抽樣定理&#xff0c;如果相鄰采樣點的相位差不超過z&#xff0c;則對應的相位解包裹處理是非常簡單的&#xff0c;理論上以某點為起始點沿某一路徑對包裹相位的差…