嵌入式Linux:進程間通信機制

進程間通信(Interprocess Communication,簡稱 IPC)是指多個進程之間相互交換數據、共享信息或同步操作的過程。

由于每個進程都有自己獨立的地址空間,不像線程共享同一地址空間,進程之間的通信需要特定的機制。

單個進程內部的模塊(如函數)之間的通信相對簡單,可以通過全局變量、參數傳遞等方式實現(xiàn),但進程間通信相對復雜,因為不同進程彼此隔離,不共享內存。

雖然大多數普通應用程序是單進程、多線程的,不需要進程間通信的復雜性,但在一些復雜或大型應用中,尤其是服務器或圖形用戶界面(GUI)程序,可能會使用多進程架構來提升性能或簡化設計。

這種情況下,IPC 機制就變得非常重要。

1、進程間通信機制

linux 系統(tǒng)繼承unix 系統(tǒng)的豐富 IPC 機制,并對其進行了擴展和改進。

UNIX 系統(tǒng)的進程間通信大致可以分為兩大流派:

System V IPC(由 AT&T 貝爾實驗室主導發(fā)展):改進了 UNIX 早期的進程間通信手段,形成了適用于單臺計算機的 IPC 機制,主要包括信號量、消息隊列和共享內存。BSD 套接字通信(由加州大學伯克利分校主導發(fā)展):BSD 在網絡通信和分布式系統(tǒng)方面做出了重要貢獻,跳出了進程間通信局限于單個計算機的限制,形成了基于套接字(Socket)的通信機制,廣泛用于網絡應用程序。

Linux 將這兩大體系都繼承了下來,并在此基礎上發(fā)展了 POSIX IPC。

POSIX(Portable Operating System Interface for Unix)標準化了多種 UNIX 系統(tǒng)功能,包括進程間通信,彌補了 System V IPC 的一些不足。

嵌入式Linux:進程間通信機制

1.1、UNIX IPC

UNIX 傳統(tǒng)的 IPC 機制包括管道、FIFO 和信號,這些機制最早由 UNIX 系統(tǒng)引入,適用于簡單的單機進程間通信。

管道(Pipe):一種單向、半雙工的通信機制,通常用于父子進程間的數據傳遞。父進程可以寫入數據,子進程可以讀取。FIFO(命名管道):類似于管道,但通過文件系統(tǒng)實現(xiàn),任何進程都可以通過路徑訪問該管道,實現(xiàn)雙向通信。信號(signal):信號是一種用于進程間異步通知的機制,可以用于進程之間的簡單通信或事件通知,例如 SIGINT(Ctrl+C 發(fā)送的中斷信號)。1.2、System V IPC

System V IPC 是 UNIX 的增強版本,主要包括信號量、消息隊列和共享內存,適合需要更復雜的進程同步與數據共享的場景。

信號量(Semaphore):用于進程間的同步,通常用于控制對共享資源的訪問。信號量用于防止多個進程同時訪問同一資源,避免資源爭用問題。消息隊列(Message Queue):允許進程以消息的形式發(fā)送和接收數據。消息隊列是一種先進先出(FIFO)的結構,支持不同類型的消息,使得進程可以基于消息類型進行處理。共享內存(Shared Memory):進程之間共享同一塊內存區(qū)域,允許它們直接讀寫數據。這是最有效的 IPC 方式,因為數據不需要在進程之間復制。1.3、POSIX IPC

POSIX IPC 是 System V IPC 的改進版本,旨在解決 System V IPC 在靈活性和可移植性上的一些不足。

POSIX 標準為 UNIX 系統(tǒng)間的兼容性提供了統(tǒng)一的接口,使得程序可以更方便地在不同的 UNIX 系統(tǒng)間移植。

POSIX 信號量:與 System V 信號量類似,用于進程同步,但提供了更靈活的接口和更強的實時性支持。POSIX 消息隊列:改進了 System V 消息隊列,允許指定消息的優(yōu)先級,并提供更簡單的接口。POSIX 共享內存:與 System V 共享內存類似,但具有更好的兼容性和可移植性,接口設計更加現(xiàn)代化。1.4、套接字(Socket)通信

套接字是一種既可以用于本地進程間通信,也可以用于網絡通信的機制,支持雙向數據傳輸。

基于套接字的 IPC 可以實現(xiàn)非常靈活的通信模式,例如客戶端-服務器架構,適合在多臺計算機之間傳遞數據。

各類 IPC 機制的對比和應用場景:

嵌入式Linux:進程間通信機制

2、管道(Pipe)

管道是一種半雙工(單向)的通信方式,通常用于父子進程之間的通信。一個進程可以向管道寫入數據,另一個進程從管道讀取數據。

