嵌入式學習筆記--LINUX系統編程--DAY03進程控制

1.進程概述

進程和程序的區別

程序:程序是存儲在存儲介質上的一個可執行文件---靜態的

進程:進程是程序的執行實例。可以說進程就是正在執行的程序。

程序是一些指令的集合,而進程是程序的執行過程,這個過程的狀態是變化的,包括進程的創建、調度和消亡。

單道程序和多道程序

單道程序:所有進程一個一個排隊執行。若 A 阻塞,B 只能等待,即使 CPU 處于空閑狀態。而在人機交互時阻塞的出現是必然的。所有這種模型在系統資源利用上及其不合理,在計算機發展歷史上存在不久,大部分便被淘汰了。以前的dos操作系統就是單道程序系統

多道程序:在計算機內存中同時存放幾道相互獨立的程序,它們在管理程序控制之下,相互穿插的運行(并行和并發)。多道程序設計必須有硬件基礎作為保證。

并行和并發

并行:同一時間內,多個程序或多條指令同時運行(一定是多核)

并發:宏觀上的并行,多個進程使用同一塊資源(CPU),但是他們快速的交叉使用CPU資源,給人一種假象,像是多個程序同時執行。

進程控制塊(PCB-process contrl block)

進程在內存中運行時,內核為每個進程分配一個 PCB(進程控制塊),維護進程相關的信息(包括數據、代碼存放點位置燈),Linux 內核的進程控制塊是 task_struct 結構體。相當于人的身份證。

PCB是操作系統中最重要的記錄型數據結構。

2.進程的狀態

三種狀態

就緒態:進程已經具備執行的一切條件,正在等待CPU的處理時間

執行態:該進程正在占用CPU運行

等待態:進程因不具備某些執行條件而暫時無法繼續執行的狀態

ps指令:用于顯示當前進程的狀態,類似于windows的任務管理器

參數:

ps 的參數非常多, 在此僅列出幾個常用的參數并大略介紹含義

-A 列出所有的進程

-w 顯示加寬可以顯示較多的資訊

-au 顯示較詳細的資訊

-aux 顯示所有包含其他使用者的進程

-ajx?更詳細、更具層次關系

USER:進程擁有者
PID:process id
%CPU:占用CPU使用率
%NAME:占用的記憶體使用率
VSZ:占用的虛擬記憶體大小
RSS:占用的記憶體大小
TTY:終端的次要裝置號碼
STAT:該進程的狀態
START:進程開始時間
TIME:執行的時間
COMMAND:執行的命令

stat狀態:

3.進程號

每個進程都有一個進程號來進標識,其類型是pid_t,進程號的范圍是0~32767(不同的版本可能有區別)。進程號總是唯一的,但是可以重用,一個進程終止后,這個進程號可以再次被使用。

  • linux中的進程號從0開始
    • 進程號0和1由內核創建
      • 0進程:調度進程,常被稱為交換進程
      • 1進程:init進程
    • 除調度進程外,所有進程都由1進程進行直接或者間接調用

PID:進程號,是一個非負整數

PPID:父進程,任何進程都由另一個進程創建,高金城稱為被創建進程的父進程。對應的進程號稱為父進程號(PPID);

PGID:進程組,一個或多個進程的集合。他們之間相互關聯,進程組可以接受同一終端的各種信號,關聯的進程有一個進程組號。

獲取進程函數

#include <sys/types.h>
#include <unistd.h>//功能:獲取本進程號
//返回值:當前進程進程號
pid_t getpid(void);//功能:獲取調度此函數的進程的父進程號
pid_t getppid(void);//功能:獲取進程組好,參數為0是返回當前PGID,否則返回指定的進程的PGID
pid_t getpgid(pid_t pid);

????????

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>int main(int argc, char const *argv[])
{printf("該進程號:%d\n",getpid());printf("該進程的父進程號:%d\n",getppid());printf("該進程的進程組號:%d\n",getpgid(0));return 0;
}

4.創建進程

