進程間通訊

進程間通訊緣由
由於每個進程都是擁有一個獨立的虛擬地址空間的,促使進程獨立,致使了進程之間須要協做。
進程間通訊分爲ide

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

首先先談談管道和共享內存函數

管道---匿名管道,命名管道操作系統

本質:管道其實就是一塊內存,是內核當中的緩衝區code

  • 匿名管道---沒有標識
    特性:
  1. 具備親緣關係的進程間通訊
  2. 半雙工,數據只能有一個流向
    進程間通訊
  3. 提供字節流服務
  4. 自帶同步與互斥功能
  5. 生命週期隨進程,進程終止,生命週期消失
  6. 若是管道爲空,則讀阻塞
  7. 若是管道爲滿,則寫阻塞
  8. 若是管道的讀端被關閉,則寫端往管道當中寫數據的時候,會形成管道破裂,而且致使進程收到SIGPIPE信號,從而進程終止
  9. 若是管道的寫端被關閉掉了,則讀端讀完管道的數據以後,read不會陷入阻塞狀態,而是返回。執行代碼剩下的正常流程
  10. 管道大小PIPE_SIZE:64K,PIPE_BUG:4K
  11. 若是寫入的數據大於PIPE_BUG的話,則不保證寫入數據的原子性

    原子性:當前操做不被打斷,換句話說,在管道的讀寫操做是不能夠被打斷的
    臨界資源:同一時間,當前資源只能被一個進程所訪問
    互斥:同一時間,保證只能有一個進程訪問臨界資源
    同步:保證臨界資源訪問的合理性blog

建立一個管道(親緣關係的進程中使用)
int pipe(int pipefd[2]);
pipefd[0]:從pipefd[0]文件描述符中去讀
pipefd[1]:從pipefd[1]文件描述符中去寫
返回值:0(成功) -1(失敗)接口

int fd[2];
    int ret = pipe(fd);
    if(ret != 0)
    {
        perror("pipe");//建立錯誤
        return 0;
    }

    pid_t pid = fork();
    if(pid < 0)
    {
        return 0;//建立失敗
    }
    else if(pid == 0)//建立成功
    {
        //child
        sleep(5);
        close(fd[0]);
        write(fd[1], "hehe", 4);
    }
    else
    {
        //father
        close(fd[1]);
        char buf[1024] = {0};
        read(fd[0], buf, sizeof(buf));
        printf("buf = %s\n", buf);
    }

命名管道---有標識的管道
特性:生命週期

  1. 具備標識符,能夠知足不一樣進程進程進程間通訊
  2. 其餘的特性和匿名管道類似
    建立命名管道的方式
    1.使用命令去出建立
    mkfifo+[命名管道文件名稱] 建立出來的文件類型是p
    2.使用函數去建立命名管道文件
    mkfifo(char* pathname,mode_t mode)

共享內存
原理:
建立共享內存的時候,首先在物理內存當中建立一塊內存,各個進程都經過頁表結構將該段內存映射到本身的虛擬內存地址空間上的共享區,各個進程經過映射的地址來進行通訊
特性:
1.共享內存是最快的進程間通訊方式
2.共享內存是不帶有同步和互斥功能的
3.寫入數據是按照覆蓋的方式進行的隊列

建立共享內存進程

int shmget(key_t key,size_t size,int shmflg)
key:共享內存標識符
    size:共享內存的大小
    shmflg:1.IPC_CREAT:若是共享內存不存在,則建立共享內存,若是已經存在,則返回
                            2.IPC_CREAT | IPC_EXCL若是共享內存依然存在,則報錯,若是不存在則建立
                            3.+權限:按位或權限,權限至關於文件的權限(使用8進制的數字)

返回值:
成功返回共享內存的操做句柄ip

將進程附加到共享內存上

void *shmat(int shmid,const void *shmaddr,int shmflg);
shmid:共享內存的操做句柄
    shmaddr:映射到共享區的地址,通常不選擇(填NULL),由操做系統本身映射到虛擬地址空間
    shmflg:0:可讀可寫        IPC_RDONLY:只讀

返回值:
返回映射到的那個地址

分離進程和共享內存

int shmdt(const void *shmaddr);

shmaddr:shmat返回的地址
返回值:
成功返回0,失敗返回1
共享內存的銷燬

int shmctl(int shmid,int cmd,struct shmid_ds *buf);
shmid:共享內存的操做句柄
    cmd:要使用什麼操做
                    IPC_STAT:獲取當前共享內存的狀態,要搭配shmid_ds一塊兒使用
                    IPC_RMID:刪除共享內存,標記共享內存爲刪除狀態
    buf:一個出參,結構體當中是共享內存的一些信息

返回值:
失敗返回-1

共享內存的生命週期

生命週期跟隨操做系統內核
    ipcs -m 查看共享內存
    ipcrm -m [shmid] 刪除一個共享內存
若是刪除一個有進程的共享內存,操做系統的作法是,先標記當前的共享內存爲destroy的狀態,而且將key設置爲0x00000000,表示當前的共享內存不能再被其餘進程所附加,同時會釋放內存,也就致使了正在附加到該共享內存上的進程有崩潰的風險,通常禁止這樣去操做。當附加的進程退出的時候,操做系統就會將共享內存清除掉

消息隊列
消息隊列本質上是內核當中的一個優先級隊列,進程經過訪問優先級隊列來增長節點或者查看節點,來進行進程間通訊
特性:生命週期跟隨進程,若是用戶進程不刪除消息隊列資源,則資源一直在操做系統內核當中自帶同步和互斥功能
接口:msgget/msgsnd/msgrcv/msgct

信號量-----計數器+PCB進程的等待隊列
做用:實現進程的控制,也就是實現進程的同步和互斥

如何實現互斥?---PV操做
1.實現互斥的時候,信號量只有兩個取值,也就是0或1,0表示當前資源不可用,1表示當前資源可用
2.當進程須要訪問一個臨界資源的時候,會先訪問信號量,預計算信號量的值
先對當前信號進行預減1操做,判斷當前信號是否小於0
若是是小於0,則表示信號量以前的值是等於0,則表示當前的臨界資源不可用;將當前的進程放到PCB等待隊列中去
若是是等於0,則表示信號量以前的值是等於1,則表示當前的臨界資源可用;訪問臨界資源,而且信號量減1,標誌着臨界資源不能夠再被訪問了
3.若是訪問完成了,則須要進行結束對臨界資源的訪問,對信號進行加1操做

減1操做:P操做
加1操做:V操做

怎麼實現同步?---計數資源的個數前提:信號量的值不在是0或者1了,信號量大於0的時候,表示有多少個資源可使用。當信號量小於0的時候,小於0的數值的絕對值表示多少個進程在等待資源1.若是須要訪問該資源的時候,對信號量進行-1,訪問資源2.若是不訪問該資源的時候,對信號量進行+13.若是當前信號量值是小於0,表示當前PCB等待隊列當中還有進程在等待資源,當一個進程結束對該資源的訪問的時候,應當喚醒PCB等待隊列當中的一個進程,使之去獲取資源4.若是當前的信號量值大於0,表示PCB等待隊列當中是沒有進程在等待的,也就是不牽扯說須要喚醒沒某個進程去獲取資源

相關文章
相關標籤/搜索