操作系統八股文整理(一)

操作系統八股文整理

    • 一、進程和線程的區別
    • 二、進程與線程的切換過程
      • 一、進程切換
        • 進程切換的步驟:
      • 二、線程切換
        • 線程切換的步驟:
      • 三、進程切換與線程切換的對比
      • 四、上下文切換的優化
    • 三、系統調用
      • 一、系統調用的觸發
      • 二、從用戶空間切換到內核空間
      • 三、執行內核函數
      • 四、從內核空間返回用戶空間
      • 五、系統調用的完整流程示例
      • 六、系統調用的性能開銷
    • 四、進程間通訊方式
      • 管道demo
      • 命名管道demo
        • 1. 創建命名管道
        • 2. 寫入者進程
        • 3. 讀取者進程
        • 編譯與運行
        • 輸出示例
        • 注意事項
      • 信號量
        • 1. 信號量的分類
        • 2. 信號量的實現
        • 2.1 系統V信號量
          • 示例代碼:
        • 2.2 POSIX信號量
          • 示例代碼:
        • 2.3 Boost信號量
          • 示例代碼:
        • 2.4 C++20 `std::semaphore`
          • 示例代碼:
        • 3. 信號量的使用場景
        • 4. 注意事項
        • 總結
      • 消息隊列demo
        • 1. 系統V消息隊列
        • 示例代碼:
        • 2. POSIX消息隊列
        • 示例代碼:
        • 3. Boost.Interprocess消息隊列
        • 示例代碼:
        • 4. 自定義線程安全消息隊列
        • 示例代碼:
        • 總結
    • 五、進程的調度
      • 進程調度的算法
        • (1)先來先服務(FCFS,First-Come-First-Served)
        • (2)短作業優先(SJF,Shortest Job First)
        • (3)優先級調度(Priority Scheduling)
        • (4)時間片輪轉(RR,Round Robin)
        • (5)多級反饋隊列(Multilevel Feedback Queue)
    • 六、線程同步的方式

一、進程和線程的區別

畫板

二、進程與線程的切換過程

進程和線程的切換是操作系統中非常重要的概念,它們涉及到系統資源的分配、調度以及上下文切換等操作。以下是進程和線程切換過程的詳細解釋:


一、進程切換

進程切換是指操作系統將 CPU 從一個進程切換到另一個進程的過程。它通常發生在以下幾種情況:

  1. 時間片用完:當前進程的時間片用完,操作系統需要選擇下一個進程運行。
  2. 進程阻塞:當前進程因等待資源(如 I/O)而阻塞,操作系統需要切換到其他就緒的進程。
  3. 更高優先級進程到來:有更高優先級的進程就緒,操作系統需要切換到該進程。
進程切換的步驟:
  1. 保存當前進程的上下文
    • 保存程序計數器(PC):記錄當前進程執行到的位置。
    • 保存寄存器狀態:包括通用寄存器、狀態寄存器等,這些寄存器保存了進程執行時的臨時數據和狀態。
    • 保存進程狀態信息:如進程的優先級、資源使用情況等。
  2. 更新進程控制塊(PCB)
    • 將當前進程的狀態從“運行態”改為“就緒態”或“阻塞態”,并更新其在進程隊列中的位置。
  3. 選擇下一個進程
    • 操作系統根據調度算法(如輪轉調度、優先級調度等)選擇下一個要運行的進程。
    • 如果沒有就緒的進程,可能會選擇進入空閑進程或等待外部事件。
  4. 恢復新進程的上下文
    • 恢復程序計數器(PC):將新進程上次運行時的位置加載到 PC 中。
    • 恢復寄存器狀態:將新進程的寄存器狀態加載到 CPU 的寄存器中。
    • 更新新進程的狀態:將新進程的狀態從“就緒態”改為“運行態”。
  5. 開始新進程運行
    • 將 CPU 的控制權交給新進程,新進程從上次保存的位置繼續執行。

二、線程切換

線程切換是指操作系統將 CPU 從一個線程切換到另一個線程的過程。線程切換的開銷通常比進程切換小,因為線程共享進程的資源,切換時不需要切換進程的上下文。

線程切換的步驟:
  1. 保存當前線程的上下文
    • 保存線程的程序計數器(PC):記錄當前線程執行的位置。
    • 保存線程的寄存器狀態:線程有自己的線程控制塊(TCB),保存線程的局部變量、棧指針等信息。
    • 保存線程狀態信息:如線程的優先級、阻塞原因等。
  2. 更新線程控制塊(TCB)
    • 將當前線程的狀態從“運行態”改為“就緒態”或“阻塞態”,并更新其在線程隊列中的位置。
  3. 選擇下一個線程
    • 操作系統根據線程調度算法(如時間片輪轉、優先級調度等)選擇下一個要運行的線程。
    • 如果沒有就緒的線程,可能會選擇進入空閑線程或等待外部事件。
  4. 恢復新線程的上下文
    • 恢復線程的程序計數器(PC):將新線程上次運行時的位置加載到 PC 中。
    • 恢復線程的寄存器狀態:將新線程的寄存器狀態加載到 CPU 的寄存器中。
    • 更新新線程的狀態:將新線程的狀態從“就緒態”改為“運行態”。
  5. 開始新線程運行
    • 將 CPU 的控制權交給新線程,新線程從上次保存的位置繼續執行。