fork函數(創建的子進程是復制一份父進程的內容,到一個新的空間)

#include <sys/types.h>
#include <unistd.h>
//創建一個新進程
pid_t fork(void)
功能:
/*fork()函數用于從一個已存在的進程中創建一個新進程,新進程稱為子進程,原進
程稱為父進程。
返回值:
成功:子進程中返回 0,父進程中返回子進程 ID。 非常重要!!!!!
失敗:返回-1*/

注意:子進程是將父進程里面的內容全部拷貝了一份,但是子進程里的fork不會再執行,會從fork的下一句開始執行,即將得到的id賦值給pid,所以fork函數在父進程里返回值為子進程id,在子進程里返回值為0(系統自動處理,避免一直執行fork,子子孫孫無窮盡也)

因為父子進程同時再運行,所以才會有兩個結果(否則 if與elseif是互斥的,不可能有兩個結果)

#include <sys/types.h>#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>int main(int argc, char **argv)
{pid_t pd = fork();if (pd < 0){perror("fork");return 1;}else if (pd == 0){while (1){printf("子進程ID-->%d,父進程ID-->%d\n", getpid(), getppid());sleep(5);}}else if (pd > 0){while (1){printf("父進程ID-->%d\n", getpid());sleep(2);}}return 0;
}

sleep函數

是將進程暫時掛起一段時間,在后續資源回收詳細介紹,可以在此處理解為延時一段時間

#include<unistd.h>
unsigned int sleep(unsigned int sec);  //進程掛起一段時間,即一直處在等待態
功能:
進程掛起指定的描述,知道指定的時間用完或者收到信號才解除掛起
返回值:
若進程掛起到sec指定的時間,則返回0,若有信號中斷,則返回剩余的秒數
注意:
進程掛起指定的秒數后程序不會立即執行,系統知識將此進程切換到就緒態

父子進程的復制

使用 fork 函數得到的子進程是父進程的一個復制品,它從父進程處繼承了整個進程的地址空間。

地址空間:包括進程上下文、進程堆棧、打開的文件描述符、信號控制設定、進程優先級、進程組號等。子進程所獨有的只有它的進程號,計時器等。因此,使用 fork 函數的代價是很大的

進程之間都是相互獨立的。

vfork函數(子進程和父進程共享一塊內存空間)

#include <sys/types.h>
#include <unistd.h>//pid_t vfork(void);創建一個新進程,但是vfork在創建進程的時候,先創建子進程,在創建父進程
/*功能:vfork()函數用于從一個已存在的進程中創建一個新進程,新進程稱為子進程,原進程稱為父進程,子進程需要退出之后(exit()),父進程才可以執行。
返回值:成功:子進程中返回 0,父進程中返回子進程 ID。失敗:返回-1。*/
pid_t vfork(void);

