Linux信號【產生-保存-處理】

目錄

前言:

1、進程信號基本概念

1.1、什么是信號?

1.2、信號的作用

2、鍵盤鍵入

2.1、ctrl+c 終止前臺進程

2.1.1、signal 注冊執行動作

3、系統調用

3.1、kill 函數

3.2、模擬實現 myKill

3.3、raise 函數

3.4、abort 函數

4、軟件條件信號產生(發送)的第三種方式:軟件條件

4.1、alarm 設置鬧鐘

5、硬件異常

5.1、除 0 導致異常

5.2、狀態寄存器

5.3、野指針導致異常

6.核心轉儲?

6.2、打開與關閉核心轉儲

6.3、核心轉儲的作用

?總結:



前言:

在?Linux?中,進程具有獨立性,進程在運行后可能 “放飛自我”,這是不利于管理的,于是需要一種約定俗成的方式來控制進程的運行,這就是?進程信號,本文將會從什么是進程信號開篇,講述各種進程信號的產生方式及作用

正文:

1、進程信號基本概念

1.1、什么是信號?

信號 是信息傳遞的承載方式,一種信號往往代表著一種執行動作,比如:

  • 雞叫 =>?天快亮了
  • 鬧鐘 =>?起床、完成任務
  • 紅綠燈 =>?紅燈停,綠燈行

當然這些都是生活中的 信號,當產生這些 信號 時,我們會立馬想到對應的 動作 ,這是因為 我們認識并能處理這些信號

我們能進行處理是因為受過教育,學習了執行動作,但對進程來說,它可沒有接受過九年義務教育,也不知道什么時候該干什么事

于是程序員們給操作系統植入了一批 指令,一個指令表示一種特殊動作,而這些指令就是 信號(進程信號)

kill -l

?

這些就是當前系統中的 進程信號,一共 62 個,其中 1~31 號信號為 普通信號(學習目標),用于 分時操作系統;剩下的 34~64 號信號為 實時信號,用于 實時操作系統

  • 分時操作系統:根據時間片實行公平調度,適用于個人電腦
  • 實時操作系統:高響應,適合任務較少、需要快速處理的平臺,比如汽車車機、火箭發射控制臺
1.2、信號的作用

早在 《Linux進程學習【進程狀態】》 我們就已經使用過?信號?了,比如:

  • kill -9 pid?終止進程運行
  • kill -19 pid?暫停進程運行
  • kill -18 pid?恢復進程運行

就連常用的?ctrl+c?和?ctrl+d?熱鍵本質上也是?信號

這么多信號,其對應功能是什么呢?

  • 可以通過?man 7 signal?進行查詢
man 7 signal

?

?簡單總結一下,1~31?號信號對應的功能如下(表格內容引用自?2021dragon?Linux中的31個普通信號)

?

?注意:?其中的?9?號 和?19?號信號是非常特殊的,不能修改其默認動作

1.3、信號的基本認知
進程信號由 信號編號 + 執行動作 構成,一個信號對應一種動作,對于進程來說,動作無非就這幾種:終止進程、暫停進程、恢復進程,3 個信號就夠用了啊,為什么要搞這么多信號?

  • 創造信號的目的不只是控制進程,還要便于管理進程,進程的終止原因有很多種,如果一概而論的話,對于問題分析是非常不友好的,所以才會將信號細分化,搞出這么多信號,目的就是為了方便定位、分析、解決問題
  • 并且 普通信號 就 31 個,這就是意味著所有普通信號都可以存儲在一個 int 中,表示是否收到該信號(信號的保存)

所以信號被細化了,不同的信號對應不同的執行動作,雖然大部分最終都是終止進程

進程的執行動作是可修改的,默認為系統預設的 默認動作

  1. 默認動作
  2. 忽略
  3. 自定義動作

所以我們可以 更改信號的執行動作(后面會專門講信號處理相關內容)

信號有這么多個,并且多個進程可以同時產生多個信號,操作系統為了管理,先描述、再組織,在 PCB 中增加了 信號相關的數據結構:signal_struct,在這個結構體中,必然存在一個 位圖結構 uint32_t signals 存儲 1~31 號信號的有無信息