三、進程切換與線程切換的對比

  1. 上下文切換的開銷
    • 進程切換:開銷較大,需要切換進程的代碼段、數據段、寄存器狀態、文件描述符等。
    • 線程切換:開銷較小,只需要切換線程的寄存器狀態和棧指針等局部信息。
  2. 資源共享
    • 進程切換:進程之間是獨立的,切換時需要切換資源。
    • 線程切換:線程共享進程的資源,切換時不需要切換資源。
  3. 調度單位
    • 進程切換:以進程為單位進行調度。
    • 線程切換:以線程為單位進行調度,線程是 CPU 調度的最小單位。
  4. 適用場景
    • 進程切換:適用于需要獨立資源的場景,如運行不同的程序。
    • 線程切換:適用于需要高并發的場景,如多線程服務器。

四、上下文切換的優化

上下文切換雖然可以提高系統的并發性,但頻繁的上下文切換會增加系統的開銷,降低系統性能。因此,操作系統和應用程序需要采取一些優化措施:

  1. 減少上下文切換的次數
    • 增加時間片的長度,減少進程或線程切換的頻率。
    • 使用高效的調度算法,減少不必要的上下文切換。
  2. 優化上下文切換的開銷
    • 減少保存和恢復的寄存器數量。
    • 使用緩存技術,減少對內存的訪問。
  3. 減少線程的阻塞時間
    • 提高 I/O 操作的效率,減少線程因 I/O 阻塞而切換的次數。
    • 使用非阻塞 I/O 或異步 I/O,減少線程的阻塞時間。

總之,進程切換和線程切換是操作系統中非常重要的機制,它們的合理使用可以提高系統的并發性和性能。

三、系統調用

系統調用是用戶程序與操作系統內核之間進行交互的橋梁。它允許用戶程序請求操作系統提供的服務,例如文件操作、進程控制、通信等。系統調用的整個流程涉及用戶空間和內核空間的切換,以及參數傳遞和結果返回。以下是系統調用的詳細流程:


一、系統調用的觸發

用戶程序需要操作系統服務時,會通過系統調用來請求。這通常通過以下方式觸發:

  1. 使用特定的指令
    • 在 x86 架構中,通常使用 int 0x80(中斷指令)或 syscall 指令來觸發系統調用。
    • 在 ARM 架構中,使用 svc 指令。
    • 這些指令會中斷用戶程序的執行,將控制權轉移到操作系統內核。
  2. 設置系統調用號和參數
    • 在觸發系統調用之前,用戶程序需要將系統調用號(標識要調用的服務)和參數(如文件名、緩沖區地址等)放置在特定的寄存器或棧中。
    • 例如,在 x86 架構中,系統調用號通常放在 eax 寄存器中,參數放在 ebxecxedx 等寄存器中。

二、從用戶空間切換到內核空間

當用戶程序觸發系統調用時,CPU 會從用戶態切換到內核態,同時操作系統會進行以下操作:

  1. 保存用戶態上下文
    • 操作系統會保存用戶程序的上下文,包括寄存器狀態(如程序計數器、棧指針等)和 CPU 的當前狀態(用戶態或內核態)。
    • 這些信息通常保存在內核為每個進程分配的內核棧中。
  2. 切換到內核態
    • CPU 的特權級別從用戶態(較低特權級別)切換到內核態(較高特權級別)。
    • 內核態允許訪問系統的全部資源,包括硬件設備和內核內存。
  3. 查找系統調用表
    • 操作系統根據系統調用號在系統調用表中查找對應的內核函數。
    • 系統調用表是一個數組,每個系統調用號對應一個內核函數指針。

三、執行內核函數

找到對應的內核函數后,操作系統會執行以下操作:

  1. 參數傳遞
    • 內核函數會從寄存器或棧中獲取用戶程序傳遞的參數。
    • 如果參數是用戶空間的地址(如文件名字符串),內核需要進行地址檢查,確保這些地址是合法的。
  2. 執行內核函數
    • 內核函數根據用戶請求執行相應的操作,例如:
      • 打開文件時,內核會查找文件系統,分配文件描述符。
      • 寫文件時,內核會將數據寫入磁盤緩沖區。
      • 創建進程時,內核會分配內存和資源,創建新的進程控制塊(PCB)。
  3. 處理錯誤和異常
    • 如果操作失敗(如文件不存在、權限不足),內核會設置錯誤碼(如 errno)。

