【Linux】進程信號 --- 信號產生

在這里插入圖片描述

👦個人主頁:Weraphael
?🏻作者簡介:目前正在學習c++和算法
??專欄:Linux
🐋 希望大家多多支持,咱一起進步!😁
如果文章有啥瑕疵,希望大佬指點一二
如果文章對你有幫助的話
歡迎 評論💬 點贊👍🏻 收藏 📂 加關注😍


目錄

  • 一、通過終端按鍵產生信號
      • 1.1 Ctrl + /
      • 1.2 Ctrl + z
  • 二、通過kill命令產生信號
  • 三、通過系統調用接口產生信號
      • 3.1 kill
      • 3.2 raise
      • 3.3 abort
  • 四、硬件異常產生信號
      • 4.1 除 0 錯誤導致異常
      • 4.2 狀態寄存器
      • 4.3 野指針異常
  • 五、軟件條件產生信號
      • 5.1 alarm 函數
  • 六、核心轉儲 core dump
      • 6.1 知識回顧
      • 6.2 打開與關閉核心轉儲
      • 6.3 核心轉儲的作用

一、通過終端按鍵產生信號

1.1 Ctrl + /

在上篇博客中(點擊跳轉),我們已經驗證了Ctrl + c是向前臺進程發送2號信號SIGINT來中斷程序。

這篇再介紹一個組合鍵:Ctrl + \。它其實是向前臺進程發送3號信號SIGQUIT來終止程序,同時會產生一個core文件。我們可以使用捕捉信號函數signal來驗證。

#include <iostream>
#include <unistd.h>
#include <signal.h>
using namespace std;void myhandler(int signum)
{// 修改的行為:打印信號編號后退出cout << "信號編號為:" << signum << endl;exit(1);
}int main()
{// 捕捉3號信號signal(SIGQUIT, myhandler);while (true){cout << "我是一個進程,我在做死循環操作" << endl;sleep(1);}return 0;
}

【程序結果】

在這里插入圖片描述

其原理再簡單重復一遍:當按下ctrl + \,操作系統識別到鍵盤上有數據,觸發了硬件中斷,CPU收到中斷請求后,會暫停當前執行的程序,保存當前狀態,并根據中斷號來調用對應硬件的方法。由于操作系統識別到是特殊控制字符,就將其轉化為3號信號發送給前臺進程

1.2 Ctrl + z

Ctrl + z會向前臺進程發送19號信號SIGSTOP。當進程收到此信號時,它會立即停止運行。

#include <iostream>
#include <unistd.h>
#include <signal.h>
using namespace std;int main()
{while (true){cout << "我是一個進程,我在做死循環操作" << endl;sleep(1);}return 0;
}

【程序結果】

在這里插入圖片描述

需要注意的是:19號信號SIGSTOP不能使用signal函數捕捉。類似地,9號信號SIGKILL用于立即終止一個進程,并且也不能被signal函數捕捉。

二、通過kill命令產生信號

kill -n <pid>
# n是信號編號

以以下代碼為例:

#include <iostream>
#include <unistd.h>
using namespace std;int main()
{while (true){cout << "進程:" << getpid() << ", 我在做死循環啦~" << endl;sleep(1);}return 0;
}

【程序結果】

在這里插入圖片描述

三、通過系統調用接口產生信號

3.1 kill

系統調用kill函數是用于向進程發送某種信號,其函數原型如下:

#include <sys/types.h>
#include <signal.h>int kill(pid_t pid, int sig);

參數解釋:

  • pid:要發送信號的進程pid
  • sig:要發送的信號編號,可以是標準信號如SIGINTSIGKILL,也可以是用戶自定義的信號。
  • 返回值:成功時,返回0;失敗時,返回-1,并設置errno來指示錯誤的原因。

我們可以使用這個kill系統調用接口來模擬實現一個kill命令。首先kill命令必須是如下形式:

kill -信號編號 進程pid

那么這里就可以巧用main函數的參數。main函數的第一個參數argc:表示字符指針數組當中的有效元素個數。argv:是一個字符指針數組(向量表),數組以NULL指針結尾,注意argv的第一個值原因是程序名。

因此,代碼如下:

#include <iostream>
#include <unistd.h>
#include <string>
#include <cstring>
#include <signal.h>
#include <sys/types.h>using namespace std;void Usage(string proc)
{cout << "格式:\n\t" << proc << " signum pid\n\n";
}// argv:程序名argv[0]、信號編號argv[1]、進程pid argv[2]
int main(int argc, char *argv[])
{// 如果是三個參數,可以殺掉用戶指定進程if (argc == 3){int signum = stoi(argv[1]);pid_t pid = stoi(argv[2]);int n = kill(pid, signum);if (n != 0){perror("kill");exit(2);}}// 如果不是輸入三個參數,就提醒用戶輸入格式else{Usage(argv[0]);exit(1);}return 0;
}

接下來我再寫一個死循環代碼程序如下:

#include <unistd.h>
#include <iostream>
using namespace std;int main()
{while (true){cout << "my pid is " << getpid() << endl;sleep(1);}return 0;
}

【程序結果】

在這里插入圖片描述

3.2 raise

raise函數用于向當前進程發送一個信號。它的基本形式如下:

#include <signal.h>int raise(int sig);
  • sig:要發送的信號編號,可以是標準信號(如SIGINTSIGTERM等),也可以是用戶自定義的信號。
  • 返回值:成功時,返回0;失敗時,返回非0值。

這個函數可以這么理解:相當于一顆地雷,當程序“踩到”它,就會產生信號并執行。