?

//信號結構體源碼(部分)
struct signal_struct {atomic_t		sigcnt;atomic_t		live;int			nr_threads;wait_queue_head_t	wait_chldexit;	/* for wait4() *//* current thread group signal load-balancing target: */struct task_struct	*curr_target;/* shared signal handling: */struct sigpending	shared_pending;/* thread group exit support */int			group_exit_code;/* overloaded:* - notify group_exit_task when ->count is equal to notify_count* - everyone except group_exit_task is stopped during signal delivery*   of fatal signals, group_exit_task processes the signal.*/int			notify_count;struct task_struct	*group_exit_task;/* thread group stop support, overloads group_exit_code too */int			group_stop_count;unsigned int		flags; /* see SIGNAL_* flags below *//** PR_SET_CHILD_SUBREAPER marks a process, like a service* manager, to re-parent orphan (double-forking) child processes* to this process instead of 'init'. The service manager is* able to receive SIGCHLD signals and is able to investigate* the process until it calls wait(). All children of this* process will inherit a flag if they should look for a* child_subreaper process at exit.*/unsigned int		is_child_subreaper:1;unsigned int		has_child_subreaper:1;//……
};

1.信號是執行的動作的信息載體,程序員在設計進程的時候,早就已經設計了其對信號的識別能力
2.信號對于進程來說是異步的,隨時可能產生,如果信號產生時,進程在處理優先級更高的事情,那么信號就不能被立即處理,此時進程需要保存信號,后續再處理
3.進程可以將 多個信號 或 還未處理 的信號存儲在 signal_struct 這個結構體中,具體信號編號,存儲在 uint32_t signals 這個位圖結構中
4.所謂的 “發送” 信號,其實就是寫入信號,修改進程中位圖結構中對應的比特位,由 0 置為 1,表示該信號產生了
5.signal_struct 屬于內核數據結構,只能由 操作系統 進行同一修改,無論信號是如何產生的,最終都需要借助 操作系統 進行發送
6.信號并不是立即處理的,它會在合適的時間段進行統一處理

?

本文講解的就是?信號產生?部分相關知識,下面正式開始學習?信號產生?


2、鍵盤鍵入

信號產生(發送)的第一種方式:鍵盤鍵入

通俗來說就是命令行操作

2.1、ctrl+c 終止前臺進程

系統卡死遇到過吧?程序死循環遇到過吧?這些都是比較常見的問題,當發生這些問題時,我們可以通過?鍵盤鍵入?ctrl + c?發出?2號信號終止前臺進程的運行

?下面是一段死循環代碼:

#include <iostream>
#include <unistd.h>
using namespace std;int main()
{while(true){cout << "我是一個進程,我正在運行…… PID: " << getpid() << endl;sleep(1);}return 0;
}

運行程序后,會一直循環打印,此時如果想要終止進程,可以直接按?ctrl + c?發出?2?號信號,終止前臺進程?

此時發出了一個 2 號信號 SIGINT 終止了該進程的運行

如何證明呢?如何證明按 ctrl + c 發出的是 2 號信號呢?

證明自有方法,前面說過,一個信號配有一個執行動作,并且執行動作是可以修改的,需要用到 signal 函數(屬于 信號處理 部分的內容,這里需要提前用一下)

2.1.1、signal 注冊執行動作

signal?函數可以用來?修改信號的執行動作,也叫注冊自定義執行動作

signal 調用成功返回上一個執行方法的值(其實就是下標,后面介紹),失敗則返回 SIG_ERR,并設置錯誤碼

返回值可以不用關注,重點在于 signal 的參數

參數1 待操作信號的編號
參數2 待注冊的新方法
參數1 就是信號編號,為 int,單純地傳遞 信號名也是可以的,因為信號名其實就是信號編號的宏定義

?

參數2?是一個函數指針,意味著需要傳遞一個?參數為?int,返回值為空的函數對象?

  • 參數?int?是執行動作的信號編號
void handler(int)	//其中的函數名可以自定義

顯然,signal?函數是一個?回調函數,當信號發出時,會去調用相應的函數,也就是執行相應的動作