四、從內核空間返回用戶空間

內核函數執行完畢后,操作系統需要將控制權返回給用戶程序:

  1. 保存內核態上下文
    • 操作系統保存內核態的上下文信息,包括內核函數的返回值(通常放在某個寄存器中)。
  2. 恢復用戶態上下文
    • 操作系統從內核棧中恢復用戶程序的上下文,包括寄存器狀態和程序計數器。
    • 這樣用戶程序可以從上次中斷的地方繼續執行。
  3. 切換回用戶態
    • CPU 的特權級別從內核態切換回用戶態。
  4. 返回結果
    • 內核將系統調用的結果(如文件描述符、返回值等)傳遞給用戶程序。
    • 如果發生錯誤,用戶程序可以通過錯誤碼(如 errno)獲取錯誤信息。

五、系統調用的完整流程示例

假設用戶程序要調用 write() 系統調用來寫文件,其流程如下:

  1. 用戶程序準備參數
    • 將系統調用號(如 1 表示 write)放入 eax 寄存器。
    • 將文件描述符、緩沖區地址和寫入字節數分別放入 ebxecxedx 寄存器。
  2. 觸發系統調用
    • 用戶程序執行 int 0x80 指令,觸發中斷。
  3. 切換到內核態
    • 操作系統保存用戶態上下文,切換到內核態。
    • 根據系統調用號 1,查找系統調用表,找到 write() 的內核函數。
  4. 執行內核函數
    • 內核函數從寄存器中獲取參數(文件描述符、緩沖區地址等)。
    • 檢查文件描述符是否有效,緩沖區地址是否合法。
    • 將數據從用戶空間的緩沖區復制到內核空間的緩沖區。
    • 寫入數據到磁盤緩沖區。
    • 如果成功,返回寫入的字節數;如果失敗,設置錯誤碼。
  5. 返回用戶態
    • 操作系統保存內核態上下文,恢復用戶態上下文。
    • 切換回用戶態,將返回值放入用戶程序的寄存器中。
  6. 用戶程序繼續執行
    • 用戶程序根據返回值判斷寫操作是否成功,并繼續執行后續代碼。

六、系統調用的性能開銷

系統調用涉及用戶空間和內核空間的切換,因此會產生一定的性能開銷:

  1. 上下文切換開銷
    • 保存和恢復寄存器狀態、切換特權級別等操作會消耗時間。
  2. 參數傳遞和檢查開銷
    • 內核需要驗證用戶空間的地址是否合法,這可能涉及額外的內存訪問。
  3. 內核函數執行開銷
    • 內核函數的執行時間取決于系統調用的復雜性(如 I/O 操作可能涉及磁盤 I/O 延遲)。
  4. 系統調用的優化
    • 現代操作系統通過減少上下文切換的次數、使用更快的切換指令(如 syscall)等方式來優化系統調用的性能。
    • 一些系統調用(如 getpid())可以通過輕量級的機制(如 vsyscallvdso)直接在用戶空間執行,避免切換到內核態。

總之,系統調用是用戶程序與操作系統交互的重要機制,其流程涉及用戶空間和內核空間的切換、參數傳遞、內核函數執行以及結果返回。雖然系統調用會產生一定的開銷,但它是實現操作系統功能的關鍵機制。

四、進程間通訊方式

畫板

管道demo

#include <iostream>
#include <unistd.h>
#include <cstring>
#include <cstdlib>int main() {int pipefd[2];  // 用于存儲管道的文件描述符pid_t pid;// 創建管道if (pipe(pipefd) == -1) {exit(EXIT_FAILURE);}// 創建子進程pid = fork();if (pid == -1) {exit(EXIT_FAILURE);}if (pid > 0) {  // 父進程// 關閉管道的寫端close(pipefd[1]);// 從管道的讀端讀取數據char buffer[128];ssize_t bytes_read = read(pipefd[0], buffer, sizeof(buffer) - 1);if (bytes_read > 0) {buffer[bytes_read] = '\0';  // 確保字符串以 null 結尾std::cout << "Parent received: " << buffer << std::endl;} else {std::cerr << "Failed to read from pipe" << std::endl;}// 關閉管道的讀端close(pipefd[0]);} else {  // 子進程// 關閉管道的讀端close(pipefd[0]);// 向管道的寫端寫入數據const char *msg = "Hello from child process!";ssize_t bytes_written = write(pipefd[1], msg, strlen(msg));if (bytes_written < 0) {std::cerr << "Failed to write to pipe" << std::endl;}// 關閉管道的寫端close(pipefd[1]);}return 0;
}

命名管道demo

1. 創建命名管道