先創建子進程,再創建父進程

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>int main(int argc, char const *argv[])
{pid_t pd = vfork();if (pd<0){perror("vfork");}else if (pd > 0){while (1){sleep(1);printf("當前進程id:%d\n",getpid());_exit(1);  //退出,結束當前進程}}else if (pd == 0){while (1){sleep(1);printf("當前進程id:%d 當前進程父進程:%d\n",getpid(),getppid());_exit(1); //退出,結束當前進程} }return 0;
}

子進程父進程共享同樣的內存

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>int main(int argc, char const *argv[])
{int num = 0;pid_t pd = vfork();if (pd < 0){perror("vfork");}else if (pd == 0){while (1){sleep(1);printf("當前進程id:%d 當前進程父進程:%d\n", getpid(), getppid());num = 1000;printf("num:%d\n", num);_exit(1); // 退出,結束當前進程}}else if (pd > 0){while (1){sleep(1);printf("當前進程id:%d\n", getpid());printf("num:%d\n", num);_exit(1); // 退出,結束當前進程}}return 0;
}

5.進程資源回收(等待)

當進程結束后,系統可以回收進程資源,但是關于進程中的例如ID號,內存地址,進程名等,系統不會回收。父子進程有序需要簡單的進程間同步,比如父進程等待子進程結束。

一般指的是父進程回收子進程。

wait函數:

#include <sys/types.h>
#include <sys/wait.h>/*
功能:等待子進程終止,如果子進程終止了,那么此函數會回收子進程的資源。調用wait函數的進程會掛起(阻塞),因為要等待子進程結束。如果有多個子進程,需要調用多個wait函數
參數:status,子進程退出時的狀態,需要提前定義,后續會把狀態存儲到這里
返回值:
成功:子進程的進程號
失敗:-1
*/
pid_t wait(int *wstatus);

案例:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>int main(int argc, char const *argv[])
{pid_t pd = fork();if (pd < 0){perror("fork");}else if (pd == 0){int i = 0;for ( i = 5; i > 0; i--){printf("子進程%u剩余生命:%d\n",getpid(),i);sleep(1);}exit(-1);}else if (pd > 0){int status = 0 ;printf("父進程%u正在等待子進程%u結束\n",getpid(),pd);wait(&status);printf("子進程%u結束.\n",pd);printf("退出狀態為%d\n",status);}return 0;
}

exit函數

#include <stdlib.h>
void exit(int status);  //退出進程,這是庫函數寫法,等價于_exit(int status)

wait參數status詳解

1.取出子進程的退出信息 WIFEXITED(status) 如果子進程是正常終止的,取出的字
段值非零

2.?WEXITSTATUS(status) 返回子進程的退出狀態,退出狀態保存在status 變量的 8~16 位。在用此宏前應先用宏 WIFEXITED 判斷子進程是否正常退出,正常退出才可以使用此宏。

3.此 status 是個 wait 的參數指向的整型變量

    else if (pd > 0){int status = 0;printf("父進程%u正在等待子進程%u結束\n", getpid(), pd);pid_t son = wait(&status); //保存退出的那個pidif (WIFEXITED(status))  //如果正常退出{printf("子進程%u結束.\n", son);printf("退出狀態為%d\n", WEXITSTATUS(status)); //打印退出狀態}}

waitpid函數

#include <sys/types.h>
#include <sys/wait.h>pid_t waitpid(pid_t pid, int *status, int options);

函數文檔注釋:

功能:等待子進程終止,如果子進程終止了,此函數會回收子進程的資源。

參數:

? ??????pid : 參數 pid 的值有以下幾種類型:
pid > 0 等待進程 ID 等于 pid 的子進程。
pid = 0 等待同一個進程組中的任何子進程,如果子進程已經加入了別的進程
組,waitpid 不會等待它。
pid = -1 等待任一子進程,此時 waitpid 和 wait 作用一樣。
pid < -1 等待指定進程組中的任何子進程,這個進程組的 ID 等于 pid 的絕對值。例如傳入-5555,對應的作用是監控進程組號為5555的每一個pid
status : 進程退出時的狀態信息。和 wait() 用法一樣。
options : options 提供了一些額外的選項來控制 waitpid()。
0:同 wait(),阻塞父進程,等待子進程退出。
WNOHANG:沒有任何已經結束的子進程,則立即返回(非阻塞)。
WUNTRACED:如果子進程暫停了則此函數馬上返回,并且不予以理會子進程
的結束狀態。(由于涉及到一些跟蹤調試方面的知識,加之極少用到)

返回值:?waitpid() 的返回值比 wait() 稍微復雜一些,一共有 3 種情況:
1) 當正常返回的時候,waitpid() 返回收集到的已經回收子進程的進程號;
2) 如果設置了選項 WNOHANG,而調用中 waitpid() 的進程發現有子進程在運行,而且沒有已退出的子進程,則返回0;如果父進程所有子進程都結束,則返回-1;如果>0,則等到了一個子進程退出,這個返回值就是退出的那個子進程
可等待
3) 如果調用中出錯,則返回-1,這時 errno 會被設置成相應的值以指示錯誤
所在。

代碼案例:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>int main(int argc, char const *argv[])
{pid_t pd = fork();if (pd < 0){perror("fork");}else if (pd == 0){int i = 0;for (i = 5; i > 0; i--){printf("子進程%u剩余生命:%d\n", getpid(), i);sleep(1);}exit(0);}else if (pd > 0){int status = 0;printf("父進程%u正在等待子進程%u結束\n", getpid(), pd);sleep(6);pid_t son = waitpid(-1,&status,WNOHANG);  //6s后檢測是否有子進程退出,如果有,則返回對應進程idprintf("son %u\n",son);if (WIFEXITED(status)){printf("子進程%u結束.\n", son);printf("退出狀態為%d\n", WEXITSTATUS(status));}}return 0;
}

6.特殊進程

6.1僵尸進程

進程已經結束,但是對應的資源沒有回收,這樣的進程稱之為僵尸進程。例如:子進程已退出,但是父進程未回收子進程資源(wait,waitpid),那么此子進程就成為僵尸進程。

有危害:因為子進程id被占用,但是系統的pid數量有限制

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>int main(int argc, char const *argv[])
{pid_t pd = fork();if (pd < 0){perror("fork");}else if (pd == 0){int i = 0;for (i = 5; i > 0; i--){printf("子進程%u剩余生命:%d\n", getpid(), i);sleep(1);}exit(0);}else if (pd > 0){//子進程結束,父進程不回收資源while (1);//父進程不能結束,否則他們會一起被回收}return 0;
}

6.2孤兒進程

父進程先結束,但是子進程未運行結束的子進程。

子進程被1號進程接管,當子進程結束時,由1號進程進行資源回收

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>int main(int argc, char const *argv[])
{pid_t pd = fork();if (pd < 0){perror("fork");}else if (pd == 0){while (1);int i = 0;for (i = 5; i > 0; i--){printf("子進程%u剩余生命:%d\n", getpid(), i);sleep(1);}exit(0);}else if (pd > 0){printf("父進程%u結束\n",getpid());}return 0;
}

6.3守護進程(精靈進程)

脫離終端的孤兒進程

暫時不寫,后續再寫。繼續往后學下去吧

7.fork創建多個子進程

創建2個子進程

1.錯誤的寫法

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>int main(int argc, char const *argv[])
{for (int i = 0; i < 2; i++){pid_t pid = fork();}while(1);return 0 ;
}

有4個a.out相關進程

實際是1個父進程,2個子進程,還有1個子進程的子進程,一共4個。

這樣會有問題,不知道哪個進程是子進程

2.正確的進程

起始上面的代碼只要能保證子進程不會再創建子進程即可

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>int main(int argc, char const *argv[])
{for (int i = 0; i < 2; i++){pid_t pid = fork();if (pid == 0) //如果是在子進程里,就不再往下運行{break;}}while(1);return 0 ;
}

3.創建多進程并且回收資源

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>int main(int argc, char const *argv[])
{int i = 0;for (i = 0; i < 2; i++){pid_t pid = fork();if (pid == 0){break;}}if (i == 0){printf("第1個子進程id:%u\n",getpid());sleep(3);exit(1);}else if(i == 1){printf("第2個子進程id:%u\n",getpid());sleep(4);exit(1);}else if(i == 2){int ret = 0;printf("父進程Id:%u\n",getpid());while(1){ret = waitpid(-1,NULL,WNOHANG);if (ret == 0){continue;}else if (ret > 0){printf("進程%u退出\n",ret);}else if(ret < 0){break;}}}return 0 ;
}

8.終端

在 UNIX 系統中,用戶通過終端登錄系統后得到一個 Shell 進程,這個終端成為Shell 進程的控制終端(Controlling Terminal),進程中,控制終端是保存在 PCB中的信息,而 fork 會復制 PCB 中的信息,因此由 Shell 進程啟動的其它進程的控制終端也是這個終端。

舉個例子:

int num = 0;
scanf("%d",&num);
printf("%d\n",num);
while(1);

在終端中,得到一個shell進程,這個shell進程的PCB中保存著終端控制信息,因此他可以控制終端。

當檢測到a.out的時候,會fork一個子進程來完成a.out的執行,fork的子進程里面不會再有./a.out這個執行執行

子進程中沒有./a.out,也會執行,這是由exec族函數完成的,后續會講。此時shell會把中斷控制權交給a.out,所以在a.out執行的時候,輸入任何命令都不起作用。a.out結束后,終端控制權移交給shell進程

此時,如果a.out也創建一個子進程呢?

代碼案例:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>int main(int argc, char const *argv[])
{pid_t pd = fork();if (pd == 0){int num = 0;sleep(1);scanf("%d",&num);printf("子進程%u中num=%d,所屬終端名為%s\n",getpid(),num,ttyname(0));}else if (pd > 0){int num = 0;sleep(3);scanf("%d",&num);printf("父進程%u中num=%d,所屬終端名為%s\n",getpid(),num,ttyname(0));}return 0 ;
}

ttyname函數

/*獲取終端名稱
返回值:
成功:終端名
失敗:NULL
參數:文件描述符*/
char *ttyname(int fd);

進程組

進程組是包含一個或多個進程的集合,屬于一個回話,fork不會改變進程組。

進程組ID

每個進程組都有唯一的進程組ID(整數),進程組由進程組ID來唯一標識,除了PID,進程組ID也是一個進程的必備屬性之一。

進程組ID一般是當前進程組中的第一個進程的ID(組長)

getpgid();//獲得進程組IDint setpgid(pid_t pid,pid_t pgid);//將pid的進程組id設置為pgid。創建一個新進程組或者假如一個已經存在的pgid

會話

多個進程組的集合叫做會話。

沒打開一個終端,必打開一個會話。

如果進程ID=進程組ID=會話ID,那么該進程為會話首進程(會長)

會話是一個或多個進程組的集合。 一個會話可以有一個控制終端。這通常是終端設備或偽終端設備; 建立與控制終端連接的會話首進程被稱為控制進程; 一個會話中的幾個進程組可分為一個前臺進程組以及一個或多個后臺進程組; 如果一個會話有一個控制終端,則它有一個前臺進程組,其它進程組為后臺進程組; 如果終端接口檢測到斷開連接,則將掛斷信號發送至控制進程(會話首進程)。

前臺進程組:該進程組中的進程能夠向終端設備進行讀、寫操作的進程組。

后臺進程組:只能向終端寫的進程組。

創建會話

創建會話的函數:

#include <sys/types.h>
#include <unistd.h>
/*
函數功能:創建一個會話,并以自己的 ID 設置進程組 ID,同時也是新會話的 ID。
返回值:
成功:返回調用進程的會話 ID
失敗:-1
*/
pid_t setsid(void);

注意事項

1) 調用進程不能是進程組組長,該進程變成新會話首進程(session header)

2) 該調用進程是組長進程,則出錯返回

3) 該進程成為一個新進程組的組長進程

4) 需有 root 權限(ubuntu 不需要)

5) 新會話丟棄原有的控制終端,該會話沒有控制終端

