<5>_Linux進程控制

目錄

一,進程創建,fork/vfork

1,fork創建子進程,操作系統都做了什么

2,寫時拷貝的做了什么

二,進程終止,echo $?

1,進程終止時,操作系統做了什么

2,進程終止的常見方式

3,如何正確終止一個程序

三,進程等待

1,為什么要進行,進程等待

2,如何等待,等待是什么

(1)進程等待必要性

(2)進程等待的方法

3,獲取子進程status

四,進程替換

1,替換原理

2,替換函數

3,函數理解

4,命名理解

五,微型shell,重新認識shell運行原理

1,原理

2,實現微型shell

點個贊吧!!!666


一,進程創建,fork/vfork

1,fork創建子進程,操作系統都做了什么

fork創建子進程,是不是系統里多了一個進程?是的!

進程=內核數據結構+ 進程代碼和數據!

進程代碼和數據,一般從磁盤中來,也就是你的C/C++程序,加載之后的結果!

創建子進程,給子進程分配對應的內核結構,必須子進程自己獨有了,因為進程具有獨立性!

理論上,子進程也要有自己的代碼和數據!

可是一般而言,我們沒有加載的過程,也就是說,子進程沒有自己的代碼和數據!

所以,子進程只能”使用“父進程的代碼和數據!

代碼:都是不可被寫的,只能讀取,所以父子共享,沒有問題!

數據:可能被修改的,所以,必須分離!

對于數據而言,什么時候分離?

如果,創建進程的時候,就直接拷貝分離。這楊樣會導致,可能拷貝子進程根本不會用到數據空間,即使用到了,也可能只是讀取。

而即使是OS,也不知道哪些空間可能會被寫入,即使提前拷貝了,也不會立馬使用。所以,OS選擇了寫時拷貝技術,將父子進程的數據進行分離。

OS為何要選擇寫時拷貝的技術,對父子進程進行分離?

用的時候再給你分配,是高效使用內存的一種表現,而且OS無法在代碼執行前預知哪些空間會被訪問。

所以,fork創建父子進程之后,代碼是共享的,內核的數據會各進程寫時拷貝一份。

2,寫時拷貝的做了什么

進程調用fork,當控制轉移到內核中的fork代碼后,內核做了以下操作:

分配新的內存塊和內核數據結構給子進程。

將父進程部分數據結構內容拷貝至子進程。

添加子進程到系統進程列表當中。

fork返回,開始調度器調度。

fork之后,父子進程代碼共享是所有代碼都共享的。

(1)我們的代碼匯編之后會,會有很多行代碼,而且每行代碼加載到內存之后,都有對應的地址。

(2)因為進程隨時可能被中斷(可能并沒有執行完),下次回來,還必須從之前的位置繼續執行(不是最開始的位置),這就要求CPU必須隨時記錄下,當前進程執行的位置,所以,CPU內有對應的寄存器EIP(PC程序計數器),用來記錄當前執行位置。

(3)寄存器在CPU內,只有一份,寄存器內的數據,是可以有多份的。進程的上下文數據,在fork創建子進程之后,對于子進程已經不重要了。雖然父子進程各自調度,各自都會修改EIP,但是已經不重要了,因為子進程已經認為自己的EIP起始值,就是fork之后的代碼。

二,進程終止,echo $?

1,進程終止時,操作系統做了什么

當然是要釋放進程申請的,相關內核數據結構和對應的數據與代碼,本質就是釋放系統資源。

2,進程終止的常見方式

(1)進程退出場景:

代碼運行完畢,結果正確。

代碼運行完畢,結果不正確。

代碼異常終止,程序崩潰了。

(2)進程退出碼:

查看退出碼使用:echo $?

0,表示成功。

非0,表示失敗,具體是幾,要看退出的原因。

程序崩潰的時候,退出碼無意義。一般而言,退出碼對應的return語句,沒有被執行。

[user@iZwz9eoohx59fs5a6ampomZ linux-52]$ cat exitcode.c
#include <stdio.h>
#include <unistd.h>
#include <string.h>int main()
{int number;for(number = 0; number < 100; number++){printf("%d: %s\n", number, strerror(number));}
}
[user@iZwz9eoohx59fs5a6ampomZ linux-52]$ ./exitcode 
0: Success
1: Operation not permitted
2: No such file or directory
3: No such process
4: Interrupted system call
5: Input/output error
6: No such device or address
7: Argument list too long
8: Exec format error
9: Bad file descriptor
10: No child processes
。。。。。。