我們先對?2?號信號注冊新動作,在嘗試按下?ctrl + c,看看它發出的究竟是不是?2?號信號

?

#include <iostream>
#include <signal.h>
#include <unistd.h>
using namespace std;void handler(int signo)
{cout << "當前 " << signo << " 號信號正在嘗試執行相應的動作" << endl;
}int main()
{//給 2 號信號注冊新方法signal(2, handler);while(true){cout << "我是一個進程,我正在運行…… PID: " << getpid() << endl;sleep(1);}return 0;
}

?

當我們修改 2 號信號的執行動作后,再次按下 ctrl + c 嘗試終止前臺進程,結果失敗了!執行動作變成了我們注冊的新動作

這足以證明 ctrl + c 就是在給前臺進程發出 2 號信號,ctrl + c 失效后,可以通過 ctrl + \ 終止進程,發出的是 3 號信號(3 號信號在發出后,會生成 核心轉儲 文件)

普通信號只有 31 個,如果把所有普通信號的執行動作都改了,會發生什么呢?難道會得到一個有著 金剛不壞 之身的進程嗎?

?

#include <iostream>
#include <signal.h>
#include <unistd.h>
using namespace std;void handler(int signo)
{cout << "當前 " << signo << " 號信號正在嘗試執行相應的動作" << endl;
}int main()
{//給所有普通信號注冊新方法for(int i = 1; i < 32; i++)signal(i, handler);while(true){cout << "我是一個進程,我正在運行…… PID: " << getpid() << endl;sleep(1);}return 0;
}

?

大部分信號的執行動作都被修改了,但 9 號信號沒有,因為 9 號信號是 SIGKILL,專門用于殺死進程,只要是進程,他都能干掉

19 號信號 SIGSTOP 也無法修改執行動作,所以前面說過,9 號 SIGKILL 和 19 號 SIGSTOP 信號是很特殊的,經過特殊設計,不能修改其執行動作!

?

2.2、硬件中斷
當我們從鍵盤按下 ctrl + c 時,發生了這些事:CPU 獲取到鍵盤 “按下” 的信號,調用鍵盤相應的 “方法” ,從鍵盤中讀取數據,讀取數據后解析,然后發出 3 號信號

其中 CPU 捕獲鍵盤 “按下” 信號的操作稱為 硬件中斷

CPU 中有很多的針腳,不同的硬件對應著不同的針腳,每一個針腳都有自己的編號,硬件與針腳一對一相連,并通過 中斷控制器(比如 8259)進行控制,當我們按下鍵盤后

中斷控制器首先給 CPU 發送信息,包括鍵盤對應的針腳號
然后 CPU 將獲取到的針腳號(中斷號)寫入 寄存器 中
最后根據 寄存器 里的 中斷號,去 中斷向量表 中查表,找到對應硬件的方法,執行它的讀取方法就行了
這樣 CPU 就知道是 鍵盤 發出的信號,然后就會去調用 鍵盤 的執行方法,通過鍵盤的讀取方法,讀取到 ctrl + c 這個信息,轉化后,就是 2 號信號,執行終止前臺進程的動作

鍵盤被按下 和 鍵盤哪些位置被按下 是不一樣的

首先鍵盤先按下,CPU 確定對應的讀取方法
其次才是通過 讀取方法 從鍵盤中讀取數據
注:鍵盤讀取方法如何進行讀取,這是驅動的事,我們不用關心

硬件中斷 的流程與 進程信號 的流程雷同,同樣是 先檢測到信號,然后再去執行相應的動作,不過此時發送的是 中斷信號,執行的是 調用相應方法罷了

信號 與 動作 的設計方式很實用,操作系統只需要關注是否有信號發出,發出后去中斷向量表中調用相應的方法即可,不用管硬件是什么樣、如何變化,做到了 操作系統 與 硬件 間的解耦


3、系統調用

除了可以通過?鍵盤鍵入?發送信號外,還可以通過直接調用?系統接口?發送信號,畢竟?bash?也是一個進程,本質上就是在進行程序替換而已

3.1、kill 函數

