日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux 进程间通信:管道、共享内存、消息队列、信号量

發布時間:2024/4/11 linux 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux 进程间通信:管道、共享内存、消息队列、信号量 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
  • 進程間通信
  • 管道
  • 共享內存
  • 消息隊列
  • 信號量

進程間通信

https://blog.csdn.net/qq_35423154/article/details/105294963
在之前的一篇博客中講過,因為每個進程都通過自己的頁表構建物理地址和虛擬地址的映射關系,使每個進程都擁有自己的虛擬地址空間,并通過這個獨立的虛擬地址空間來對物理內存進行操作,所有的進程都只能訪問自己的虛擬地址,而不能直接訪問物理內存,所以多個進程無法訪問同一塊區域,無法實現通信。

因為這種獨立性,進程之間無法直接進行通信,操作系統就為了解決這種問題,提出了多重適用于不同情境下的通信方式。

數據傳輸:管道、消息隊列
數據共享:共享內存
進程控制:信號量


管道

原理:管道的本質其實就是內核中的一塊緩沖區,多個進程通過訪問同一個緩沖區就可以實現進程間的通信

管道分為兩種:匿名管道、命名管道

匿名管道

匿名 管道是內核中的一塊緩沖區,因為沒有具體的文件描述符,所以匿名管道只能適用于具有親緣關系的進程間通信父進程在創建管道的時候操作系統會返回管道的文件描述符,然后生成子進程時子進程會通過拷貝父進程的pcb來獲取到這個管道的描述符,所以他們可以通過這個文件描述符來訪問同一個管道,來實現進程間的通信。而不具備親緣關系的進程則無法通過這個文件描述符來訪問同一個管道。

接口

#include <unistd.h>
功能:創建一無名管道
原型
int pipe(int fd[2]);
參數
fd:文件描述符數組,其中fd[0]表示讀端, fd[1]表示寫端
返回值:成功返回0,失敗返回-1

一開始父進程創建管道

父進程fork創建子進程

關閉多余描述符

就這樣,子進程通過寫入端fd[1]向管道寫入數據,父進程通過讀入端fd[0]從管道讀取數據,來實現進程間的通信。

#include<stdio.h> #include<stdlib.h> #include<unistd.h>int main() {int pipefd[2];pipe(pipefd);int pid = fork();if(pid == 0){close(pipefd[0]);while(1){write(pipefd[1], "hello world", 12);sleep(3);}close(pipefd[1]);exit(0);}else if(pid > 0){close(pipefd[1]);char buff[1024];while(1){read(pipefd[0], buff, 12);printf("%s\n", buff);}close(pipefd[0]);}return 0;

這是一個簡單的管道


運行后每三秒寫端會寫入數據,然后讀端立即讀入數據。

因為父子進程究竟是誰先執行這一點我們無法知道,假設如果子進程還沒寫入,父進程卻已經開始讀了,這時候應該是會讀不到東西的,但是這種情況并沒有發生,這里就牽扯到了管道的讀寫特性。

