Linux篇: 進程控制

一、進程創建

1.1 fork函數初識

在Linux中,fork函數是非常重要的函數,它從已存在進程中創建一個新進程。新進程為子進程,而原進程為父進程。

返回值:
在子進程中返回0,父進程中返回子進程的PID,子進程創建失敗返回-1。

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

分配新的內存塊和內核數據結構給子進程。
將父進程部分數據結構內容拷貝至子進程。
添加子進程到系統進程列表當中。
fork返回,開始調度器調度。
fork之后,父子進程代碼共享。例如:

這里可以看到,Before只輸出了一次,而After輸出了兩次。其中,Before是由父進程打印的,而調用fork函數之后打印的兩個After,則分別由父進程和子進程兩個進程執行。也就是說,fork之前父進程獨立執行,而fork之后父子兩個執行流分別執行。

注意: fork之后,父進程和子進程誰先執行完全由調度器決定,而一般父進程是先退出的,因為父進程要等待子進程(此內容會在進程等待章節講)。

1.2 fork函數返回值

子進程返回0,父進程返回的是子進程的pid。

這是為什么呢?

一個父進程可以創建多個子進程(1:n),而一個子進程只能有一個父進程。因此,對于子進程來說,父進程是不需要被標識的,子進程肯定是被創建出來的,有子進程一定就會有父進程;而對于父進程來說,子進程是需要被標識的,因為父進程創建子進程的目的是讓其執行任務的,父進程只有知道了子進程的PID才能很好的對該子進程指派任務并且可以用PID和指令去查看進程的信息。

為什么fork函數有兩個返回值?

父進程調用fork函數后,為了創建子進程,fork函數內部將會進行一系列操作,包括創建子進程的進程控制塊(PCB)、創建子進程的進程地址空間(struct files_struct)、創建子進程對應的頁表等等。子進程創建完畢后,操作系統還需要將子進程的進程控制塊添加到系統進程雙鏈表當中,此時子進程便創建完畢了。所以說子進程也用父進程的代碼,return不也是代碼嗎,所以說父子進程都要return,所以有兩個返回值。

1.3 寫時拷貝(和C++中的深拷貝類似)

當子進程剛剛被創建時,子進程和父進程的數據和代碼是共享的,即父子進程的代碼和數據通過頁表映射到物理內存的同一塊空間。只有當父進程或子進程需要修改數據時,才將父進程的數據在內存當中拷貝一份,然后再進行修改。

為什么數據要進行寫時拷貝?

?進程具有獨立性。多進程運行,需要獨享各種資源,多進程運行期間互不干擾,不能讓子進程的修改影響到父進程。

為什么不在創建子進程的時候就進行將父進程的所有數據進行寫時拷貝?

子進程也可能使用父進程的數據,并且在子進程不對數據進行寫入的情況下,沒有必要對數據進行拷貝,我們應該按需分配,在需要修改數據的時候再分配(延時分配),這樣可以高效的使用內存空間。

代碼會不會進行寫時拷貝?

一般情況下是不會的,在進行進程替換的時候,則需要進行代碼的寫時拷貝。

fork常規用法

  1. 一個進程希望復制自己,使子進程同時執行不同的代碼段。例如父進程等待客戶端請求,生成子進程來處理請求。
  2. 一個進程要執行一個不同的程序。例如子進程從fork返回后,調用exec函數。

fork調用失敗的原因

fork函數創建子進程也可能會失敗,有以下兩種情況:

  1. 系統中有太多的進程,內存空間不足,子進程創建失敗。
  2. 實際用戶的進程數超過了限制,子進程創建失敗

二、進程終止

2.1 進程退出場景

進程退出只有三種情況:

  1. 代碼運行完畢,結果正確
  2. 代碼運行完畢,結果不正確
  3. 代碼異常終止(進程崩潰)

2.2 進程退出碼

我們都知道main函數是代碼的入口,但實際上main函數只是用戶級別代碼的入口,main函數也是被其他函數調用的,例如在VS2013當中main函數就是被一個名為__tmainCRTStartup的函數所調用,而__tmainCRTStartup函數又是通過加載器被操作系統所調用的,也就是說main函數是間接性被操作系統所調用的。

既然main函數是間接性被操作系統所調用的,那么當main函數調用結束后就應該給操作系統返回相應的退出信息,而這個所謂的退出信息就是以退出碼的形式作為main函數的返回值返回,我們一般以0表示代碼成功執行完畢,以非0表示代碼執行過程中出現錯誤,這就是為什么我們都在main函數的最后返回0的原因。