3,如何正確終止一個程序

使用 exit vs return 語句。

return語句,就是終止進程的,return+退出碼。

exit語句,在代碼的任何地方調用,都表示直接終止進程。

exit的頭文件是stdlib.h,exit(int status)有一個參數,這個參數就是退出碼。

exit最后也會調用exit, 但在調用exit之前,還做了其他工作:

1. 執行用戶通過 atexit或on_exit定義的清理函數

2. 關閉所有打開的流,所有的緩存數據均被寫入

3. 調用_exit

三,進程等待

1,為什么要進行,進程等待

(1)子進程退出,父進程不管子進程,子進程就要處于僵尸狀態。

(2)父進程創建子進程,是要讓子進程辦事的,那么子進程把任務完成的怎么樣,父進程關系嗎?如果需要,如果得知?如果不需要,如何處理?

2,如何等待,等待是什么

(1)進程等待必要性

之前講過,子進程退出,父進程如果不管不顧,就可能造成‘僵尸進程’的問題,進而造成內存泄漏。

另外,進程一旦變成僵尸狀態,那就刀槍不入,“殺人不眨眼”的kill -9 也無能為力,因為誰也沒有辦法殺死一個已經死去的進程。

最后,父進程派給子進程的任務完成的如何,我們需要知道。如果子進程運行完成,結果對還是不對,或者是否正常退出。

父進程通過進程等待的方式,回收子進程資源,獲取子進程退出信息。

(2)進程等待的方法

wait方法

頭文件:

#include<sys/types.h>

#include<sys/wait.h>

函數:

pid_t wait(int*status);

返回值:

成功返回被等待進程pid,失敗返回-1。

參數:

輸出型參數,獲取子進程退出狀態,不關心則可以設置成為NULL

基本驗證-等待僵尸進程

