【Linux】—— 進程程序替換

目錄

序言

(一)替換原理

1、進程角度——見見豬跑

?1?? 認識 execl 函數

2、程序角度——看圖理解

(二)替換函數

1、命名理解

?2、函數理解

1??execlp

2??execv

3??execvp

4??execle

5??execve

6??execve

(三)自制shell

總結


序言

在前面的文章中,我已經詳細的講解了進程的創建。但是大家是否知道創建子進程的目的是什么呢?

  • 其實很簡單,無非就是讓子進程幫我 (父進程) 執行特定的任務而已

此時又有一個問題被衍生出來了:那就是子進程如果指向一個全新的程序代碼時呢?

  • 基于上述這樣的問題,就需要用到本節講到的 — 程序替換

(一)替換原理

1、進程角度——見見豬跑

用fork創建子進程后執行的是和父進程相同的程序(但有可能執行不同的代碼分支),子進程往往要調用一種exec函數以執行另一個程序。

?1?? 認識 execl 函數

execl 是一個在操作系統中用于進程替換的系統調用函數,它允許將當前的進程映像替換為另一個可執行文件的映像。下面是關于 execl 函數的詳細解釋:

int execl(const char *path, const char *arg0, ... /* (char *) NULL */);

參數說明:

  • path:用于指定要替換為的新程序的路徑。
  • arg0:新程序的名稱。該參數在新程序中被作為?argv[0]?參數傳遞。

返回值:

  • 如果調用成功,execl?函數不會返回,因為進程被替換為了新的程序映像;
  • 若發生錯誤,該函數將返回 -1,并設置全局變量?errno?以指示錯誤類型。

💨 當我們到 man 手冊中去查找時,查詢如下:

?接下來,我簡單的寫段代碼對 execl 函數的返回值進行介紹:

   #include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <sys/wait.h>int main(){pid_t id = fork();if(id == 0){//childprintf("我是子進程: %d\n", getpid());int n= execl("/bin/lssssss", "lssssss", "-a", "-ln", NULL); //lsssss: 不存在                                                                                                   printf("you can see me : %d\n",n);exit(0);}sleep(5);//父進程printf("我是父進程: %d\n", getpid());waitpid(id, NULL, 0);return 0 ;}

程序執行如下:

【說明】?

  1. ?在子進程中,我們打印出子進程的進程ID,并調用 execl() 函數來將子進程的映像替換為 /bin/lssssss 這個不存在的命令;
  2. 由于該命令不存在,所以 execl() 函數會失敗,execl() 函數將返回 -1。因此,打印出 you can see me:" 后的返回值將是 -1。

【結論】

  1. 如果替換成功,不會有返回值,如果替換失敗,一定有返回值 ;
  2. 如果失敗了,必定返回;只要有返回值,就失敗了;
  3. 因此不用對該函數進行返回值判斷,只要繼續向后運行一定是失敗的!

?


?有了上述對 execl 函數的認識,我相信下面這段代碼對大家來說就小菜一碟了:

   #include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <sys/wait.h>int main(){printf("begin...\n");printf("begin...\n");printf("begin...\n");printf("begin...\n");// 執行進程替換execl("/bin/ls", "ls", "-l", NULL);printf("end...\n");printf("end...\n");printf("end...\n");printf("end...\n");      return 0;}
  • ?程序輸出執行結果如下:

?

【說明】

?

在上述示例中,execl 函數被調用來將當前進程替換為 /bin/ls,并傳遞 -l 參數給 ls 命令。如果 execl 函數執行成功,當前進程的映像將被替換為新的 ls 程序的映像。如果 execl 函數執行失敗,將打印相應的錯誤信息。

需要注意的是,在調用 execl 函數時,需要指定新程序的完整路徑,并確保該路徑下的程序可執行。同時,還可以傳遞其他命令行參數給新程序,后續的參數通過參數列表傳遞,以 NULL 結束。


2、程序角度——看圖理解

Linux->進程程序替換_linux程序替換_波奇~的博客-CSDN博客