我們首先需要創建一個命名管道。這可以通過命令行工具 mkfifo 或在程序中使用 mkfifo() 系統調用來完成。

創建管道的命令行方式:

mkfifo /tmp/myfifo

創建管道的程序方式:

#include <sys/types.h>
#include <sys/stat.h>
#include <iostream>int main() {// 創建命名管道if (mkfifo("/tmp/myfifo", 0666) == -1) {perror("mkfifo");return -1;}std::cout << "Named pipe created at /tmp/myfifo" << std::endl;return 0;
}
2. 寫入者進程

寫入者進程向命名管道中寫入數據。

writer.cpp:

#include <iostream>
#include <fcntl.h>
#include <unistd.h>
#include <cstring>int main() {const char *fifo_path = "/tmp/myfifo";const char *message = "Hello from writer process!\n";// 打開管道文件(寫模式)int fd = open(fifo_path, O_WRONLY);if (fd == -1) {perror("open");return -1;}// 向管道寫入數據if (write(fd, message, strlen(message)) == -1) {perror("write");close(fd);return -1;}std::cout << "Message sent: " << message << std::endl;// 關閉管道文件描述符close(fd);return 0;
}
3. 讀取者進程

讀取者進程從命名管道中讀取數據。

reader.cpp

#include <iostream>
#include <fcntl.h>
#include <unistd.h>
#include <cstring>int main() {const char *fifo_path = "/tmp/myfifo";char buffer[128];// 打開管道文件(讀模式)int fd = open(fifo_path, O_RDONLY);if (fd == -1) {perror("open");return -1;}// 從管道讀取數據ssize_t bytes_read = read(fd, buffer, sizeof(buffer) - 1);if (bytes_read == -1) {perror("read");close(fd);return -1;}buffer[bytes_read] = '\0';  // 確保字符串以 null 結尾std::cout << "Message received: " << buffer << std::endl;// 關閉管道文件描述符close(fd);return 0;
}
編譯與運行
  1. 創建管道
    • 如果沒有提前創建管道,運行 writer 時會阻塞,直到管道被創建。
    • 可以手動創建管道:bash復制
mkfifo /tmp/myfifo
- <font style="color:rgb(6, 6, 7);">或者運行創建管道的程序。</font>
  1. 編譯代碼
    • writer.cppreader.cpp 分別保存為文件。
    • 使用以下命令編譯:bash復制
g++ -o writer writer.cpp
g++ -o reader reader.cpp
  1. 運行程序
    • 在一個終端運行讀取者程序:
./reader
- <font style="color:rgb(6, 6, 7);">在另一個終端運行寫入者程序:</font>
./writer
輸出示例
  • reader終端Message received: Hello from writer process!
  • writer終端Message sent: Hello from writer process!
注意事項
  1. 阻塞特性
    • 默認情況下,讀取者會阻塞,直到有數據可讀。
    • 寫入者會阻塞,直到有讀取者打開管道。
    • 如果不希望阻塞,可以在 open() 時使用 O_NONBLOCK 標志。
  2. 管道文件路徑
    • 確保管道文件路徑一致(這里是 /tmp/myfifo)。
    • 如果路徑不存在,寫入者會阻塞,直到管道被創建。
  3. 清理
    • 使用完管道后,可以手動刪除管道文件:bash復制
rm /tmp/myfifo
  1. 跨進程通信

命名管道允許不相關的進程之間通信,因此讀取者和寫入者可以是完全獨立的程序。

通過這個示例,你可以看到命名管道如何實現進程間通信,非常適合需要跨進程傳遞數據的場景。

信號量

信號量(Semaphore)是一種用于進程間或線程間同步的機制,用于控制對共享資源的訪問。它通過維護一個計數器來實現同步,當計數器大于零時,表示資源可用;當計數器為零時,表示資源已被占用。信號量通常用于解決互斥(Mutex)和同步(Sync)問題。

以下是信號量的基本操作:

  1. P操作(Wait/Down/Decrease):將信號量的值減1。如果減1后信號量的值小于0,則調用進程或線程阻塞,等待信號量的值變為非負。
  2. V操作(Signal/Up/Increase):將信號量的值加1。如果加1后信號量的值大于0,則喚醒一個等待該信號量的進程或線程。
1. 信號量的分類

信號量主要分為兩種:

  • 二進制信號量(Binary Semaphore):值只能為0或1,用于互斥訪問。
  • 計數信號量(Counting Semaphore):值可以是任意非負整數,用于控制多個資源的訪問。
2. 信號量的實現

在C++中,信號量可以通過多種方式實現,包括系統V IPC信號量、POSIX信號量、Boost庫以及C++20標準中的std::semaphore

2.1 系統V信號量

系統V信號量是基于IPC機制的信號量實現,使用semgetsemop等函數。

示例代碼:

cpp復制

#include <iostream>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>// P操作
void P(int semid) {struct sembuf sb;sb.sem_num = 0;sb.sem_op = -1; // 等待信號量sb.sem_flg = 0;semop(semid, &sb, 1);
}// V操作
void V(int semid) {struct sembuf sb;sb.sem_num = 0;sb.sem_op = 1; // 釋放信號量sb.sem_flg = 0;semop(semid, &sb, 1);
}int main() {key_t key = ftok("semfile", 65); // 創建IPC keyint semid = semget(key, 1, 0666 | IPC_CREAT); // 創建信號量集// 初始化信號量union semun {int val;struct semid_ds* buf;unsigned short* array;} arg;arg.val = 1; // 初始值為1semctl(semid, 0, SETVAL, arg);// 模擬生產者和消費者if (fork() == 0) {// 子進程:消費者sleep(1); // 等待生產者P(semid); // 等待信號量std::cout << "Consumer: Consumed resource" << std::endl;V(semid); // 釋放信號量} else {// 父進程:生產者P(semid); // 等待信號量std::cout << "Producer: Produced resource" << std::endl;V(semid); // 釋放信號量}wait(nullptr); // 等待子進程結束semctl(semid, 0, IPC_RMID, arg); // 刪除信號量集return 0;
}
2.2 POSIX信號量

POSIX信號量是另一種實現方式,使用sem_opensem_waitsem_post等函數。

示例代碼:

cpp復制

#include <iostream>
#include <semaphore.h>
#include <thread>
#include <unistd.h>int main() {sem_t sem;sem_init(&sem, 0, 1); // 初始化信號量,初始值為1// 生產者線程std::thread producer([&sem]() {sem_wait(&sem); // 等待信號量std::cout << "Producer: Produced resource" << std::endl;sem_post(&sem); // 釋放信號量});// 消費者線程std::thread consumer([&sem]() {sleep(1); // 等待生產者sem_wait(&sem); // 等待信號量std::cout << "Consumer: Consumed resource" << std::endl;sem_post(&sem); // 釋放信號量});producer.join();consumer.join();sem_destroy(&sem); // 銷毀信號量return 0;
}
2.3 Boost信號量

Boost庫提供了跨平臺的信號量實現,使用boost::interprocess::named_semaphore

示例代碼:

cpp復制

#include <boost/interprocess/sync/named_semaphore.hpp>
#include <iostream>
#include <thread>
#include <unistd.h>int main() {using namespace boost::interprocess;named_semaphore sem(open_or_create, "test_semaphore", 1); // 創建或打開信號量// 生產者線程std::thread producer([&sem]() {sem.wait();std::cout << "Producer: Produced resource" << std::endl;sem.post();});// 消費者線程std::thread consumer([&sem]() {sleep(1); // 等待生產者sem.wait();std::cout << "Consumer: Consumed resource" << std::endl;sem.post();});producer.join();consumer.join();named_semaphore::remove("test_semaphore"); // 刪除信號量return 0;
}
2.4 C++20 std::semaphore

C++20標準引入了std::semaphore,使得信號量的使用更加簡潔。

示例代碼:

cpp復制

#include <iostream>
#include <semaphore>
#include <thread>
#include <unistd.h>int main() {std::counting_semaphore<1> sem(1); // 創建信號量,初始值為1// 生產者線程std::thread producer([&sem]() {sem.acquire(); // 等待信號量std::cout << "Producer: Produced resource" << std::endl;sem.release(); // 釋放信號量});// 消費者線程std::thread consumer([&sem]() {sleep(1); // 等待生產者sem.acquire(); // 等待信號量std::cout << "Consumer: Consumed resource" << std::endl;sem.release(); // 釋放信號量});producer.join();consumer.join();return 0;
}
3. 信號量的使用場景

信號量主要用于以下場景:

  1. 互斥(Mutex):確保多個線程或進程不會同時訪問共享資源。
  2. 同步(Sync):協調線程或進程的執行順序,例如生產者-消費者問題。
4. 注意事項
  • 死鎖:如果信號量的使用不當,可能會導致死鎖。例如,多個線程或進程同時等待同一個信號量。
  • 資源泄漏:在使用系統V或POSIX信號量時,需要確保信號量在程序結束時被正確刪除,否則可能會導致資源泄漏。
  • 性能:信號量的使用會引入上下文切換的開銷,因此需要合理設計同步機制。
總結

信號量是一種強大的同步機制,適用于多種并發場景。根據具體需求,可以選擇系統V信號量、POSIX信號量、Boost信號量或C++20標準中的std::semaphore

消息隊列demo

在C++中,消息隊列是一種常見的進程間通信(IPC)機制,允許不同進程之間以異步方式交換消息。以下是關于C++消息隊列的實現和使用方法的總結:

1. 系統V消息隊列

系統V消息隊列是一種傳統的IPC機制,基于msggetmsgsndmsgrcv等函數。

示例代碼:

cpp復制

#include <sys/ipc.h>
#include <sys/msg.h>
#include <iostream>
#include <cstring>struct message {long msg_type;char msg_text[100];
};int main() {key_t key = ftok("progfile", 65); // 創建IPC keyint msgid = msgget(key, 0666 | IPC_CREAT); // 創建消息隊列message msg;msg.msg_type = 1;strcpy(msg.msg_text, "Hello World!");msgsnd(msgid, &msg, sizeof(msg), 0); // 發送消息std::cout << "Message sent: " << msg.msg_text << std::endl;// 接收消息msgrcv(msgid, &msg, sizeof(msg), 1, 0);std::cout << "Message received: " << msg.msg_text << std::endl;msgctl(msgid, IPC_RMID, nullptr); // 刪除消息隊列return 0;
}

此代碼展示了如何創建消息隊列、發送和接收消息。

2. POSIX消息隊列

POSIX消息隊列提供了另一種實現方式,使用mq_openmq_sendmq_receive等函數。

示例代碼:

cpp復制

#include <mqueue.h>
#include <iostream>
#include <cstring>int main() {mqd_t mq;struct mq_attr attr;attr.mq_flags = 0;attr.mq_maxmsg = 10;attr.mq_msgsize = 1024;attr.mq_curmsgs = 0;mq = mq_open("/my_mq", O_CREAT | O_WRONLY, 0644, &attr); // 創建消息隊列std::string msg = "Hello POSIX!";mq_send(mq, msg.c_str(), msg.size(), 0); // 發送消息std::cout << "Message sent: " << msg << std::endl;mq_close(mq);mq_unlink("/my_mq"); // 刪除消息隊列return 0;
}
3. Boost.Interprocess消息隊列

Boost庫提供了跨平臺的消息隊列實現,基于共享內存。

示例代碼:

cpp復制

#include <boost/interprocess/ipc/message_queue.hpp>
#include <iostream>int main() {using namespace boost::interprocess;// 發送端message_queue mq(open_or_create, "test_mq", 100, sizeof(int));int msg = 42;mq.send(&msg, sizeof(msg), 0);std::cout << "Message sent: " << msg << std::endl;// 接收端int rcv_msg;size_t msg_size;unsigned priority;mq.receive(&rcv_msg, sizeof(rcv_msg), msg_size, priority);std::cout << "Message received: " << rcv_msg << std::endl;message_queue::remove("test_mq"); // 刪除消息隊列return 0;
}
4. 自定義線程安全消息隊列

如果需要在多線程環境中使用消息隊列,可以結合std::queuestd::mutexstd::condition_variable實現線程安全的消息隊列。

示例代碼:

cpp復制

#include <queue>
#include <mutex>
#include <condition_variable>
#include <thread>
#include <iostream>struct Message {int messageType;std::string payload;
};class MessageQueue {
public:void enqueue(const Message& message) {std::unique_lock<std::mutex> lock(mutex_);queue_.push(message);condition_.notify_one();}Message dequeue() {std::unique_lock<std::mutex> lock(mutex_);condition_.wait(lock, [this] { return !queue_.empty(); });Message message = queue_.front();queue_.pop();return message;}private:std::queue<Message> queue_;std::mutex mutex_;std::condition_variable condition_;
};void producer(MessageQueue& mq) {Message msg{1, "Hello"};mq.enqueue(msg);
}void consumer(MessageQueue& mq) {Message msg = mq.dequeue();std::cout << "Received: " << msg.payload << std::endl;
}int main() {MessageQueue mq;std::thread prod(producer, std::ref(mq));std::thread cons(consumer, std::ref(mq));prod.join();cons.join();return 0;
}
總結
  • 系統V和POSIX消息隊列適用于進程間通信,但需要處理IPC資源的創建和銷毀。
  • Boost.Interprocess提供了跨平臺的實現,基于共享內存。
  • 自定義線程安全消息隊列適用于多線程環境。

根據具體需求選擇合適的消息隊列實現方式。

五、進程的調度

進程調度的算法

進程調度的核心是調度算法,不同的算法適用于不同的場景。常見的調度算法包括:

(1)先來先服務(FCFS,First-Come-First-Served)
  • 原理:按照進程到達的順序分配CPU。
  • 優點:簡單直觀。
  • 缺點:可能導致短作業等待時間過長(“短作業饑餓”問題)。
(2)短作業優先(SJF,Shortest Job First)
  • 原理:優先調度預計運行時間最短的進程。
  • 優點:可以有效減少平均等待時間。
  • 缺點:可能導致長作業饑餓,且需要預估進程運行時間。