當我們的代碼運行起來就變成了進程,當進程結束后main函數的返回值實際上就是該進程的進程退出碼,我們可以使用echo $?命令查看最近一次進程退出的退出碼信息。
例如,對于下面這個簡單的代碼:

代碼運行結束后,我們可以用下列指令查看該進程的進程退出碼。

echo $?

?

為什么以0表示代碼執行成功,以非0表示代碼執行錯誤?

?因為代碼執行成功只有一種情況,成功了就是成功了,而代碼執行錯誤卻有多種原因,例如內存空間不足、非法訪問以及棧溢出等等,我們就可以用這些非0的數字分別表示代碼執行錯誤的原因。這樣做利于區分。

C語言當中的strerror函數可以通過錯誤碼,獲取該錯誤碼在C語言當中對應的錯誤信息:

實際上Linux中的ls、pwd等命令都是可執行程序,使用這些命令后我們也可以查看其對應的退出碼。
可以看到,這些命令成功執行后,其退出碼也是0。

但是命令執行錯誤后,其退出碼就是非0的數字,該數字具體代表某一錯誤信息。

注意:?退出碼都有對應的字符串含義,幫助用戶確認執行失敗的原因,而這些退出碼具體代表什么含義是人為規定的,不同環境下相同的退出碼的字符串含義可能不同。?

2.3 進程常見退出方法

1)正常終止(可以通過 1. 從main返回 2. 調用exit 3. _exit 異常退出: echo $? 查看進程退出碼)

2)?異常中止: ctrl + c,信號終止。

2.4 進程正常退出

2.4.1 return退出

在main函數中使用return退出進程是我們常用的方法。

例如,在main函數最后使用return退出進程。

2.4.2 exit函數

使用exit函數退出進程也是我們常用的方法,exit函數可以在代碼中的任何地方退出進程,并且exit函數在退出進程前會做一系列工作:

執行用戶通過atexit或on_exit定義的清理函數。
關閉所有打開的流,所有的緩存數據均被寫入。
調用_exit函數終止進程。
例如,以下代碼中exit終止進程前會將該進程的緩沖區當中的數據輸出。

2.4.3 _exit函數

使用_exit函數退出進程的方法我們并不經常使用,_exit函數也可以在代碼中的任何地方退出進程,但是_exit函數會直接終止進程,并不會在退出進程前會做刷新,這和實現有關系,exit是庫函數,_exit是系統調用,exit的實現是封裝的_exit系統調用但包含了會刷新一下緩沖區,而_exit函數是系統調用,并沒有緩沖區的概念,自然就不會刷新。刷新和fflush(stdout)或\n一個概念。

例如,以下代碼中使用_exit終止進程,則緩沖區當中的數據將不會被輸出。

2.4.4 return、exit和_exit之間的區別與聯系

只有在main函數當中的return才能起到退出進程的作用,子函數當中return不能退出進程,而exit函數和_exit函數在代碼中的任何地方使用都可以起到退出進程的作用。

使用exit函數退出進程前,exit函數會執行用戶定義的清理函數、沖刷緩沖,關閉流等操作,然后再終止進程,而_exit函數會直接終止進程,不會做任何收尾工作。

執行return num等同于執行exit(num),因為調用main函數運行結束后,會將main函數的返回值當做exit的參數來調用exit函數。
在這里插入圖片描述
使用exit函數退出進程前,exit函數會先執行用戶定義的清理函數、沖刷緩沖,關閉流等操作,然后再調用_exit函數終止進程。

2.4進程異常退出

1) 向進程發生信號導致進程異常退出。例如,在進程運行過程中向進程發生kill -9信號使得進程異常退出,或是使用Ctrl+C使得進程異常退出等。

2)?代碼錯誤導致進程運行時異常退出。例如,代碼當中存在野指針問題使得進程運行時異常退出,或是出現除0的情況使得進程運行時異常退出等。

三、進程等待

3.1 進程等待的必要性

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

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

最后,父進程派給子進程的任務完成的如何,我們需要知道。如,子進程運行完成,結果對還是不對, 或者是否正常退出。 父進程通過進程等待的方式,回收子進程資源,獲取子進程退出信息。

3.2 獲取子進程status