6) 建立新會話時,先調用 fork, 父進程終止,子進程調用 setsid

代碼:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>int main(int argc, char const *argv[])
{pid_t pd = fork();if (pd == 0){printf("%u\n",getsid(0));setsid();while(1){}}else if (pd > 0){exit(1);}return 0 ;
}

9.exec函數族

在 Windows 平臺下,我們可以通過雙擊運行可執行程序,讓這個可執行程序成為一個進程;而在 Linux 平臺,我們可以通過 ./ 運行,讓一個可執行程序成為一個進程。 但是,如果我們本來就運行著一個程序(進程),我們如何在這個進程內部啟動一個外部程序,由內核將這個外部程序讀入內存,使其執行起來成為一個進程呢?這里我們通過 exec 函數族實現。(即可以實現從一個運行的終端啟動另一個程序)。

exec 指的是一組函數,一共有 6個

核心特點:

核心特點總結(四大特點)

    1. “換魂不換殼”

      • 不換:進程ID(PID)、父進程、打開的文件描述符、信號設置、環境變量等全部保留原樣

      • 全換:進程的代碼段、數據段、堆棧等被徹底替換為指定的新程序。

    2. 一山不容二虎

      • exec?調用成功后沒有返回值,因為原來的程序代碼已經被完全覆蓋了,執行邏輯永遠跳不到?exec?之后的代碼。

      • 如果調用失敗(如找不到指定程序),則會返回?-1,并設置?errno,然后繼續執行原程序的后續代碼。

    3. 一族六將,功能各異

      這是一個函數,包含多個函數(如?execl,?execv,?execle,?execve,?execlp,?execvp),它們最終都調用同一個系統調用?execve。