管道的讀寫特性:
  • 如果管道中沒有數據,則調用read讀取數據會阻塞
  • 如果管道中數據滿了,則調用write寫入數據會阻塞
  • 如果管道的所有讀端pipefd[0]被關閉,則繼續調用write會因為無法讀出而產生異常導致進程退出
  • 如果管道的所有寫端pipefd[1]被關閉,則繼續調用read,因為無法再次寫入,read讀完管道中的所有數據后不再阻塞,返回0退出
  • 命名管道

    匿名管道的限制就是只能在親緣關系的進程間通信,如果我們想為不相關的進程交換數據,就可以使用命名管道。

    原理:命名管道也是內核中的一塊緩沖區,但是它具有標識符。這個標識符是一個可見于文件系統的管道文件,能夠被其他進程找到并打開管道文件來獲取管道的操作句柄,多個進程可以通過打開這個管道文件來訪問同一塊緩沖區來實現通信

    接口:

    int mkfifo(const char *filename,mode_t mode);
    mode:權限掩碼
    filename:管道的標識符,通過這個標識符來訪問管道
    返回值:若成功則返回0,否則返回-1

    #include<stdio.h> #include<unistd.h> #include<stdlib.h> #include<fcntl.h> #include<sys/types.h> #include<sys/stat.h>int main() {mkfifo("test", 0664);int pid = fork();if(pid > 0){int read_fd = open("test", O_RDONLY);char buff[1024];read(read_fd, buff, 12);printf("buff:%s\n", buff);close(read_fd);}else if(pid == 0){int write_fd = open("test", O_WRONLY);write(write_fd, "hello world", 12);close(write_fd);exit(0);}return 0; }


    試驗一下

    open打開命名管道的特性:

    1. 若文件以只讀打開,則會阻塞,直到文件被以寫的方式打開
    2. 若文件以只寫打開,則會阻塞,直到文件被以讀的方式打開

    管道的特性:

    1. 管道是半雙工通信(可以選擇方向的單向傳輸),這個可以從上面的示意圖看出來
    2. 管道的讀寫特性(無論命名匿名都一樣)
    若管道中沒有數據則讀操作堵塞,如果管道中數據滿了寫操作堵塞。
    如果管道中所有讀端關閉則寫端觸發異常,如果所有寫端關閉則讀端讀完數據后不堵塞返回0
    3. 管道聲明周期隨進程,打開管道的所有進程退出后管道就會被釋放。
    4. 管道提供字節流傳輸服務
    5. 命名管道額外有一個打開特性,只讀打開會阻塞直到被以寫打開,只寫打開會阻塞直到被以讀打開
    6. 管道自帶同步和互斥


    共享內存

    共享內存即在物理內存上開辟一塊空間,然后多個進程通過頁表將這同一個物理內存映射到自己的虛擬地址空間中,通過自己的虛擬地址空間來訪問這塊物理內存,達到了數據共享的目的。
    如圖:


    也正是因為這種特性,使得共享內存成為了最快的進程間通信的方式因為它直接通過虛擬地址來訪問物理內存,比前面的管道和后面的消息隊列少了內核態和用戶態的幾次數據拷貝和交互。

    共享內存的使用流程:

    1. 創建共享內存
    2. 將共享內存映射到虛擬地址空間
    3. 進行操作
    4. 解除映射關系
    5. 釋放共享內存

    接口:

  • 創建共享內存
    int shmget(key_t key, size_t size, int shmflg)
  • key:這個共享內存段名字
    size:共享內存大小
    shmflg:由九個權限標志構成,它們的用法和創建文件時使用的mode模式標志是一樣的
    返回值:成功返回共享內存標識符,失敗返回-1
    頭文件:
    #include <sys/ipc.h>
    #include <sys/shm.h>

  • 將共享內存映射到虛擬地址空間
    void *shmat(int shmid, const void *shmaddr, int shmflg)
  • shmid: 共享內存標識
    shmaddr:指定連接的地址
    shmflg:權限標志
    返回值:成功返回指向共享內存映射在虛擬地址空間的指針(即首地址),失敗返回-1
    頭文件:
    #include <sys/types.h>
    #include <sys/shm.h>

  • 共享內存管理
    int shmctl(int shmid, int cmd, struct shmid_ds *buf)
  • shmid:由shmget返回的共享內存標識碼
    cmd:將要采取的動作
    buf:指向一個保存著共享內存的模式狀態和訪問權限的數據結構
    返回值:成功返回0,失敗返回-1
    頭文件:
    #include <sys/types.h>
    #include <sys/shm.h>

  • 解除映射關系
    int shmdt(const void *shmaddr)
  • shmaddr: 由shmat所返回的指針
    返回值:成功返回0,失敗返回-1
    頭文件:
    #include <sys/types.h>
    #include <sys/shm.h>

    特性:

    1. 共享內存是最快的進程間通信方式
    2. 生命周期隨內核
    3. 不自帶同步與互斥,但可以借助信號量來實現同步與互斥


    消息隊列

    消息隊列是內核中的一個優先級隊列,多個進程通過訪問同一個隊列,進行添加節點或者獲取節點來實現通信。

    接口:

  • 創建消息隊列
    int msgget(key_t key, int msgflg);
  • key:消息隊列對象的關鍵字
    msgflg:消息隊列的建立標志和存取權限
    返回值:成功執行時,返回消息隊列標識值。失敗返回-1
    頭文件:
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>

  • 進程可以向隊列中添加/獲取節點
    添加節點:
    int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
    獲取節點:
    ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
    int msgflg);
  • msqid:消息隊列對象的標識符
    msgp:消息緩沖區指針
    msgsz:消息數據的長度
    msgtyp:決定從隊列中返回哪條消息
    msgflg:消息隊列狀態
    返回值:成功執行時,返回0。失敗返回-1
    頭文件:
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>

  • 刪除消息隊列
    int msgctl(int msqid, int cmd, struct msqid_ds *buf)
  • msqid:消息隊列對象的標識符
    cmd:函數要對消息隊列進行的操作
    buf:取出系統保存的消息隊列的msqid_ds 數據,并將其存入參數buf 指向的msqid_ds 結構中
    返回值:成功執行時,返回0。失敗返回-1
    頭文件:
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>

    特性:

    1. 自帶同步與互斥
    2. 生命周期隨內核


    信號量

    信號量其實是內核中的一個計數器和阻塞隊列,通過信號量來對臨界資源的訪問進行控制,來實現進程間的同步與互斥

    例如有一個能容納n人的餐廳,則用一個計數器表示n,如果有人進入則n - 1,如果有人出來則n + 1,只有n > 0時才能進入,如果n <= 0時,則說明沒有位置,需要將進程掛起并放入阻塞隊列中,直到有人出來使資源釋放時,才能將后續進程從阻塞隊列中喚醒獲取資源

    同步:
    通過條件判斷實現臨界資源訪問的合理性

    互斥:
    通過同一時間的唯一訪問來實現臨界資源訪問的安全性

    POSIX信號量
    POSIX信號量和SystemV信號量作用相同,都是用于同步操作,達到無沖突的訪問共享資源目的。 但POSIX可以用于線程間同步

    #include <semaphore.h>//初始化信號量 int sem_init(sem_t *sem, int pshared, unsigned int value); /* 參數:pshared:0表示線程間共享,非零表示進程間共享value:信號量初值 *///銷毀信號量 int sem_destroy(sem_t *sem);//等待信號量 int sem_wait(sem_t *sem); //功能:等待信號量,會將信號量的值減1//發布信號量 int sem_post(sem_t *sem); //功能:發布信號量,表示資源使用完畢,可以歸還資源了。將信號量值加1。

    總結

    以上是生活随笔為你收集整理的Linux 进程间通信:管道、共享内存、消息队列、信号量的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。