上訴這張圖,我來給大家隆重介紹一下!!

  1. 那么實際上呢,我曾經講過,當你出啟動一個進程時,那你是不是就要有PCB啊,會創建一個PCB;
  2. 對于一個進程,也要有自己的虛擬利空間,也要有自己的列表,那么當前進程的代碼數據,它都要經過頁表映射到物理內存的特定區,所以呢,那么當我們當前的這個進程,它在執行代碼時,如果執行了你剛剛所調用的系統調用exec等這樣的接口時,它就會根據你所傳入的程序的路徑和你要執行的程序的名稱及選項,把磁盤當中的一個其他的程序加載到我們對應的內存,用新程序的代碼來替換,此時當用我們對應的當前進程去替換我們老進程的數據和代碼,把數據和代碼用新的程序全部給你重新替換一遍;
  3. 其中上圖中右側那部分基本不變啊,當然了,你替換的時候,如果空間要增多,那你就重新再去調整頁面就行了;
  4. 反正呢,我們在替換時就相當于當前我們的進程的內核數據結構不變,而把這個進程所匹配的代碼和數據用新的程序它的代碼數據來進行替換,這個行為就叫做程序替換

💨接下來,回答一個大家可能關心的問題?那就是進程進行程序替換有沒有創建新進程呢?

  1. 答案是沒有創建新的進程,為什么沒有創建新的進程呢?很簡單,因為我只是把一個新的程序加載到我們當前進程所對應的代碼和數據段;
  2. 然后呢,我讓CPU去調度當前進程,它就可以跑起來了,在這其中,我們并沒有創建新的進程,因為當前進程的內核、PCB、地址空間,尤其是PCB的pid沒有變化
    ?

而站在程序的角度,我們可以這樣去進行理解:

  1. 假設當前我是一個進程,我家里閑的沒事兒干,躺在那兒看電視呢,突然有一個人把我拉走了,讓我就給它辦一些它對應的事情,那么站在程序的角度呢,它是不是就相當于被動的被加載到的到了內存當中;
  2. 站在程序的角度,那么其中就相當于這個程序被加載了,是不是相當于這個程序就直接被加載到內存了?所以呢,我們也可以稱我們對應的exec以及之后學習到的這些程序替換函數,我們就可以稱它為叫做加載器。
    ?

(二)替換函數

在操作系統中,有幾個常用的進程替換函數可以使用,包括 exec 系列函數。下面是對它們的簡要介紹:

exec 系列函數用于執行一個新的程序映像,將當前進程替換為新程序。這些函數包括:

  • execl:接收可變數量的參數作為命令行參數傳遞給新程序。
  • execv:接收參數數組,其中第一個元素是新程序的路徑,后續元素是命令行參數。
  • execle:與?execl?類似,但額外接收一個環境變量數組作為參數。
  • execve:與?execv?類似,但額外接收一個環境變量數組作為參數。
  • execlp:與?execl?類似,但允許通過環境變量?PATH?自動搜索可執行文件的路徑。
  • execvp:與?execv?類似,但允許通過環境變量?PATH?自動搜索可執行文件的路徑。

這些函數在調用成功時不會返回,因為進程映像已被替換為新程序。如果調用失敗,它們將返回 -1,并設置全局變量 errno 指示錯誤類型。

  • ?接下來,我們通過 man手冊去對其進行查詢:

?

?

1、命名理解

這些函數原型看起來很容易混,但只要掌握了規律就很好記:

  • l(list) : 表示參數采用列表
  • v(vector) : 參數用數組
  • p(path) : 有p自動搜索環境變量PATH
  • e(env) : 表示自己維護環境變量
    ?

?2、函數理解

在上述我們已經對 execl 函數進行了詳解,接下來我逐個對剩余的函數進行解釋。

1??execlp

execlp 函數是 exec 系列函數之一,用于執行一個新的程序映像并替換當前進程。execlp 函數通過在系統的標準路徑(由 PATH 環境變量指定)中搜索可執行文件來確定要執行的程序。

  • execlp 函數的原型如下:
int execlp(const char *file, const char *arg, ...);
  • file:參數是要執行的程序文件名稱或路徑。如果在?PATH?中找到匹配的可執行文件,則只需提供文件名即可。
  • arg:參數是要傳遞給新程序的命令行參數列表。需要以空指針結尾。