#include <unistd.h>
int execl(const char *path, const char *arg, .../* (char *) NULL */);
int execlp(const char *file,cconst char *arg, ... /* (char *) NULL */);
int execle(const char *path, const char *arg, .../*, (char *) NULL, char
* const envp[] */);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execve(const char *filename, char *const argv[], char *const envp[]);

一個進程調用 exec 后,除了進程 ID,進程還保留了下列特征不變: 父進程號 進程組號 控制終端 根目錄 當前工作目錄 進程信號屏蔽集 未處理信號 ...

六個exec函數只有execve是真正的系統調用函數,其他的都是在此基礎上封裝的庫函數。

代碼案例1:execl執行ls指令

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>int main(int argc, char const *argv[])
{execl("/bin/ls","-a","-l",NULL);printf("hello world\n");return 0 ;
}

exclp執行ls命令(p:即path,env下面的path都包含)

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>int main(int argc, char const *argv[])
{//execl("/bin/ls","-a","-l",NULL);execlp("ls","-a","-l",NULL);printf("hello world\n");return 0 ;
}

execv實現:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>int main(int argc, char const *argv[])
{//execl("/bin/ls","-a","-l",NULL);char *temp[]={"-a","-l",NULL};execv("/bin/ls",temp);printf("hello world\n");return 0 ;
}