Linux 提供了無名管道和命名管道兩種類型。

無名管道(Anonymous Pipe):只能在具有親緣關系的進程間使用,比如父進程和子進程。命名管道(Named Pipe 或 FIFO):通過文件系統(tǒng)中的路徑來創(chuàng)建,任意進程都可以訪問。

示例:

代碼語言:JavaScript代碼運行次數:0運行復制

int main() {    int fd[2];    pipe(fd); // 創(chuàng)建無名管道    if (fork() == 0) { // 子進程        close(fd[0]); // 關閉讀取端        write(fd[1], "Hello, parent!", 15);        close(fd[1]);    } else { // 父進程        char buffer[20];        close(fd[1]); // 關閉寫入端        read(fd[0], buffer, sizeof(buffer));        printf("Received: %sn", buffer);        close(fd[0]);    }    return 0;}

3、消息隊列(Message Queue)

消息隊列是一種先進先出的隊列,允許進程以消息的形式發(fā)送和接收數據。

消息隊列可以支持多種類型的消息,通過消息類型實現(xiàn)多種目的的通信。

示例:進程A可以向隊列發(fā)送一個帶有特定類型的消息,而進程B可以根據消息類型進行處理。

代碼語言:javascript代碼運行次數:0運行復制

struct msgbuf {    long mtype;    char mtext[100];};int main() {    key_t key = ftok("msgqueue", 65);    int msgid = msgget(key, 0666 | IPC_CREAT);    struct msgbuf message;    message.mtype = 1; // 消息類型    snprintf(message.mtext, sizeof(message.mtext), "Hello Message Queue");    msgsnd(msgid, &message, sizeof(message.mtext), 0);    return 0;}

4、共享內存(Shared Memory)

共享內存是最快的 IPC 機制之一,因為進程之間直接訪問同一塊內存區(qū)域,而不需要拷貝數據。

通常使用 shmget()、shmat() 和 shmdt() 函數進行共享內存的創(chuàng)建和訪問。

示例:

代碼語言:javascript代碼運行次數:0運行復制

int main() {    key_t key = ftok("shmfile",65);    int shmid = shmget(key, 1024, 0666|IPC_CREAT);    char *str = (char*) shmat(shmid, (void*)0, 0);    strcpy(str, "Hello Shared Memory");    printf("Data written in memory: %sn", str);    shmdt(str);    return 0;}

5、信號量(Semaphore)

信號量是一種用于進程同步的機制,通常用于控制多個進程對共享資源的訪問。

嵌入式系統(tǒng)中,信號量通常用來避免多個進程同時訪問同一資源,防止數據競爭。

示例:信號量可以通過 semget() 和 semop() 函數來操作,用于鎖定或解鎖資源。

代碼語言:javascript代碼運行次數:0運行復制

int main() {    key_t key = ftok("semfile",65);    int semid = semget(key, 1, 0666 | IPC_CREAT);    struct sembuf sem_lock = {0, -1, 0}; // 減1操作    struct sembuf sem_unlock = {0, 1, 0}; // 加1操作    semop(semid, &sem_lock, 1); // 上鎖    printf("Critical sectionn");    semop(semid, &sem_unlock, 1); // 解鎖    return 0;}

6、套接字(Socket)

套接字不僅支持本地進程間通信,還可以用于網絡通信。

基于套接字的 IPC 支持雙向通信,比較靈活,適合嵌入式系統(tǒng)中進程之間需要頻繁且復雜的數據交互的情況。

示例:

代碼語言:javascript代碼運行次數:0運行復制

int main() {    int sv[2];    socketpair(AF_UNIX, SOCK_STREAM, 0, sv);    if (fork() == 0) { // 子進程        close(sv[0]);        write(sv[1], "Hello from child", 16);        close(sv[1]);    } else { // 父進程        char buffer[20];        close(sv[1]);        read(sv[0], buffer, sizeof(buffer));        printf("Received: %sn", buffer);        close(sv[0]);    }    return 0;}

7、信號(Signal)

信號是用來通知進程發(fā)生某種事件的機制。進程可以捕獲、忽略或處理信號,典型的信號包括 SIGINT(中斷信號)和 SIGKILL(殺死進程信號)。

示例:處理 SIGINT 信號(Ctrl+C)。

代碼語言:javascript代碼運行次數:0運行復制

void sigint_handler(int sig) {    printf("Caught signal %dn", sig);}int main() {    signal(SIGINT, sigint_handler);    while (1) {        printf("Running...n");        sleep(1);    }    return 0;}

進程間通信的機制多種多樣,選擇合適的方式取決于應用場景的需求。

? 版權聲明
THE END
喜歡就支持一下吧
點贊12 分享