(3)優先級調度(Priority Scheduling)
  • 原理:根據進程的優先級分配CPU,優先級高的進程優先運行。
  • 優點:可以滿足不同進程的緊急程度需求。
  • 缺點:低優先級的進程可能會被餓死(“優先級倒置”問題)。
(4)時間片輪轉(RR,Round Robin)
  • 原理:將CPU時間分成固定長度的時間片(Time Quantum),每個就緒態進程輪流運行一個時間片。
  • 優點:公平性好,響應速度快,適合交互式系統。
  • 缺點:時間片大小的選擇會影響系統性能。
(5)多級反饋隊列(Multilevel Feedback Queue)
  • 原理:將就緒隊列分為多個優先級隊列,每個隊列采用不同的調度算法。進程在不同隊列之間動態遷移。
  • 優點:綜合了多種調度算法的優點,兼顧公平性和效率。
  • 缺點:實現復雜,需要合理設計隊列之間的遷移策略。

六、線程同步的方式

畫板

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

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

相關文章

卷積神經網絡(CNN)之 EfficientNet

在深度學習領域&#xff0c;模型的計算效率與性能之間的平衡一直是一個核心挑戰。隨著卷積神經網絡&#xff08;CNN&#xff09;在圖像分類、目標檢測等任務中取得顯著成果&#xff0c;模型的復雜度和計算需求也急劇增加。2019年&#xff0c;Google Research 提出的 EfficientN…

學生選課管理系統數據庫設計報告

學生選課管理系統數據庫設計報告 一、需求分析 &#xff08;一&#xff09;項目背景 學生選課管理系統是高校教學管理的重要組成部分&#xff0c;旨在實現學生選課、課程管理、成績錄入與查詢等功能的自動化和信息化。通過該系統&#xff0c;學生可以方便地選擇課程&#xf…

工具介紹《Awsome-Redis-Rogue-Server 與 redis-rogue-server》

1. 核心功能與攻擊場景 redis-rogue-server 基于Redis主從復制漏洞&#xff08;CVE未公開&#xff09;&#xff0c;針對Redis 4.x~5.0.5版本設計&#xff0c;通過偽造惡意主節點強制同步惡意模塊&#xff08;.so文件&#xff09;實現遠程代碼執行&#xff08;RCE&#xff09;。…

Razor C# 變量

Razor C# 變量 引言 在ASP.NET MVC和Razor視圖引擎中,變量是構建動態網頁的基礎。理解Razor C#變量的使用對于開發者來說至關重要。本文將詳細介紹Razor C#變量的概念、類型、作用域以及如何在實際項目中有效使用它們。 一、Razor C# 變量的概念 Razor C# 變量是存儲在Raz…

【QA】模板方法模式在Qt中有哪些應用?

在 Qt 框架中&#xff0c;模板方法模式&#xff08;Template Method Pattern&#xff09;被廣泛應用于框架的設計中&#xff0c;通過定義算法骨架并允許子類在不改變結構的情況下重寫部分步驟。以下是 Qt 中典型的應用場景及示例&#xff1a; 1. 事件處理&#xff08;Event Ha…

有趣的算法實踐:整數反轉與回文檢測(Java實現)

題目描述&#xff1a;整數反轉與回文檢測 要求實現兩個功能&#xff1a; 將輸入的整數反轉&#xff08;保留符號&#xff0c;如輸入-123返回-321&#xff09;判斷反轉后的數是否為回文數&#xff08;正反讀相同&#xff09; 示例&#xff1a; 輸入&#xff1a;123 → 反轉結…

【協作開發】低成本一鍵復刻github的gitea

在閱讀 next-public 時&#xff0c;反思原本的需求&#xff0c;是否本未倒置&#xff0c;故而重新調研當下開源現狀。發現 gitea 完全滿足商業軟件的開發要求&#xff0c;并且價格足夠低&#xff0c;使用足夠方便&#xff0c;其他同類軟件完全不用看了&#xff0c;真是世界級的…

基于“動手學強化學習”的知識點(二):第 15 章 模仿學習(gym版本 >= 0.26)

第 15 章 模仿學習&#xff08;gym版本 &#xff1e; 0.26&#xff09; 摘要 摘要 本系列知識點講解基于動手學強化學習中的內容進行詳細的疑難點分析&#xff01;具體內容請閱讀動手學強化學習&#xff01; 對應動手學強化學習——模仿學習 # -*- coding: utf-8 -*-import gy…

JAVA面試_進階部分_Java JVM:垃圾回收(GC 在什么時候,對什么東西,做了什么事情)

在什么時候&#xff1a; 首先需要知道&#xff0c;GC又分為minor GC 和 Full GC&#xff08;major GC&#xff09;。Java堆內存分為新生代和老年代&#xff0c;新生代 中又分為1個eden區和兩個Survior區域。 一般情況下&#xff0c;新創建的對象都會被分配到eden區&#xff…