execve實現

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>int main(int argc, char const *argv[])
{//execl("/bin/ls","-a","-l",NULL);extern char **environ;char *temp[]={"-a","-l",NULL};execve("/bin/ls",temp,environ);printf("hello world\n");return 0 ;
}

system函數

#include <stdlib.h>
/*system函數會調用fork函數,產生子進程,子進程會調用exec啟動/bin/sh -c string來執行參數string所代表的命令,命令執行完畢后返回原調用進程
參數:要執行的字符串
返回值:
如果command為null,則返回非0,一般為1
如果system()在調用/bin/sh時候失敗,返回127,其他原因返回-1
注意:system調用成功才會返回對應的返回值,如果連fork都調用失敗,肯定不會成功,一般返回0為成功
*/
int system(const char *command);
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>int main(int argc, char const *argv[])
{int status = system("ls -al");if (WIFEXITED(status)){printf("the exit status is %d\n",status);}else{printf("非正常退出\n");}return 0 ;
}

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

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

相關文章

【90頁PPT】新能源汽車數字化轉型SAP解決方案(附下載方式)

篇幅所限&#xff0c;本文只提供部分資料內容&#xff0c;完整資料請看下面鏈接 https://download.csdn.net/download/2501_92808859/91777751 資料解讀&#xff1a;《新能源汽車數字化轉型SAP解決方案》 ??詳細資料請看本解讀文章的最后內容?? 在全球汽車產業加速向電…

LSM6DS3姿態芯片和LIS2MD磁力計芯片數據讀取(stm32)

上代碼main.c 代碼示例#include "lsm6ds3.h" #include "lis2md.h"int16_t Acc[3] {0}, GYO[3] {0},Mag[3] {0};int main(void) {I2C_GPIO_Config(); //配置IIC使用端口Delayms(500); //延時Lsm6ds3_Init( ); …