下面進程等待所使用的兩個函數wait和waitpid,都有一個status參數,該參數是一個輸出型參數,由操作系統進行填充。
如果對status參數傳入NULL,表示不關心子進程的退出狀態信息。否則,操作系統會通過該參數,將子進程的退出信息反饋給父進程。

status是一個整型變量,但status不能簡單的當作整型來看待,status的不同比特位所代表的信息不同,具體細節如下(只研究status低16比特位):

在status的低16比特位當中,高8位表示進程的退出狀態,即退出碼。進程若是被信號所殺,則低7位表示終止信號,而第8位比特位是core dump標志。

我們通過一系列位操作,就可以根據status得到進程的退出碼和退出信號。

exitCode = (status >> 8) & 0xFF; //退出碼
exitSignal = status & 0x7F;      //退出信號

? ? ?記不住也是沒關系的,系統當中提供了兩個宏來獲取退出碼和退出信號。

exitNormal = WIFEXITED(status);  //是否正常退出
exitCode = WEXITSTATUS(status);  //獲取退出碼

當一個進程非正常退出時,說明該進程是被信號所殺,那么該進程的退出碼也就沒有意義了。

3.3 進程等待的方法

3.3.1 wait函數

原型:pid_t wait(int* status);

作用:等待任意子進程。

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

參數:輸出型參數,獲取子進程的退出狀態,不關心可設置為NULL。