2024年消費者權益數據分析

&#x1f4c5; 2024年315消費者權益數據分析 數據見&#xff1a;https://mp.weixin.qq.com/s/eV5GoionxhGpw7PunhOVnQ 一、引言 在數字化時代&#xff0c;消費者維權數據對于市場監管、商家誠信和行業發展具有重要價值。本文基于 2024年315平臺線上投訴數據&#xff0c;采用數…

設計模式Python版 訪問者模式

文章目錄 前言一、訪問者模式二、訪問者模式示例 前言 GOF設計模式分三大類&#xff1a; 創建型模式&#xff1a;關注對象的創建過程&#xff0c;包括單例模式、簡單工廠模式、工廠方法模式、抽象工廠模式、原型模式和建造者模式。結構型模式&#xff1a;關注類和對象之間的組…

安全無事故連續天數計算,python 時間工具的高效利用

安全天數計算&#xff0c;數據系統時間直取&#xff0c;安全標準高效便捷好用。 筆記模板由python腳本于2025-03-17 23:50:52創建&#xff0c;本篇筆記適合對python時間工具有研究欲的coder翻閱。 【學習的細節是歡悅的歷程】 博客的核心價值&#xff1a;在于輸出思考與經驗&am…

大型語言模型(LLM)部署中的內存消耗計算

在部署大型語言模型&#xff08;LLM&#xff09;時&#xff0c;顯存&#xff08;VRAM&#xff09;的合理規劃是決定模型能否高效運行的核心問題。本文將通過詳細的公式推導和示例計算&#xff0c;系統解析模型權重、鍵值緩存&#xff08;KV Cache&#xff09;、激活內存及額外開…

Mysql表的查詢

一&#xff1a;創建一個新的數據庫&#xff08;companydb),并查看數據庫。 二&#xff1a;使用該數據庫&#xff0c;并創建表worker。 mysql> use companydb;mysql> CREATE TABLE worker(-> 部門號 INT(11) NOT NULL,-> 職工號 INT(11) NOT NULL,-> 工作時間 D…

ASP.NET Webform和ASP.NET MVC 后臺開發 大概80%常用技術

本文涉及ASP.NET Webform和ASP.NET MVC 后臺開發大概80%技術 2019年以前對標 深圳22K左右 廣州18K左右 武漢16K左右 那么有人問了2019年以后的呢&#xff1f; 答&#xff1a;吉祥三寶。。。 So 想繼續看下文的 得有自己的獨立判斷能力。 C#.NET高級筆試題 架構 優化 性能提…

首頁性能優化

首頁性能提升是前端優化中的核心任務之一&#xff0c;因為首頁是用戶訪問的第一入口&#xff0c;其加載速度和交互體驗直接影響用戶的留存率和轉化率。 1. 性能瓶頸分析 在優化之前&#xff0c;首先需要通過工具分析首頁的性能瓶頸。常用的工具包括&#xff1a; Chrome DevTo…

一周學會Flask3 Python Web開發-SQLAlchemy刪除數據操作-班級模塊

鋒哥原創的Flask3 Python Web開發 Flask3視頻教程&#xff1a; 2025版 Flask3 Python web開發 視頻教程(無廢話版) 玩命更新中~_嗶哩嗶哩_bilibili 首頁list.html里加上刪除鏈接&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta c…

改變一生的思維模型【12】笛卡爾思維模型

目錄 基本結構 警惕認知暗礁 案例分析應用 一、懷疑階段:破除慣性認知 二、解析階段:拆解問題為最小單元 三、整合階段:重構邏輯鏈條 四、檢驗階段:多維驗證解決方案 總結與啟示 笛卡爾說,唯獨自己的思考是可以相信的。 世界上所有的事情,都是值得被懷疑的,但是…

需求文檔(PRD,Product Requirement Document)的基本要求和案例參考:功能清單、流程圖、原型圖、邏輯能力和表達能力

文章目錄 引言I 需求文檔的基本要求結構清晰內容完整語言準確圖文結合版本管理II 需求文檔案例參考案例1:電商平臺“商品中心”功能需求(簡化版)案例2:教育類APP“記憶寶盒”非功能需求**案例3:軟件項目的功能需求模板3.1 功能需求III 需求文檔撰寫技巧1. **從核心邏輯出發…

五大方向全面對比 IoTDB 與 OpenTSDB

對比系列第三彈&#xff0c;詳解 IoTDB VS OpenTSDB&#xff01; 之前&#xff0c;我們已經深入探討了時序數據庫 Apache IoTDB 與 InfluxDB、Apache HBase 在架構設計、性能和功能方面等多個維度的區別。還沒看過的小伙伴可以點擊閱讀&#xff1a; Apache IoTDB vs InfluxDB 開…