數據傳輸安全-IKE工作過程

在前面的課程中&#xff0c;你已經掌握了&#xff1a;IPSec的目標&#xff1a;提供機密性、完整性、身份認證、防重放。IPSec的執行者&#xff1a;AH和ESP協議。IPSec的規則手冊&#xff1a;SA&#xff08;安全關聯&#xff09;&#xff0c;包含了所有保護參數&#xff08;算法…

翻譯-同位協同克里金算法

同點協同克里金法 副標題: 地統計學課程 主要內容: 同點協同克里金法 摘要 同點協同克里金法是一種多元地統計學技術,用于借助次要變量(協變量)來估算主要變量。本課程介紹了同點協同克里金法,并將其與簡單克里金法和簡單協同克里金法進行了比較。通過一個假設的案例…

國家統計局數據分析01——機器學習

我們前面爬取過國家統計局數據爬取——機器學習-CSDN博客的數據,我們接下來就對爬取的數據進行分析。 這是一個完整的數據分析代碼,主要用于加載、清洗和探索政府統計數據。讓我為你詳細解釋每個部分: 1. 導入必要的庫 import pandas as pd # 數據處理和分析的核心庫 imp…

【雜談】-混沌理論能否賦予機器差異化思考能力?

混沌理論能否賦予機器差異化思考能力&#xff1f; 文章目錄混沌理論能否賦予機器差異化思考能力&#xff1f;1、AI與結構化思維的本質特征2、人類思維的獨特優勢3、混沌算法的創新實踐4、混沌算法的作用機理5、混沌算法的應用實例5.1 音樂創作革新5.2 圖像生成突破5.3 科學發現…

jQuery.print插件:網頁內容打印與導出指南

本文還有配套的精品資源&#xff0c;點擊獲取 簡介&#xff1a;在網頁開發中&#xff0c;為了實現內容的打印或導出&#xff0c;可以利用jQuery.print插件&#xff0c;該插件提供了一個簡易的解決方案&#xff0c;尤其適用于表格數據。本指南詳細介紹了如何使用jQuery.print…

w嵌入式分享合集125

自己的原文哦~ https://blog.51cto.com/whaosoft/14165530 一、常用電路基礎公式 1.歐姆定律計算 計算電阻電路中電流、電壓、電阻和功率之間的關系。 歐姆定律解釋了電壓、電流和電阻之間的關系&#xff0c;即通過導體兩點間的電流與這兩點間的電勢差成正比。說…

Docker 核心技術:Union File System

大家好&#xff0c;我是費益洲。UnionFS 作為 Docker 的技術核心之一&#xff0c;實現了 Docker 鏡像的分層輕量化構建、容器資源的隔離復用等目的。本文將從核心原理、主流技術實現簡單介紹 UnionFS。 核心原理 Linux 的聯合文件系統&#xff08;Union File System&#xff0c…

MongoDB 文檔模型設計:JSON 結構的靈活性與陷阱

MongoDB 文檔模型設計&#xff1a;JSON 結構的靈活性與陷阱第一章&#xff1a;MongoDB 文檔模型基礎與核心特性1.1 MongoDB 文檔模型的哲學基礎1.2 文檔模型的優勢分析1.3 與關系型數據庫的深度對比第二章&#xff1a;文檔設計模式與最佳實踐2.1 嵌入式模式&#xff08;Embeddi…

基于SQL大型數據庫的智能問答系統優化

一、食用指南 基于SQL數據庫的智能問答系統設計與實現介紹了在數據庫中創建表格數據問答系統的基本方法&#xff0c;我們可以向該系統提出關于數據庫數據的問題&#xff0c;最終獲得自然語言答案。 為了針對數據庫編寫有效的查詢&#xff0c;我們需要向模型提供表名、表結構和…

【計算機網絡】前端基礎知識Cookie、localStorage、sessionStorage 以及 Token