信號的發送主要是通過?kill?函數進行發送

返回值:成功返回?0,失敗返回?-1?并設置錯誤碼

參數1:待操作進程的?PID

參數2:待發送的信號

下面來簡單用一下(程序運行?5?秒后,自己把自己殺死)

?

#include <iostream>
#include <signal.h>
#include <unistd.h>
using namespace std;int main()
{int n = 1;while (true){cout << "我是一個進程,已經運行了 " << n << " 秒 PID: " << getpid() << endl;sleep(1);n++;if (n > 5)kill(getpid(), SIGKILL);}return 0;
}

kill?函數當然也可以發送其他信號,這里就不一一展示了,其實命令行中的?kill?命令就是對?kill?函數的封裝,kill -信號編號 -PID?其中的參數2、3不正是?kill?函數所需要的參數嗎?所以我們可以嘗試自己搞一個?myKill?命令

3.2、模擬實現 myKill

這里就直接利用?命令行參數?簡單實現了

#include <iostream>
#include <string>
#include <signal.h>using namespace std;void Usage(string proc)
{// 打印使用信息cout << "\tUsage: \n\t";cout << proc << " 信號編號 目標進程" << endl;exit(2);
}int main(int argc, char *argv[])
{// 參數個數要嚴格限制if (argc != 3){Usage(argv[0]);}//獲取兩個參數int signo = atoi(argv[1]);int pid = atoi(argv[2]);//執行信號發送kill(pid, signo);return 0;
}

下面隨便跑一個進程,然后用自己寫的?myKill?命令給進程發信號

我們可以把這個程序改造下,改成進程替換的方式,讓后將自己寫的命令進行安裝,就能像?kill?一樣直接使用了

3.3、raise 函數

發送信號的還有一個?raise?函數,這個函數比較奇怪,只能?自己給自己發信號

返回值:成功返回?0,失敗返回?非0

就只有一個參數:待發送的信號

可以這樣理解:raise?是對?kill?函數的封裝,每次傳遞的都是自己的?PID