// 會話·1[user@iZwz9eoohx59fs5a6ampomZ linux-53]$ cat myproc.c
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{pid_t id = fork();if(id < 0){perror("fork");exit(1); //標識進程運行完畢,結果不正確}else if(id == 0){//子進程int cnt = 5;while(cnt){printf("cnt: %d, 我是子進程, pid: %d, ppid: %d\n", cnt, getpid(), getppid());sleep(1);cnt--;}exit(0);}else{//父進程printf("我是父進程,pid: %d, ppid: %d\n", getpid(), getppid());sleep(7);pid_t ret = wait(NULL);//阻塞式等待if(ret > 0){printf("等待子進程成功,ret: %d\n", ret);}while(1){printf("cnt: %d, 我是父進程, pid: %d, ppid: %d\n", getpid(), getppid());sleep(1);                                              }}
}// 會話·2[user@iZwz9eoohx59fs5a6ampomZ linux-53]$ ./myproc 
我是父進程,pid: 31946, ppid: 30659
cnt: 5, 我是子進程, pid: 31947, ppid: 31946
cnt: 4, 我是子進程, pid: 31947, ppid: 31946
cnt: 3, 我是子進程, pid: 31947, ppid: 31946
cnt: 2, 我是子進程, pid: 31947, ppid: 31946
cnt: 1, 我是子進程, pid: 31947, ppid: 31946
等待子進程成功,ret: 31947
cnt: 31946, 我是父進程, pid: 30659, ppid: -386691177
cnt: 31946, 我是父進程, pid: 30659, ppid: -386691177
cnt: 31946, 我是父進程, pid: 30659, ppid: -386691177
^C

waitpid方法

函數:

pid_ t waitpid(pid_t pid, int *status, int options);

返回值:

當正常返回的時候waitpid返回收集到的子進程的進程ID;

如果設置了選項WNOHANG,而調用中waitpid發現沒有已退出的子進程可收集,則返回0;

如果調用中出錯,則返回-1,這時errno會被設置成相應的值以指示錯誤所在;

參數:

pid:

Pid=-1,等待任一個子進程。與wait等效。

Pid>0.等待其進程ID與pid相等的子進程。

status:

WIFEXITED(status): 若為正常終止子進程返回的狀態,則為真.(查看進程是否是正常退出)

WEXITSTATUS(status): 若WIFEXITED非零,提取子進程退出碼。(查看進程的退出碼)

options:

WNOHANG: 若pid指定的子進程沒有結束,則waitpid()函數返回0,不予以等待。若正常結束,則返回該子進程的ID。

獲取子進程退出的結果

如果子進程已經退出,調用wait/waitpid時,wait/waitpid會立即返回,并且釋放資源,獲得子進程退出信息。

如果在任意時刻調用wait/waitpid,子進程存在且正常運行,則進程可能阻塞。

如果不存在該子進程,則立即出錯返回。

3,獲取子進程status

wait和waitpid,都有一個status參數,該參數是一個輸出型參數,由操作系統填充。

如果傳遞NULL,表示不關心子進程的退出狀態信息。

否則,操作系統會根據該參數,將子進程的退出信息反饋給父進程。

status不能簡單的當作整形來看待,可以當作位圖來看待,具體細節如下圖(只研究status低16比特位)

// 會話1[user@iZwz9eoohx59fs5a6ampomZ linux-53]$ ./myproc
我是子進程: 5
我是子進程: 4
我是子進程: 3
我是子進程: 2
我是子進程: 1
子進程執行完畢,子進程的退出碼:11
[user@iZwz9eoohx59fs5a6ampomZ linux-53]$ cat myproc.c
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{pid_t id = fork();if(id == 0){// 子進程int cnt = 5;while(cnt){printf("我是子進程: %d\n", cnt);sleep(1);cnt--;}exit(11);}else{// 父進程int status = 0;// 只有子進程退出的時候,父進程才會waitpid函數,進行返回,此時父進程還活著// wait/waitpid 可以在目前的情況下,讓進程退出具有一定的順序性// 將來可以讓父進程進行更多的收尾工作// id > 0  等待指定進程// id== 0  TODO// id== -1 等待任意一個子進程退出,等價于wait()pid_t result = waitpid(id, &status, 0);//阻塞狀態下,等待子進程退出if(result > 0){// 可以不這么檢測// printf("父進程等待成功,退出碼:%d\n,退出信號:%d\n", (status>>8)&0xFF, status & 0x7F);if(WIFEXITED(status)){// 子進程是正常退出的printf("子進程執行完畢,子進程的退出碼:%d\n", WEXITSTATUS(status));}else{   printf("子進程異常退出:%d\n", WIFEXITED(status));}}}
}// 會話2
[user@iZwz9eoohx59fs5a6ampomZ linux-53]$ while :; do ps axj | head -1 && ps axj | grep myproc |grep ; sleep 1; echo "-----------------------------------------";donePPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
-----------------------------------------PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
-----------------------------------------PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
-----------------------------------------PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
-----------------------------------------PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
-----------------------------------------PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
30659 17885 17885 30659 pts/1    17885 S+    1001   0:00 ./myproc
17885 17886 17885 30659 pts/1    17885 S+    1001   0:00 ./myproc
-----------------------------------------PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
30659 17885 17885 30659 pts/1    17885 S+    1001   0:00 ./myproc
17885 17886 17885 30659 pts/1    17885 S+    1001   0:00 ./myproc
-----------------------------------------PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
30659 17885 17885 30659 pts/1    17885 S+    1001   0:00 ./myproc
17885 17886 17885 30659 pts/1    17885 S+    1001   0:00 ./myproc
-----------------------------------------PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
30659 17885 17885 30659 pts/1    17885 S+    1001   0:00 ./myproc
17885 17886 17885 30659 pts/1    17885 S+    1001   0:00 ./myproc
-----------------------------------------PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
30659 17885 17885 30659 pts/1    17885 S+    1001   0:00 ./myproc
17885 17886 17885 30659 pts/1    17885 S+    1001   0:00 ./myproc
-----------------------------------------PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
-----------------------------------------PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
-----------------------------------------PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
^C

四,進程替換

1,替換原理

fork()之后,父子各自執行父進程代碼的一部分,父子代碼共享,數據寫時拷貝各自一份。如果子進程就想有自己的代碼,執行一個全新的程序呢?這時就使用到進程替換。

用fork創建子進程后執行的是和父進程相同的程序(但有可能執行不同的代碼分支),子進程往往要調用一種exec函數以執行另一個程序。當進程調用一種exec函數時,該進程的用戶空間代碼和數據完全被新程序替換,從新程序的啟動例程開始執行。調用exec并不創建新進程,所以調用exec前后該進程的id并未改變。

2,替換函數

其實有六種以exec開頭的函數,統稱exec函數:

#include <unistd.h>
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ...,char *const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execve(const char *path, char *const argv[], char *const envp[]);

3,函數理解

這些函數如果調用成功則加載新的程序從啟動代碼開始執行,不再返回。

如果調用出錯則返回-1。

所以exec函數只有出錯的返回值而沒有成功的返回值。

最后一個參數,必須是NULL,要標識參數傳遞完畢。

案例:創建子進程,只使用最簡單的exec函數。

printf("當前進程的結束代碼!\n");為什么不打印呢?

因為,execl是程序替換,該函數成功調用之后,會將當前進程的所有代碼數據都進行替換,包括已經執行和沒有執行的,所以一旦調用成功,后續的所有代碼都不會執行。

printf("當前進程的開始代碼!\n");也被替換了,只是因為它在execl之前就打印了,才會顯示出來"當前進程的開始代碼!"。

[user@iZwz9eoohx59fs5a6ampomZ linux-54]$ cat myproc.c
#include <stdio.h>
#include <unistd.h>int main()
{printf("當前進程的開始代碼!\n");printf("當前進程的結束代碼!\n");return 0;
}
[user@iZwz9eoohx59fs5a6ampomZ linux-54]$ ./myproc 
當前進程的開始代碼!
當前進程的結束代碼!
[user@iZwz9eoohx59fs5a6ampomZ linux-54]$ cat myproc.c
#include <stdio.h>
#include <unistd.h>int main()
{printf("當前進程的開始代碼!\n");execl("/usr/bin/ls", "ls", "-l", "-a", "-i", NULL);printf("當前進程的結束代碼!\n");return 0;
}
[user@iZwz9eoohx59fs5a6ampomZ linux-54]$ ./myproc 
當前進程的開始代碼!
total 28
1449872 drwxrwxr-x  2 user user 4096 Jul  3 13:46 .
1441793 drwxrwxr-x 11 user user 4096 Jul  3 13:34 ..
1449895 -rw-rw-r--  1 user user   64 Jul  3 13:35 makefile
1449894 -rwxrwxr-x  1 user user 8536 Jul  3 13:46 myproc
1449896 -rw-rw-r--  1 user user  222 Jul  3 13:45 myproc.c

4,命名理解

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

l(list) : 表示參數采用列表

v(vector) : 參數用數組

p(path) : 有p自動搜索環境變量PATH

e(env) : 表示自己維護環境變量

exec調用舉例如下:

#include <unistd.h>
int main()
{char *const argv[] = {"ps", "-ef", NULL};char *const envp[] = {"PATH=/bin:/usr/bin", "TERM=console", NULL};execl("/bin/ps", "ps", "-ef", NULL);// 帶p的,可以使用環境變量PATH,無需寫全路徑execlp("ps", "ps", "-ef", NULL);// 帶e的,需要自己組裝環境變量execle("ps", "ps", "-ef", NULL, envp);execv("/bin/ps", argv);// 帶p的,可以使用環境變量PATH,無需寫全路徑execvp("ps", argv);// 帶e的,需要自己組裝環境變量execve("/bin/ps", argv, envp);exit(0);
}

五,微型shell,重新認識shell運行原理

1,原理

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

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

1. 獲取命令行

2. 解析命令行

3. 建立一個子進程(fork)

4. 替換子進程(execvp)

5. 父進程等待子進程退出(wait)

2,實現微型shell

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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>#define NUM 1024
#define SIZE 32
#define SEP " "// 保存完整的命令行字符串
char cmd_line[NUM];
// 保存打散之后的命令行字符串
char *g_argv[SIZE];// shell 運行原理:通過子進程執行命令,父進程等待&&解析命令
int main()
{//0.命令行解釋器,一定是一個常駐內存的進程,不退出while(1){//1.打印出提示信息 [root@localhost myshell]#printf("[root@localhost myshell]# ");fflush(stdout);memset(cmd_line, '\0', sizeof cmd_line);//2.獲取用戶的輸入,輸入的是各自指令和選型"ls -a -l -i"if(fgets(cmd_line, sizeof cmd_line, stdin) == NULL){continue;                                                                            }cmd_line[strlen(cmd_line)-1] = '\0';//去掉空行,把\n變成\0,字符串長度下標就是\n,"ls -a -l -i\n\0 "//printf("echo: %s\n", cmd_line);//3.把輸入的命令行字符串解析,從"ls -a -l -i",變成"ls","-a","-i","-l"//第一次調用,要傳入原始字符串g_argv[0] = strtok(cmd_line, SEP);int index = 1;// 加顏色if(strcmp(g_argv[0], "ls") == 0){g_argv[index++] = "--color=auto";                                                    }// 設置ll命令別名if(strcmp(g_argv[0], "ll") == 0){g_argv[0] = "ls";g_argv[index++] = "-l";g_argv[index++] = "--color=auto";}//第二次調用,如果還要解析原始字符串,傳入NULLwhile(g_argv[index++] = strtok(NULL, SEP));//for(index = 0; g_argv[index]; index++)//    printf("g_argv[%d]: %s\n", index, g_argv[index]);//4.執行命令,內置命令,讓父進程(shell)自己執行的命令,就叫做內置(內鍵)命令        //內置命令,本質就是shell中的一個函數調用if(strcmp(g_argv[0], "cd") == 0)        {if(g_argv[1] != NULL) chdir(g_argv[1]);continue;}//5.父進程調用子進程執行,fork()//子進程pid_t id = fork();if(id == 0){printf("下面的功能讓是子進程執行的\n");execvp(g_argv[0], g_argv);exit(1);}//父進程int status = 0;pid_t ret = waitpid(id, &status, 0);if(ret > 0) printf("exit code: %d\n", WEXITSTATUS(status));}
}

點個贊吧!!!666

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

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

相關文章

阿里云服務器正確配置 Docker 國內鏡像的方法

&#x1f4e6; 原理說明&#xff1a;什么是“Docker 鏡像加速器”&#xff1f; Docker 默認會從官方倉庫 registry-1.docker.io 拉取鏡像。由于網絡原因&#xff0c;在中國大陸訪問這個地址較慢甚至失敗。 鏡像加速器的作用是&#xff1a; 在國內部署一個緩存服務器&#xf…

PH熱榜 | 2025-07-05

1. todai 標語&#xff1a;你的第一份個性化快樂生活指數 介紹&#xff1a;Todai 是你個人的人工智能助手&#xff0c;幫助你獲得心理清晰和情感平衡。你可以隨時隨地記錄自己的情緒&#xff0c;發現情緒變化的規律&#xff0c;并獲取基于科學的工具。 產品網站&#xff1a;…

c++ duiLib環境集成

duiLib的Github鏈接&#xff1a;https://github.com/duilib/duilib 使用vcpkg快速安裝duilib以及配置。步驟如下&#xff1a; 1、用git下載vcpkg&#xff0c;下載報錯&#xff0c;這個錯誤通常表明在Git克隆過程中&#xff0c;與GitHub服務器的SSL連接被意外重置。改用http下…

一項基于粒子圖像測速PIV系統的泥石流模擬沖擊實驗

1實驗背景 全國進入“七下八上”防汛關鍵期&#xff0c;泥石流作為山區常見地質災害&#xff0c;突發性強&#xff0c;破壞力大&#xff0c;對人民群眾生命財產安全造成威脅&#xff0c;傳統觀測手段難以實現對碎石運動軌跡與水流場耦合效應的精細觀測。而粒子圖像測速PIV技術…

ADAS功能介紹

ADAS功能介紹 ADAS&#xff08;Advanced Driving Assistance System&#xff09;高級駕駛輔助系統&#xff0c;可分為如下幾大類功能。 IA&#xff08;Information Assist&#xff09;信息輔助類 IA類功能&#xff0c;均不包含駕駛行為的控制。這些功能又可以進一步細分為三…

【LUT技術專題】CLUT代碼講解

本文是對CLUT技術的代碼講解&#xff0c;原文解讀請看CLUT文章講解。 1、原文概要 CLUT利用矩陣在保持3DLUT映射能力的前提下顯著降低了參數量。整體流程如下所示。 整體還是基于3D-LUT的框架&#xff0c;只不過添加了一個壓縮自適應的變換矩陣。作者使用的損失函數在3DLUT的…

在LinuxMint 22.1(Ubuntu24.04)上安裝使用同花順遠航版

剛剛在LinuxMint 22.1(Ubuntu24.04)安裝完成同花順遠航版&#xff0c;體驗特別好&#xff0c;忍不住要及時給深受Linux平臺無好用行情軟件之苦的朋友們進行分享了。在此之前我一直只能用同花順Linux原生版的行情軟件&#xff0c;但是該軟件只有很基本的行情功能&#xff0c;而且…

解決vue3路由配合Transition時跳轉導致頁面不渲染的問題

問題復現 <router-view v-slot"{ Component, route }"><transition name"fade" mode"out-in"><keep-alive><component :is"Component" :key"route.path" /></keep-alive></transition>…

java: 無法訪問org.springframework.boot.SpringApplication,類文件具有錯誤的版本 61.0, 應為 52.0

問題 java: 無法訪問org.springframework.boot.SpringApplication 錯誤的類文件: /D:/.m2/repository/org/springframework/boot/spring-boot/3.3.13/spring-boot-3.3.13.jar!/org/springframework/boot/SpringApplication.class 類文件具有錯誤的版本 61.0, 應為 52.0 請刪除…

Docker拉取nacos鏡像

以下是使用 Docker 拉取并運行 Nacos&#xff08;阿里巴巴開源的配置中心和服務發現組件&#xff09;鏡像的詳細指南&#xff1a; 1. 拉取 Nacos 官方鏡像 拉取最新版 Nacos 鏡像&#xff08;推薦指定版本以避免兼容性問題&#xff09;&#xff1a; # 拉取最新版本&#xff…

【CTF-Web環境搭建】kali

Kali虛擬機下載 這里在官網上下載下kali虛擬機Get Kali | Kali Linux 網速比較慢的話打開一下加速器 下載完成后 得到一個壓縮包 選擇一個合適的地方將這個壓縮包解壓一下 記住這個文件目錄 這里為了后續方便 簡歷一個叫做Virtual Machines的文件夾 里面就可以放不同的虛擬機…

微服務架構的演進:邁向云原生

微服務架構的演進&#xff1a;邁向云原生ps:最近在學習的時候&#xff0c;發現好多技術方案最終都有云原生的影子&#xff0c;這里淺談一下云原生的發展趨勢隨著互聯網技術的發展&#xff0c;軟件開發模式經歷了從單體應用到微服務架構的重大轉變。而在今天&#xff0c;微服務架…

服務器如何配置防火墻規則開放/關閉端口?

配置服務器防火墻規則&#xff08;開放/關閉端口&#xff09;是服務器安全管理的基礎操作&#xff0c;不同操作系統和防火墻工具的配置方式有所不同。以下是主流系統的詳細操作指南&#xff1a;一、Linux系統&#xff08;iptables/firewalld/UFW&#xff09;1. iptables&#x…

基于SpringBoot+Redis實現外呼頻次限制功能

針對外呼場景中的號碼頻次限制需求&#xff08;如每3天只能呼出1000通電話&#xff09;&#xff0c;我可以提供一個基于Spring Boot和Redis的完整解決方案。 方案設計 核心思路 使用Redis的計數器過期時間機制 采用滑動窗口算法實現精確控制 通過Lua腳本保證原子性操作 實…

下一代 2D 圖像設計工具「GitHub 熱點速覽」

長期以來&#xff0c;2D 設計領域似乎已是 Adobe 與 Figma 的天下&#xff0c;層疊的圖層、熟悉的工具欄&#xff0c;一切都顯得那么順理成章&#xff0c;卻也讓不少設計師在創意的邊界上感到了些許乏力。當我們以為設計工具的革新只能是小修小補時&#xff0c;開源社區再次扮演…

L58.【LeetCode題解】模擬算法習題集1(Z 字形變換、外觀數列)

目錄 1.Z 字形變換 方法1: 模擬 代碼 提交結果 方法2:優化后的模擬 代碼 提交結果 2.外觀數列 方法1:模擬 代碼 提交結果 方法2:打表 知識回顧 代碼 1.Z 字形變換 https://leetcode.cn/problems/zigzag-conversion/ 將一個給定字符串 s 根據給定的行數 numRows &…

Flink MySQL CDC 環境配置與驗證

一、MySQL 服務器配置詳解 1. 啟用二進制日志&#xff08;Binlog&#xff09; MySQL CDC 依賴二進制日志獲取增量數據&#xff0c;需在 MySQL 配置文件&#xff08;my.cnf 或 my.ini&#xff09;中添加以下配置&#xff1a; # 啟用二進制日志 log-binmysql-bin # 二進制日志…

如何查看自己電腦的CUDA版本?

在搜索欄輸入命令提示符 打開 輸入 nvidia-smi圖片中的兩個是CUDA版本和顯卡的信息

opencv使用 GStreamer 硬解碼和 CUDA 加速的方案

在Conda環境中從源代碼編譯OpenCV&#xff08;支持CUDA和GStreamer&#xff09; 以下是完整的方案步驟&#xff0c;包括必要的依賴庫安裝過程&#xff1a; 1. 安裝Miniconda&#xff08;如果尚未安裝&#xff09; # 下載Miniconda安裝腳本 wget https://repo.anaconda.com/m…

Java面試寶典:多線程一

1. run() vs start() 陷阱題 下面程序的運行結果 public static void main(String[] args) {Thread t = new Thread(