例如,創建子進程后,父進程可使用wait函數一直等待子進程,直到子進程退出后讀取子進程的退出信息。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
int main()
{pid_t id = fork();if(id == 0){//childint count = 10;while(count--){printf("I am child...PID:%d, PPID:%d\n", getpid(), getppid());sleep(1);}exit(0);}//fatherint status = 0;pid_t ret = wait(&status);if(ret > 0){//wait successprintf("wait child success...\n");if(WIFEXITED(status)){//exit normalprintf("exit code:%d\n", WEXITSTATUS(status));}}sleep(3);return 0;
}

?我們可以使用以下監控腳本對進程進行實時監控:

[cl@VM-0-15-centos procWait]$ while :; do ps axj | head -1 && ps axj | grep proc | grep -v grep;echo "######################";sleep 1;done

這時我們可以看到,當子進程退出后,父進程讀取了子進程的退出信息,子進程也就不會變成僵尸進程了。

3.3.2 waitpid函數

函數原型:pid_t waitpid(pid_t pid, int* status, int options);

返回值:
1、等待成功返回被等待進程的pid。
2、如果設置了選項WNOHANG,而調用中waitpid發現沒有已退出的子進程可收集,則返回0。
3、如果調用中出錯,則返回-1,這時errno會被設置成相應的值以指示錯誤所在。

參數:
1、pid:待等待子進程的pid,若設置為-1,則等待任意子進程。
2、status:輸出型參數,獲取子進程的退出狀態,不關心可設置為NULL。
3、options:當設置為WNOHANG時,若等待的子進程沒有結束,則waitpid函數直接返回0,不予以等待。若正常結束,則返回該子進程的pid。

例如,創建子進程后,父進程可使用waitpid函數一直等待子進程(此時將waitpid的第三個參數設置為0),直到子進程退出后讀取子進程的退出信息。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
int main()
{pid_t id = fork();if (id == 0){//child          int count = 10;while (count--){printf("I am child...PID:%d, PPID:%d\n", getpid(), getppid());sleep(1);}exit(0);}//father           int status = 0;pid_t ret = waitpid(id, &status, 0);if (ret >= 0){//wait success                    printf("wait child success...\n");if (WIFEXITED(status)){//exit normal                                 printf("exit code:%d\n", WEXITSTATUS(status));}else{//signal killed                              printf("killed by siganl %d\n", status & 0x7F);}}sleep(3);return 0;
}

在父進程運行過程中,我們可以嘗試使用kill -9命令將子進程殺死,這時父進程也能等待子進程成功。

注意:?被信號殺死而退出的進程,其退出碼將沒有意義。

3.3.3 wait VS waitpid

waitwaitpid 都是用于等待子進程結束并獲取子進程狀態的系統調用,但在使用上有一些區別:

  1. wait: wait 系統調用會使當前進程阻塞,直到任意一個子進程結束為止。它會暫停當前進程的執行,直到有子進程結束,然后返回結束子進程的進程號。如果沒有子進程退出,wait 會一直阻塞等待。

  2. waitpid: waitpid 允許指定等待的子進程的進程號,通過傳遞不同的參數可以實現不同的等待方式。可以通過指定 pidoptions 參數來控制等待的子進程,如等待特定進程、等待非阻塞狀態等。與 wait 不同,waitpid 可以通過傳遞參數來選擇是否阻塞等待子進程結束。

總的來說,waitwaitpid 都是用于等待子進程結束的系統調用,而 waitpid 提供了更多的靈活性和控制選項,可以更精確地指定等待的子進程。

四、進程程序替換

4.1 原理

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

當進程調用一種exec函數時,該進程的用戶空間代碼和數據完全被新程序替換,并從新程序的啟動例程開始執行。

當進行進程程序替換時,有沒有創建新的進程?

進程程序替換之后,該進程對應的PCB、進程地址空間以及頁表等數據結構都沒有發生改變,只是進程在物理內存當中的數據和代碼發生了改變,所以并沒有創建新的進程,而且進程程序替換前后該進程的pid并沒有改變。

子進程進行進程程序替換后,會影響父進程的代碼和數據嗎?

子進程剛被創建時,與父進程共享代碼和數據,但當子進程需要進行進程程序替換時,也就意味著子進程需要對其數據和代碼進行寫入操作,這時便需要將父子進程共享的代碼和數據進行寫時拷貝,此后父子進程的代碼和數據也就分離了,因此子進程進行程序替換后不會影響父進程的代碼和數據。

4.2 替換函數

替換函數有六種以exec開頭的函數,它們統稱為exec函數:

4.2.1 execl函數

一 int execl(const char *path, const char *arg, ...);

第一個參數是要執行程序的路徑,第二個參數是可變參數列表,表示你要如何執行這個程序,并以NULL結尾。

舉例:

execl("/usr/bin/ls", "ls", "-a", "-i", "-l", NULL);

?4.2.2 execlp函數

二、int execlp(const char *file, const char *arg, ...);

舉例:

execl("/usr/bin/ls", "ls", "-a", "-i", "-l", NULL);

第一個參數是要執行程序的名字,第二個參數是可變參數列表,表示你要如何執行這個程序,并以NULL結尾。

4.2.3 execle函數

int execle(const char *path, const char *arg, ..., char *const envp[]);

第一個參數是要執行程序的路徑,第二個參數是可變參數列表,表示你要如何執行這個程序,并以NULL結尾,第三個參數是你自己設置的環境變量。

例如,你設置了MYVAL環境變量,在mycmd程序內部就可以使用該環境變量。

char* myenvp[] = { "MYVAL=2021", NULL };
execle("./mycmd", "mycmd", NULL, myenvp);

4.3.4 execv函數

int execv(const char *path, char *const argv[]);

第一個參數是要執行程序的路徑,第二個參數是一個指針數組,數組當中的內容表示你要如何執行這個程序,數組以NULL結尾。

例如,要執行的是ls:

char* myargv[] = { "ls", "-a", "-i", "-l", NULL };
execv("/usr/bin/ls", myargv);

4.3.5 execvp函數

int execvp(const char *file, char *const argv[]);

例如,要執行的是ls程序:

char* myargv[] = { "ls", "-a", "-i", "-l", NULL };
execvp("ls", myargv);

4.3.6 execve函數

int execve(const char *path, char *const argv[], char *const envp[]);

第一個參數是要執行程序的路徑,第二個參數是一個指針數組,數組當中的內容表示你要如何執行這個程序,數組以NULL結尾,第三個參數是你自己設置的環境變量。

例如,你設置了MYVAL環境變量,在mycmd程序內部就可以使用該環境變量。

char* myargv[] = { "mycmd", NULL };
char* myenvp[] = { "MYVAL=2021", NULL };
execve("./mycmd", myargv, myenvp);

4.3 函數解釋

  • 這些函數如果調用成功,則加載指定的程序并從啟動代碼開始執行,不再返回。
  • 如果調用出錯,則返回-1。

也就是說,exec系列函數只要返回了,就意味著調用失敗。

4.4 命名理解

這六個exec系列函數的函數名都以exec開頭,其后綴的含義如下:

  • l(list):表示參數采用列表的形式,一一列出。
  • v(vector):表示參數采用數組的形式。
  • p(path):表示能自動搜索環境變量PATH,進行程序查找。
  • e(env):表示可以傳入自己設置的環境變量。

事實上,只有execve才是真正的系統調用,其它五個函數最終都是調用的execve,所以execve在man手冊的第2節,而其它五個函數在man手冊的第3節,也就是說其他五個函數實際上是對系統調用execve進行了封裝,以滿足不同用戶的不同調用場景的。

下圖為exec系列函數族之間的關系:

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

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

相關文章

OSI七層模型/TCP四層模型

協議&#xff1a; 協議是雙方共同指定的一組規則&#xff0c;在網絡通信中表示通信雙方傳遞數據和解釋數據的一組規則。 從A上傳文件到服務器B,需要在A和B之間制定一個雙方都認可的規則&#xff0c;這個規則就叫文件傳輸協議&#xff0c;該協議是ftp協議的一個初級版本&#…

LeetCode 刷題 [C++] 第226題.翻轉二叉樹

題目描述 給你一棵二叉樹的根節點 root &#xff0c;翻轉這棵二叉樹&#xff0c;并返回其根節點。 題目分析 深度優先搜索&#xff08;DFS&#xff09;- 遞歸方式 對于二叉樹的鏡像問題&#xff0c;很容易想到的就是使用遞歸來解決&#xff0c;自底向上依次翻轉每一個節點…

2024年騰訊云優惠券領取頁面_代金券使用方法_新老用戶均可

騰訊云代金券領取渠道有哪些&#xff1f;騰訊云官網可以領取、官方媒體賬號可以領取代金券、完成任務可以領取代金券&#xff0c;大家也可以在騰訊云百科蹲守代金券&#xff0c;因為騰訊云代金券領取渠道比較分散&#xff0c;騰訊云百科txybk.com專注匯總優惠代金券領取頁面&am…

『大模型筆記』Sora:探索大型視覺模型的前世今生、技術內核及未來趨勢

Sora:探索大型視覺模型的前世今生、技術內核及未來趨勢 文章目錄 一. 摘要二. 引言楊立昆推薦的關于世界模型的真正含義(或應該是什么)的好文章。原文:Sora: A Review on Background, Technology, Limitations, and Opportunities of Large Vision Models譯文:Sora探索大型…

百度SEO快排原理是什么?如何快速排名方法?

前言&#xff1a;我之前說過我不打算寫這個快速排序。 首先&#xff0c;我從來沒有在自己的網站上操作過所謂的快速排序。 其次&#xff0c;我不能像網上很多人寫的那樣透露百度快速排序的秘密&#xff08;說實話&#xff0c;你可以透露秘密&#xff09;。 方法是有了&#xff…

Linux系統運維腳本:編寫bash腳本程序監控服務器的磁盤空間,在磁盤使用率超過閾值時發送警告郵件

目 錄 一、要求 二、解決方案 &#xff08;一&#xff09;解決思路 &#xff08;二&#xff09;方案 三、腳本程序實現 &#xff08;一&#xff09;腳本代碼和解釋 1、腳本代碼 2、代碼解釋 &#xff08;二&#xff09;腳本驗證 1、腳本編輯 2、給予執…

使用遞歸求解數組最大值(c++題解)

題目描述 輸入一個整數n&#xff08;n不大于1000&#xff09;&#xff0c;接下來分別為n個整數&#xff0c;請使用遞歸求取最大值。 輸入格式 第一行&#xff1a;正整數n。 第二行&#xff1a;n個整數。 輸出格式 輸出最大值 樣例 樣例輸入 復制2 1 2樣例輸出 復制2 …

Postman: 前端必備工具還是后端獨享利器

Postman 的使用場景&#xff1a;適用于前端和后端 Postman 是一個流行的 API 測試與開發工具。它被廣泛地應用在前后端開發的過程中&#xff0c;但是很多人對于它的使用場景存在疑惑。那么&#xff0c;到底是前端用還是后端用呢&#xff1f;本文將從多個角度詳細解答這個問題。…

Node.js_基礎知識(CommonJS模塊化)

CommonJS模塊化規范 加載時機&#xff1a; 服務器端: 模塊的加載是運行時同步加載的&#xff0c;node.js實現了模塊化規范瀏覽器端: 模塊需要提前編譯打包處理&#xff0c;需使用Browserify編譯打包&#xff0c;推薦使用ESM 暴露模塊&#xff1a;module.exports、exports導入模…

“а”搭配使用更地道,柯橋外貿俄語培訓

1、а именно 就是說&#xff0c;就是&#xff0c;正是 例&#xff1a; в то время, а именно год назад. 那時, 也就是一年前。 не кто иной, а именно г-н Ван. 不是別人&#xff0c;就是王先生 2、а наоборот …

【嵌入式——QT】QListWidget

QListWidget類提供了一個基于項的列表小部件&#xff0c;QListWidgetItem是列表中的項&#xff0c;該篇文章中涉及到的功能有添加列表項&#xff0c;插入列表項&#xff0c;刪除列表項&#xff0c;清空列表&#xff0c;向上移動列表項&#xff0c;向下移動列表項。 常用API a…

C語言數據結構基礎——雙鏈表專題

前言 書接上回&#xff0c;雙鏈表便是集齊帶頭、雙向、循環等幾乎所有元素的單鏈表PLUS. 1.初始化、創建雙鏈表 typedef int LTDataType; typedef struct LTNode {LTDataType data;struct LTNode* next;struct LTNode* prev; }LTNode; 不同于單鏈表&#xff0c;此時每個節點應…

selenium初始學習--打開新標簽操作

selenium 打開新標簽操作 簡單說一下使用 環境 &#xff1a;python 3.9 selenium 4,18 初始化操作 目的 打開bilibilie網站并搜索視頻&#xff08;電影&#xff09; 并點擊觀看 操作 打開應用并搜索網址 from selenium import webdriver import timefrom selenium.webdr…

PySide6+VSCode Python可視化環境搭建

#記住在cmd中運行&#xff0c;不要在vscode里運行&#xff0c;否則env會裝到工程目錄下 python -m venv env #env\Scripts\activate.bat pip install pyside6 下載本期源碼 vscode裝一個PYQT Integration插件&#xff0c;設置好兩個路徑&#xff08;下面有個腳本用于獲取路徑&…

MySQL 數據庫表設計和優化

一、數據結構設計 正確的數據結構設計對數據庫的性能是非常重要的。 在設計數據表時&#xff0c;盡量遵循一下幾點&#xff1a; 將數據分解為合適的表&#xff0c;每個表都應該有清晰定義的目的&#xff0c;避免將過多的數據存儲在單個表中。使用適當的數據類型來存儲數據&…

2020小學甲組--恢復數組

題目描述 有一個數組a[1..n]&#xff0c;但是這個數組的內容丟失了&#xff0c;你要嘗試恢復它。已知以下的三個事實&#xff1a; 1、對于1<i<n&#xff0c;都有a[i]>0&#xff0c;且所有的a[i]互不相同。即a數組保存的全部都是正整數&#xff0c;且互不相同。 2、…

挑戰杯 基于機器視覺的車道線檢測

文章目錄 1 前言2 先上成果3 車道線4 問題抽象(建立模型)5 幀掩碼(Frame Mask)6 車道檢測的圖像預處理7 圖像閾值化8 霍夫線變換9 實現車道檢測9.1 幀掩碼創建9.2 圖像預處理9.2.1 圖像閾值化9.2.2 霍夫線變換 最后 1 前言 &#x1f525; 優質競賽項目系列&#xff0c;今天要分…

范偉:你們怎么老提1,200呢,有什么典故啊?趙本山:沒有啊!

范偉&#xff1a;你們怎么老提1,200呢,有什么典故啊?趙本山&#xff1a;沒有啊&#xff01; --小品《面子》&#xff08;中3&#xff09;的臺詞 表演者&#xff1a;趙本山 高秀敏 范偉 &#xff08;接上&#xff09; 范偉&#xff1a;哎吃啊 趙&#xff1a;哎呀這電視看的挺…

Acwing枚舉、模擬與排序(一)

連號區間數 原題鏈接&#xff1a;https://www.acwing.com/problem/content/1212/ 初始最小值和最大值的依據是題目給出的數據范圍。只要在數據范圍之外就可以。 連號的時候&#xff0c;相鄰元素元素之間&#xff0c;差值為1。那么區間右邊界和左邊界&#xff0c;的值的差&#…

cAdvisor+Prometheus+Grafana 搞定Docker容器監控平臺

cAdvisorPrometheusGrafana cAdvisorPrometheusGrafana 搞定Docker容器監控平臺1、先給虛擬機上傳cadvisor2、What is Prometheus?2.1、架構圖 3、利用docker安裝普羅米修斯4、安裝grafana cAdvisorPrometheusGrafana 搞定Docker容器監控平臺 1、先給虛擬機上傳cadvisor cAd…