#include <iostream>
#include <signal.h>
#include <unistd.h>
using namespace std;int main()
{int n = 1;while (true){cout << "我是一個進程,已經運行了 " << n << " 秒 PID: " << getpid() << endl;sleep(1);n++;if (n > 5)raise(SIGKILL); //自己殺死自己    }return 0;
}

3.4、abort 函數

abort?是?C?語言提供的一個函數,它的作用是?給自己發送?6?號?SIGABRT?信號

沒有返回值,也沒有參數

值得一提的是,abort?函數即使在修改執行動作后,最后仍然會發送?6?號信號

#include <iostream>
#include <signal.h>
#include <unistd.h>
using namespace std;void handler(int signo)
{cout << "收到了 " << signo << " 號信號,已執行新動作" << endl;
}int main()
{signal(6, handler);// signal(SIGABRT, handler);    //這種寫法也是可以的int n = 1;while (true){cout << "我是一個進程,已經運行了 " << n << " 秒 PID: " << getpid() << endl;sleep(1);n++;if (n > 5)abort();}return 0;
}

即使執行了我們新注冊的方法,abort 最后仍然會發出 6 號信號終止進程

同樣是終止進程,C語言 還提供了一個更好用的函數:exit(),所以 abort 用的比較少,了解即可

總的來說,系統調用中舉例的這三個函數關系是:kill 包含 raise,raise 包含 abort,作用范圍是在逐漸縮小的

4、軟件條件信號產生(發送)的第三種方式:軟件條件

?

其實這種方式我們之前就接觸過了:管道讀寫時,如果讀端關閉,那么操作系統會發送信號終止寫端,這個就是 軟件條件 引發的信號發送,發出的是 13 號 SIGPIPE 信號

4.1、alarm 設置鬧鐘


系統為我們提供了 鬧鐘(報警):alarm,這個 鬧鐘 可不是用來起床的,而是用來 定時 的

返回值:如果上一個鬧鐘還有剩余時間,則返回剩余時間,否則返回?0
參數:想要設定的時間,單位是秒

當時間到達鬧鐘中的預設時間時,鬧鐘會響,并且發送?14?號?SIGALRM?信號

比如這樣:

#include <iostream>
#include <signal.h>
#include <unistd.h>
using namespace std;int main()
{alarm(5);   //設定一個五秒后的鬧鐘int n = 1;while (true){cout << "我是一個進程,已經運行了 " << n << " 秒 PID: " << getpid() << endl;sleep(1);n++;}return 0;
}

?

#include <iostream>
#include <signal.h>
#include <unistd.h>
using namespace std;void handler(int signo)
{cout << "收到了 " << signo << " 號信號,已執行新動作" << endl;int n = alarm(10);cout << "上一個鬧鐘剩余時間: " << n << endl;
}int main()
{signal(SIGALRM, handler);alarm(10);   //設定一個十秒后的鬧鐘while(true){cout << "我是一個進程,我正在運行…… PID: " << getpid() << endl;sleep(1);};return 0;
}

系統中不止一個鬧鐘,所以?OS?需要?先描述,再組織,將這些鬧鐘管理起來

可以借助鬧鐘,簡單測試一下當前服務器的算力

?

5、硬件異常

最后一種產生(發送)信號的方式是:硬件異常

所謂?硬件異常?其實就是我們在寫程序最常遇到的各種報錯,比如?除 0、野指針

5.1、除 0 導致異常

先來看一段簡單的錯誤代碼

#include <iostream>
using namespace std;int main()
{int n = 10;n /= 0;return 0;
}

顯然是會報錯的是,畢竟?0?不能作為常數?

?

根據報錯信息,可以推測出此時發送的是?8?號?SIGFPE?信號(浮點異常)

讓我們通過?signal?更改?8?號信號的執行動作,嘗試逆天改命,讓?除 0 合法?

#include <iostream>
#include <signal.h>
#include <unistd.h>
using namespace std;void handler(int signo)
{cout << "雖然除 0 了,但我不終止進程" << endl;
}int main()
{signal(SIGFPE, handler);int n = 10;n /= 0;return 0;
}

結果:一直在死循環似的發送信號,明明只發生了一次 除 0 行為

想要明白背后的原理,需要先認識一下?狀態寄存器

5.2、狀態寄存器

在 CPU 中,存在很多 寄存器,其中大部分主要用來存儲數據信息,用于運算,除此之外,還存在一種特殊的 寄存器 =》 狀態寄存器,這個 寄存器 專門用來檢測當前進程是否出現錯誤行為,如果有,就會把 狀態寄存器(位圖結構)中對應的比特位置 1,意味著出現了 異常

當操作系統檢測到 狀態寄存器 出現異常時,會根據其中的值,向出現異常的進程 輪詢式 的發送信號,目的就是讓進程退出

比如上面的 除 0 代碼,發生異常后,CPU 將 狀態寄存器 修改,變成 異常狀態,操作系統檢測到 異常 后會向進程發送 8 號信號,即使我們修改了 8 號信號的執行動作,但 因為狀態寄存器仍然處于異常狀態,所以操作系統才會不斷發送 8 號信號,所以才會死循環式的打印

能讓?狀態寄存器?變為?異常?的都不是小問題,需要立即終止進程,然后尋找、解決問題

畢竟如果讓?除 0?變為合法,那最終的結果是多少呢?所以操作系統才會不斷發送信號,目的就是?終止進程的運行

5.3、野指針導致異常

除了?除 0?異常外,還有一個?臭名昭著?的異常:野指針問題

?

#include <iostream>
using namespace std;int main()
{int* ptr = nullptr;*ptr = 10;return 0;
}

?

Segmentation fault?段錯誤?這是每個?C/C++?程序猿都會遇到的問題,因為太容易觸發了,出現段錯誤問題時,操作系統會發送?11號?SIGSEGV?信號終止進程,可以通過修改執行動作驗證,這里不再演示

那么?野指針?問題是如何引發的呢?

借用一下?共享內存?中的圖~

野指針問題主要分為兩類:

  1. 指向不該指向的空間
  2. 權限不匹配,比如只讀的區域,偏要去寫

共識:在執行 *ptr = 10 這句代碼時,首先會進行 虛擬地址 -> 真實(物理)地址 之間的轉換

指向不該指向的空間:這很好理解,就是頁表沒有將 這塊虛擬地址空間 與 真實(物理)地址空間 建立映射關系,此時進行訪問時 MMU 識別到異常,于是 MMU 直接報錯,操作系統識別到 MMU 異常后,向對應的進程發出終止信號

權限不匹配:頁表中除了保存映射關系外,還會保存該區域的權限情況,比如 是否命中 / RW 等權限,當發生操作與權限不匹配時,比如 nullptr 只允許讀取,并不允許其他行為,此時解引用就會觸發 MMU 異常,操作系統識別到后,同樣會對對應的進程發出終止信號

頁表中的屬性

  • 是否命中
  • RW 權限
  • UK 權限(不必關心

?

注:MMU 是內存管理單元,主要負責 虛擬地址 與 物理地址 間的轉換工作,同時還會識別各種異常行為

一旦引發硬件層面的問題,操作系統會直接發信號,立即終止進程

到目前為止,我們學習了很多信號,分別對應著不同的情況,其中有些信號還反映了異常信息,所以將信號進行細分,還是很有必要的

6.核心轉儲?

?對于某些信號來說,當終止進程后,需要進行?core dump,產生核心轉儲文件

?比如:3號 SIGQUIT4號 SIGILL5號 SIGTRAP6號 SIGABRT7號 SIGBUS8號 SIGFPE11號 SIGSEGV24號 SIGXCPU25號 SIGXFSZ31號 SIGSYS?都是可以產生核心轉儲文件的

不同信號的動作(Action

  • Trem?-> 單純終止進程
  • Core?-> 先發生核心轉儲,生成核心轉儲文件(前提是此功能已打開),再終止進程

但在前面的學習中,我們用過?36811?號信號,都沒有發現?核心轉儲?文件啊

難道是我們的環境有問題嗎?

確實,當前環境確實有問題,因為它是?云服務器,而?云服務器?中默認是關閉核心轉儲功能的

6.2、打開與關閉核心轉儲

通過指令?ulimit -a?查看當前系統中的資源限制情況

ulimit -a

可以看到,當前系統中的核心轉儲文件大小為?0,即不生成核心轉儲文件

通過指令手動設置核心轉儲文件大小

ulimit -c 1024

?

現在可以生成核心轉儲文件了

就拿之前的?野指針?代碼測試,因為它發送的是?11?號信號,會產生?core dump?文件

?

?核心轉儲文件是很大的,而有很多信號都會產生核心轉儲文件,所以云服務器一般默認是關閉的

云服務器上是可以部署服務的,一般程序發生錯誤后,會立即重啟
如果打開了核心轉儲,一旦程序 不斷掛掉、又不斷重啟,那么必然會產生大量的核心轉儲文件,當文件足夠多時,磁盤被擠滿,導致系統 IO 異常,最終會導致整個服務器掛掉的
還有一個重要問題是 core 文件中可能包含用戶密碼等敏感信息,不安全

關閉核心轉儲很簡單,設置為?0?就好了

ulimit -c 0

6.3、核心轉儲的作用

如此大的核心轉儲文件有什么用呢?

答案是?調試

沒錯,核心轉儲文件可以調試,并且直接從出錯的地方開始調試

這種調試方式叫做?事后調試

調試方法:

  1. gcc / g++?編譯時加上?-g?生成可調試文件
  2. 運行程序,生成?core-dump?文件
  3. gdb 程序?進入調試模式
  4. core-file core.file?利用核心轉儲文件,快速定位至出錯的地方

?

之前在?進程創建、控制、等待?中,我們談到了?當進程異常退出時(被信號終止),不再設置退出碼,而是設置?core dump?位 及 終止信號

也就是說,父進程可以借此判斷子進程是否產生了?核心轉儲?文件

?

?總結:

信號產生部分就到此,下一篇信號保存

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

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

相關文章

人工智能福利站,初識人工智能,圖神經網絡學習,第五課

&#x1f3c6;作者簡介&#xff0c;普修羅雙戰士&#xff0c;一直追求不斷學習和成長&#xff0c;在技術的道路上持續探索和實踐。 &#x1f3c6;多年互聯網行業從業經驗&#xff0c;歷任核心研發工程師&#xff0c;項目技術負責人。 &#x1f389;歡迎 &#x1f44d;點贊?評論…

nginx設置緩存時間、日志分割、開啟多進程、網頁壓縮、配置防盜鏈

一、設置緩存時間 當網頁數據返回給客戶端后&#xff0c;可針對靜態網頁設置緩存時間&#xff0c;在配置文件內的http段內server段添加location&#xff0c;更改字段expires 1d來實現&#xff1a;避免重復請求&#xff0c;加快訪問速度 第一步&#xff1a;修改主配置文件 #修…

Vulnhub靶機:basic_pentesting_2

一、介紹 運行環境&#xff1a;Virtualbox 攻擊機&#xff1a;kali&#xff08;10.0.2.4&#xff09; 靶機&#xff1a;basic_pentesting_2&#xff08;10.0.2.7&#xff09; 目標&#xff1a;獲取靶機root權限和flag 靶機下載地址&#xff1a;https://download.vulnhub.c…

如何選擇O2OA(翱途)開發平臺的部署架構?

概述 O2OA(翱途)開發平臺[下稱O2OA開發平臺或者O2OA]支持公有云&#xff0c;私有云和混合云部署&#xff0c;也支持復雜的網絡結構下的分布式部署。本篇主要介紹O2OA(翱途)開發平臺支持的部署環境以及常用的集群部署架構。 軟硬件環境說明 支持的云化平臺&#xff1a; 華為云…

分布式概念:編碼一個簡單分布式系統

分布式系統是由多臺計算機組成的系統&#xff0c;它們通過網絡連接并共同完成任務。以下是一個簡單的分布式系統的編碼示例&#xff1a; 定義系統架構 首先&#xff0c;需要定義系統的架構。包括確定系統中的計算節點、通信機制和任務分配方式等。 編寫計算節點代碼 每個計算節…

算法簡介:遞歸

遞歸 1. 遞歸1.1 基線條件和遞歸條件 2. 棧2.1 調用棧2.2 遞歸調用棧 1. 遞歸 循環和遞歸可以實現相同的功能&#xff0c;如&#xff1a; 循環 def look_for_key(main_box)pile main_box.make_a_pile_to_look_thorugh()while pile is not empty:box pile.grab_a_box()for …

LLM 聊天對話界面chatwebui 增加實時語音tts功能

類似豆包聊天,可以實時語音回復 1、聊天界面 streamlit頁面 參考界面:https://blog.csdn.net/weixin_42357472/article/details/133199866 stream_web.py 2、 增加實時語音tts功能(接入melotts api服務) 參考:https://blog.csdn.net/weixin_42357472/article/detai…

vue3學習 ref和reactive的使用

使用ref聲明一個響應式對象并使用 <script lang"ts" setup> import { ref } from vue; const message ref("HelloWorld") message.value"被修改了啊~~" </script> <template>{{ message }} </template>ref() 接收參數…

Docker容器與虛擬化技術:OpenEuler 使用 docker-compose 部署 LNMP

目錄 一、實驗 1.環境 2.OpenEuler 部署 docker-compose 3.docker-compose 部署 LNMP 二、問題 1.ntpdate未找到命令 2.timedatectl 如何設置時區與時間同步 3.php網頁顯示時區不對 一、實驗 1.環境 &#xff08;1&#xff09;主機 表1 主機 系統架構版本IP備注Lin…

docker啟動容器報錯:ERRO[0000] error waiting for container: context canceled,解決方法

系統環境&#xff1a;ubuntu16.04&#xff0c;已安裝docker 執行命令&#xff1a;sudo docker run -it --privileged --shm-size128g -v /home:/home docker-image /bin/bash 報錯 docker: Error response from daemon: could not select device driver "" with …

Android PMS實戰——Hook技術介紹(十四)

在了解了 PMS 的調用流程之后,都有那些用處呢?首先幫助了解 Android 包管理系統原理,還有就是配合 AMS 通過 Hook 技術,實現熱更新、插件化等功能。 我們可以通過反射獲取到 PackageParser 對象,再反射調用它的 parsePackage() 傳入 apk 路徑完成解析獲取到 Package 對象,…

厚膜電阻與薄膜電阻相比,特點是什么?

厚膜電阻與薄膜電阻是兩種常見的電阻器件&#xff0c;它們之間的特點主要有以下幾個方面&#xff1a; 1. 厚度不同&#xff1a;厚膜電阻的膜層厚度較大&#xff0c;一般在幾微米到幾十微米之間&#xff0c;而薄膜電阻的膜層厚度較薄&#xff0c;一般在幾納米到幾微米之間。 2. …

單片機精進之路-9ds18b20溫度傳感器

ds18b20復位時序圖&#xff0c;先將b20的數據引腳拉低至少480us&#xff0c;然后再將數據引腳拉高15-60us&#xff0c;再去將測傳感器的數據引腳是不是變低電平并保持60-240us&#xff0c;如果是&#xff0c;則說明檢測到溫度傳感器&#xff0c;并正常工作。需要在240us后才能檢…

xss高級靶場

一、環境 XSS Game - Ma Spaghet! | PwnFunction 二、開始闖關 第一關 看看代碼 試一下直接寫 明顯進來了為什么不執行看看官方文檔吧 你不執行那我就更改單標簽去使用唄 ?somebody<img%20src1%20onerror"alert(1)"> 防御&#xff1a; innerText 第二關…

Codeforces Round 930 (Div. 2) (A~B)

比賽&#xff1a;Codeforces Round 930 (Div. 2) (A~B) 目錄&#xff1a;A B A題&#xff1a;Shuffle Party 標簽: 模擬 題目大意 給你一個數組 a1,a2,…,an。最初&#xff0c;每個 1 ≤ i ≤ n都有 ai i&#xff0c;整數 k ≥ 2的運算 swap(k)定義如下&#xff1a; 設 d是…

Python圖像形態學處理:腐蝕、膨脹、禮帽、黑帽……

文章目錄 二值形態學灰度形態學 python圖像處理教程&#xff1a;初步&#x1f4f7;插值變換 最基礎的形態學操作有四個&#xff0c;分別是腐蝕、膨脹、開計算和閉計算&#xff0c;【scipy.ndimage】分別實現了二值數組和灰度數組的這四種運算。而針對灰度圖像&#xff0c;【sc…

Office/WPS 好用的PPT插件-智能選擇布局

軟件介紹 PPT大珩助手是一款全新設計的Office PPT插件&#xff0c;它是一款功能強大且實用的PPT輔助工具&#xff0c;能夠輕松幫助您修改、優化和管理幻燈片。憑借豐富的功能和用戶友好的界面&#xff0c;PPT大珩助手能夠助力您打造出精美而專業的演示文稿。我們致力于為用戶提…

Flutter學習7 - Dart 泛型

1、泛型類 //泛型類 class Cache<T> {final Map<String, T> _cache {};void saveData(String key, T value) {_cache[key] value;}//泛型方法T? getData(String key) {return _cache[key];} }void main() {Cache<int> cache1 Cache();const String name…

NGINX的重寫與反向代理機制解析

目錄 引言 一、重寫功能 &#xff08;一&#xff09;if指令 1.判斷訪問使用的協議 2.判斷文件 &#xff08;二&#xff09;return指令 1.設置返回狀態碼 2.返回指定內容 3.指定URL &#xff08;三&#xff09;set指令 1.手動輸入變量值 2.調用其它變量值為自定義變…

RISC-V特權架構 - CSR寄存器

RV32/64 特權架構 - CSR寄存器 1 CSR地址空間2 CSR定義2.1 用戶級2.2 監管級2.3 超級監管級2.4 機器級 3 CSR訪問3.1 CSRRW3.2 CSRRS3.3 CSRRC3.4 CSRRWI3.5 CSRRSI3.6 CSRRCI 本文屬于《 RISC-V指令集基礎系列教程》之一&#xff0c;歡迎查看其它文章。 1 CSR地址空間 RISC&…