例如:程序是死循環打印,當打印完3條消息后,就立馬殺掉進程。

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>using namespace std;int main()
{int cnt = 3;while (true){cout << "My pid is " << getpid() << endl;sleep(1);cnt--;if (cnt == 0){// 9號信號raise(SIGKILL);}}return 0;
}

【程序結果】

在這里插入圖片描述

這個函數通常搭配signal函數使用。因為有些信號終止程序不會像9號信號一樣給killed提示,比方說2號信號SIGINT

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>using namespace std;int main()
{int cnt = 3;while (true){cout << "My pid is " << getpid() << endl;sleep(1);cnt--;if (cnt == 0){// 2號信號raise(SIGINT);}}return 0;
}

【程序結果】

在這里插入圖片描述

當加了signal函數后,就直觀很多了。代碼如下:

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>using namespace std;void handler(int signum)
{cout << "我得到了2號信號SIGINT" << endl;exit(1);
}int main()
{signal(2, handler);int cnt = 3;while (true){cout << "My pid is " << getpid() << endl;sleep(1);cnt--;if (cnt == 0){// 2號信號raise(SIGINT);}}return 0;
}

【程序結果】

在這里插入圖片描述

這個函數其實封裝了系統調用接口kill,例如以上代碼等價于kill(getpid(), 2);

3.3 abort

abort函數用于向自己發送一個6號信號SIGABRT,異常終止當前進程的執行。

函數原型如下:

#include <stdlib.h>void abort(void);

【代碼樣例】

#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>using namespace std;void handler(int signum)
{cout << "我得到了6號信號SIGABRT" << endl;exit(1);
}int main()
{signal(6, handler);int cnt = 3;while (true){cout << "My pid is " << getpid() << endl;sleep(1);cnt--;if (cnt == 0){abort();}}return 0;
}

【程序結果】

在這里插入圖片描述

值得一提的是,abort函數即使在修改執行動作后(沒有exit(int)),最后仍然會發送6號信號來退出進程。

因此,abort函數也是被封裝的,相當于kill(getpid(), 6);

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

在這里插入圖片描述

四、硬件異常產生信號

4.1 除 0 錯誤導致異常

程序異常本質是進程收到了某種信號。

比方說,我們可以使用signal函數來捕捉除0異常的進程收到了幾號信號。

#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>using namespace std;void handler(int signum)
{cout << "我收到了" << signum << "號信號" << endl;
}int main()
{for (int i = 1; i <= 31; i++){signal(i, handler);}printf("1 / 0 = %d\n", 1 / 0);return 0;
}

【程序結果】

在這里插入圖片描述

從上我們發現:

  1. 操作系統發送8號信號SIGFPE
  2. 只發生了一次 除0異常,進程非但沒退出,還在死循環打印。

首先大部分進程對信號的處理行為都是終止程序,這里沒有終止程序是因為我們修改了系統默認的行為,在handler函數最后一行補上exit函數即可正常終止。

但非常奇怪的是,我們代碼中只有一次除0異常啊,正常來說只需要打印一次就行了,即使進程沒有退出,將進程阻塞不就好了。為什么會一直死循環打印呢?想要明白背后的原理,需要先認識一下狀態寄存器

4.2 狀態寄存器

代碼中除0和野指針行為,操作系統是怎么知道的呢?

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

在這里插入圖片描述

當檢測到狀態寄存器中某個異常標志位被設置為1時,硬件(寄存器)會向CPU發送一個硬件中斷,CPU收到來自硬件的中斷信號后,又因為操作系統是這些硬件的“管理者”,它會暫停當前正在執行的進程。然后檢查當前進程的上下文異常標志位的具體含義,以確定發生了什么樣的異常。根據異常類型調用相應的異常處理程序。這些程序通常是預先定義好的。

所以現在就可以解釋為什么進程收到來自操作系統的異常信號后,雖然我們修改了默認處理動作(不退出),但是會死循環打印的情況。這是因為操作系統一直檢測當前進程的狀態寄存器仍然處于異常狀態,再加上不退出進程,此時進程一直在被調度運行,所以操作系統才會不斷發送8號信號,才會死循環式的打印。

因此,異常信號被捕捉不是為了解決什么問題,而是讓用戶清除的知道程序是因為什么而掛掉的。

4.3 野指針異常

#include <iostream>
#include <unistd.h>
#include <cstring>
#include <string>
#include <signal.h>
using namespace std;int main()
{int *p = nullptr;*p = 10;return 0;
}

【程序結果】

在這里插入圖片描述

Segmentation fault是一個段錯誤,這是每個C/C++程序猿都會遇到的問題,因為太容易觸發了,出現段錯誤問題時,操作系統會發送11SIGSEGV信號終止進程,這里不再證明演示。

在這里插入圖片描述

在這里插入圖片描述

那么 野指針問題是如何引發的呢?本質上就是:虛擬地址轉化為物理地址失敗。比方說:虛擬地址映射到不屬于它的物理空間(越界訪問)、對該物理空間沒有寫的權限等。具體解釋如下:

  • 當代的MMU已經被集成在CPU內部,除此之外CPU還有一個寄存器,稱為BadVAddrBad Virtual Address,壞虛擬地址)。這個寄存器的作用是在虛擬地址轉換失敗時,將引起異常的虛擬地址存儲起來。

  • 所以,硬件(寄存器)會向CPU發送一個硬件中斷,CPU收到來自硬件的中斷信號后,又因為操作系統是這些硬件的“管理者”,它會暫停當前正在執行的進程。然后檢查當前進程的上下文異常標志位的具體含義,以確定發生了什么樣的異常。根據異常類型調用相應的異常處理程序。這些程序通常是預先定義好的。

五、軟件條件產生信號

異常不僅由硬件產生,還可以由軟件產生

比如我們之前的管道,如果一開始讀寫端都打開,但是關閉了讀端,那么寫端進程就會收到一個13號信號SIGPIPE。這就是一種軟件異常。【Linux】進程間通信之匿名管道

5.1 alarm 函數

alarm函數是一個系統調用接口,用來設置一個定時器。其功能是:在指定的秒數后發送14號信號SIGALRM給調用進程。該信號的默認處理動作是終止當前進程。

原型如下:

#include <unistd.h>unsigned int alarm(unsigned int seconds);
  • seconds:指定的秒數,即多少秒后發送SIGALRM信號。如果參數為0,則任何已設置的定時器都會被取消,但不會產生信號。
  • 返回值:返回調用之前的剩余定時器時間。如果之前沒有設置定時器,則返回0
  • aralm函數的原理:我們知道每個進程都可以使用alarm設置鬧鐘,所以操作系統中一定有大量的鬧鐘。所以操作系統要管理鬧鐘,所以鬧鐘就會用struct結構體描述,一定有進程pid(表示哪個進程設置的鬧鐘)等字段,然后用鏈表等數據結構管理起來。這樣所謂的鬧鐘管理就變成了對鏈表等的增刪查改。操作系統底層中alarm的底層所用的時間用的是時間戳,只要系統的當前時間大于等于里面設置的時間,就會發送信號。我們遍歷鏈表的時候是比較浪費時間的。所以用一個小堆是最簡單的。

【代碼樣例】

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

【程序結果】

在這里插入圖片描述

那我們就可以在執行主要任務的同時,去定時完成其他任務了。例如:

#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>using namespace std;void work()
{cout << "我在完成其他任務" << endl;
}void handler(int signum)
{// 異常捕捉時,再設置一個定時器完成其他任務alarm(5);work();
}int main()
{// 設定一個5s的定時器alarm(5);// 信號捕捉signal(14, handler);int n = 1;while (true){// 主要任務cout << "我是一個進程,已經運行了 " << n++ << " 秒 PID: " << getpid() << endl;sleep(1);}return 0;
}

【程序結果】

在這里插入圖片描述

六、核心轉儲 core dump

6.1 知識回顧

我們可以使用以下命令來看信號的詳細信息手冊

man 7 signal

在這里插入圖片描述
我們可以注意到,常見的信號中大部分是終止Term(terminate)信號的。還有一些是暫停(Stop),繼續(Cont),忽略(Ign)。

那么這個核心Core是什么東西呢?

在我們當時學習進程控制的時候,還有一個這個字段core dump標志我們還沒提到過(【Linux】進程控制 )

在這里插入圖片描述

當一個進程異常退出時(參看上面ActionCore信號),該core dump標志會被設置成1,并且會生成一個core.pid二進制文件,然后不再設置退出碼(為0,因為這種情況下程序沒有正常的執行路徑結束)

我們可以寫代碼來驗證一下:

#include <iostream>
#include <unistd.h>
#include <string>
#include <cstring>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>using namespace std;int main()
{pid_t pid = fork();if (pid == 0) // 子進程{int cnt = 500;while (cnt--){cout << "I am child process, pid:" << getpid() << ", cnt:" << cnt << endl;sleep(1);}// 打印5句話后子進程退出exit(1);}else // 父進程{int status = 0;pid_t rid = waitpid(pid, &status, 0);if (rid == pid){cout << "child quit info, rid:" << rid << ", exit code:" << ((status >> 8) & 0xff)<< ", exit signal:" << (status & 0x7f) << ", core dump:" << ((status >> 7) & 1)<< endl;}}return 0;
}

【程序結果】

在這里插入圖片描述

為什么沒有生成核心轉儲文件啊?難道是我們的環境有問題嗎?

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

6.2 打開與關閉核心轉儲

通過以下指令可以查看當前系統中的資源限制情況

 ulimit -a 

在這里插入圖片描述

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

通過以下指令手動設置核心轉儲文件大小(打開核心轉儲文件)

ulimit -c <大小自己定>
# 關閉大小設置為0即可
# 重啟xhell自動關閉

在這里插入圖片描述

我們可以再來測試一下,是否dump core是否還為1

在這里插入圖片描述

我們觀察到:核心轉儲文件是很大的

6.3 核心轉儲的作用

當一個進程在出現異常的時候,操作系統會將進程在內存中的運行信息進行核心轉儲,轉儲到當前進程的運行目錄下(磁盤),形成core.pid這樣的二進制大文件(核心轉儲文件)。

核心轉儲的作用是什么呢?

答案是:調試(事后調試),并且直接從出錯的地方開始調試

以下是調試樣例代碼

#include <iostream>using namespace std;int main()
{int a = 1;int b = 0;a = a / b;cout << "a = " << a << endl;return 0;
}

調試方法如下(四個步驟):

  1. 首先gcc/g++在編譯時加-g生成可調試文件

在這里插入圖片描述

  1. 運行程序,生成 core-dump 文件

在這里插入圖片描述

  1. gdb 可執行文件進入調試模式

在這里插入圖片描述

  1. gdb命令行輸入:core-file <核心轉儲文件>。即利用核心轉儲文件,快速定位至出錯的地方。

在這里插入圖片描述

最后一個問題:既然核心轉儲文件這么好用,為什么大多數的云服務器默認是將它關閉的呢?

現在許多大型IT公司以及許多中小型企業,都傾向于使用云服務器來托管其后端服務和應用程序。如果打開的話,當應用程序如果收到某個信號,核心轉儲文件會在進程的當前目錄下創建一個大文件。而有很多進程都可能會產生核心轉儲文件,當文件足夠多時,磁盤被擠滿,導致系統IO異常,最終會導致整個服務器掛掉的,所以云服務器一般默認是關閉的。

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

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

相關文章

【轉型Web3開發第二課】Dapp開發入門基礎 | 02 | MetaMask配置網絡

本文首發于公眾號&#xff1a;Keegan小鋼 前言 完成了《轉型 Web3 開發第一課》之后&#xff0c;得到了不少讀者的認可&#xff0c;很多都在問什么時候開始下一課&#xff0c;近期終于抽出了時間開始搞起這第二課。 這第二課的主題為「Dapp開發入門基礎」&#xff0c;即想要轉…

淺談Visual Studio 2022

Visual Studio 2022&#xff08;VS2022&#xff09;提供了眾多強大的功能和改進&#xff0c;旨在提高開發者的效率和體驗。以下是一些關鍵功能的概述&#xff1a;12 64位支持&#xff1a;VS2022的64位版本不再受內存限制困擾&#xff0c;主devenv.exe進程不再局限于4GB&#xf…

【ffmpeg命令入門】重新編碼媒體流、設置碼率、設置幀速率

文章目錄 前言ffmpeg的描述重新編碼媒體流重新編碼媒體流的命令ffmpeg支持的媒體流 設置視頻碼率視頻碼率是什么設置視頻的碼率 設置文件幀數率幀數率是什么ffmpeg設置幀數率 總結 前言 在數字媒體處理領域&#xff0c;ffmpeg是一款非常強大的工具&#xff0c;它可以用來進行媒…

在線生成privacy-policy和 service-agreement

生成ppsa 網站 下面兩個是要收費的 局部功能可用 #1 Privacy Policy Generator - Privacy Policieshttps://termify.io/ 下面這個網站 創建一個是免費的 如果想要privacy-policy和 service-agreement 需要創建兩個賬戶 Termly

Java學習|JSON 處理庫:Gson、FastJson、Jackson的比較與使用指南

文章目錄 一、Gson簡介與使用注意事項1.1 簡介1.2 使用注意事項1.3 代碼示例序列化反序列化 二、FastJson簡介與使用注意事項2.1 簡介2.2 使用注意事項2.3 代碼示例序列化反序列化 三、Jackson簡介與使用注意事項3.1 簡介3.2 使用注意事項3.3 代碼示例序列化反序列化 四、關聯和…

svn不能記住密碼,反復彈出GNOME,自動重置svn.simple文件

1. 修改文件 打開 ~/.subversion/auth/svn.simple/xxx 更新前 K 15 svn:realmstring V 32 xxxxx //svn 地址&#xff0c;庫的地址 K 8 username V 4 xxx //用戶名 END在頂部插入下面內容&#xff0c; 注意&#xff0c;如果密碼不對&#xff0c;則文件文法正常生效 更新后…

37、PHP 實現一個鏈表中包含環,請找出該鏈表的環的入口結點

題目&#xff1a; 題目描述 PHP 實現一個鏈表中包含環&#xff0c;請找出該鏈表的環的入口結點。 描述&#xff1a; 一個鏈表中包含環&#xff0c;請找出該鏈表的環的入口結點。 <?php /*class ListNode{var $val;var $next NULL;function __construct($x){$this->v…

自動駕駛車道線檢測系列—3D-LaneNet: End-to-End 3D Multiple Lane Detection

文章目錄 1. 摘要概述2. 背景介紹3. 方法3.1 俯視圖投影3.2 網絡結構3.2.1 投影變換層3.2.2 投影變換層3.2.3 道路投影預測分支 3.3 車道預測頭3.4 訓練和真實值關聯 4. 實驗4.1 合成 3D 車道數據集4.2 真實世界 3D 車道數據集4.3 評估結果4.4 評估圖像僅車道檢測 5. 總結和討論…

windows下gcc編譯C、C++程序 MinGW編譯器

文章目錄 1、概要2、MinGW安裝2.1 編譯器下載2.2 編譯器安裝2.3 設置環境變量2.4 查看gcc版本信息 3、編譯C、C程序3.1 編寫Hello World.c3.2 編譯C程序3.3 運行程序3.4 編譯C程序 1、概要 GCC原名為GNU C語言編譯器&#xff08;GNU C Compiler&#xff09;&#xff0c;只能處…

SpringBoot 實現整合kafka的簡單使用

1、引入kafka的依賴 <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-stream-kafka</artifactId></dependency>2、配置kafka spring:kafka:bootstrap-servers: 156.65.20.76:9092,156.65.20.…

Linux系統下weblogic10.3.6版本打補丁步驟

linux系統 weblogic補丁壓縮包&#xff1a;p35586779_1036_Generic.zip 鏈接&#xff1a;https://pan.baidu.com/s/1EEz_zPX-VHp5EU5LLxfxjQ 提取碼&#xff1a;XXXX &#xff08;補丁壓縮包中包含以下東西&#xff09; 打補丁步驟&#xff1a; 1.備份原weblogic(需要先確保服…

Langchain[3]:Langchain架構演進與功能擴展:流式事件處理、事件過濾機制、回調傳播策略及裝飾器應用

Langchain[3]:Langchain架構演進與功能擴展&#xff1a;流式事件處理、事件過濾機制、回調傳播策略及裝飾器應用 1. Langchain的演變 v0.1: 初始版本&#xff0c;包含基本功能。 從0.1~0.2完成的特性&#xff1a; 通過事件流 API 提供更好的流式支持。標準化工具調用支持Tool…

【linux 100條命令】

以下介紹一些常見的 Linux 命令&#xff1a; 1. ls &#xff1a;用于列出目錄中的內容。 - 常用選項&#xff1a; - -l &#xff1a;以長格式顯示詳細信息&#xff0c;包括文件權限、所有者、所屬組、文件大小、修改時間等。 - -a &#xff1a;顯示所有文件&#xff0c;包…

哪些基于 LLMs 的產品值得開發?從用戶體驗和市場接受度的角度探討

編者按&#xff1a;在大語言模型&#xff08;LLMs&#xff09;相關技術高速發展的今天&#xff0c;哪些基于 LLMs 的產品真正值得我們投入精力開發&#xff1f;如何從用戶體驗和市場接受度的角度評估這些產品的潛力&#xff1f; 今天為大家分享的這篇文章&#xff0c;作者的核心…

從代理模式到注解開發

代理模式 package org.example.proxy;public class ProxyClient {public static void main(String[] args) {ProxyBuilder proxyBuilder new ProxyBuilder();proxyBuilder.build();} }interface BuildDream {void build(); }class CustomBuilder implements BuildDream {Over…

visual studio開發C++項目遇到的坑

文章目錄 1.安裝的時候&#xff0c;順手安裝了C模板&#xff0c;導致新建項目執行出問題2.生成的exe&#xff0c;打開閃退問題3.項目里宏的路徑不對&#xff0c;導致后面編譯沒有輸出4. vs編譯ui&#xff0c;warning跳過&#xff0c;未成功5.vs編譯.h&#xff0c;warning跳過&a…

K8S 中的 CRI、OCI、CRI shim、containerd

K8S 如何創建容器&#xff1f; 下面這張圖&#xff0c;就是經典的 K8S 創建容器的步驟&#xff0c;可以說是冗長復雜&#xff0c;至于為什么設計成這樣的架構&#xff0c;繼續往下讀。 前半部分 CRI&#xff08;Container Runtime Interface&#xff0c;容器運行時接口&#xf…

避免海外業務中斷,TikTok養號注意事項

TikTok已成為企業和個人拓展海外業務的重要平臺。然而&#xff0c;由于平臺規則嚴格&#xff0c;賬號被封禁或限制訪問的風險始終存在。為了確保用戶在TikTok上的業務順利進行&#xff0c;著重說一些養號的注意事項。 文章分為三個部分&#xff0c;分別是遵守平臺規則、養號策略…

Qt判定鼠標是否在該多邊形的線條上

要判斷鼠標是否在由QPainterPath或一系列QPointF點定義的多邊形的線條上&#xff0c;你可以使用以下步驟&#xff1a; 獲取鼠標當前位置&#xff1a;在鼠標事件中&#xff0c;使用QMouseEvent的pos()方法獲取鼠標的當前位置。 檢查點與線段的距離&#xff1a;遍歷多邊形的每條…

面試高級 Java 工程師:2024 年的見聞與思考

面試高級 Java 工程師&#xff1a;2024 年的見聞與思考 由于公司業務拓展需要&#xff0c;公司招聘一名高級java工程研發工程師&#xff0c;主要負責新項目的研發及老項目的維護升級。我作為一名技術面試官&#xff0c;參與招聘高級 Java 工程師&#xff0c;我見證了技術領域的…