一、先搞懂&#xff1a;為什么需要這些“存儲工具”&#xff1f; 網頁是“無狀態”的——比如你登錄一個網站&#xff0c;瀏覽器和服務器每次通信都是獨立的&#xff0c;服務器默認記不住你是誰。為了讓服務器“記住”用戶狀態&#xff08;比如登錄狀態、購物車內容&#xff09…

【語法】【C+V】【mermaid】本身常用圖表類型用法快查【CSDN不支持,VSCODE可用】

文章目錄 Mermaid 簡介Mermaid 由三部分組成Mermaid 的使用方法復雜圖表結構的技巧餅圖簡介餅圖語法餅圖示例雷達圖簡介雷達圖語法雷達圖語法細節標題軸曲線選項 雷達圖示例時間線簡介時間線語法時間線示例樹形圖簡介樹形圖語法樹形圖示例數據包圖簡介數據包圖語法1&#xff1a…

不止效率工具:AI 在文化創作中如何重構 “靈感邏輯”?

一、引言1.1 AI 創作的崛起在當今時代&#xff0c;AI 技術在文化創作領域的迅猛發展已成為不可忽視的現象。從文字創作領域中&#xff0c;AI 能夠快速生成小說、詩歌&#xff0c;一些新聞媒體也開始運用 AI 撰寫新聞稿件&#xff1b;到視覺藝術方面&#xff0c;AI 繪畫軟件能依…

軟考-系統架構設計師 專家系統(ES)詳細講解

個人博客&#xff1a;blogs.wurp.top 一、ES的核心概念與價值 1. 什么是專家系統&#xff08;ES&#xff09;&#xff1f; 專家系統是一種模擬人類專家解決特定領域問題的智能計算機程序系統。它運用特定領域內大量專家水平的知識和經驗&#xff0c;進行推理和判斷&#xff…

Vue3+TS+Element-Plus+el-tree創建樹節點

1、一級樹應用效果&#xff1a;代碼&#xff1a;MaterialCategory.vue<script setup lang"ts" name"MaterialCategory"> ...... // 創建樹&#xff08;一級樹&#xff09; const createTree (dataList: IMaterialCategory[]) > {// 將原始數據轉…

C++基礎(④鏈表反轉(鏈表 + 迭代 / 遞歸))

鏈表反轉&#xff08;鏈表 迭代 / 遞歸&#xff09; 題目描述&#xff1a;給你單鏈表的頭節點 head&#xff0c;請你反轉鏈表&#xff0c;并返回反轉后的鏈表頭節點。 示例&#xff1a;輸入鏈表 1→2→3→4→5 → 輸出 5→4→3→2→1。 思路提示&#xff1a;迭代法&#xff1a…

面向企業級產品開發的自動化腳本實戰

引言&#xff1a; 在產品開發團隊中&#xff0c;設計師、產品經理和工程師之間的協作常常伴隨著大量重復性工作&#xff1a;手動整理設計稿鏈接、更新產品需求文檔、同步項目狀態...這些工作不僅耗時&#xff0c;還容易出錯。本文將帶你編寫一個Python腳本&#xff0c;自動化這…

科技賦能生態,智慧守護農林,匯嶺生態開啟農林產業現代化新篇章

在我國&#xff0c;農林業作為國民經濟的基礎產業&#xff0c;不僅關乎國家糧食安全與生態平衡&#xff0c;更是鄉村振興戰略實施的核心領域。近年來&#xff0c;國家高度重視“三農”問題&#xff0c;大力推進鄉村振興戰略&#xff0c;強調要實現農業農村現代化&#xff0c;促…

貪心算法面試常見問題分類解析

一、貪心算法問題 1. 跳躍游戲系列 能否到達終點: def canJump(nums):max_reach = 0for i in range(len(nums)):if i > max_reach:return Falsemax_reach = max(max_reach, i + nums[i])return True 最少步數: def jump(nums):jumps = end = max_pos = 0for i in range(l…