做視頻網(wǎng)站如何賺錢品牌推廣的渠道有哪些
1. 進(jìn)程的基礎(chǔ)概念
1.1 進(jìn)程是什么?
定義:
- 進(jìn)程是操作系統(tǒng)管理的一個(gè)程序?qū)嵗?。它包含程序代碼及其當(dāng)前活動(dòng)的狀態(tài)。每個(gè)進(jìn)程有自己的內(nèi)存地址空間,擁有獨(dú)立的棧、堆、全局變量等。操作系統(tǒng)通過(guò)進(jìn)程來(lái)分配資源(如 CPU 時(shí)間、內(nèi)存等)并管理任務(wù)的執(zhí)行。
進(jìn)程 vs 程序:
- 程序:靜態(tài)的代碼和數(shù)據(jù),存儲(chǔ)在磁盤上。
- 進(jìn)程:程序的一個(gè)運(yùn)行實(shí)例,包括程序的代碼、活動(dòng)狀態(tài)、資源等。
1.2 進(jìn)程的內(nèi)存布局
一個(gè)典型進(jìn)程的內(nèi)存分布如下:
內(nèi)存段 | 描述 |
---|---|
棧(Stack) | 存儲(chǔ)函數(shù)調(diào)用時(shí)的局部變量、函數(shù)調(diào)用鏈等。棧從高地址向低地址增長(zhǎng)。 |
空間(Gap) | 堆和棧之間未分配的內(nèi)存區(qū)域,用于防止二者相互干擾。 |
堆(Heap) | 用于動(dòng)態(tài)內(nèi)存分配(如使用 malloc() ),堆從低地址向高地址增長(zhǎng)。 |
BSS段(BSS Segment) | 存儲(chǔ)未初始化的全局變量和靜態(tài)變量。 |
數(shù)據(jù)段(Data Segment) | 存儲(chǔ)已初始化的全局變量和靜態(tài)變量。 |
代碼段(Text Segment) | 存儲(chǔ)程序的代碼,即指令。 |
#include <stdio.h>
#include <stdlib.h>// 全局變量(已初始化) - 數(shù)據(jù)段
int global_initialized = 42;// 靜態(tài)變量(未初始化) - BSS 段
static int static_uninitialized;// 函數(shù) - 代碼段
void display_addresses() {// 局部變量 - 棧段int local_variable = 0;printf("代碼段 (Text Segment): %p\n", (void*)display_addresses);printf("數(shù)據(jù)段 (Data Segment) (已初始化全局變量): %p\n", (void*)&global_initialized);printf("BSS 段 (BSS Segment) (未初始化靜態(tài)變量): %p\n", (void*)&static_uninitialized);printf("棧段 (Stack Segment) (局部變量): %p\n", (void*)&local_variable);
}int main() {// 堆內(nèi)存 - 堆段int* heap_variable = (int*)malloc(10 * sizeof(int));// 顯示各內(nèi)存段的地址display_addresses();printf("堆段 (Heap Segment) (動(dòng)態(tài)分配內(nèi)存): %p\n", (void*)heap_variable);// 再次分配內(nèi)存,看看堆地址是否變化int* heap_variable2 = (int*)malloc(10 * sizeof(int));printf("堆段 (Heap Segment) (再次分配內(nèi)存): %p\n", (void*)heap_variable2);// 釋放內(nèi)free(heap_variable);free(heap_variable2);return 0;
}
運(yùn)行結(jié)果:
代碼段 (Text Segment): 0x55d05bad21a9
數(shù)據(jù)段 (Data Segment) (已初始化全局變量): 0x55d05bad5010
BSS 段 (BSS Segment) (未初始化靜態(tài)變量): 0x55d05bad5018
棧段 (Stack Segment) (局部變量): 0x7ffc49a5c084
堆段 (Heap Segment) (動(dòng)態(tài)分配內(nèi)存): 0x55d05c49c2a0
堆段 (Heap Segment) (再次分配內(nèi)存): 0x55d05c49c6e0
1.3 進(jìn)程的生命周期
進(jìn)程在操作系統(tǒng)中的生命周期包括以下幾個(gè)狀態(tài):
進(jìn)程狀態(tài) | 描述 |
---|---|
新建(New) | 進(jìn)程被創(chuàng)建,操作系統(tǒng)為其分配資源。 |
就緒(Ready) | 進(jìn)程已經(jīng)準(zhǔn)備好運(yùn)行,等待調(diào)度器分配 CPU。 |
運(yùn)行(Running) | 進(jìn)程正在 CPU 上執(zhí)行指令。 |
阻塞(Blocked/Waiting) | 進(jìn)程在等待某些條件(如 I/O 操作完成),無(wú)法繼續(xù)執(zhí)行。 |
終止(Terminated) | 進(jìn)程執(zhí)行完畢或被強(qiáng)制終止,操作系統(tǒng)回收其資源。 |
狀態(tài)轉(zhuǎn)換圖:
新建 (New) → 就緒 (Ready):
當(dāng)一個(gè)新進(jìn)程被創(chuàng)建且已經(jīng)準(zhǔn)備好運(yùn)行時(shí),它從“新建”狀態(tài)轉(zhuǎn)變?yōu)椤熬途w”狀態(tài)。
就緒 (Ready) → 運(yùn)行 (Running):
當(dāng)調(diào)度程序選擇一個(gè)就緒狀態(tài)的進(jìn)程來(lái)運(yùn)行時(shí),它從“就緒”狀態(tài)轉(zhuǎn)變?yōu)椤斑\(yùn)行”狀態(tài)。
運(yùn)行 (Running) → 阻塞 (Blocked):
如果運(yùn)行中的進(jìn)程需要等待某些資源(如I/O操作),它會(huì)從“運(yùn)行”狀態(tài)轉(zhuǎn)變?yōu)椤白枞睜顟B(tài)。
阻塞 (Blocked) → 就緒 (Ready):
當(dāng)阻塞狀態(tài)的進(jìn)程等待的事件發(fā)生后,它會(huì)重新進(jìn)入“就緒”狀態(tài),準(zhǔn)備再次運(yùn)行。
運(yùn)行 (Running) → 就緒 (Ready):
如果進(jìn)程由于某種原因沒(méi)有完成執(zhí)行而被中斷,它將從“運(yùn)行”狀態(tài)返回到“就緒”狀態(tài),等待下次被調(diào)度。
運(yùn)行 (Running) → 終止 (Term):
當(dāng)進(jìn)程完成執(zhí)行或被強(qiáng)制終止時(shí),它從“運(yùn)行”狀態(tài)轉(zhuǎn)變?yōu)椤敖K止”狀態(tài)。
2. 進(jìn)程的創(chuàng)建與管理
進(jìn)程的創(chuàng)建與管理是操作系統(tǒng)并發(fā)機(jī)制的核心。在 Unix/Linux 系統(tǒng)中,fork()
和 exec()
是創(chuàng)建和管理進(jìn)程的兩個(gè)主要系統(tǒng)調(diào)用。
2.1 使用 fork()
創(chuàng)建進(jìn)程
fork()
是 Unix/Linux 中用于創(chuàng)建新進(jìn)程的系統(tǒng)調(diào)用。它會(huì)復(fù)制當(dāng)前進(jìn)程,創(chuàng)建一個(gè)新的子進(jìn)程。子進(jìn)程幾乎完全與父進(jìn)程相同,但它們有獨(dú)立的內(nèi)存空間和資源。
#include <stdio.h>
#include <unistd.h>int main() {pid_t pid = fork(); // 創(chuàng)建一個(gè)子進(jìn)程if (pid < 0) {// fork() 失敗perror("Fork failed");return 1;} else if (pid == 0) {// 子進(jìn)程代碼printf("This is the child process with PID: %d\n", getpid());} else {// 父進(jìn)程代碼printf("This is the parent process with PID: %d\n", getpid());printf("Created child process with PID: %d\n", pid);}return 0;
}
運(yùn)行結(jié)果:
This is the parent process with PID: 1683911
Created child process with PID: 1683912
liber@liber-VMware-Virtual-Platform:/home/c$ This is the child process with PID: 1683912
解釋:
fork()
創(chuàng)建一個(gè)新的子進(jìn)程,子進(jìn)程是父進(jìn)程的副本。pid_t pid = fork()
:fork()
返回兩次,一次在父進(jìn)程中返回子進(jìn)程的 PID,一次在子進(jìn)程中返回 0。通過(guò)判斷pid
的值,可以區(qū)分當(dāng)前代碼是運(yùn)行在父進(jìn)程還是子進(jìn)程中。
2.2 使用 exec()
系列函數(shù)替換進(jìn)程映像
在創(chuàng)建了子進(jìn)程后,常常需要在子進(jìn)程中執(zhí)行不同的程序,這時(shí)可以使用 exec()
系列函數(shù)來(lái)替換當(dāng)前進(jìn)程的映像。exec()
系列函數(shù)包括 execl()
、execv()
、execlp()
、execvp()
等。
下面是關(guān)于 exec
系列函數(shù)概述的表格,展示了每個(gè)函數(shù)的使用方式、參數(shù)類型及其特點(diǎn):
函數(shù)名稱 | 參數(shù)類型 | 程序路徑 | 查找方式 | 說(shuō)明 |
---|---|---|---|---|
execl() | 參數(shù)列表(可變參數(shù)) | 需要指定完整路徑 | 不查找路徑 | 通過(guò)傳遞一個(gè)參數(shù)列表執(zhí)行指定路徑的程序,參數(shù)列表必須以NULL 結(jié)尾。 |
execv() | 參數(shù)數(shù)組 | 需要指定完整路徑 | 不查找路徑 | 通過(guò)傳遞一個(gè)參數(shù)數(shù)組執(zhí)行指定路徑的程序,參數(shù)數(shù)組最后一個(gè)元素必須是NULL 。 |
execlp() | 參數(shù)列表(可變參數(shù)) | 只需指定程序名稱 | 使用PATH 查找 | 通過(guò)傳遞一個(gè)參數(shù)列表執(zhí)行指定名稱的程序,系統(tǒng)在PATH 環(huán)境變量中查找該程序的路徑。參數(shù)列表必須以NULL 結(jié)尾。 |
execvp() | 參數(shù)數(shù)組 | 只需指定程序名稱 | 使用PATH 查找 | 通過(guò)傳遞一個(gè)參數(shù)數(shù)組執(zhí)行指定名稱的程序,系統(tǒng)在PATH 環(huán)境變量中查找該程序的路徑。參數(shù)數(shù)組最后一個(gè)元素必須是NULL 。 |
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>int main() {pid_t pid;// 使用 execl() 執(zhí)行 /bin/lspid = fork();if (pid == 0) {// 在子進(jìn)程中執(zhí)行printf("Child process (PID: %d) using execl() to execute /bin/ls -l\n", getpid());execl("/bin/ls", "ls", "-l", NULL); // 執(zhí)行 ls -lperror("execl failed");exit(EXIT_FAILURE);} else if (pid > 0) {// 父進(jìn)程代碼wait(NULL); // 等待子進(jìn)程完成printf("execl() Child process completed.\n");}// 使用 execv() 執(zhí)行 /bin/echopid = fork();if (pid == 0) {// 在子進(jìn)程中執(zhí)行printf("Child process (PID: %d) using execv() to execute /bin/echo 'Hello, World!'\n", getpid());char *args1[] = {"/bin/echo", "Hello,", "World!", NULL};execv("/bin/echo", args1); // 執(zhí)行 echo "Hello, World!"perror("execv failed");exit(EXIT_FAILURE);} else if (pid > 0) {// 父進(jìn)程代碼wait(NULL); // 等待子進(jìn)程完成printf("execv() Child process completed.\n");}// 使用 execlp() 執(zhí)行 lspid = fork();if (pid == 0) {// 在子進(jìn)程中執(zhí)行printf("Child process (PID: %d) using execlp() to execute ls -a\n", getpid());execlp("ls", "ls", "-a", NULL); // 執(zhí)行 ls -aperror("execlp failed");exit(EXIT_FAILURE);} else if (pid > 0) {// 父進(jìn)程代碼wait(NULL); // 等待子進(jìn)程完成printf("execlp() Child process completed.\n");}// 使用 execvp() 執(zhí)行 echopid = fork();if (pid == 0) {// 在子進(jìn)程中執(zhí)行printf("Child process (PID: %d) using execvp() to execute echo 'This is execvp!'\n", getpid());char *args2[] = {"echo", "This", "is", "execvp!", NULL};execvp("echo", args2); // 執(zhí)行 echo "This is execvp!"perror("execvp failed");exit(EXIT_FAILURE);} else if (pid > 0) {// 父進(jìn)程代碼wait(NULL); // 等待子進(jìn)程完成printf("execvp() Child process completed.\n");}printf("All child processes completed. Parent process exiting.\n");return 0;
}
運(yùn)行結(jié)果:
Child process (PID: 1697610) using execl() to execute /bin/ls -l
總計(jì) 24
-rwxrwxr-x 1 liber liber 16424 8月 13 22:58 code
-rw-rw-r-- 1 liber liber 2346 8月 13 22:58 code.c
execl() Child process completed.
Child process (PID: 1697612) using execv() to execute /bin/echo ‘Hello, World!’
Hello, World!
execv() Child process completed.
Child process (PID: 1697614) using execlp() to execute ls -a
. … code code.c
execlp() Child process completed.
Child process (PID: 1697616) using execvp() to execute echo ‘This is execvp!’
This is execvp!
execvp() Child process completed.
All child processes completed. Parent process exiting.
解釋:
- 在第一個(gè)子進(jìn)程中,使用
execl()
函數(shù)執(zhí)行/bin/ls
命令。參數(shù)列表包括路徑/bin/ls
、程序名稱ls
以及命令行參數(shù)-l
。如果成功,子進(jìn)程會(huì)替換為ls -l
的執(zhí)行。 - 在第二個(gè)子進(jìn)程中,使用
execv()
函數(shù)執(zhí)行/bin/echo
命令。參數(shù)通過(guò)一個(gè)數(shù)組args1
傳遞,包含程序路徑/bin/echo
及參數(shù)"Hello," "World!"
。如果成功,子進(jìn)程會(huì)輸出Hello, World!
。 - 在第三個(gè)子進(jìn)程中,使用
execlp()
函數(shù)執(zhí)行ls -a
命令。只需要提供程序名稱ls
,系統(tǒng)會(huì)在PATH
環(huán)境變量中查找ls
的路徑。成功時(shí),子進(jìn)程會(huì)替換為ls -a
的執(zhí)行。 - 在第四個(gè)子進(jìn)程中,使用
execvp()
函數(shù)執(zhí)行echo
命令。參數(shù)通過(guò)數(shù)組args2
傳遞,包括程序名稱echo
及參數(shù)"This", "is", "execvp!"
。系統(tǒng)會(huì)在PATH
環(huán)境變量中查找echo
的路徑。成功時(shí),子進(jìn)程會(huì)輸出This is execvp!
。
3. 進(jìn)程的同步與等待
3.1 wait()
和 waitpid()
函數(shù)
父進(jìn)程可以使用 wait()
或 waitpid()
函數(shù)來(lái)等待子進(jìn)程終止,并獲取子進(jìn)程的退出狀態(tài)。waitpid()
是 wait()
的增強(qiáng)版本,允許你等待特定的子進(jìn)程或以非阻塞方式等待。它不僅可以像 wait()
一樣等待任意一個(gè)子進(jìn)程結(jié)束,還可以通過(guò)傳遞子進(jìn)程的 PID 來(lái)等待特定的子進(jìn)程。
wait:
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>int main() {pid_t pid = fork(); // 創(chuàng)建子進(jìn)程if (pid == 0) {// 子進(jìn)程部分printf("Child process with PID: %d\n", getpid());sleep(2); // 模擬子進(jìn)程工作} else if (pid > 0) {// 父進(jìn)程部分printf("Parent waiting using wait().\n");wait(NULL); // 使用 wait() 等待子進(jìn)程printf("Child process has terminated.\n");}return 0;
}
運(yùn)行結(jié)果:
Parent waiting using wait().
Child process with PID: 1777167
Child process has terminated.
解釋:
- 子進(jìn)程執(zhí)行
sleep(2)
來(lái)模擬工作,父進(jìn)程使用wait()
等待子進(jìn)程結(jié)束。
waitpid:
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>int main() {pid_t pid = fork(); // 創(chuàng)建子進(jìn)程if (pid == 0) {// 子進(jìn)程部分printf("Child process with PID: %d\n", getpid());sleep(3); // 模擬子進(jìn)程工作} else if (pid > 0) {// 父進(jìn)程部分printf("Parent waiting using waitpid().\n");waitpid(pid, NULL, 0); // 使用 waitpid() 等待特定子進(jìn)程printf("Child process has terminated.\n");}return 0;
}
運(yùn)行結(jié)果:
Parent waiting using waitpid().
Child process with PID: 1779327
Child process has terminated.
解釋:
- 子進(jìn)程執(zhí)行
sleep(3)
來(lái)模擬工作,父進(jìn)程使用waitpid()
等待這個(gè)特定的子進(jìn)程。
4. 進(jìn)程間通信(IPC)
進(jìn)程間通信是多個(gè)進(jìn)程之間交換數(shù)據(jù)的機(jī)制。常見(jiàn)的 IPC 機(jī)制包括管道(Pipes)、消息隊(duì)列(Message Queues)、共享內(nèi)存(Shared Memory)和信號(hào)量(Semaphores)。
4.1 管道(Pipe)
管道是一種常用的 IPC 機(jī)制,允許一個(gè)進(jìn)程向另一個(gè)進(jìn)程傳遞數(shù)據(jù)。管道分為匿名管道和命名管道(FIFO)。
**匿名管道:**適用于有親緣關(guān)系的進(jìn)程間通信,比如父子進(jìn)程。它的特點(diǎn)是臨時(shí)性,不會(huì)在文件系統(tǒng)中留下痕跡。
#include <stdio.h>
#include <unistd.h>int main() {int fd[2];char buffer[30];pipe(fd); // 創(chuàng)建管道pid_t pid = fork();if (pid == 0) {// 子進(jìn)程:向管道寫入數(shù)據(jù)write(fd[1], "Hello from child", 17);} else if (pid > 0) {// 父進(jìn)程:從管道讀取數(shù)據(jù)read(fd[0], buffer, sizeof(buffer));printf("Received from child process: %s\n", buffer);}return 0;
}
運(yùn)行結(jié)果:
Received from child process: Hello from child
解釋:
pipe(fd)
創(chuàng)建一個(gè)匿名管道,fd[0]
是讀端,fd[1]
是寫端。- 子進(jìn)程寫入數(shù)據(jù),父進(jìn)程從管道讀取數(shù)據(jù),實(shí)現(xiàn)簡(jiǎn)單的進(jìn)程間通信。
**命名管道(FIFO):**是一種特殊的文件,它允許無(wú)親緣關(guān)系的進(jìn)程通過(guò)管道文件進(jìn)行通信。命名管道在文件系統(tǒng)中存在,可以由任何進(jìn)程打開(kāi)和使用。
創(chuàng)建命名管道并寫入數(shù)據(jù):
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>int main() {char *fifo = "/tmp/my_fifo"; // 定義FIFO的路徑// 創(chuàng)建命名管道mkfifo(fifo, 0666);// 打開(kāi)FIFO的寫端int fd = open(fifo, O_WRONLY);char message[] = "Hello from writer!";write(fd, message, strlen(message) + 1); // 寫入數(shù)據(jù)到FIFOclose(fd); // 關(guān)閉FIFOreturn 0;
}
運(yùn)行結(jié)果:
光標(biāo)一直閃爍,等待被消費(fèi)。
讀取命名管道中的數(shù)據(jù):
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>int main() {char *fifo = "/tmp/my_fifo"; // 定義FIFO的路徑char buffer[100];// 打開(kāi)FIFO的讀端int fd = open(fifo, O_RDONLY);read(fd, buffer, sizeof(buffer)); // 從FIFO中讀取數(shù)據(jù)printf("Reader process received: %s\n", buffer);close(fd); // 關(guān)閉FIFO// 刪除命名管道文件unlink(fifo);return 0;
}
運(yùn)行結(jié)果:
Reader process received: Hello from writer!
解釋:
-
需要編譯兩個(gè)文件,打開(kāi)兩個(gè)終端編譯運(yùn)行兩個(gè)不同的代碼文件,一個(gè)生產(chǎn),一個(gè)消費(fèi)。
-
一個(gè)進(jìn)程通過(guò)打開(kāi)命名管道的寫端來(lái)發(fā)送數(shù)據(jù),另一個(gè)進(jìn)程通過(guò)打開(kāi)命名管道的讀端來(lái)接收數(shù)據(jù)。
-
讀完數(shù)據(jù)后,使用
unlink(fifo)
刪除命名管道文件
4.2 消息隊(duì)列(Message Queue)
消息隊(duì)列是一種進(jìn)程間通信(IPC)機(jī)制,允許進(jìn)程通過(guò)消息的形式進(jìn)行異步通信。與管道不同,消息隊(duì)列支持有序存儲(chǔ)消息,并允許進(jìn)程以不同的優(yōu)先級(jí)發(fā)送和接收消息。消息隊(duì)列是持久的,即使創(chuàng)建消息隊(duì)列的進(jìn)程退出,消息隊(duì)列仍然存在,直到顯式刪除為止。
以下是包含操作說(shuō)明的消息隊(duì)列操作表格:
函數(shù) | 參數(shù) | 參數(shù)描述 | 操作 | 返回值 |
---|---|---|---|---|
msgget() | key | 消息隊(duì)列的鍵值,唯一標(biāo)識(shí)消息隊(duì)列。 | 創(chuàng)建或獲取消息隊(duì)列 | 成功時(shí)返回消息隊(duì)列標(biāo)識(shí)符,失敗時(shí)返回 -1 |
msgflg | 標(biāo)志位,用于設(shè)置權(quán)限和操作選項(xiàng),如 IPC_CREAT 表示創(chuàng)建消息隊(duì)列。 | |||
msgsnd() | msqid | 消息隊(duì)列標(biāo)識(shí)符(通過(guò) msgget() 獲取)。 | 向消息隊(duì)列發(fā)送消息 | 成功時(shí)返回 0 ,失敗時(shí)返回 -1 |
msgp | 指向消息結(jié)構(gòu)的指針,包含消息類型和消息正文。 | |||
msgsz | 消息正文的大小。 | |||
msgflg | 控制操作的標(biāo)志位,如 IPC_NOWAIT 表示非阻塞發(fā)送。 | |||
msgrcv() | msqid | 消息隊(duì)列標(biāo)識(shí)符。 | 從消息隊(duì)列接收消息 | 成功時(shí)返回接收到的消息大小,失敗時(shí)返回 -1 |
msgp | 指向存儲(chǔ)接收到消息的結(jié)構(gòu)的指針。 | |||
msgsz | 消息正文的大小。 | |||
msgtyp | 指定要接收的消息類型,0 表示接收隊(duì)列中的第一個(gè)消息。 | |||
msgflg | 控制操作的標(biāo)志位,如 IPC_NOWAIT 表示非阻塞接收。 | |||
msgctl() | msqid | 消息隊(duì)列標(biāo)識(shí)符。 | 控制消息隊(duì)列操作 | 成功時(shí)返回 0 ,失敗時(shí)返回 -1 |
cmd | 指定要執(zhí)行的操作,如 IPC_RMID 刪除消息隊(duì)列,IPC_STAT 獲取消息隊(duì)列信息。 | |||
buf | 可選參數(shù),用于存儲(chǔ)或傳遞消息隊(duì)列的狀態(tài)信息。 |
詳細(xì)操作說(shuō)明:
-
msgget()
:操作: 用于創(chuàng)建一個(gè)新的消息隊(duì)列或獲取一個(gè)現(xiàn)有的消息隊(duì)列。
典型用法: 如果消息隊(duì)列不存在且設(shè)置了
IPC_CREAT
標(biāo)志,則會(huì)創(chuàng)建一個(gè)新的消息隊(duì)列。否則,返回現(xiàn)有的消息隊(duì)列標(biāo)識(shí)符。 -
msgsnd()
:操作: 將消息發(fā)送到指定的消息隊(duì)列中。
典型用法: 將消息結(jié)構(gòu)
msgp
中的消息添加到msqid
標(biāo)識(shí)的隊(duì)列中。如果設(shè)置了IPC_NOWAIT
,則在隊(duì)列已滿時(shí)不會(huì)阻塞。 -
msgrcv()
:操作: 從指定的消息隊(duì)列中接收消息。
典型用法: 從
msqid
標(biāo)識(shí)的隊(duì)列中接收消息,并將其存儲(chǔ)在msgp
指向的結(jié)構(gòu)中。如果msgtyp
為0
,接收隊(duì)列中的第一個(gè)消息;如果msgtyp
為正數(shù),接收該類型的消息。 -
msgctl()
:操作: 執(zhí)行消息隊(duì)列的控制操作,例如刪除消息隊(duì)列或查詢隊(duì)列的狀態(tài)。
典型用法: 使用
IPC_RMID
標(biāo)志刪除消息隊(duì)列,或使用IPC_STAT
獲取隊(duì)列的狀態(tài)信息。
發(fā)送消息的程序(sender.c)
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>#define MAX 100// 定義消息結(jié)構(gòu)
struct mesg_buffer {long mesg_type;char mesg_text[MAX];
} message;int main() {key_t key;int msgid;// 生成唯一的鍵key = ftok("progfile", 65);// 創(chuàng)建消息隊(duì)列并返回標(biāo)識(shí)符msgid = msgget(key, 0666 | IPC_CREAT);// 消息類型設(shè)為 1message.mesg_type = 1;while(1) {printf("Enter a message to send: ");fgets(message.mesg_text, MAX, stdin);// 向消息隊(duì)列發(fā)送消息msgsnd(msgid, &message, sizeof(message), 0);// 如果輸入 "exit",結(jié)束聊天if (strncmp(message.mesg_text, "exit", 4) == 0) {break;}}return 0;
}
運(yùn)行結(jié)果:
liber@liber-VMware-Virtual-Platform:/home/c$ gcc sender.c -o sender
liber@liber-VMware-Virtual-Platform:/home/c$ ./sender
Enter a message to send: hello
Enter a message to send: exit
解釋:
-
msgget()
用于創(chuàng)建或獲取消息隊(duì)列。使用ftok()
函數(shù)生成一個(gè)唯一的鍵值,用于標(biāo)識(shí)消息隊(duì)列。 -
msgsnd()
在sender
程序中用于發(fā)送消息。消息類型設(shè)置為 1,消息內(nèi)容通過(guò)用戶輸入獲取。
接收消息的程序(receiver.c)
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>#define MAX 100// 定義消息結(jié)構(gòu)
struct mesg_buffer {long mesg_type;char mesg_text[MAX];
} message;int main() {key_t key;int msgid;// 生成唯一的鍵key = ftok("progfile", 65);// 創(chuàng)建消息隊(duì)列并返回標(biāo)識(shí)符msgid = msgget(key, 0666 | IPC_CREAT);while(1) {// 從消息隊(duì)列中接收消息msgrcv(msgid, &message, sizeof(message), 1, 0);printf("Received message: %s", message.mesg_text);// 如果接收到 "exit",結(jié)束聊天if (strncmp(message.mesg_text, "exit", 4) == 0) {// 刪除消息隊(duì)列msgctl(msgid, IPC_RMID, NULL);break;}}return 0;
}
運(yùn)行結(jié)果:
liber@liber-VMware-Virtual-Platform:/home/c$ gcc receiver.c -o receiver
liber@liber-VMware-Virtual-Platform:/home/c$ ./receiver
Received message: hello
Received message: exit
解釋:
msgrcv()
在receiver
程序中用于接收消息。它會(huì)阻塞,直到接收到類型為 1 的消息。msgctl()
當(dāng)接收到的消息內(nèi)容為"exit"
時(shí),使用msgctl()
函數(shù)刪除消息隊(duì)列,清理 IPC 資源。
4.3共享內(nèi)存(Shared Memory)
共享內(nèi)存是一種高效的進(jìn)程間通信(IPC)機(jī)制,允許多個(gè)進(jìn)程直接訪問(wèn)同一塊內(nèi)存區(qū)域,從而實(shí)現(xiàn)數(shù)據(jù)的快速共享和交換。共享內(nèi)存是所有 IPC 機(jī)制中最快的一種,因?yàn)閿?shù)據(jù)不需要在進(jìn)程之間復(fù)制,而是所有參與的進(jìn)程可以直接訪問(wèn)同一片物理內(nèi)存,以下是共享內(nèi)存操作的基本函數(shù)及其用途。
函數(shù) | 參數(shù) | 操作 | 描述 |
---|---|---|---|
shmget() | key : 共享內(nèi)存段的唯一標(biāo)識(shí)符,通常由 ftok() 生成。 | 創(chuàng)建或獲取共享內(nèi)存段 | 創(chuàng)建或獲取共享內(nèi)存段,并返回一個(gè)標(biāo)識(shí)符(shmid)。如果內(nèi)存段不存在且設(shè)置了 IPC_CREAT ,則創(chuàng)建一個(gè)新的共享內(nèi)存段。 |
size : 共享內(nèi)存段的大小(以字節(jié)為單位)。 | |||
shmflg : 標(biāo)志位,常用值包括 IPC_CREAT 用于創(chuàng)建新內(nèi)存段,0666 設(shè)置權(quán)限。 | |||
shmat() | shmid : 共享內(nèi)存段的標(biāo)識(shí)符,由 shmget() 返回。 | 附加共享內(nèi)存段到進(jìn)程的地址空間 | 將共享內(nèi)存段附加到當(dāng)前進(jìn)程的地址空間中,使得進(jìn)程可以訪問(wèn)該內(nèi)存。返回一個(gè)指向共享內(nèi)存的指針。 |
shmaddr : 附加到的內(nèi)存地址,通常為 NULL 讓系統(tǒng)自動(dòng)選擇地址。 | |||
shmflg : 操作標(biāo)志位,通常為 0 (讀寫),也可以設(shè)置為 SHM_RDONLY 只讀。 | |||
shmdt() | shmaddr : 要分離的共享內(nèi)存段的地址,必須是 shmat() 返回的地址。 | 從進(jìn)程的地址空間分離共享內(nèi)存段 | 將共享內(nèi)存段從當(dāng)前進(jìn)程的地址空間分離,之后進(jìn)程不能再訪問(wèn)該內(nèi)存。 |
shmctl() | shmid : 共享內(nèi)存段的標(biāo)識(shí)符。 | 控制共享內(nèi)存段 | 控制共享內(nèi)存段的行為,例如刪除共享內(nèi)存段以釋放系統(tǒng)資源,或者獲取共享內(nèi)存段的狀態(tài)信息。 |
cmd : 控制命令,如 IPC_RMID (刪除共享內(nèi)存段)或 IPC_STAT (獲取狀態(tài)信息)。 | |||
buf : 一個(gè) struct shmid_ds 類型的緩沖區(qū),用于存儲(chǔ)或傳遞內(nèi)存段的狀態(tài)信息。 |
寫入共享內(nèi)存:
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>int main() {// 生成唯一的鍵值key_t key = ftok("shmfile", 65);// 創(chuàng)建共享內(nèi)存段,返回標(biāo)識(shí)符int shmid = shmget(key, 1024, 0666 | IPC_CREAT);// 將共享內(nèi)存段附加到進(jìn)程的地址空間char *str = (char*) shmat(shmid, (void*)0, 0);// 向共享內(nèi)存寫入數(shù)據(jù)strcpy(str, "Hello from Process A!");printf("Data written to shared memory: %s\n", str);// 分離共享內(nèi)存段shmdt(str);return 0;
}
運(yùn)行結(jié)果:
Data written to shared memory: Hello from Process A!
解釋:
**shmget()
**創(chuàng)建或獲取一個(gè)共享內(nèi)存段,大小為 1024 字節(jié)。IPC_CREAT
標(biāo)志表示如果共享內(nèi)存段不存在則創(chuàng)建它。
**shmat()
**將共享內(nèi)存段附加到進(jìn)程的地址空間,使得進(jìn)程可以訪問(wèn)該內(nèi)存。返回一個(gè)指向該內(nèi)存段的指針。
讀取共享內(nèi)存:
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>int main() {// 生成與進(jìn)程 A 相同的鍵值key_t key = ftok("shmfile", 65);// 獲取共享內(nèi)存段的標(biāo)識(shí)符int shmid = shmget(key, 1024, 0666);// 將共享內(nèi)存段附加到進(jìn)程的地址空間char *str = (char*) shmat(shmid, (void*)0, 0);// 讀取共享內(nèi)存中的數(shù)據(jù)printf("Data read from shared memory: %s\n", str);// 分離共享內(nèi)存段shmdt(str);// 刪除共享內(nèi)存段shmctl(shmid, IPC_RMID, NULL);return 0;
}
運(yùn)行結(jié)果:
Data read from shared memory: Hello from Process A!
解釋:
**shmdt()
**從進(jìn)程的地址空間分離共享內(nèi)存段。
**shmctl()
**刪除共享內(nèi)存段,釋放資源。
4.4 信號(hào)量(Semaphores)
信號(hào)量是一種用于進(jìn)程或線程間同步的機(jī)制,用來(lái)控制對(duì)共享資源的訪問(wèn)。信號(hào)量通過(guò)維護(hù)一個(gè)計(jì)數(shù)器來(lái)控制多個(gè)進(jìn)程或線程對(duì)臨界資源的訪問(wèn)。信號(hào)量可以用于解決競(jìng)態(tài)條件問(wèn)題,確保多個(gè)進(jìn)程或線程在并發(fā)訪問(wèn)共享資源時(shí)不會(huì)產(chǎn)生沖突。
函數(shù) | 參數(shù) | 參數(shù)描述 | 操作 | 描述 |
---|---|---|---|---|
sem_init() | sem | 指向信號(hào)量對(duì)象的指針,用于初始化信號(hào)量。 | 初始化信號(hào)量 | 初始化一個(gè)信號(hào)量,設(shè)置其初始值。根據(jù) pshared 的值決定信號(hào)量用于線程間同步還是進(jìn)程間共享。 |
pshared | 指定信號(hào)量的作用范圍,0 信號(hào)量用于線程間同步,非 0 信號(hào)量在進(jìn)程間共享。 | |||
value | 設(shè)置信號(hào)量的初始值,表示資源的初始可用數(shù)量。 | |||
sem_wait() | sem | 指向信號(hào)量對(duì)象的指針,表示要等待的信號(hào)量。 | 等待信號(hào)量 | 執(zhí)行 P 操作。如果信號(hào)量值大于 0,信號(hào)量值減 1 并繼續(xù)執(zhí)行;如果信號(hào)量值為 0,則阻塞,直到信號(hào)量值大于 0。 |
sem_post() | sem | 指向信號(hào)量對(duì)象的指針,表示要釋放的信號(hào)量。 | 釋放信號(hào)量 | 執(zhí)行 V 操作。將信號(hào)量的值加 1,如果有進(jìn)程或線程在等待該信號(hào)量,則喚醒其中一個(gè)。 |
sem_destroy() | sem | 指向信號(hào)量對(duì)象的指針,用于銷毀信號(hào)量。 | 銷毀信號(hào)量 | 銷毀信號(hào)量并釋放與其相關(guān)的資源。僅適用于未用于進(jìn)程間共享的信號(hào)量。 |
使用信號(hào)量同步兩個(gè)線程:
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>sem_t sem;void* thread_func1(void* arg) {printf("Thread 1: Waiting for semaphore...\n");sem_wait(&sem); // 等待信號(hào)量printf("Thread 1: Inside critical section.\n");sem_post(&sem); // 釋放信號(hào)量return NULL;
}void* thread_func2(void* arg) {printf("Thread 2: Waiting for semaphore...\n");sem_wait(&sem); // 等待信號(hào)量printf("Thread 2: Inside critical section.\n");sem_post(&sem); // 釋放信號(hào)量return NULL;
}int main() {pthread_t t1, t2;// 初始化信號(hào)量,初始值為 1sem_init(&sem, 0, 1);// 創(chuàng)建兩個(gè)線程pthread_create(&t1, NULL, thread_func1, NULL);pthread_create(&t2, NULL, thread_func2, NULL);// 等待兩個(gè)線程完成pthread_join(t1, NULL);pthread_join(t2, NULL);// 銷毀信號(hào)量sem_destroy(&sem);return 0;
}
liber@liber-VMware-Virtual-Platform:/home/c$ ./receiver
Thread 1: Waiting for semaphore…
Thread 1: Inside critical section.
Thread 2: Waiting for semaphore…
Thread 2: Inside critical section.
liber@liber-VMware-Virtual-Platform:/home/c$ ./receiver
Thread 1: Waiting for semaphore…
Thread 2: Waiting for semaphore…
Thread 2: Inside critical section.
Thread 1: Inside critical section.
liber@liber-VMware-Virtual-Platform:/home/c$ ./receiver
Thread 2: Waiting for semaphore…
Thread 2: Inside critical section.
Thread 1: Waiting for semaphore…
Thread 1: Inside critical section.
解釋:
-
**
sem_init()
**初始化信號(hào)量sem
,初始值設(shè)置為 1,表示資源最開(kāi)始是可用的。第二個(gè)參數(shù)為 0,表示這是一個(gè)線程級(jí)別的信號(hào)量。 -
**
sem_wait()
**在每個(gè)線程的臨界區(qū)(Critical Section)前調(diào)用sem_wait()
,等待信號(hào)量。如果信號(hào)量值為 1,則進(jìn)入臨界區(qū)并將信號(hào)量值減為 0;如果信號(hào)量值為 0,線程將阻塞,直到信號(hào)量變?yōu)?1。 -
**
sem_post()
**在臨界區(qū)操作完成后調(diào)用sem_post()
,釋放信號(hào)量,將信號(hào)量值加 1。如果有其他線程在等待信號(hào)量,它們將被喚醒。 -
**
sem_destroy()
**在程序結(jié)束時(shí),銷毀信號(hào)量sem
,釋放其相關(guān)的資源。 -
信號(hào)量在這個(gè)程序中確實(shí)起到了同步的作用,確保了同時(shí)只有一個(gè)線程可以進(jìn)入臨界區(qū)(信號(hào)量值為 1 時(shí),只有一個(gè)線程可以繼續(xù),另一個(gè)線程必須等待)
具體的同步過(guò)程:
-
通過(guò)使用信號(hào)量(
sem_t sem
),確保兩個(gè)線程不會(huì)同時(shí)進(jìn)入臨界區(qū)(即thread_func1
和thread_func2
中的代碼塊printf("Inside critical section.\n");
) -
信號(hào)量通過(guò)阻塞和喚醒機(jī)制協(xié)調(diào)多個(gè)線程的執(zhí)行順序。例如,當(dāng)
sem_wait(&sem)
在一個(gè)線程中被調(diào)用時(shí),如果信號(hào)量的值為 0,該線程將被阻塞,直到另一個(gè)線程調(diào)用sem_post(&sem)
釋放信號(hào)量。 -
當(dāng)
thread_func1
或thread_func2
開(kāi)始執(zhí)行時(shí),它們都會(huì)調(diào)用sem_wait(&sem)
。此時(shí),信號(hào)量的初始值為 1,表示資源(即進(jìn)入臨界區(qū)的權(quán)限)是可用的。 -
假設(shè)
thread_func1
先調(diào)用sem_wait(&sem)
,信號(hào)量值減為 0,thread_func1
進(jìn)入臨界區(qū)。thread_func2
此時(shí)如果調(diào)用sem_wait(&sem)
,因?yàn)樾盘?hào)量值已經(jīng)是 0,它將被阻塞,直到thread_func1
調(diào)用sem_post(&sem)
釋放信號(hào)量。 -
thread_func1
退出臨界區(qū)后調(diào)用sem_post(&sem)
,信號(hào)量值重新變?yōu)?1,系統(tǒng)將喚醒thread_func2
,允許它進(jìn)入臨界區(qū)。
5. 進(jìn)程的信號(hào)處理
進(jìn)程的信號(hào)處理是指在操作系統(tǒng)中,進(jìn)程對(duì)信號(hào)(Signal)的接收和響應(yīng)方式。信號(hào)是一種異步的進(jìn)程間通信方式,通常用于通知進(jìn)程發(fā)生了某種事件,如終止、暫停、繼續(xù)執(zhí)行或捕獲某些異常情況。進(jìn)程可以通過(guò)定義信號(hào)處理程序來(lái)響應(yīng)特定的信號(hào),執(zhí)行相應(yīng)的處理邏輯。
5.1 常見(jiàn)信號(hào)及默認(rèn)行為
信號(hào) | 描述 | 默認(rèn)行為 |
---|---|---|
SIGINT | 終端中斷信號(hào),用戶按 Ctrl+C 觸發(fā) | 終止進(jìn)程 |
SIGTERM | 請(qǐng)求進(jìn)程終止,允許進(jìn)程進(jìn)行清理工作 | 終止進(jìn)程 |
SIGKILL | 強(qiáng)制終止進(jìn)程,無(wú)法被捕獲或忽略 | 強(qiáng)制終止進(jìn)程 |
SIGCHLD | 子進(jìn)程狀態(tài)改變(如退出或停止) | 忽略 |
SIGHUP | 終端掛起或控制終端關(guān)閉時(shí)發(fā)送,通常用于通知守護(hù)進(jìn)程重新加載配置文件 | 終止進(jìn)程 |
SIGQUIT | 從終端發(fā)出的退出信號(hào)(通常是 Ctrl+\ ),會(huì)生成核心轉(zhuǎn)儲(chǔ) | 生成核心轉(zhuǎn)儲(chǔ)并終止進(jìn)程 |
SIGILL | 非法指令執(zhí)行,如執(zhí)行未定義的機(jī)器指令 | 生成核心轉(zhuǎn)儲(chǔ)并終止進(jìn)程 |
SIGABRT | 調(diào)用 abort() 函數(shù)引發(fā)的信號(hào),表示進(jìn)程異常終止 | 生成核心轉(zhuǎn)儲(chǔ)并終止進(jìn)程 |
SIGFPE | 算術(shù)運(yùn)算錯(cuò)誤(如除以零或溢出),浮點(diǎn)異常 | 生成核心轉(zhuǎn)儲(chǔ)并終止進(jìn)程 |
SIGSEGV | 段錯(cuò)誤,非法內(nèi)存訪問(wèn) | 生成核心轉(zhuǎn)儲(chǔ)并終止進(jìn)程 |
SIGPIPE | 向無(wú)讀者的管道寫數(shù)據(jù)時(shí)引發(fā),通常在管道通信中出現(xiàn) | 終止進(jìn)程 |
SIGBUS | 總線錯(cuò)誤,非法內(nèi)存訪問(wèn)(如未對(duì)齊的內(nèi)存訪問(wèn)) | 生成核心轉(zhuǎn)儲(chǔ)并終止進(jìn)程 |
SIGALRM | 由 alarm() 函數(shù)觸發(fā)的定時(shí)信號(hào) | 終止進(jìn)程 |
SIGUSR1 | 用戶自定義信號(hào) 1 | 終止進(jìn)程(用戶可定義處理行為) |
SIGUSR2 | 用戶自定義信號(hào) 2 | 終止進(jìn)程(用戶可定義處理行為) |
SIGTRAP | 斷點(diǎn)陷阱,用于調(diào)試,通常由調(diào)試器捕獲 | 生成核心轉(zhuǎn)儲(chǔ)并終止進(jìn)程 |
SIGURG | 緊急情況(out-of-band data)到達(dá)套接字 | 忽略 |
SIGXCPU | 超出 CPU 時(shí)間限制 | 生成核心轉(zhuǎn)儲(chǔ)并終止進(jìn)程 |
SIGXFSZ | 超出文件大小限制 | 生成核心轉(zhuǎn)儲(chǔ)并終止進(jìn)程 |
SIGVTALRM | 虛擬計(jì)時(shí)器到期,通常用于跟蹤用戶時(shí)間消耗 | 終止進(jìn)程 |
SIGPROF | 計(jì)時(shí)器到期,通常用于統(tǒng)計(jì)進(jìn)程的 CPU 使用情況 | 終止進(jìn)程 |
SIGWINCH | 終端窗口大小改變 | 忽略 |
SIGTSTP | 終端停止信號(hào),用戶按 Ctrl+Z 觸發(fā),暫停進(jìn)程 | 停止進(jìn)程 |
SIGCONT | 繼續(xù)執(zhí)行已停止的進(jìn)程 | 繼續(xù)執(zhí)行(如果之前停止,則恢復(fù)執(zhí)行) |
SIGSTOP | 停止進(jìn)程,無(wú)法捕獲或忽略 | 停止進(jìn)程 |
SIGTTIN | 后臺(tái)進(jìn)程組試圖從終端讀取數(shù)據(jù)時(shí)引發(fā)的信號(hào) | 停止進(jìn)程 |
SIGTTOU | 后臺(tái)進(jìn)程組試圖向終端寫數(shù)據(jù)時(shí)引發(fā)的信號(hào) | 停止進(jìn)程 |
5.2 信號(hào)處理函數(shù)
函數(shù) | 參數(shù) | 參數(shù)描述 | 用途 | 描述 |
---|---|---|---|---|
signal() | signum | 要處理的信號(hào)編號(hào),如 SIGINT | 注冊(cè)簡(jiǎn)單的信號(hào)處理程序 | 用于指定一個(gè)信號(hào)處理函數(shù),當(dāng)進(jìn)程接收到特定信號(hào)時(shí)執(zhí)行。 |
handler | 信號(hào)處理函數(shù)的指針,或特殊值 SIG_IGN (忽略信號(hào))和 SIG_DFL (默認(rèn)處理) | |||
sigaction() | signum | 要處理的信號(hào)編號(hào),如 SIGINT | 注冊(cè)信號(hào)處理程序,替代 signal | sigaction 是 signal 的增強(qiáng)版本,允許更詳細(xì)地控制信號(hào)處理行為。 |
act | 包含新信號(hào)處理動(dòng)作的 struct sigaction 結(jié)構(gòu)的指針 | |||
oldact | 保存先前信號(hào)處理設(shè)置的 struct sigaction 結(jié)構(gòu)的指針 | |||
sigprocmask() | how | 指定如何修改信號(hào)屏蔽字的操作,如 SIG_BLOCK , SIG_UNBLOCK , SIG_SETMASK | 改變信號(hào)屏蔽字,阻塞或解除阻塞信號(hào) | 用于檢查和更改進(jìn)程的信號(hào)屏蔽字,可以阻塞或解除阻塞某些信號(hào)。 |
set | 新的信號(hào)屏蔽字,表示要阻塞的信號(hào) | |||
oldset | 保存先前信號(hào)屏蔽字的指針 | |||
sigpending() | set | 存儲(chǔ)當(dāng)前掛起信號(hào)的信號(hào)集的指針 | 檢查當(dāng)前掛起的信號(hào) | 檢查進(jìn)程當(dāng)前掛起的信號(hào)集,即那些已經(jīng)發(fā)送但因被阻塞而未處理的信號(hào)。 |
sigsuspend() | mask | 新的信號(hào)屏蔽字,用于臨時(shí)替換當(dāng)前信號(hào)屏蔽字 | 臨時(shí)替換信號(hào)屏蔽字并掛起進(jìn)程,等待特定信號(hào) | 替換信號(hào)屏蔽字并掛起進(jìn)程執(zhí)行,直到接收到一個(gè)未被屏蔽的信號(hào)。 |
raise() | sig | 要發(fā)送的信號(hào)編號(hào) | 向當(dāng)前進(jìn)程發(fā)送信號(hào) | 用于在當(dāng)前進(jìn)程中發(fā)送信號(hào),相當(dāng)于在進(jìn)程內(nèi)部自發(fā)生成一個(gè)信號(hào)。 |
kill() | pid | 目標(biāo)進(jìn)程的進(jìn)程 ID | 向指定進(jìn)程發(fā)送信號(hào) | 向指定的進(jìn)程發(fā)送信號(hào),可以發(fā)送任何信號(hào),而不僅僅是 SIGKILL 。 |
sig | 要發(fā)送的信號(hào)編號(hào) | |||
pause() | 無(wú) | 無(wú) | 掛起進(jìn)程執(zhí)行,直到接收到一個(gè)信號(hào) | 掛起進(jìn)程的執(zhí)行,直到收到并處理一個(gè)信號(hào)。 |
簡(jiǎn)單案例
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>// 信號(hào)處理函數(shù)
void handle_signal(int sig) {switch (sig) {case SIGINT:printf("Caught SIGINT (Ctrl+C), but not terminating the process.\n");break;case SIGTERM:printf("Caught SIGTERM, terminating the process.\n");exit(0);break;case SIGCHLD:printf("Caught SIGCHLD, cleaning up child process.\n");wait(NULL); // 清理子進(jìn)程break;case SIGHUP:printf("Caught SIGHUP, reloading configuration...\n");// 模擬重新加載配置break;case SIGQUIT:printf("Caught SIGQUIT (Ctrl+\\), generating core dump.\n");abort(); // 生成核心轉(zhuǎn)儲(chǔ)并終止進(jìn)程break;default:printf("Caught signal %d, no specific handler defined.\n", sig);}
}int main() {// 使用 signal() 注冊(cè)信號(hào)處理函數(shù)signal(SIGINT, handle_signal);signal(SIGTERM, handle_signal);signal(SIGCHLD, handle_signal);signal(SIGHUP, handle_signal);signal(SIGQUIT, handle_signal);// 創(chuàng)建一個(gè)子進(jìn)程來(lái)演示 SIGCHLDif (fork() == 0) {printf("Child process started, will terminate in 5 seconds...\n");sleep(5);exit(0);}// 模擬守護(hù)進(jìn)程的運(yùn)行while (1) {printf("Running... (PID: %d)\n", getpid());sleep(2); // 模擬長(zhǎng)期運(yùn)行的進(jìn)程}return 0;
}
如何運(yùn)行和測(cè)試:
- 按
Ctrl+C
:觸發(fā)SIGINT
信號(hào),處理函數(shù)會(huì)打印一條消息,但進(jìn)程不會(huì)終止。 - 發(fā)送
SIGTERM
:可以通過(guò)命令kill -SIGTERM <PID>
發(fā)送SIGTERM
信號(hào),處理函數(shù)會(huì)終止進(jìn)程。 - 按
Ctrl+\
:觸發(fā)SIGQUIT
信號(hào),處理函數(shù)會(huì)生成核心轉(zhuǎn)儲(chǔ)文件并終止進(jìn)程。 - 發(fā)送
SIGHUP
:可以通過(guò)命令kill -SIGHUP <PID>
發(fā)送SIGHUP
信號(hào),處理函數(shù)會(huì)模擬重新加載配置文件。 - 等待子進(jìn)程終止:子進(jìn)程終止時(shí),父進(jìn)程捕獲
SIGCHLD
信號(hào)并清理子進(jìn)程。
運(yùn)行結(jié)果:
第一個(gè)終端:
Running… (PID: 1933357)
Child process started, will terminate in 5 seconds…
^CCaught SIGINT (Ctrl+C), but not terminating the process.
Caught SIGINT (Ctrl+C), but not terminating the process.
Running… (PID: 1933357)
Caught SIGCHLD, cleaning up child process.
Running… (PID: 1933357)
Caught SIGHUP, reloading configuration…
Running… (PID: 1933357)
Caught SIGTERM, terminating the process.
第二個(gè)終端:
liber@liber-VMware-Virtual-Platform:~$ kill -SIGHUP 1933357
liber@liber-VMware-Virtual-Platform:~$ kill -SIGTERM 1933357
6 常見(jiàn)的進(jìn)程調(diào)度算法
6.1 先來(lái)先服務(wù)調(diào)度(FCFS, First-Come, First-Served)
先來(lái)先服務(wù)調(diào)度是一種最簡(jiǎn)單的調(diào)度算法。進(jìn)程按照它們到達(dá)就緒隊(duì)列的順序依次運(yùn)行,先到達(dá)的進(jìn)程先執(zhí)行,后到達(dá)的進(jìn)程后執(zhí)行。這個(gè)算法使用一個(gè)FIFO(First In, First Out)隊(duì)列來(lái)管理進(jìn)程順序。
#include <stdio.h>// 定義進(jìn)程結(jié)構(gòu)體,用來(lái)保存每個(gè)進(jìn)程的相關(guān)信息
struct Process {int pid; // 進(jìn)程IDint arrival_time; // 到達(dá)時(shí)間int burst_time; // 執(zhí)行時(shí)間(運(yùn)行所需時(shí)間)int completion_time;// 完成時(shí)間(進(jìn)程完成執(zhí)行的時(shí)間點(diǎn))int waiting_time; // 等待時(shí)間(進(jìn)程在就緒隊(duì)列中等待的時(shí)間)int turnaround_time;// 周轉(zhuǎn)時(shí)間(從到達(dá)到完成所用的總時(shí)間)
};// FCFS調(diào)度算法的實(shí)現(xiàn)
void fcfs_scheduling(struct Process processes[], int n) {int current_time = 0; // 用于跟蹤當(dāng)前時(shí)間進(jìn)度// 按照到達(dá)時(shí)間對(duì)進(jìn)程排序,以確保先到的進(jìn)程先執(zhí)行for (int i = 0; i < n; i++) {// 如果當(dāng)前時(shí)間小于進(jìn)程的到達(dá)時(shí)間,CPU需要空閑等待if (current_time < processes[i].arrival_time) {current_time = processes[i].arrival_time;}// 計(jì)算進(jìn)程的完成時(shí)間processes[i].completion_time = current_time + processes[i].burst_time;// 計(jì)算周轉(zhuǎn)時(shí)間 = 完成時(shí)間 - 到達(dá)時(shí)間processes[i].turnaround_time = processes[i].completion_time - processes[i].arrival_time;// 計(jì)算等待時(shí)間 = 周轉(zhuǎn)時(shí)間 - 執(zhí)行時(shí)間processes[i].waiting_time = processes[i].turnaround_time - processes[i].burst_time;// 更新當(dāng)前時(shí)間為此進(jìn)程的完成時(shí)間current_time = processes[i].completion_time;}
}// 打印所有進(jìn)程的信息,包括PID、到達(dá)時(shí)間、執(zhí)行時(shí)間、完成時(shí)間、等待時(shí)間、和周轉(zhuǎn)時(shí)間
void print_processes(struct Process processes[], int n) {printf("PID\tArrival\tBurst\tCompletion\tWaiting\tTurnaround\n");for (int i = 0; i < n; i++) {printf("%d\t%d\t%d\t%d\t\t%d\t%d\n",processes[i].pid,processes[i].arrival_time,processes[i].burst_time,processes[i].completion_time,processes[i].waiting_time,processes[i].turnaround_time);}
}int main() {int n; // 進(jìn)程數(shù)量// 用戶輸入進(jìn)程的數(shù)量printf("輸入進(jìn)程數(shù)量: ");scanf("%d", &n);struct Process processes[n]; // 創(chuàng)建進(jìn)程數(shù)組// 用戶輸入每個(gè)進(jìn)程的到達(dá)時(shí)間和執(zhí)行時(shí)間for (int i = 0; i < n; i++) {printf("輸入進(jìn)程 %d 的到達(dá)時(shí)間和執(zhí)行時(shí)間: ", i + 1);scanf("%d %d", &processes[i].arrival_time, &processes[i].burst_time);processes[i].pid = i + 1; // 進(jìn)程ID從1開(kāi)始}// 調(diào)用FCFS調(diào)度算法fcfs_scheduling(processes, n);// 打印調(diào)度結(jié)果print_processes(processes, n);return 0;
}
運(yùn)行結(jié)果:
輸入進(jìn)程數(shù)量: 5
輸入進(jìn)程 1 的到達(dá)時(shí)間和執(zhí)行時(shí)間: 1 1
輸入進(jìn)程 2 的到達(dá)時(shí)間和執(zhí)行時(shí)間: 2 2
輸入進(jìn)程 3 的到達(dá)時(shí)間和執(zhí)行時(shí)間: 3 3
輸入進(jìn)程 4 的到達(dá)時(shí)間和執(zhí)行時(shí)間: 4 4
輸入進(jìn)程 5 的到達(dá)時(shí)間和執(zhí)行時(shí)間: 5 5
PID Arrival Burst Completion Waiting Turnaround
1 1 1 2 0 1
2 2 2 4 0 2
3 3 3 7 1 4
4 4 4 11 3 7
5 5 5 16 6 11
6.2 短作業(yè)優(yōu)先調(diào)度(SJF, Shortest Job First)
短作業(yè)優(yōu)先調(diào)度是一種非搶占式調(diào)度算法。每次選擇執(zhí)行時(shí)間(即“作業(yè)長(zhǎng)度”)最短的進(jìn)程進(jìn)行調(diào)度。它可以降低平均等待時(shí)間,因?yàn)槎套鳂I(yè)可以盡快完成,減少后續(xù)作業(yè)的等待時(shí)間。
需要注意的是,SJF算法需要知道每個(gè)作業(yè)的執(zhí)行時(shí)間(burst time),但在實(shí)際系統(tǒng)中,這通常是未知的,所以SJF在實(shí)際中主要作為理論模型或通過(guò)預(yù)測(cè)實(shí)現(xiàn)。
#include <stdio.h>
#include <limits.h> // 用于獲取整型的最大值(INT_MAX)// 定義進(jìn)程結(jié)構(gòu)體
struct Process {int pid; // 進(jìn)程IDint arrival_time; // 到達(dá)時(shí)間int burst_time; // 執(zhí)行時(shí)間(運(yùn)行所需時(shí)間)int completion_time;// 完成時(shí)間int waiting_time; // 等待時(shí)間int turnaround_time;// 周轉(zhuǎn)時(shí)間int is_completed; // 標(biāo)記進(jìn)程是否已完成
};// SJF調(diào)度算法的實(shí)現(xiàn)
void sjf_scheduling(struct Process processes[], int n) {int completed = 0; // 已完成的進(jìn)程數(shù)int current_time = 0; // 當(dāng)前時(shí)間// 當(dāng)所有進(jìn)程都未完成時(shí),繼續(xù)調(diào)度while (completed != n) {int shortest_index = -1; // 保存最短作業(yè)的索引int shortest_burst = INT_MAX; // 保存最短作業(yè)的執(zhí)行時(shí)間,初始值設(shè)為最大整數(shù)// 尋找當(dāng)前時(shí)間點(diǎn)上未完成的最短作業(yè)for (int i = 0; i < n; i++) {// 條件:進(jìn)程已經(jīng)到達(dá)、未完成、并且執(zhí)行時(shí)間最短if (processes[i].arrival_time <= current_time && !processes[i].is_completed) {if (processes[i].burst_time < shortest_burst) {shortest_burst = processes[i].burst_time;shortest_index = i; // 記錄該進(jìn)程的索引}}}// 如果找到了合適的作業(yè)(即shortest_index有效)if (shortest_index != -1) {// 1. 將當(dāng)前時(shí)間增加該短作業(yè)的執(zhí)行時(shí)間current_time += processes[shortest_index].burst_time;// 2. 更新該進(jìn)程的完成時(shí)間 = 當(dāng)前時(shí)間processes[shortest_index].completion_time = current_time;// 3. 計(jì)算周轉(zhuǎn)時(shí)間 = 完成時(shí)間 - 到達(dá)時(shí)間processes[shortest_index].turnaround_time = processes[shortest_index].completion_time - processes[shortest_index].arrival_time;// 4. 計(jì)算等待時(shí)間 = 周轉(zhuǎn)時(shí)間 - 執(zhí)行時(shí)間processes[shortest_index].waiting_time = processes[shortest_index].turnaround_time - processes[shortest_index].burst_time;// 5. 將該進(jìn)程標(biāo)記為已完成processes[shortest_index].is_completed = 1;// 6. 更新已完成的進(jìn)程數(shù)completed++;} else {// 如果沒(méi)有合適的作業(yè)可以運(yùn)行,時(shí)間遞增current_time++;}}
}// 打印所有進(jìn)程的信息,包括PID、到達(dá)時(shí)間、執(zhí)行時(shí)間、完成時(shí)間、等待時(shí)間、和周轉(zhuǎn)時(shí)間
void print_processes(struct Process processes[], int n) {printf("PID\tArrival\tBurst\tCompletion\tWaiting\tTurnaround\n");for (int i = 0; i < n; i++) {printf("%d\t%d\t%d\t%d\t\t%d\t%d\n",processes[i].pid,processes[i].arrival_time,processes[i].burst_time,processes[i].completion_time,processes[i].waiting_time,processes[i].turnaround_time);}
}int main() {int n; // 進(jìn)程數(shù)量// 用戶輸入進(jìn)程的數(shù)量printf("輸入進(jìn)程數(shù)量: ");scanf("%d", &n);struct Process processes[n]; // 創(chuàng)建進(jìn)程數(shù)組// 用戶輸入每個(gè)進(jìn)程的到達(dá)時(shí)間和執(zhí)行時(shí)間for (int i = 0; i < n; i++) {printf("輸入進(jìn)程 %d 的到達(dá)時(shí)間和執(zhí)行時(shí)間: ", i + 1);scanf("%d %d", &processes[i].arrival_time, &processes[i].burst_time);processes[i].pid = i + 1; // 設(shè)置進(jìn)程IDprocesses[i].is_completed = 0; // 初始化進(jìn)程為未完成狀態(tài)}// 調(diào)用SJF調(diào)度算法sjf_scheduling(processes, n);// 打印調(diào)度結(jié)果print_processes(processes, n);return 0;
}
運(yùn)行結(jié)果:
輸入進(jìn)程數(shù)量: 5
輸入進(jìn)程 1 的到達(dá)時(shí)間和執(zhí)行時(shí)間: 1 2
輸入進(jìn)程 2 的到達(dá)時(shí)間和執(zhí)行時(shí)間: 1 1
輸入進(jìn)程 3 的到達(dá)時(shí)間和執(zhí)行時(shí)間: 1 4
輸入進(jìn)程 4 的到達(dá)時(shí)間和執(zhí)行時(shí)間: 1 5
輸入進(jìn)程 5 的到達(dá)時(shí)間和執(zhí)行時(shí)間: 1 3
PID Arrival Burst Completion Waiting Turnaround
1 1 2 4 1 3
2 1 1 2 0 1
3 1 4 11 6 10
4 1 5 16 10 15
5 1 3 7 3 6
6.3 最高優(yōu)先級(jí)調(diào)度(Priority Scheduling)
最高優(yōu)先級(jí)調(diào)度是一種調(diào)度策略,進(jìn)程根據(jù)其優(yōu)先級(jí)進(jìn)行調(diào)度,優(yōu)先級(jí)越高的進(jìn)程越早被調(diào)度。該算法可以是搶占式的(高優(yōu)先級(jí)進(jìn)程可以打斷正在運(yùn)行的低優(yōu)先級(jí)進(jìn)程)或非搶占式的(正在運(yùn)行的進(jìn)程不會(huì)被打斷)。
在非搶占式優(yōu)先級(jí)調(diào)度中,當(dāng)前運(yùn)行的進(jìn)程會(huì)一直運(yùn)行至完成,而在搶占式調(diào)度中,當(dāng)一個(gè)高優(yōu)先級(jí)的進(jìn)程到達(dá)時(shí),它會(huì)立即中斷當(dāng)前正在運(yùn)行的低優(yōu)先級(jí)進(jìn)程。
非搶占式優(yōu)先級(jí)調(diào)度:
#include <stdio.h>// 定義進(jìn)程結(jié)構(gòu)體
struct Process {int pid; // 進(jìn)程IDint arrival_time; // 到達(dá)時(shí)間int burst_time; // 執(zhí)行時(shí)間(運(yùn)行所需時(shí)間)int priority; // 優(yōu)先級(jí)int completion_time;// 完成時(shí)間int waiting_time; // 等待時(shí)間int turnaround_time;// 周轉(zhuǎn)時(shí)間int is_completed; // 標(biāo)記進(jìn)程是否已完成
};// 非搶占式優(yōu)先級(jí)調(diào)度算法的實(shí)現(xiàn)
void priority_scheduling(struct Process processes[], int n) {int completed = 0; // 已完成的進(jìn)程數(shù)int current_time = 0; // 當(dāng)前時(shí)間// 當(dāng)所有進(jìn)程都未完成時(shí),繼續(xù)調(diào)度while (completed != n) {int highest_priority_index = -1; // 保存最高優(yōu)先級(jí)進(jìn)程的索引int highest_priority = __INT_MAX__; // 用于比較優(yōu)先級(jí),初始為最大值(數(shù)值越低優(yōu)先級(jí)越高)// 尋找當(dāng)前時(shí)間點(diǎn)上未完成的最高優(yōu)先級(jí)進(jìn)程for (int i = 0; i < n; i++) {if (processes[i].arrival_time <= current_time && !processes[i].is_completed) {if (processes[i].priority < highest_priority) {highest_priority = processes[i].priority;highest_priority_index = i; // 記錄該進(jìn)程的索引}}}// 如果找到了合適的進(jìn)程(即highest_priority_index有效)if (highest_priority_index != -1) {// 1. 將當(dāng)前時(shí)間增加該進(jìn)程的執(zhí)行時(shí)間current_time += processes[highest_priority_index].burst_time;// 2. 更新該進(jìn)程的完成時(shí)間 = 當(dāng)前時(shí)間processes[highest_priority_index].completion_time = current_time;// 3. 計(jì)算周轉(zhuǎn)時(shí)間 = 完成時(shí)間 - 到達(dá)時(shí)間processes[highest_priority_index].turnaround_time = processes[highest_priority_index].completion_time - processes[highest_priority_index].arrival_time;// 4. 計(jì)算等待時(shí)間 = 周轉(zhuǎn)時(shí)間 - 執(zhí)行時(shí)間processes[highest_priority_index].waiting_time = processes[highest_priority_index].turnaround_time - processes[highest_priority_index].burst_time;// 5. 將該進(jìn)程標(biāo)記為已完成processes[highest_priority_index].is_completed = 1;// 6. 更新已完成的進(jìn)程數(shù)completed++;} else {// 如果沒(méi)有合適的進(jìn)程可以運(yùn)行,時(shí)間遞增current_time++;}}
}// 打印所有進(jìn)程的信息,包括PID、到達(dá)時(shí)間、執(zhí)行時(shí)間、優(yōu)先級(jí)、完成時(shí)間、等待時(shí)間和周轉(zhuǎn)時(shí)間
void print_processes(struct Process processes[], int n) {printf("PID\tArrival\tBurst\tPriority\tCompletion\tWaiting\tTurnaround\n");for (int i = 0; i < n; i++) {printf("%d\t%d\t%d\t%d\t\t%d\t\t%d\t%d\n",processes[i].pid,processes[i].arrival_time,processes[i].burst_time,processes[i].priority,processes[i].completion_time,processes[i].waiting_time,processes[i].turnaround_time);}
}int main() {int n;// 用戶輸入進(jìn)程的數(shù)量printf("輸入進(jìn)程數(shù)量: ");scanf("%d", &n);struct Process processes[n];// 用戶輸入每個(gè)進(jìn)程的到達(dá)時(shí)間、執(zhí)行時(shí)間和優(yōu)先級(jí)for (int i = 0; i < n; i++) {printf("輸入進(jìn)程 %d 的到達(dá)時(shí)間、執(zhí)行時(shí)間和優(yōu)先級(jí): ", i + 1);scanf("%d %d %d", &processes[i].arrival_time, &processes[i].burst_time, &processes[i].priority);processes[i].pid = i + 1; // 設(shè)置進(jìn)程IDprocesses[i].is_completed = 0; // 初始化進(jìn)程為未完成狀態(tài)}// 調(diào)用優(yōu)先級(jí)調(diào)度算法priority_scheduling(processes, n);// 打印調(diào)度結(jié)果print_processes(processes, n);return 0;
}
運(yùn)行結(jié)果:
輸入進(jìn)程數(shù)量: 3
輸入進(jìn)程 1 的到達(dá)時(shí)間、執(zhí)行時(shí)間和優(yōu)先級(jí): 0 5 2
輸入進(jìn)程 2 的到達(dá)時(shí)間、執(zhí)行時(shí)間和優(yōu)先級(jí): 1 3 1
輸入進(jìn)程 3 的到達(dá)時(shí)間、執(zhí)行時(shí)間和優(yōu)先級(jí): 2 4 3
PID Arrival Burst Priority Completion Waiting Turnaround
1 0 5 2 5 0 5
2 1 3 1 8 4 7
3 2 4 3 12 6 10
說(shuō)明:在非搶占式優(yōu)先級(jí)調(diào)度中,當(dāng)一個(gè)進(jìn)程開(kāi)始執(zhí)行后,即使有更高(本代碼數(shù)字越小,優(yōu)先級(jí)越高)優(yōu)先級(jí)的進(jìn)程到達(dá),也不會(huì)被打斷,直到該進(jìn)程完成。
搶占式優(yōu)先級(jí)調(diào)度:
#include <stdio.h>
#include <limits.h>// 定義進(jìn)程結(jié)構(gòu)體
struct Process {int pid; // 進(jìn)程IDint arrival_time; // 到達(dá)時(shí)間int burst_time; // 執(zhí)行時(shí)間(運(yùn)行所需時(shí)間)int remaining_time; // 剩余執(zhí)行時(shí)間int priority; // 優(yōu)先級(jí)int completion_time;// 完成時(shí)間int waiting_time; // 等待時(shí)間int turnaround_time;// 周轉(zhuǎn)時(shí)間
};// 搶占式優(yōu)先級(jí)調(diào)度算法的實(shí)現(xiàn)
void preemptive_priority_scheduling(struct Process processes[], int n) {int current_time = 0; // 當(dāng)前時(shí)間int completed = 0; // 已完成的進(jìn)程數(shù)while (completed != n) {int highest_priority_index = -1;int highest_priority = INT_MAX;// 尋找當(dāng)前時(shí)間點(diǎn)上優(yōu)先級(jí)最高且仍未完成的進(jìn)程for (int i = 0; i < n; i++) {if (processes[i].arrival_time <= current_time && processes[i].remaining_time > 0) {if (processes[i].priority < highest_priority) {highest_priority = processes[i].priority;highest_priority_index = i;}}}// 如果找到了合適的進(jìn)程if (highest_priority_index != -1) {// 運(yùn)行該進(jìn)程一個(gè)時(shí)間單位processes[highest_priority_index].remaining_time--;current_time++;// 如果進(jìn)程完成if (processes[highest_priority_index].remaining_time == 0) {completed++;processes[highest_priority_index].completion_time = current_time;processes[highest_priority_index].turnaround_time = processes[highest_priority_index].completion_time - processes[highest_priority_index].arrival_time;processes[highest_priority_index].waiting_time = processes[highest_priority_index].turnaround_time - processes[highest_priority_index].burst_time;}} else {// 如果沒(méi)有進(jìn)程可以運(yùn)行,時(shí)間遞增current_time++;}}
}// 打印所有進(jìn)程的信息,包括PID、到達(dá)時(shí)間、執(zhí)行時(shí)間、優(yōu)先級(jí)、完成時(shí)間、等待時(shí)間和周轉(zhuǎn)時(shí)間
void print_processes(struct Process processes[], int n) {printf("PID\tArrival\tBurst\tPriority\tCompletion\tWaiting\tTurnaround\n");for (int i = 0; i < n; i++) {printf("%d\t%d\t%d\t%d\t\t%d\t\t%d\t%d\n",processes[i].pid,processes[i].arrival_time,processes[i].burst_time,processes[i].priority,processes[i].completion_time,processes[i].waiting_time,processes[i].turnaround_time);}
}int main() {int n;// 用戶輸入進(jìn)程的數(shù)量printf("輸入進(jìn)程數(shù)量: ");scanf("%d", &n);struct Process processes[n];// 用戶輸入每個(gè)進(jìn)程的到達(dá)時(shí)間、執(zhí)行時(shí)間和優(yōu)先級(jí)for (int i = 0; i < n; i++) {printf("輸入進(jìn)程 %d 的到達(dá)時(shí)間、執(zhí)行時(shí)間和優(yōu)先級(jí): ", i + 1);scanf("%d %d %d", &processes[i].arrival_time, &processes[i].burst_time, &processes[i].priority);processes[i].pid = i + 1; // 設(shè)置進(jìn)程IDprocesses[i].remaining_time = processes[i].burst_time; // 初始化剩余執(zhí)行時(shí)間}// 調(diào)用搶占式優(yōu)先級(jí)調(diào)度算法preemptive_priority_scheduling(processes, n);// 打印調(diào)度結(jié)果print_processes(processes, n);return 0;
}
運(yùn)行結(jié)果:
輸入進(jìn)程數(shù)量: 3
輸入進(jìn)程 1 的到達(dá)時(shí)間、執(zhí)行時(shí)間和優(yōu)先級(jí): 0 5 2
輸入進(jìn)程 2 的到達(dá)時(shí)間、執(zhí)行時(shí)間和優(yōu)先級(jí): 1 3 1
輸入進(jìn)程 3 的到達(dá)時(shí)間、執(zhí)行時(shí)間和優(yōu)先級(jí): 2 4 3
PID Arrival Burst Priority Completion Waiting Turnaround
1 0 5 2 8 3 8
2 1 3 1 4 0 3
3 2 4 3 12 6 10
說(shuō)明:本代碼優(yōu)先級(jí)數(shù)字越小,優(yōu)先級(jí)越高。
時(shí)間點(diǎn) 0:
- 只有P1到達(dá),P1開(kāi)始執(zhí)行,因?yàn)樗俏ㄒ坏倪M(jìn)程。
時(shí)間點(diǎn) 1:
- P2到達(dá),P2的優(yōu)先級(jí)(1)高于正在執(zhí)行的P1的優(yōu)先級(jí)(2)。
- 根據(jù)搶占式調(diào)度規(guī)則,P2立即搶占P1,P1被掛起。
- P2開(kāi)始執(zhí)行。
時(shí)間點(diǎn) 4:
- P2完成執(zhí)行(它的執(zhí)行時(shí)間是3個(gè)單位,從時(shí)間點(diǎn)1到4)。
- P1繼續(xù)執(zhí)行,因?yàn)榇藭r(shí)沒(méi)有優(yōu)先級(jí)更高的進(jìn)程。
時(shí)間點(diǎn) 2:
- P3到達(dá),雖然它的優(yōu)先級(jí)(3)低于P1(2),但此時(shí)P1已經(jīng)被P2搶占,P3需要等待。
- P1繼續(xù)執(zhí)行。
時(shí)間點(diǎn) 8:
- P1完成執(zhí)行,P3開(kāi)始執(zhí)行。
時(shí)間點(diǎn) 12:
- P3完成執(zhí)行。
6.4 輪轉(zhuǎn)調(diào)度(Round Robin, RR)
**輪轉(zhuǎn)調(diào)度(Round Robin, RR)**是一種時(shí)間片輪轉(zhuǎn)調(diào)度算法。每個(gè)進(jìn)程按照到達(dá)順序進(jìn)入隊(duì)列,并按照固定的時(shí)間片(time quantum)運(yùn)行。當(dāng)一個(gè)進(jìn)程的時(shí)間片用完時(shí),它被放到隊(duì)列的末尾,等待下一輪的調(diào)度。如果在時(shí)間片內(nèi)進(jìn)程未完成執(zhí)行,它將在下一輪繼續(xù)執(zhí)行。輪轉(zhuǎn)調(diào)度非常適合時(shí)間共享系統(tǒng),它在處理器上公平地分配CPU時(shí)間,使得每個(gè)進(jìn)程都有機(jī)會(huì)運(yùn)行。
#include <stdio.h>// 定義進(jìn)程結(jié)構(gòu)體
struct Process {int pid; // 進(jìn)程IDint arrival_time; // 到達(dá)時(shí)間int burst_time; // 執(zhí)行時(shí)間int remaining_time; // 剩余執(zhí)行時(shí)間int completion_time;// 完成時(shí)間int waiting_time; // 等待時(shí)間int turnaround_time;// 周轉(zhuǎn)時(shí)間
};// 輪轉(zhuǎn)調(diào)度算法的實(shí)現(xiàn)
void round_robin_scheduling(struct Process processes[], int n, int time_quantum) {int current_time = 0; // 當(dāng)前時(shí)間int completed = 0; // 已完成的進(jìn)程數(shù)int i = 0; // 進(jìn)程索引// 循環(huán)直到所有進(jìn)程完成while (completed != n) {// 如果當(dāng)前進(jìn)程已到達(dá)且仍有剩余時(shí)間需要執(zhí)行if (processes[i].arrival_time <= current_time && processes[i].remaining_time > 0) {// 如果剩余時(shí)間大于時(shí)間片,則運(yùn)行時(shí)間片大小的時(shí)間if (processes[i].remaining_time > time_quantum) {current_time += time_quantum; // 增加當(dāng)前時(shí)間processes[i].remaining_time -= time_quantum; // 減少剩余執(zhí)行時(shí)間} else {// 否則運(yùn)行剩余時(shí)間并完成進(jìn)程current_time += processes[i].remaining_time; // 增加當(dāng)前時(shí)間processes[i].remaining_time = 0; // 設(shè)置剩余執(zhí)行時(shí)間為0processes[i].completion_time = current_time; // 更新完成時(shí)間// 計(jì)算周轉(zhuǎn)時(shí)間 = 完成時(shí)間 - 到達(dá)時(shí)間processes[i].turnaround_time = processes[i].completion_time - processes[i].arrival_time;// 計(jì)算等待時(shí)間 = 周轉(zhuǎn)時(shí)間 - 執(zhí)行時(shí)間processes[i].waiting_time = processes[i].turnaround_time - processes[i].burst_time;completed++; // 進(jìn)程已完成,增加已完成的進(jìn)程數(shù)}}// 循環(huán)遍歷下一個(gè)進(jìn)程i = (i + 1) % n;}
}// 打印所有進(jìn)程的信息,包括PID、到達(dá)時(shí)間、執(zhí)行時(shí)間、完成時(shí)間、等待時(shí)間和周轉(zhuǎn)時(shí)間
void print_processes(struct Process processes[], int n) {printf("PID\tArrival\tBurst\tCompletion\tWaiting\tTurnaround\n");for (int i = 0; i < n; i++) {printf("%d\t%d\t%d\t%d\t\t%d\t%d\n",processes[i].pid,processes[i].arrival_time,processes[i].burst_time,processes[i].completion_time,processes[i].waiting_time,processes[i].turnaround_time);}
}int main() {int n, time_quantum;// 用戶輸入進(jìn)程數(shù)量printf("輸入進(jìn)程數(shù)量: ");scanf("%d", &n);struct Process processes[n];// 用戶輸入每個(gè)進(jìn)程的到達(dá)時(shí)間和執(zhí)行時(shí)間for (int i = 0; i < n; i++) {printf("輸入進(jìn)程 %d 的到達(dá)時(shí)間和執(zhí)行時(shí)間: ", i + 1);scanf("%d %d", &processes[i].arrival_time, &processes[i].burst_time);processes[i].pid = i + 1; // 設(shè)置進(jìn)程IDprocesses[i].remaining_time = processes[i].burst_time; // 初始化剩余執(zhí)行時(shí)間為執(zhí)行時(shí)間}// 用戶輸入時(shí)間片大小printf("輸入時(shí)間片大小: ");scanf("%d", &time_quantum);// 調(diào)用輪轉(zhuǎn)調(diào)度算法round_robin_scheduling(processes, n, time_quantum);// 打印調(diào)度結(jié)果print_processes(processes, n);return 0;
}
運(yùn)行結(jié)果:
輸入進(jìn)程數(shù)量: 3
輸入進(jìn)程 1 的到達(dá)時(shí)間和執(zhí)行時(shí)間: 0 5
輸入進(jìn)程 2 的到達(dá)時(shí)間和執(zhí)行時(shí)間: 1 3
輸入進(jìn)程 3 的到達(dá)時(shí)間和執(zhí)行時(shí)間: 2 4
輸入時(shí)間片大小: 2
PID Arrival Burst Completion Waiting Turnaround
1 0 5 12 7 12
2 1 3 9 5 8
3 2 4 11 5 9
時(shí)間點(diǎn) 0-2:
- P1到達(dá)并執(zhí)行2個(gè)時(shí)間單位(時(shí)間片大小為2),剩余執(zhí)行時(shí)間為3。
- 當(dāng)前時(shí)間為2。
時(shí)間點(diǎn) 2-4:
- P2到達(dá)(時(shí)間點(diǎn)1),開(kāi)始執(zhí)行2個(gè)時(shí)間單位,剩余執(zhí)行時(shí)間為1。
- 當(dāng)前時(shí)間為4。
時(shí)間點(diǎn) 4-6:
- P3到達(dá)(時(shí)間點(diǎn)2),開(kāi)始執(zhí)行2個(gè)時(shí)間單位,剩余執(zhí)行時(shí)間為2。
- 當(dāng)前時(shí)間為6。
時(shí)間點(diǎn) 6-8:
- P1輪到再次執(zhí)行,執(zhí)行2個(gè)時(shí)間單位,剩余1個(gè)時(shí)間單位。
- 當(dāng)前時(shí)間為8。
時(shí)間點(diǎn) 8-9:
- P2輪到繼續(xù)執(zhí)行,執(zhí)行1個(gè)時(shí)間單位,完成任務(wù)。
- 當(dāng)前時(shí)間為9。
時(shí)間點(diǎn) 9-10:
- P3繼續(xù)執(zhí)行,執(zhí)行1個(gè)時(shí)間單位,剩余1個(gè)時(shí)間單位。
- 當(dāng)前時(shí)間為10。
時(shí)間點(diǎn) 10-11:
- P3最后執(zhí)行剩余的1個(gè)時(shí)間單位,完成任務(wù)。
- 當(dāng)前時(shí)間為11。
時(shí)間點(diǎn) 11-12:
- P1最后執(zhí)行剩余的1個(gè)時(shí)間單位,完成任務(wù)。
- 當(dāng)前時(shí)間為12。
6.5 設(shè)置進(jìn)程優(yōu)先級(jí)
設(shè)置進(jìn)程優(yōu)先級(jí)是指在操作系統(tǒng)中為每個(gè)進(jìn)程分配一個(gè)優(yōu)先級(jí)值,該值決定了進(jìn)程在調(diào)度過(guò)程中的優(yōu)先順序。優(yōu)先級(jí)越高的進(jìn)程通常會(huì)比優(yōu)先級(jí)低的進(jìn)程更早獲得CPU資源,這對(duì)于實(shí)時(shí)操作系統(tǒng)和任務(wù)管理至關(guān)重要。在Linux中,使用nice
命令可以設(shè)置進(jìn)程的優(yōu)先級(jí)。nice
值的范圍通常是從-20(最高優(yōu)先級(jí))到19(最低優(yōu)先級(jí)),默認(rèn)值為0
命令示例:
# 以更高優(yōu)先級(jí)啟動(dòng)程序
nice -n -10 ./my_program# 調(diào)整正在運(yùn)行的進(jìn)程優(yōu)先級(jí)
renice -n 5 -p 1234 # 將 PID 為 1234 的進(jìn)程優(yōu)先級(jí)降低# 查看
top
運(yùn)行結(jié)果:
第一個(gè)終端:
liber@liber-VMware-Virtual-Platform:/home/c$ nice -n 10 ./receiver
第二個(gè)終端:
top - 16:47:25 up 1 day, 22:26, 9 users, load average: 2.84, 2.60, 2.17
任務(wù): 355 total, 2 running, 353 sleeping, 0 stopped, 0 zombie
%Cpu(s): 13.0 us, 24.8 sy, 41.1 ni, 19.6 id, 0.0 wa, 0.0 hi, 1.4 si, 0.0 st
MiB Mem : 3868.2 total, 122.3 free, 2645.8 used, 1346.7 buff/cache
MiB Swap: 3868.0 total, 2832.5 free, 1035.5 used. 1222.5 avail Mem進(jìn)程號(hào) USER PR NI VIRT RES SHR %CPU %MEM TIME+ COMMAND
2100616 liber 30 10 2680 1408 1408 R 77.9 0.0 0:46.70 receiver
說(shuō)明:top命令是一個(gè)動(dòng)態(tài)顯示當(dāng)前系統(tǒng)任務(wù)的命令,它可以顯示每個(gè)進(jìn)程的PR(優(yōu)先級(jí))和NI(nice值)。
第三個(gè)終端:
liber@liber-VMware-Virtual-Platform:~$ sudo renice -n 5 -p 2100616
[sudo] liber 的密碼:
2100616 (process ID) 舊優(yōu)先級(jí)為 10,新優(yōu)先級(jí)為 5
7. 進(jìn)程的內(nèi)存管理
7.1 地址空間
- 物理地址:這是計(jì)算機(jī)內(nèi)存芯片上的實(shí)際地址,由內(nèi)存硬件直接使用。
- 邏輯地址(虛擬地址):這是由CPU生成的地址,是相對(duì)與進(jìn)程獨(dú)立的虛擬地址空間的地址,通常通過(guò)地址轉(zhuǎn)換機(jī)制映射到物理地址。
7.2 內(nèi)存分配
- 靜態(tài)分配:在程序編譯時(shí)為變量分配固定的內(nèi)存空間,無(wú)法在運(yùn)行時(shí)調(diào)整。
- 動(dòng)態(tài)分配:在程序運(yùn)行時(shí),根據(jù)需要分配和釋放內(nèi)存,例如使用堆(heap)和棧(stack)進(jìn)行管理。
7.3 常見(jiàn)的機(jī)制
機(jī)制 | 描述 |
---|---|
分段 | 內(nèi)存被劃分為不同的段,每個(gè)段可以包含不同類型的數(shù)據(jù),如代碼段、數(shù)據(jù)段、棧段等。每個(gè)段有自己的基址和長(zhǎng)度。分段使得程序員可以將邏輯相關(guān)的內(nèi)存部分分開(kāi)處理,有助于保護(hù)和共享內(nèi)存。 |
分頁(yè) | 內(nèi)存被劃分為固定大小的頁(yè)(通常是4KB)。虛擬內(nèi)存的每一頁(yè)可以映射到物理內(nèi)存的任意一頁(yè)。分頁(yè)減少了內(nèi)存碎片的產(chǎn)生,使內(nèi)存管理更加高效。 |
虛擬內(nèi)存 | 虛擬內(nèi)存是一種內(nèi)存管理技術(shù),它允許操作系統(tǒng)使用磁盤空間來(lái)擴(kuò)展物理內(nèi)存的容量。通過(guò)將不常用的頁(yè)面交換到磁盤,虛擬內(nèi)存讓系統(tǒng)能夠運(yùn)行超出實(shí)際物理內(nèi)存容量的程序。 |
頁(yè)面置換算法 | 當(dāng)物理內(nèi)存滿了,需要將某些頁(yè)面換出到磁盤,這時(shí)會(huì)使用頁(yè)面置換算法。 |
7.4 頁(yè)面置換算法
7.4.1 FIFO(First-In, First-Out)頁(yè)面置換算法
FIFO頁(yè)面置換算法基于“先來(lái)先服務(wù)”的原則。它將最早進(jìn)入內(nèi)存的頁(yè)面作為最先被置換的候選者,不考慮頁(yè)面的使用頻率或最近使用時(shí)間。當(dāng)需要置換頁(yè)面時(shí),系統(tǒng)會(huì)選擇最早進(jìn)入內(nèi)存的頁(yè)面,將其換出,然后將新頁(yè)面加載到該位置。使用隊(duì)列(Queue)來(lái)跟蹤頁(yè)面的順序。隊(duì)列的頭部是最早進(jìn)入的頁(yè)面,尾部是最新進(jìn)入的頁(yè)面。當(dāng)新頁(yè)面進(jìn)入時(shí),移除隊(duì)列頭部的頁(yè)面,將新頁(yè)面添加到尾部。
簡(jiǎn)單案例:
#include <stdio.h>#define MAX_FRAMES 3 // 最大頁(yè)框數(shù)void fifo_page_replacement(int pages[], int n) {int frames[MAX_FRAMES] = {-1, -1, -1}; // 初始化頁(yè)框?yàn)榭?#xff0c;-1 表示空閑int current = 0; // 當(dāng)前頁(yè)框的索引int page_faults = 0; // 記錄頁(yè)面錯(cuò)誤次數(shù)printf("FIFO Page Replacement\n");printf("Page\tFrames\n");for (int i = 0; i < n; i++) {int page = pages[i];int found = 0;// 檢查頁(yè)面是否已經(jīng)在頁(yè)框中for (int j = 0; j < MAX_FRAMES; j++) {if (frames[j] == page) {found = 1; // 頁(yè)面已在內(nèi)存中,不需要替換break;}}if (!found) {// 如果頁(yè)面不在內(nèi)存中,替換最早進(jìn)入的頁(yè)面frames[current] = page; // 替換當(dāng)前頁(yè)框中的頁(yè)面current = (current + 1) % MAX_FRAMES; // 更新索引,確保循環(huán)替換page_faults++;// 打印當(dāng)前頁(yè)框的內(nèi)容printf("%d\t", page);for (int j = 0; j < MAX_FRAMES; j++) {if (frames[j] != -1) {printf("%d ", frames[j]); // 打印頁(yè)框中的頁(yè)面} else {printf("- "); // 打印空閑頁(yè)框}}printf("\n");}}printf("Total Page Faults: %d\n", page_faults); // 打印總的頁(yè)面錯(cuò)誤次數(shù)
}int main() {int n;// 輸入頁(yè)面序列長(zhǎng)度printf("輸入頁(yè)面序列長(zhǎng)度: ");scanf("%d", &n);int pages[n];// 輸入頁(yè)面序列printf("輸入頁(yè)面序列(以空格分隔): ");for (int i = 0; i < n; i++) {scanf("%d", &pages[i]);}fifo_page_replacement(pages, n);return 0;
}
運(yùn)行結(jié)果:
輸入頁(yè)面序列長(zhǎng)度: 6
輸入頁(yè)面序列(以空格分隔): 1 3 0 5 6 3
FIFO Page Replacement
Page Frames
1 1 - -
3 1 3 -
0 1 3 0
5 5 3 0
6 5 6 0
3 5 6 3
Total Page Faults: 6
解釋:
-
頁(yè)面1加載到內(nèi)存,產(chǎn)生頁(yè)面錯(cuò)誤,內(nèi)存狀態(tài):
1 - -
。 -
頁(yè)面3加載到內(nèi)存,產(chǎn)生頁(yè)面錯(cuò)誤,內(nèi)存狀態(tài):
1 3 -
。 -
頁(yè)面0加載到內(nèi)存,產(chǎn)生頁(yè)面錯(cuò)誤,內(nèi)存狀態(tài):
1 3 0
。 -
頁(yè)面5加載到內(nèi)存,替換
1
(最早進(jìn)入),產(chǎn)生頁(yè)面錯(cuò)誤,內(nèi)存狀態(tài):5 3 0
。 -
頁(yè)面6加載到內(nèi)存,替換
3
(最早進(jìn)入),產(chǎn)生頁(yè)面錯(cuò)誤,內(nèi)存狀態(tài):5 6 0
。 -
頁(yè)面3加載到內(nèi)存,替換
0
,產(chǎn)生頁(yè)面錯(cuò)誤,內(nèi)存狀態(tài):5 6 3
。
7.4.2 LRU(Least Recently Used)頁(yè)面置換算法
LRU頁(yè)面置換算法基于“最近最少使用”原則,選擇最久未被訪問(wèn)的頁(yè)面進(jìn)行置換。系統(tǒng)需要記錄每個(gè)頁(yè)面的最后一次訪問(wèn)時(shí)間或訪問(wèn)順序。當(dāng)需要置換頁(yè)面時(shí),選擇記錄中訪問(wèn)時(shí)間最久遠(yuǎn)的頁(yè)面進(jìn)行置換。
簡(jiǎn)單案例:
#include <stdio.h>#define MAX_FRAMES 3 // 最大頁(yè)框數(shù)void lru_page_replacement(int pages[], int n) {int frames[MAX_FRAMES] = {-1, -1, -1}; // 初始化頁(yè)框?yàn)榭?#xff0c;-1 表示空閑int counter[MAX_FRAMES] = {0}; // 記錄每個(gè)幀的使用時(shí)間int page_faults = 0;int time = 0; // 用于記錄當(dāng)前的時(shí)間printf("LRU Page Replacement\n");printf("Page\tFrames\n");for (int i = 0; i < n; i++) {int page = pages[i];int found = 0;// 檢查頁(yè)面是否已經(jīng)在頁(yè)框中for (int j = 0; j < MAX_FRAMES; j++) {if (frames[j] == page) {found = 1; // 頁(yè)面已在內(nèi)存中,不需要替換counter[j] = ++time; // 更新此頁(yè)面的使用時(shí)間break;}}if (!found) {// 查找最近最少使用的幀int lru_index = 0;for (int j = 1; j < MAX_FRAMES; j++) {if (counter[j] < counter[lru_index]) {lru_index = j; // 選擇最近最少使用的頁(yè)面進(jìn)行替換}}// 替換最久未使用的頁(yè)面frames[lru_index] = page;counter[lru_index] = ++time; // 更新使用時(shí)間page_faults++;// 打印當(dāng)前頁(yè)框的內(nèi)容printf("%d\t", page);for (int j = 0; j < MAX_FRAMES; j++) {if (frames[j] != -1) {printf("%d ", frames[j]); // 打印頁(yè)框中的頁(yè)面} else {printf("- "); // 打印空閑頁(yè)框}}printf("\n");}}printf("Total Page Faults: %d\n", page_faults); // 打印總的頁(yè)面錯(cuò)誤次數(shù)
}int main() {int n;// 輸入頁(yè)面序列長(zhǎng)度printf("輸入頁(yè)面序列長(zhǎng)度: ");scanf("%d", &n);int pages[n];// 輸入頁(yè)面序列printf("輸入頁(yè)面序列(以空格分隔): ");for (int i = 0; i < n; i++) {scanf("%d", &pages[i]);}// 調(diào)用LRU頁(yè)面置換算法lru_page_replacement(pages, n);return 0;
}
運(yùn)行結(jié)果:
輸入頁(yè)面序列長(zhǎng)度: 6
輸入頁(yè)面序列(以空格分隔): 1 3 0 3 5 6
LRU Page Replacement
Page Frames
1 1 - -
3 1 3 -
0 1 3 0
5 5 3 0
6 5 3 6
Total Page Faults: 5
解釋:
-
頁(yè)面1加載到內(nèi)存,產(chǎn)生頁(yè)面錯(cuò)誤,內(nèi)存狀態(tài):
1 - -
。 -
頁(yè)面3加載到內(nèi)存,產(chǎn)生頁(yè)面錯(cuò)誤,內(nèi)存狀態(tài):
1 3 -
。 -
頁(yè)面0加載到內(nèi)存,產(chǎn)生頁(yè)面錯(cuò)誤,內(nèi)存狀態(tài):
1 3 0
。 -
頁(yè)面3已經(jīng)在內(nèi)存中,更新其使用時(shí)間,無(wú)需替換,不產(chǎn)生頁(yè)面錯(cuò)誤。
-
頁(yè)面5加載到內(nèi)存,替換最近最少使用的頁(yè)面
1
,產(chǎn)生頁(yè)面錯(cuò)誤,內(nèi)存狀態(tài):5 3 0
。 -
頁(yè)面6加載到內(nèi)存,替換最近最少使用的頁(yè)面
0
,產(chǎn)生頁(yè)面錯(cuò)誤,內(nèi)存狀態(tài):5 3 6
。
7.4.3 LFU(Least Frequently Used)頁(yè)面置換算法
LFU頁(yè)面置換算法基于“最少使用頻率”原則,選擇在一段時(shí)間內(nèi)使用次數(shù)最少的頁(yè)面進(jìn)行置換。為每個(gè)頁(yè)面維護(hù)一個(gè)計(jì)數(shù)器,每次訪問(wèn)該頁(yè)面時(shí),計(jì)數(shù)器加1。當(dāng)需要置換頁(yè)面時(shí),選擇計(jì)數(shù)器值最小的頁(yè)面。
簡(jiǎn)單案例:
#include <stdio.h>#define MAX_FRAMES 3 // 最大頁(yè)框數(shù)void lfu_page_replacement(int pages[], int n) {int frames[MAX_FRAMES] = {-1, -1, -1}; // 初始化頁(yè)框?yàn)榭?#xff0c;-1 表示空閑int frequency[MAX_FRAMES] = {0}; // 記錄每個(gè)幀的使用頻率int age[MAX_FRAMES] = {0}; // 記錄每個(gè)幀的使用順序,用于解決頻率相同時(shí)的選擇int page_faults = 0;int time = 0; // 用于記錄每次頁(yè)面訪問(wèn)的時(shí)間printf("LFU Page Replacement\n");printf("Page\tFrames\n");for (int i = 0; i < n; i++) {int page = pages[i];int found = 0;// 檢查頁(yè)面是否已經(jīng)在頁(yè)框中for (int j = 0; j < MAX_FRAMES; j++) {if (frames[j] == page) {found = 1; // 頁(yè)面已在內(nèi)存中frequency[j]++; // 增加使用頻率age[j] = ++time; // 更新此頁(yè)面的訪問(wèn)時(shí)間break;}}if (!found) {// 選擇使用頻率最低且最早進(jìn)入的頁(yè)面進(jìn)行替換int lfu_index = 0;for (int j = 1; j < MAX_FRAMES; j++) {// 比較頻率,頻率低的優(yōu)先if (frequency[j] < frequency[lfu_index]) {lfu_index = j;} // 如果頻率相同,選擇最早進(jìn)入的頁(yè)面else if (frequency[j] == frequency[lfu_index] && age[j] < age[lfu_index]) {lfu_index = j;}}// 替換使用頻率最低的頁(yè)面frames[lfu_index] = page;frequency[lfu_index] = 1; // 重置使用頻率age[lfu_index] = ++time; // 更新頁(yè)面進(jìn)入時(shí)間page_faults++;}// 無(wú)論頁(yè)面是否已經(jīng)在內(nèi)存中,都打印當(dāng)前頁(yè)框的內(nèi)容printf("%d\t", page);for (int j = 0; j < MAX_FRAMES; j++) {if (frames[j] != -1) {printf("%d ", frames[j]); // 打印頁(yè)框中的頁(yè)面} else {printf("- "); // 打印空閑頁(yè)框}}printf("\n");}printf("Total Page Faults: %d\n", page_faults); // 打印總的頁(yè)面錯(cuò)誤次數(shù)
}int main() {int n;// 輸入頁(yè)面序列長(zhǎng)度printf("輸入頁(yè)面序列長(zhǎng)度: ");scanf("%d", &n);int pages[n];// 輸入頁(yè)面序列printf("輸入頁(yè)面序列(以空格分隔): ");for (int i = 0; i < n; i++) {scanf("%d", &pages[i]);}// 調(diào)用 LFU 頁(yè)面置換算法lfu_page_replacement(pages, n);return 0;
}
運(yùn)行結(jié)果:
輸入頁(yè)面序列長(zhǎng)度: 8
輸入頁(yè)面序列(以空格分隔): 1 2 3 4 2 1 5 6
LFU Page Replacement
Page Frames
1 1 - -
2 1 2 -
3 1 2 3
4 4 2 3
2 4 2 3
1 4 2 1
5 5 2 1
6 5 2 6
Total Page Faults: 7
解釋:
-
頁(yè)面1:加載到內(nèi)存,產(chǎn)生頁(yè)面錯(cuò)誤,內(nèi)存狀態(tài):
1 - -
。 -
頁(yè)面2:加載到內(nèi)存,產(chǎn)生頁(yè)面錯(cuò)誤,內(nèi)存狀態(tài):
1 2 -
。 -
頁(yè)面3:加載到內(nèi)存,產(chǎn)生頁(yè)面錯(cuò)誤,內(nèi)存狀態(tài):
1 2 3
。 -
頁(yè)面4:在頻率相同的情況下,應(yīng)該選擇最早的頁(yè)面
1
替換,產(chǎn)生頁(yè)面錯(cuò)誤,內(nèi)存狀態(tài):4 2 3
。 -
頁(yè)面2:已在內(nèi)存中,增加其頻率,無(wú)需替換。
-
頁(yè)面1:在頻率相同的情況下,應(yīng)該選擇最早的頁(yè)面
3
替換,產(chǎn)生頁(yè)面錯(cuò)誤,內(nèi)存狀態(tài):4 2 1
。 -
頁(yè)面5:在頻率相同的情況下,應(yīng)該選擇最早的頁(yè)面
4
替換,產(chǎn)生頁(yè)面錯(cuò)誤,內(nèi)存狀態(tài):5 2 1
。 -
頁(yè)面6:在頻率相同的情況下,應(yīng)該選擇最早的頁(yè)面
1
替換,產(chǎn)生頁(yè)面錯(cuò)誤,內(nèi)存狀態(tài):5 2 6
7.5 內(nèi)存映射
內(nèi)存映射(Memory Mapping)是操作系統(tǒng)提供的一種機(jī)制,它允許將文件或其他對(duì)象(如設(shè)備內(nèi)存)直接映射到進(jìn)程的地址空間。通過(guò)內(nèi)存映射,進(jìn)程可以像訪問(wèn)普通內(nèi)存一樣訪問(wèn)文件中的數(shù)據(jù),而不需要使用傳統(tǒng)的I/O操作。
mmap()
是一種高效的內(nèi)存管理技術(shù),可以將文件或設(shè)備映射到進(jìn)程的地址空間,允許進(jìn)程通過(guò)內(nèi)存直接訪問(wèn)文件內(nèi)容。
簡(jiǎn)單案例:
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>int main() {// 打開(kāi)要映射的文件int fd = open("example.txt", O_RDONLY);if (fd == -1) {perror("open");return 1;}// 獲取文件大小struct stat st;if (fstat(fd, &st) == -1) {perror("fstat");close(fd);return 1;}// 將文件內(nèi)容映射到內(nèi)存char *mapped = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);if (mapped == MAP_FAILED) {perror("mmap");close(fd)return 1;}// 關(guān)閉文件描述符close(fd);// 現(xiàn)在可以直接訪問(wèn)文件內(nèi)容printf("File content:\n%.*s\n", (int)st.st_size, mapped);// 解除內(nèi)存映射if (munmap(mapped, st.st_size) == -1) {perror("munmap");return 1;}return 0;
}
運(yùn)行結(jié)果:
前提準(zhǔn)備:創(chuàng)建example.txt,隨便輸入一些內(nèi)容,我輸入的是HI,Word!
File content:
HI,Word!
解釋:
- 使用
mmap
將文件內(nèi)容映射到進(jìn)程的地址空間。PROT_READ
表示映射區(qū)域是可讀的,MAP_PRIVATE
表示創(chuàng)建私有副本,修改映射內(nèi)容不會(huì)影響原文件。 - 映射成功后,可以通過(guò)指針
mapped
直接訪問(wèn)文件內(nèi)容。 - 在不需要時(shí),使用
munmap
解除內(nèi)存映射,釋放資源。
8. 進(jìn)程的同步與鎖機(jī)制
進(jìn)程同步 是指在多個(gè)進(jìn)程或線程并發(fā)執(zhí)行時(shí),控制它們的執(zhí)行順序或訪問(wèn)順序,以確保共享資源被正確使用。進(jìn)程同步的目標(biāo)是避免以下問(wèn)題:
- 競(jìng)爭(zhēng)條件:多個(gè)進(jìn)程或線程同時(shí)訪問(wèn)或修改共享資源,可能導(dǎo)致數(shù)據(jù)不一致或不可預(yù)知的行為。
- 死鎖:兩個(gè)或多個(gè)進(jìn)程或線程相互等待對(duì)方釋放資源,導(dǎo)致所有進(jìn)程或線程都無(wú)法繼續(xù)執(zhí)行。
- 資源饑餓:某些進(jìn)程或線程長(zhǎng)期無(wú)法獲得資源,導(dǎo)致無(wú)法正常執(zhí)行。
鎖機(jī)制 是實(shí)現(xiàn)進(jìn)程同步的常用手段之一。鎖可以用來(lái)確保在某一時(shí)刻,只有一個(gè)進(jìn)程或線程能夠訪問(wèn)特定的共享資源
進(jìn)程同步與鎖機(jī)制的主要類型及其特點(diǎn):
機(jī)制類型 | 描述 | 操作 | 適用場(chǎng)景 |
---|---|---|---|
互斥鎖 (Mutex) | 確保同一時(shí)刻只有一個(gè)進(jìn)程或線程可以訪問(wèn)共享資源,避免數(shù)據(jù)競(jìng)爭(zhēng)。 | Lock :請(qǐng)求鎖,鎖被占用時(shí)阻塞。Unlock :釋放鎖。 | 需要獨(dú)占訪問(wèn)資源的場(chǎng)景,如更新共享數(shù)據(jù)結(jié)構(gòu)。 |
讀寫鎖 (Read-Write Lock) | 允許多個(gè)讀者同時(shí)讀取,但寫者獨(dú)占訪問(wèn),適用于讀多寫少的情況。 | Read Lock :請(qǐng)求讀鎖,可并發(fā)。Write Lock :請(qǐng)求寫鎖,獨(dú)占。Unlock :釋放鎖。 | 讀多寫少的資源訪問(wèn),如緩存。 |
自旋鎖 (Spinlock) | 進(jìn)程或線程忙等待,持續(xù)檢查鎖狀態(tài),適用于鎖定時(shí)間很短的情況。 | Lock :忙等待獲取鎖。Unlock :釋放鎖。 | 短時(shí)間鎖定的臨界區(qū),避免上下文切換的開(kāi)銷。 |
信號(hào)量 (Semaphore) | 用于限制同時(shí)訪問(wèn)共享資源的進(jìn)程或線程的數(shù)量,控制并發(fā)。 | P (Wait) :請(qǐng)求減少信號(hào)量值,阻塞等待。V (Signal) :增加信號(hào)量值,可能喚醒等待進(jìn)程。 | 控制資源訪問(wèn)數(shù)量,如限制同時(shí)訪問(wèn)數(shù)據(jù)庫(kù)連接數(shù)。 |
條件變量 (Condition Variable) | 用于進(jìn)程或線程間的信號(hào)傳遞,等待某一條件發(fā)生時(shí)進(jìn)行同步。 | Wait :等待條件滿足。Signal :條件滿足時(shí)發(fā)信號(hào)喚醒。 | 需要在特定條件下進(jìn)行線程或進(jìn)程間的同步,如生產(chǎn)者-消費(fèi)者模型。 |
屏障 (Barrier) | 使一組線程或進(jìn)程必須全部到達(dá)某個(gè)同步點(diǎn)后才能繼續(xù)執(zhí)行,常用于并行計(jì)算。 | Wait :等待所有線程或進(jìn)程到達(dá)屏障點(diǎn)。 | 并行計(jì)算中各部分任務(wù)需要同步協(xié)調(diào)的場(chǎng)景,如階段性任務(wù)同步。 |
8.1 互斥鎖 (Mutex)
互斥鎖是用于防止多個(gè)線程或進(jìn)程同時(shí)訪問(wèn)共享資源的鎖機(jī)制?;コ怄i保證在任一時(shí)刻,只有一個(gè)線程可以持有鎖并訪問(wèn)受保護(hù)的共享資源。其他線程在嘗試獲取已被持有的互斥鎖時(shí),會(huì)被阻塞,直到鎖被釋放。
- 鎖定(Lock):線程請(qǐng)求互斥鎖。如果鎖已經(jīng)被其他線程持有,請(qǐng)求線程會(huì)被阻塞,直到鎖可用。
- 解鎖(Unlock):線程釋放互斥鎖,使得其他阻塞的線程可以繼續(xù)執(zhí)行。
#include <stdio.h>
#include <pthread.h>// 定義互斥鎖
pthread_mutex_t lock;void *thread_function(void *arg) {int id = *(int *)arg;// 嘗試獲取互斥鎖pthread_mutex_lock(&lock);// 進(jìn)入臨界區(qū),訪問(wèn)共享資源printf("Thread %d is executing\n", id);// 模擬臨界區(qū)操作for (int i = 0; i < 5; i++) {printf("Thread %d is in critical section %d\n", id, i+1);}// 釋放互斥鎖pthread_mutex_unlock(&lock);return NULL;
}int main() {pthread_t thread1, thread2;int id1 = 1, id2 = 2;// 初始化互斥鎖pthread_mutex_init(&lock, NULL);// 創(chuàng)建兩個(gè)線程pthread_create(&thread1, NULL, thread_function, &id1);pthread_create(&thread2, NULL, thread_function, &id2);// 等待兩個(gè)線程完成pthread_join(thread1, NULL);pthread_join(thread2, NULL);// 銷毀互斥鎖pthread_mutex_destroy(&lock);return 0;
}
運(yùn)行結(jié)果:
Thread 1 is executing
Thread 1 is in critical section 1
Thread 1 is in critical section 2
Thread 1 is in critical section 3
Thread 1 is in critical section 4
Thread 1 is in critical section 5
Thread 2 is executing
Thread 2 is in critical section 1
Thread 2 is in critical section 2
Thread 2 is in critical section 3
Thread 2 is in critical section 4
Thread 2 is in critical section 5
解釋:
-
pthread_mutex_init(&lock, NULL);
初始化互斥鎖。在使用互斥鎖前,必須先初始化它。 -
每個(gè)線程在進(jìn)入臨界區(qū)前都會(huì)嘗試獲取互斥鎖。使用
pthread_mutex_lock(&lock);
來(lái)獲取鎖。如果鎖已經(jīng)被其他線程持有,當(dāng)前線程會(huì)被阻塞,直到鎖可用。 -
進(jìn)入臨界區(qū)后,線程可以安全地訪問(wèn)共享資源,如打印消息或修改共享變量。
-
完成臨界區(qū)操作后,使用
pthread_mutex_unlock(&lock);
釋放鎖,使得其他線程可以繼續(xù)執(zhí)行。 -
(線程同步)
pthread_join(thread1, NULL);
和pthread_join(thread2, NULL);
用于等待兩個(gè)線程完成執(zhí)行,確保主線程在它們執(zhí)行完畢后才繼續(xù)。 -
pthread_mutex_destroy(&lock);
在程序結(jié)束時(shí)銷毀互斥鎖,釋放資源。
8.2 讀寫鎖 (Read-Write Lock)
讀寫鎖 (Read-Write Lock) 是一種允許多個(gè)線程同時(shí)讀取共享資源,但只允許一個(gè)線程寫入共享資源的鎖機(jī)制。讀寫鎖提供了更高的并發(fā)性,因?yàn)樗试S多線程并發(fā)讀取數(shù)據(jù),同時(shí)防止寫操作和其他讀寫操作的沖突。
- 讀鎖(Read Lock):多個(gè)線程可以同時(shí)獲取讀鎖,允許并發(fā)讀取共享資源。
- 寫鎖(Write Lock):獲取寫鎖時(shí),必須等待所有讀鎖和其他寫鎖被釋放,確保寫操作的獨(dú)占性。
- 解鎖(Unlock):釋放讀鎖或?qū)戞i,允許其他等待的線程繼續(xù)執(zhí)行。
#include <stdio.h>
#include <pthread.h>// 定義讀寫鎖
pthread_rwlock_t rwlock;// 共享資源
int shared_resource = 0;void *reader(void *arg) {int id = *(int *)arg;// 獲取讀鎖pthread_rwlock_rdlock(&rwlock);// 讀取共享資源printf("Reader %d: Shared resource = %d\n", id, shared_resource);// 釋放讀鎖pthread_rwlock_unlock(&rwlock);return NULL;
}void *writer(void *arg) {int id = *(int *)arg;// 獲取寫鎖pthread_rwlock_wrlock(&rwlock);// 寫入共享資源shared_resource += 10;printf("Writer %d: Updated shared resource to %d\n", id, shared_resource);// 釋放寫鎖pthread_rwlock_unlock(&rwlock);return NULL;
}int main() {pthread_t threads[3];int ids[3] = {1, 2, 3};// 初始化讀寫鎖pthread_rwlock_init(&rwlock, NULL);// 創(chuàng)建兩個(gè)讀線程和一個(gè)寫線程pthread_create(&threads[0], NULL, reader, &ids[0]);pthread_create(&threads[1], NULL, writer, &ids[1]);pthread_create(&threads[2], NULL, reader, &ids[2]);// 等待所有線程完成for (int i = 0; i < 3; i++) {pthread_join(threads[i], NULL);}// 銷毀讀寫鎖pthread_rwlock_destroy(&rwlock);return 0;
}
運(yùn)行結(jié)果:
Reader 1: Shared resource = 0
Writer 2: Updated shared resource to 10
Reader 3: Shared resource = 10
解釋:
-
pthread_rwlock_init(&rwlock, NULL);
初始化讀寫鎖。在使用讀寫鎖前,必須先初始化它。 -
每個(gè)讀線程在讀取共享資源前會(huì)獲取讀鎖。使用
pthread_rwlock_rdlock(&rwlock);
來(lái)獲取讀鎖。多個(gè)讀線程可以同時(shí)獲取讀鎖,允許并發(fā)讀取。 -
讀取共享資源后,使用
pthread_rwlock_unlock(&rwlock);
釋放讀鎖。 -
寫線程在修改共享資源前會(huì)獲取寫鎖。使用
pthread_rwlock_wrlock(&rwlock);
來(lái)獲取寫鎖。獲取寫鎖后,寫線程可以獨(dú)占訪問(wèn)共享資源。 -
修改共享資源后,使用
pthread_rwlock_unlock(&rwlock);
釋放寫鎖,允許其他讀寫操作繼續(xù)執(zhí)行。 -
pthread_join(threads[i], NULL);
用于等待所有線程完成執(zhí)行,確保主線程在它們執(zhí)行完畢后才繼續(xù)。 -
pthread_rwlock_destroy(&rwlock);
在程序結(jié)束時(shí)銷毀讀寫鎖,釋放資源。
8.3 自旋鎖 (Spinlock)
自旋鎖 (Spinlock) 是一種忙等待的鎖機(jī)制。在等待獲取鎖時(shí),線程不會(huì)進(jìn)入睡眠狀態(tài),而是持續(xù)檢查鎖的狀態(tài),直到獲得鎖為止。由于自旋鎖不會(huì)導(dǎo)致線程上下文切換,因此適用于鎖定時(shí)間非常短的場(chǎng)景。
- 鎖定(Lock):線程嘗試獲取自旋鎖。如果鎖已被占用,線程會(huì)在獲取鎖之前一直循環(huán)檢查鎖的狀態(tài)。
- 解鎖(Unlock):線程釋放自旋鎖,允許其他等待的線程獲取鎖。
#include <stdio.h>
#include <pthread.h>// 定義自旋鎖
pthread_spinlock_t spinlock;// 共享資源
int shared_resource = 0;void *thread_function(void *arg) {int id = *(int *)arg;// 嘗試獲取自旋鎖pthread_spin_lock(&spinlock);// 進(jìn)入臨界區(qū),訪問(wèn)共享資源printf("Thread %d is executing\n", id);shared_resource += 10;printf("Thread %d updated shared resource to %d\n", id, shared_resource);// 釋放自旋鎖pthread_spin_unlock(&spinlock);return NULL;
}int main() {pthread_t thread1, thread2;int id1 = 1, id2 = 2;// 初始化自旋鎖pthread_spin_init(&spinlock, PTHREAD_PROCESS_PRIVATE);// 創(chuàng)建兩個(gè)線程pthread_create(&thread1, NULL, thread_function, &id1);pthread_create(&thread2, NULL, thread_function, &id2);// 等待兩個(gè)線程完成pthread_join(thread1, NULL);pthread_join(thread2, NULL);// 銷毀自旋鎖pthread_spin_destroy(&spinlock);return 0;
}
運(yùn)行結(jié)果:
liber@liber-VMware-Virtual-Platform:/home/c$ ./receiver
Thread 2 is executing
Thread 2 updated shared resource to 10
Thread 1 is executing
Thread 1 updated shared resource to 20
解釋:
-
pthread_spin_init(&spinlock, PTHREAD_PROCESS_PRIVATE);
初始化自旋鎖。在使用自旋鎖前,必須先初始化它。這里的PTHREAD_PROCESS_PRIVATE
表示自旋鎖僅用于單個(gè)進(jìn)程內(nèi)的線程之間。 -
每個(gè)線程在進(jìn)入臨界區(qū)前都會(huì)嘗試獲取自旋鎖。使用
pthread_spin_lock(&spinlock);
來(lái)獲取鎖。自旋鎖不會(huì)讓線程進(jìn)入睡眠,而是會(huì)忙等待直到鎖可用。 -
進(jìn)入臨界區(qū)后,線程可以安全地訪問(wèn)共享資源,如增加共享變量的值。
-
完成臨界區(qū)操作后,使用
pthread_spin_unlock(&spinlock);
釋放鎖,使得其他線程可以繼續(xù)執(zhí)行。 -
pthread_join(thread1, NULL);
和pthread_join(thread2, NULL);
用于等待兩個(gè)線程完成執(zhí)行,確保主線程在它們執(zhí)行完畢后才繼續(xù)。 -
pthread_spin_destroy(&spinlock);
在程序結(jié)束時(shí)銷毀自旋鎖,釋放資源。
8.4 信號(hào)量 (Semaphore)
信號(hào)量 (Semaphore) 是一種用于控制多個(gè)進(jìn)程或線程對(duì)共享資源進(jìn)行訪問(wèn)的同步機(jī)制。信號(hào)量包含一個(gè)計(jì)數(shù)器,用來(lái)表示當(dāng)前可用資源的數(shù)量。信號(hào)量可以用來(lái)解決資源的互斥訪問(wèn)問(wèn)題,特別是在需要限制訪問(wèn)資源的進(jìn)程或線程數(shù)量時(shí)。
- P 操作 (Wait/Down):試圖將信號(hào)量的值減1,如果信號(hào)量的值為0,則進(jìn)程或線程會(huì)被阻塞,直到信號(hào)量的值大于0。
- V 操作 (Signal/Up):將信號(hào)量的值加1,如果有等待的進(jìn)程或線程,則喚醒它們。
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>// 定義信號(hào)量
sem_t semaphore;// 共享資源
int shared_resource = 0;void *thread_function(void *arg) {int id = *(int *)arg;// 嘗試獲取信號(hào)量sem_wait(&semaphore); // P 操作,等待信號(hào)量// 進(jìn)入臨界區(qū),訪問(wèn)共享資源printf("Thread %d is executing\n", id);shared_resource += 10;printf("Thread %d updated shared resource to %d\n", id, shared_resource);// 釋放信號(hào)量sem_post(&semaphore); // V 操作,釋放信號(hào)量return NULL;
}int main() {pthread_t threads[3];int ids[3] = {1, 2, 3};// 初始化信號(hào)量,初始值為1,表示同一時(shí)刻只能有一個(gè)線程進(jìn)入臨界區(qū)sem_init(&semaphore, 0, 1);// 創(chuàng)建三個(gè)線程for (int i = 0; i < 3; i++) {pthread_create(&threads[i], NULL, thread_function, &ids[i]);}// 等待所有線程完成for (int i = 0; i < 3; i++) {pthread_join(threads[i], NULL);}// 銷毀信號(hào)量sem_destroy(&semaphore);return 0;
}
運(yùn)行結(jié)果(其中一個(gè)):
Thread 1 is executing
Thread 1 updated shared resource to 10
Thread 2 is executing
Thread 2 updated shared resource to 20
Thread 3 is executing
Thread 3 updated shared resource to 30
解釋:
-
sem_init(&semaphore, 0, 1);
初始化信號(hào)量。0
表示信號(hào)量只在當(dāng)前進(jìn)程內(nèi)有效,1
是信號(hào)量的初始值,表示同時(shí)只能有一個(gè)線程訪問(wèn)資源。 -
每個(gè)線程在進(jìn)入臨界區(qū)前會(huì)執(zhí)行
sem_wait(&semaphore);
進(jìn)行 P 操作(Wait/Down)。如果信號(hào)量的值為正,則將其減1并進(jìn)入臨界區(qū)。如果信號(hào)量的值為0,線程將被阻塞,直到信號(hào)量的值大于0。 -
在臨界區(qū)內(nèi),線程可以安全地訪問(wèn)和修改共享資源。
-
完成操作后,使用
sem_post(&semaphore);
進(jìn)行 V 操作(Signal/Up),將信號(hào)量的值加1,允許其他被阻塞的線程繼續(xù)執(zhí)行。 -
pthread_join(threads[i], NULL);
用于等待所有線程完成執(zhí)行,確保主線程在它們執(zhí)行完畢后才繼續(xù)。 -
sem_destroy(&semaphore);
在程序結(jié)束時(shí)銷毀信號(hào)量,釋放資源。
8.5 條件變量 (Condition Variable)
條件變量 (Condition Variable) 是一種允許線程等待某個(gè)條件發(fā)生并在條件滿足時(shí)進(jìn)行通知的同步機(jī)制。條件變量通常與互斥鎖一起使用,以確保在檢查或修改共享數(shù)據(jù)時(shí)線程不會(huì)進(jìn)入競(jìng)爭(zhēng)條件。
- 等待 (Wait):線程在條件變量上等待,等待期間會(huì)釋放與之關(guān)聯(lián)的互斥鎖,直到被喚醒。當(dāng)被喚醒時(shí),線程重新獲取互斥鎖并繼續(xù)執(zhí)行。
- 通知 (Signal/Broadcast):喚醒等待條件變量的一個(gè)或所有線程。
Signal
通知一個(gè)線程,Broadcast
通知所有等待線程。
#include <stdio.h>
#include <pthread.h>#define BUFFER_SIZE 5// 共享資源
int buffer[BUFFER_SIZE];
int count = 0;// 互斥鎖和條件變量
pthread_mutex_t mutex;
pthread_cond_t cond_producer;
pthread_cond_t cond_consumer;void *producer(void *arg) {for (int i = 0; i < 10; i++) {pthread_mutex_lock(&mutex);// 如果緩沖區(qū)滿,等待消費(fèi)者消費(fèi)while (count == BUFFER_SIZE) {pthread_cond_wait(&cond_producer, &mutex);}// 生產(chǎn)數(shù)據(jù)buffer[count] = i;printf("Produced: %d\n", buffer[count]);count++;// 通知消費(fèi)者pthread_cond_signal(&cond_consumer);pthread_mutex_unlock(&mutex);}return NULL;
}void *consumer(void *arg) {for (int i = 0; i < 10; i++) {pthread_mutex_lock(&mutex);// 如果緩沖區(qū)空,等待生產(chǎn)者生產(chǎn)while (count == 0) {pthread_cond_wait(&cond_consumer, &mutex);}// 消費(fèi)數(shù)據(jù)printf("Consumed: %d\n", buffer[0]);// 將緩沖區(qū)中的數(shù)據(jù)前移,保持順序for (int j = 0; j < count - 1; j++) {buffer[j] = buffer[j + 1];}count--;// 通知生產(chǎn)者pthread_cond_signal(&cond_producer);pthread_mutex_unlock(&mutex);}return NULL;
}int main() {pthread_t prod_thread, cons_thread;// 初始化互斥鎖和條件變量pthread_mutex_init(&mutex, NULL);pthread_cond_init(&cond_producer, NULL);pthread_cond_init(&cond_consumer, NULL);// 創(chuàng)建生產(chǎn)者和消費(fèi)者線程pthread_create(&prod_thread, NULL, producer, NULL);pthread_create(&cons_thread, NULL, consumer, NULL);// 等待線程完成pthread_join(prod_thread, NULL);pthread_join(cons_thread, NULL);// 銷毀互斥鎖和條件變量pthread_mutex_destroy(&mutex);pthread_cond_destroy(&cond_producer);pthread_cond_destroy(&cond_consumer);return 0;
}
運(yùn)行結(jié)果(其中一個(gè)):
Produced: 0
Produced: 1
Produced: 2
Produced: 3
Produced: 4
Consumed: 0
Consumed: 1
Consumed: 2
Consumed: 3
Consumed: 4
Produced: 5
Produced: 6
Produced: 7
Produced: 8
Produced: 9
Consumed: 5
Consumed: 6
Consumed: 7
Consumed: 8
Consumed: 9
解釋:
pthread_mutex_init(&mutex, NULL);
初始化互斥鎖。pthread_cond_init(&cond_producer, NULL);
和pthread_cond_init(&cond_consumer, NULL);
初始化生產(chǎn)者和消費(fèi)者條件變量。
生產(chǎn)者線程操作:
- 生產(chǎn)者線程在向緩沖區(qū)添加數(shù)據(jù)之前,會(huì)先獲取互斥鎖
pthread_mutex_lock(&mutex);
。 - 如果緩沖區(qū)已滿,生產(chǎn)者線程會(huì)等待
pthread_cond_wait(&cond_producer, &mutex);
,并釋放互斥鎖,直到消費(fèi)者線程喚醒它。 - 添加數(shù)據(jù)后,生產(chǎn)者線程會(huì)通知消費(fèi)者線程
pthread_cond_signal(&cond_consumer);
,并釋放互斥鎖pthread_mutex_unlock(&mutex);
。
消費(fèi)者線程操作:
- 消費(fèi)者線程在消費(fèi)數(shù)據(jù)之前,同樣會(huì)獲取互斥鎖。
- 如果緩沖區(qū)為空,消費(fèi)者線程會(huì)等待
pthread_cond_wait(&cond_consumer, &mutex);
,并釋放互斥鎖,直到生產(chǎn)者線程喚醒它。 - 消費(fèi)數(shù)據(jù)后,消費(fèi)者線程會(huì)通知生產(chǎn)者線程
pthread_cond_signal(&cond_producer);
,并釋放互斥鎖。
同步操作:
-
pthread_join(prod_thread, NULL);
和pthread_join(cons_thread, NULL);
用于等待兩個(gè)線程完成執(zhí)行。 -
在程序結(jié)束時(shí),銷毀互斥鎖和條件變量以釋放資源。
8.6 屏障 (Barrier)
屏障 (Barrier) 是一種同步機(jī)制,它使得一組線程必須全部到達(dá)某個(gè)同步點(diǎn)后,才能繼續(xù)執(zhí)行。這通常用于并行計(jì)算中,當(dāng)各個(gè)線程都完成了各自的一部分任務(wù)后,再繼續(xù)執(zhí)行下一步操作。
- 等待 (Wait):線程在屏障處等待,直到所有其他線程都到達(dá)屏障。只有當(dāng)所有線程都到達(dá)屏障時(shí),它們才能繼續(xù)執(zhí)行。
#include <stdio.h>
#include <pthread.h>// 定義屏障
pthread_barrier_t barrier;void *thread_function(void *arg) {int id = *(int *)arg;// 每個(gè)線程完成各自的計(jì)算任務(wù)printf("Thread %d is performing its task before the barrier\n", id);// 等待其他線程到達(dá)屏障pthread_barrier_wait(&barrier);// 所有線程都到達(dá)屏障后繼續(xù)執(zhí)行printf("Thread %d has crossed the barrier and is continuing\n", id);return NULL;
}int main() {pthread_t threads[4];int ids[4] = {1, 2, 3, 4};// 初始化屏障,計(jì)數(shù)為4(表示4個(gè)線程需要同步)pthread_barrier_init(&barrier, NULL, 4);// 創(chuàng)建4個(gè)線程for (int i = 0; i < 4; i++) {pthread_create(&threads[i], NULL, thread_function, &ids[i]);}// 等待所有線程完成for (int i = 0; i < 4; i++) {pthread_join(threads[i], NULL);}// 銷毀屏障pthread_barrier_destroy(&barrier);return 0;
}
運(yùn)行結(jié)果(其中一個(gè)):
Thread 1 is performing its task before the barrier
Thread 3 is performing its task before the barrier
Thread 2 is performing its task before the barrier
Thread 4 is performing its task before the barrier
Thread 4 has crossed the barrier and is continuing
Thread 1 has crossed the barrier and is continuing
Thread 2 has crossed the barrier and is continuing
Thread 3 has crossed the barrier and is continuing
解釋:
-
thread_barrier_init(&barrier, NULL, 4);
初始化屏障,并設(shè)置需要同步的線程數(shù)量為4。即所有4個(gè)線程都需要到達(dá)屏障后,才能繼續(xù)執(zhí)行。 -
每個(gè)線程在執(zhí)行完各自的任務(wù)后,調(diào)用
pthread_barrier_wait(&barrier);
進(jìn)入屏障等待狀態(tài)。 -
當(dāng)所有線程都到達(dá)屏障后,屏障會(huì)解除,所有線程將繼續(xù)執(zhí)行后續(xù)的任務(wù)。
-
pthread_join(threads[i], NULL);
用于等待所有線程完成執(zhí)行,確保主線程在它們執(zhí)行完畢后才繼續(xù)。 -
pthread_barrier_destroy(&barrier);
在程序結(jié)束時(shí)銷毀屏障,釋放資源。
9. GDB進(jìn)程調(diào)試
gdb
是 GNU Debugger,用于調(diào)試程序,包括設(shè)置斷點(diǎn)、單步執(zhí)行、查看變量、分析崩潰等。
# 編譯程序時(shí)添加調(diào)試信息
gcc -g my_program.c -o my_program #記得加-g# 啟動(dòng) gdb 調(diào)試
gdb ./my_program# 常用 gdb 命令
break main # 設(shè)置斷點(diǎn)在 main 函數(shù)
run # 開(kāi)始運(yùn)行程序
next # 單步執(zhí)行
print var # 打印變量值
bt # 打印調(diào)用棧
簡(jiǎn)單案例(test.c):
#include <stdio.h>void fun() {int array[5];for (int i = 0; i <= 5; i++) { // 錯(cuò)誤:i 應(yīng)該小于 5array[i] = i * 10;printf("array[%d] = %d\n", i, array[i]);}
}int main() {fun();return 0;
}
運(yùn)行結(jié)果:
liber@liber-VMware-Virtual-Platform:/home/c$ gcc -g test.c -o test
liber@liber-VMware-Virtual-Platform:/home/c$ gdb ./test
…
Type “apropos word” to search for commands related to “word”…
Reading symbols from ./test…
(gdb) break fun #設(shè)置斷點(diǎn)
Breakpoint 1 at 0x1175: file test.c, line 3.
(gdb) run #開(kāi)始運(yùn)行程序
Starting program: /home/c/test…
Breakpoint 1, fun () at test.c:3
3 void fun() {
(gdb) next
5 for (int i = 0; i <= 5; i++) { // 錯(cuò)誤:i 應(yīng)該小于 5
(gdb) next
6 array[i] = i * 10;
(gdb) print i #打印變量i
…
(gdb) next
7 printf(“array[%d] = %d\n”, i, array[i]);
(gdb) next
array[5] = 50 #數(shù)組越界
5 for (int i = 0; i <= 5; i++) { // 錯(cuò)誤:i 應(yīng)該小于 5
(gdb) next #下一步直接返回
9 }
(gdb) next
main () at test.c:13
13 return 0; #程序結(jié)束
(gdb)
說(shuō)明:加粗字段是指令。
?