下面是一個使用 execlp 函數的示例:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>int main()
{pid_t id = fork();if(id == 0){//childprintf("我是子進程: %d\n", getpid());char *const myargv[] = {"ls","-a","-l","-n",NULL};execlp("ls","-ls", "-a","-l","-n",NULL);                                                                                                                                     exit(1);}sleep(1);int status = 0;//父進程printf("我是父進程: %d\n", getpid());waitpid(id, &status, 0);printf("child exit code: %d\n", WEXITSTATUS(status));return 0 ;
}

?輸出展示:

?


2??execv

execv 是一個系統調用函數,用于在當前進程的上下文中執行一個新的程序。

  • 函數原型如下:
int execv(const char *path, char *const argv[]);

參數說明:

  • path?是一個字符串,表示要執行的程序的路徑。
  • argv?是一個以 NULL 結尾的字符串數組,表示要傳遞給執行的程序的命令行參數。

execv 執行成功時不會返回,而是直接將當前進程替換為新的程序。如果 execv 調用失敗,它會返回 -1,并且當前進程的狀態不會改變。

下面是一個使用 execv 的示例:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>int main()
{pid_t id = fork();if(id == 0){//childprintf("我是子進程: %d\n", getpid());char *const myargv[] = {"ls","-a","-l","-n",NULL};execv("/bin/ls", myargv); //lsssss: 不存在                                                                                                                                    exit(1);}sleep(1);int status = 0;//父進程printf("我是父進程: %d\n", getpid());waitpid(id, &status, 0);printf("child exit code: %d\n", WEXITSTATUS(status));return 0 ;
}

輸出展示:

?【說明】

  • 使用 execv 執行了? ls a l?-n 命令。"/bin/ls" 指定了要執行的程序的路徑,而 myargv 數組包含了命令行參數。當 execv 成功執行時,當前進程就會被 ls 程序所替代,并且輸出文件列表。
  • 需要注意的是,execv 函數需要提供完整的可執行文件路徑,并且命令行參數在數組 argv 中以 NULL 結尾。

?



3??execvp

execvp 是一個系統調用函數,與 execv 類似,用于在當前進程的上下文中執行一個新的程序。它的參數形式稍有不同,主要是在指定程序路徑時可以省略路徑。

  • 函數原型如下:
int execvp(const char *file, char *const argv[]);

參數說明:

  • file?是一個字符串,表示要執行的程序的路徑。如果?file?不包含斜杠字符(/),那么系統會按照標準的搜索路徑規則來查找可執行文件。
  • argv?是一個以 NULL 結尾的字符串數組,表示要傳遞給執行的程序的命令行參數。

execv 不同的是,execvp 可以在當前進程的環境變量 PATH 指定的路徑中搜索要執行的程序,而不需要提供完整的路徑。

下面是一個使用 execvp 的示例:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>int main()
{pid_t id = fork();if(id == 0){//childprintf("我是子進程: %d\n", getpid());char *const myargv[] = {"ls","-a","-l","-n",NULL};execvp("ls", myargv);                                                                                                                               exit(1);}sleep(1);int status = 0;//父進程printf("我是父進程: %d\n", getpid());waitpid(id, &status, 0);printf("child exit code: %d\n", WEXITSTATUS(status));return 0 ;
}

輸出展示:

?

?【說明】

  • 我們使用 execvp 執行了 ls a l?-n 命令。由于 "ls" 是一個簡單的命令,而不是一個具體的可執行文件路徑,所以 execvp 會在系統的 PATH 環境變量中搜索 "ls" 可執行文件,并執行該文件。當 execvp 成功執行時,當前進程就會被 ls 程序所替代,并且輸出文件列表。
  • execv 相比,execvp 更加靈活,因為它可以直接使用程序名稱而不需要指定完整路徑。

4??execle

execle 是一個系統調用函數,用于在當前進程的上下文中執行一個新的程序,并且可以指定環境變量。

  • 函數原型如下:
int execle(const char *path, const char *arg0, ..., const char *argn, char *const envp[]);

參數說明:

  • path:是一個字符串,表示要執行的程序的路徑。
  • arg:到?argn?是一系列以 NULL 結尾的字符串,表示要傳遞給執行的程序的命令行參數。
  • envp:是一個以 NULL 結尾的字符串數組,表示要設置給新程序的環境變量。

execv execvp不同的是,execle 可以顯式地指定環境變量,而不是繼承當前進程的環境變量。

下面是一個使用?execle 的示例:

?

  • 首先,為了跟上述的代碼區分開,我另外在創了一個文件,在里面放入了相應的信息,目的就是通過我們的【myproc】去調用other目錄下的【otherproc】:

  • ?otherproc.cc代碼如下:
#include <iostream>
#include <unistd.h>
#include <stdlib.h>using namespace std;int main()
{for(int i = 0; i < 5; i++){cout << "----------------------------------------------------------------"<< endl;cout << "我是另一個程序,我的pid是: " << getpid() << endl;cout << " MYENV: " << (getenv("MYENV")==NULL?"NULL":getenv("MYENV")) << endl;cout << " PATH: " << (getenv("PATH")==NULL?"NULL":getenv("PATH")) << endl;cout << "----------------------------------------------------------------"<< endl;sleep(1);}return 0;
}

【說明】

  1. 用于輸出當前程序的PID和環境變量。程序會循環輸出這些信息,并每秒鐘輸出一次;
  2. 程序會使用 getpid() 函數獲取當前進程的PID,并使用 getenv() 函數獲取環境變量的值;
  3. 需要注意的是,getenv() 函數用于獲取指定環境變量的值。在程序中,使用了 "MYENV""PATH" 作為要獲取的環境變量名,而對于 PATH來說在系統中默認是有的

輸出展示:

?

接下來,我們退出 other 目錄,對【myproc.c】進行改造:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>int main()
{pid_t id = fork();if(id == 0){//childprintf("我是子進程: %d\n", getpid());char *const myenv[]={"MYENV=YouCanSeeMe",NULL};execle("./other/otherproc","otherproc",NULL,myenv);                                                                                                                             exit(1);}sleep(1);int status = 0;//父進程printf("我是父進程: %d\n", getpid());waitpid(id, &status, 0);printf("child exit code: %d\n", WEXITSTATUS(status));return 0 ;
}
  • 輸出展示:

  1. ?上述不難看出,當我們調用 execle函數時,發生的是覆蓋式的調入。老的數據會被覆蓋掉;
  2. 此時即傳入了我自己定義的環境變量

但是有一天,我不想傳自己的環境變量了。此時,我想傳系統的環境變量,此時我們可以怎么做呢?

💨 此時,我們需要引入一個概念:extern char **environ;

  • 在 POSIX 標準中,全局環境變量是一個字符串指針數組,其中每個指針指向一個以 key=value 格式表示的環境變量字符串;
  • 通過使用 extern char **environ; 的聲明,我們可以在程序中訪問這個全局環境變量數組。

接下來,我們改動一下代碼:

?

輸出展示:

?【說明】

  • 我們再進行程序此時呢我們可以發現,用我們的程序去運行 other 時,此時【myenv】當前是沒有的,但是【PATH】此時還是有的。確實交給子進程了

當我不僅想傳系統的環境變量,還想把自己的環境變量都傳給子進程時,該怎么做呢?

此時我們需要在認識一個接口:putenv

putenv?是一個 C 語言標準庫函數,用于設置環境變量的值。它可以添加新的環境變量或修改已存在環境變量的值。

  • 函數原型如下:
int putenv(char *string);
  1. 參數 string 是一個以 "key=value" 格式表示的字符串;
  2. key 是要設置或修改的環境變量的名稱;
  3. value 是要將該環境變量設置為的值。

代碼展示:

?

輸出展示:

除了上述這樣的做法之外,我們還可以像下述這樣去進行操作:

  • 我們在 myproc.c 中不進行 putenv 操作,我們在當前命令行中進行 export操作:

?輸出展示:?

有了上述的理解。接下來解釋一個問題:

?在之前學習環境變量時,我們知道 環境變量具有全局屬性,可以被子進程繼承下去,但是是怎么辦到的呢?

  1. 很簡單,因為所有的指令都是bash的子進程,而bash執行所有的指令,都可以直接通過exec去執行;
  2. 我們要給子進程把bash的環境變量交給子進程,只需要調用 【execle】,然后再把我們指定的環境變量,直接以最后一個參數的形式傳給子進程,此時子進程就拿到了!!

?


5??execve
?

execve?是一個系統調用函數,它會替換當前進程的映像,將其替換為新程序的映像,并開始執行新程序。

  • 函數原型如下:
int execve(const char *filename, char *const argv[], char *const envp[]);

參數說明:?

  • 參數 filename 是要執行的程序的路徑;
  • 第二個參數 argv[] 是一個字符串數組,它包含了傳遞給新程序的命令行參數;
  • envp[] 是一個字符串數組,它包含了傳遞給新程序的環境變量。

這個跟上述的 execle 函數是類似的。在這里就不做過多演示。


6??execve


事實上,只有execve是真正的系統調用,其它五個函數最終都調用 execve,所以execve在man手冊第2節,其它函數在man手冊第3節
?

?

?這些函數之間的關系如下圖所示:

?


(三)自制shell


我們可以用下圖的時間軸來表示事件的發生次序。其中時間從左向右。shell由標識為sh的方塊代表,它隨著時間的流逝從左向右移動。shell從用戶讀入字符串"ls"。shell建立一個新的進程,然后在那個進程中運行ls程序并等待那個進程結束。

?

然后shell讀取新的一行輸入,建立一個新的進程,在這個進程中運行程序 并等待這個進程結束。
所以要寫一個shell,需要循環以下過程:

  • 1. 獲取命令行
  • 2. 解析命令行
  • 3. 建立一個子進程(fork)
  • 4. 替換子進程(execvp)
  • 5. 父進程等待子進程退出(wait)
    ?

根據這些思路,和我們前面的學的技術,就可以自己來實現一個shell了
實現代碼:
?

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>#define MAX 1024
#define ARGC 64
#define SEP " "int split(char *commandstr, char *argv[])
{assert(commandstr);assert(argv);argv[0] = strtok(commandstr, SEP);if(argv[0] == NULL) return -1;int i = 1;while((argv[i++] = strtok(NULL, SEP)));return 0;
}void debugPrint(char *argv[])
{for(int i = 0; argv[i]; i++){printf("%d: %s\n", i, argv[i]);}
}int main()
{while(1){char commandstr[MAX] = {0};char *argv[ARGC] = {NULL};printf("[zhangsan@mymachine currpath]# ");fflush(stdout);char *s = fgets(commandstr, sizeof(commandstr), stdin);assert(s);// 保證在release方式發布的時候,因為去掉assert了,所以s就沒有被使用// 而帶來的編譯告警, 什么都沒做,但是充當一次使用(void)s;commandstr[strlen(commandstr)-1] = '\0';int n = split(commandstr, argv);if(n != 0) continue;//debugPrint(argv);// version 1pid_t id = fork();assert(id >= 0);(void)id;if(id == 0){//childexecvp(argv[0], argv);exit(1);}int status = 0;waitpid(id, &status, 0);}
}

?💨 整體代碼:進程替換代碼?


總結

以上便是關于進程程序替換的全部內容。接下來,簡單回顧下本文都講了什么!!

進程程序替換是指在一個正在運行的進程中,用另外一個可執行程序替換當前進程的執行內容,從而使新的程序代碼開始執行。

進程程序替換通常用于實現進程的動態更新、功能擴展或進程間通信。在 Linux 系統中,常用的進程程序替換函數是 exec 函數族,包括 execlexecleexeclpexecvexecvp 等。

這些函數可以加載新的可執行文件,并用其替換當前進程的執行內容,從而運行新的程序。替換后,新的程序將繼承原進程的一些屬性,如進程 ID、文件描述符等。

需要注意以下幾點:

  1. 替換后,原有的程序代碼、數據和堆棧信息都會被新的程序取代,因此原有進程的狀態將完全丟失。
  2. 替換的新程序需具備執行權限,并與原進程使用相同的用戶身份執行,否則可能會導致權限問題。
  3. 替換后,新程序的命令行參數、環境變量等可以與原進程不同,從而實現不同的功能。

到此,本文便講解完畢了。感謝大家的觀看與支持!!!

?

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

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

相關文章

機器學習重要內容:特征工程之特征抽取

目錄 1、簡介 2、?為什么需要特征工程 3、特征抽取 3.1、簡介 3.2、特征提取主要內容 3.3、字典特征提取 3.4、"one-hot"編碼 3.5、文本特征提取 3.5.1、英文文本 3.5.2、結巴分詞 3.5.3、中文文本 3.5.4、Tf-idf ?所屬專欄&#xff1a;人工智能 文中提…

LLaMA長度外推高性價比trick:線性插值法及相關改進源碼閱讀及相關記錄

前言 最近&#xff0c;開源了可商用的llama2&#xff0c;支持長度相比llama1的1024&#xff0c;拓展到了4096長度&#xff0c;然而&#xff0c;相比GPT-4、Claude-2等支持的長度&#xff0c;llama的長度外推顯得尤為重要&#xff0c;本文記錄了三種網絡開源的RoPE改進方式及相…

Vue-打印組件頁面

場景: 需要將頁面的局部信息打印出來&#xff0c;只在前端實現&#xff0c;不要占用后端的資源。經過百度經驗&#xff0c;決定使用 print-js和html2canvas組件。 1. 下載包 npm install print-js --save npm install --save html2canvas 2. 組件內引用 <script>impo…

C語言之數組指針和指針數組

C語言之數組指針和指針數組 一、含義二、定義2.1 指針數組2.2 數組指針 三、使用3.1 指針數組在參數傳遞時的使用3.1.1 指針數組的排序3.2 數組指針在參數傳遞時的使用 一、含義 指針數組&#xff1a;顧名思義&#xff0c;其為一個數組&#xff0c;數組里面存放著多個指針&…

C#生成隨機驗證碼

以下是一個簡單的C#驗證碼示例&#xff1a; private void GenerateCaptcha() {// 生成隨機字符串string chars "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";Random random new Random();string captchaString new string(Enumerable.Repe…

TPAMI, 2023 | 用壓縮隱逆向神經網絡進行高精度稀疏雷達成像

CoIR: Compressive Implicit Radar | IEEE TPAMI, 2023 | 用壓縮隱逆向神經網絡進行高精度稀疏雷達成像 注1:本文系“無線感知論文速遞”系列之一,致力于簡潔清晰完整地介紹、解讀無線感知領域最新的頂會/頂刊論文(包括但不限于Nature/Science及其子刊;MobiCom, Sigcom, MobiSy…

Java【算法 04】HTTP的認證方式之DIGEST認證詳細流程說明及舉例

HTTP的認證方式之DIGEST 1.是什么2.認值流程2.1 客戶端發送請求2.2 服務器返回質詢信息2.2.1 質詢參數2.2.2 質詢舉例 2.3 客戶端生成響應2.4 服務器驗證響應2.5 服務器返回響應 3.算法3.1 SHA-2563.1.1 Response3.1.2 A13.1.3 A2 3.2 MD53.2.1 Request-Digest3.2.2 A13.2.3 A2…

CSS3 中新增了哪些常見的特性?

聚沙成塔每天進步一點點 ? 專欄簡介? 圓角&#xff08;Border Radius&#xff09;? 漸變&#xff08;Gradients&#xff09;? 陰影&#xff08;Box Shadow&#xff09;? 文本陰影&#xff08;Text Shadow&#xff09;? 透明度&#xff08;Opacity&#xff09;? 過渡&…

Spring boot與Spring cloud 之間的關系

Spring boot與Spring cloud 之間的關系 Spring boot 是 Spring 的一套快速配置腳手架&#xff0c;可以基于spring boot 快速開發單個微服務&#xff0c;Spring Boot&#xff0c;看名字就知道是Spring的引導&#xff0c;就是用于啟動Spring的&#xff0c;使得Spring的學習和使用…

MATLAB中xlsread函數用法

目錄 語法 說明 示例 將工作表讀取到數值矩陣 讀取元胞的范圍 讀取列 請求數值、文本和原始數據 對工作表執行函數 請求自定義輸出 局限性 xlsread函數的功能是讀取Microsoft Excel 電子表格文件 語法 num xlsread(filename) num xlsread(filename,sheet) num x…

Nacos和GateWay路由轉發NotFoundException: 503 SERVICE_UNAVAILABLE “Unable to find

問題再現&#xff1a; 2023-08-15 16:51:16,151 DEBUG [reactor-http-nio-2][CompositeLog.java:147] - [dc73b32c-1] Encoding [{timestampTue Aug 15 16:51:16 CST 2023, path/content/course/list, status503, errorService Unavai (truncated)...] 2023-08-15 16:51:16,17…

leetcode27—移除元素

思路&#xff1a; 參考26題目雙指針的思想&#xff0c;只不過這道題不是快慢指針。 看到示例里面數組是無序的&#xff0c;也就是說后面的元素也是可能跟給定 val值相等的&#xff0c;那么怎么處理呢。就想到了從前往后遍歷&#xff0c;如果left對應的元素 val時&#xff0c…

汽車制造業上下游協作時 外發數據如何防泄露?

數據文件是制造業企業的核心競爭力&#xff0c;一旦發生數據外泄&#xff0c;就會給企業造成經濟損失&#xff0c;嚴重的&#xff0c;可能會帶來知識產權剽竊損害、名譽傷害等。汽車制造業&#xff0c;會涉及到重要的汽車設計圖紙&#xff0c;像小米發送汽車設計圖紙外泄事件并…

[足式機器人]Part5 機械設計 Ch00/01 緒論+機器結構組成與連接 ——【課程筆記】

本文僅供學習使用 本文參考&#xff1a; 《機械設計》 王德倫 馬雅麗課件與日常作業可登錄網址 http://edu.bell-lab.com/manage/#/login&#xff0c;選擇觀摩登錄&#xff0c;查看2023機械設計2。 機械設計-Ch00Ch01——緒論機器結構組成與連接 Ch00-緒論0.1 何為機械設計——…

12.Eclipse導入Javaweb項目

同事復制一份他的項目給我ekp.rar (懶得從SVN上拉取代碼了)放在workspace1目錄下 新建一個文件夾 workspace2&#xff0c;Eclipse切換到workspace2工作空間 選擇Import導入 選擇導入的項目(這里是放到workspace1里面) 拷貝一份到workspace2里面 例子 所有不是在自己電腦上開發…

可白嫖的4家免費CDN,并測試其網絡加速情況(2023版)

網站加載速度優化過程中&#xff0c;不可避免的會用上CDN來加速資源的請求速度。但是市面上的CDN資源幾乎都是要收費的&#xff0c;而且價格還不便宜&#xff0c;對于小公司站長來講&#xff0c;這將是一筆不小的開銷。不過還是有一些良心公司給我們提供了免費的資源&#xff0…

ZooKeeper的基本概念

集群角色 通常在分布式系統中&#xff0c;構成一個集群的每一臺機器都有自己的角色&#xff0c;最典型的集群模式就是Master/Slave模式(主備模式)。在這種模式中&#xff0c;我們把能夠處理所有寫操作的機器稱為Master機器&#xff0c;把所有通過異步復制方式獲取最新數據&…

Redis_億級訪問量數據處理

11. 億級訪問量數據處理 11.1 場景表述 手機APP用戶登錄信息&#xff0c;一天用戶登錄ID或設備ID電商或者美團平臺&#xff0c;一個商品對應的評論文章對應的評論APP上有打卡信息網站上訪問量統計統計新增用戶第二天還留存商品評論的排序月活統計統計獨立訪客(Unique Vistito…

【BEV】3D視覺 PRELIMINARY

這里的知識來自于論文 Delving into the Devils of Bird’s-eye-view Perception: A Review, Evaluation and Recipe 的 Appendix B.1 部分來自 這篇文章 從透視圖轉向鳥瞰圖。&#xff08;Xw、Yw、Zw&#xff09;、&#xff08;Xc、Yc、Zc&#xff09;表示世界World坐標和相…

Android學習之路(4) UI控件之Button (按鈕)與 ImageButton (圖像按鈕)

本節引言&#xff1a; 今天給大家介紹的Android基本控件中的兩個按鈕控件&#xff0c;Button普通按鈕和ImageButton圖像按鈕&#xff1b; 其實ImageButton和Button的用法基本類似&#xff0c;至于與圖片相關的則和后面ImageView相同&#xff0c;所以本節 只對Button進行講解&am…