Linux進程間通信

Linux下的進程間通信機制:大致包括:

管道
信號(在Windows上成爲消息)
消息隊列
共享內存
信號量
套接字(socket)


一、基本概念

  • 管道(Pipe)及有名管道(named pipe):管道可用於具有親緣關係進程間的通信,有名管道克服了管道沒有名字的限制,因此,除具有管道所具有的功能外,它還允許無親緣關係進程間的通信;
  • 信號(Signal):信號是比較複雜的通信方式,用於通知接受進程有某種事件發生,除了用於進程間通信外,進程還可以發送信號給進程本身;linux除了支持Unix早期信號語義函數sigal外,還支持語義符合Posix.1標準的信號函數sigaction(實際上,該函數是基於BSD的,BSD爲了實現可靠信號機制,又能夠統一對外接口,用sigaction函數重新實現了signal函數);
  • 報文(Message)隊列(消息隊列):消息隊列是消息的鏈接表,包括Posix消息隊列system V消息隊列。有足夠權限的進程可以向隊列中添加消息,被賦予讀權限的進程則可以讀走隊列中的消息。消息隊列克服了信號承載信息量少,管道只能承載無格式字節流以及緩衝區大小受限等缺點。
  • 共享內存:使得多個進程可以訪問同一塊內存空間,是最快的可用IPC形式。是針對其他通信機制運行效率較低而設計的。往往與其它通信機制,如信號量結合使用,來達到進程間的同步及互斥。
  • 信號量(semaphore):主要作爲進程間以及同一進程不同線程之間的同步手段。
  • 套接口(Socket):更爲一般的進程間通信機制,可用於不同機器之間的進程間通信。起初是由Unix系統的BSD分支開發出來的,但現在一般可以移植到其它類Unix系統上:Linux和System V的變種都支持套接字。

二、管道實例:

  類似於水管,在兩個進程之間用來傳遞數據。管道是半雙工的,而且只能在共同父進程的進程之間使用。

int pipe ( int filedes[2] );

filedes返回兩個文件描述符,filedes[0]爲讀端,filedes[1]爲寫端。創建成功返回 0

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h> 
#include <string.h>
int main()
{
    int fd[2];
    pid_t pid;
    char buf[64] = "i'm parent process!!\n";   //父進程要寫入管道的內容
    char line[64];

    if (0 != pipe(fd))    //創建管道
    {
        fprintf(stderr,"Fail to create pipe!");
        return 0;      
    }

    pid = fork();
    if (pid < 0)
    {
        printf("fork failed!");
        return 0 ;
    }

    else if(0 == pid)   
    {
        close(fd[0]);   //關閉讀管道,使父進程只能寫
        write(fd[1],buf,strlen(buf));
        close(fd[1]);   //關閉寫管道
    }

    else
    {
        close(fd[1]);   //關閉寫管道
        read(fd[0],line,64);    //只能讀
        printf("DATA From Parents:%s",line);
        close(fd[0]);
    }
    return 0;

}

三、共享內存

共享內存:進程需要可以被其他進程瀏覽的內存塊。希望訪問這個內存塊的其他進程請求對它的訪問,或由創建它的進程授予訪問內存塊的權限。可以訪問特定內存塊的所有進程對它具有即時可見性。
  共享內存被映射到使用它的每個進程的地址空間。所以,它看起來像是另一個在進程內聲明的變量。當一個進程寫共享內存,所有的進程都立即知道寫入的內容,而且可以訪問。進程間共享內存的關係與函數間全局變量的關係相似。

int shmget(key_t key, size_t size, int shmflg);
void *shmat(int shmid, const void *shmaddr, int shmflg);
int shmdt(const void *shmaddr);
ftok():系統建立IPC通訊 (消息隊列、信號量和共享內存) 時必須指定一個ID值。通常情況下,該id值通過ftok函數得到

當然,也可以直接使用內核的:PC_PRIVATE
IPC_PRIVATE和ftok的比較

函數解析
int shmget(key_t key, size_t size, int shmflg);
shmget 用來創建共享內存(成功創建,返回一個共享ID):

  • key是由ftok()函數生成的唯一關鍵字,用來標識該段內存,
  • size參數指定需要的內存字節數
  • shmflg:內存操作方式,有讀/寫兩種

把共享內存區對象映射到調用進程的地址空間
void *shmat(int shmid, const void *shmaddr, int shmflg);

  • shmid:共享內存ID
  • shmaddr:指定共享內存出現在進程內存地址的什麼位置,直接指定爲NULL讓內核自己決定一個合適的地址位置
  • shmflg : SHM_RDONLY:爲只讀模式,其他爲讀寫模式

從程序中分離一塊共享內存:
int shmdt(const void *shmaddr);

創建共享內存並向其中寫入數據:

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>

int main()
{
    int shmid;
    char *ptr;
    char *shm_str = "string in 0X90";

    shmid = shmget(0x90,1024,SHM_W|IPC_CREAT|SHM_R|IPC_EXCL);  //創建共享內存(我們已經指定了0x99這段內存,所以無需調用ftok()函數獲得標誌)

    if (-1 == shmid)
    {
        perror("create share memory failed!");
    }

    ptr = (char *)shmat(shmid,0,0);   //通過共享內存ID獲取共享地址內存

    if ((void *) -1 == ptr)
        perror("get share memory");
    strcpy(ptr,shm_str);
    shmdt(ptr);
    return 0;
}

讀取數據:

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>

int main()
{
    int shmid;
    char *ptr;

    shmid = shmget(0x90,1024,SHM_W|SHM_R|IPC_EXCL);  //創建共享內存

    if (-1 == shmid)
    {
        perror("create share memory!");
    }

    ptr = (char *)shmat(shmid,0,0);   //通過共享內存ID獲取共享地址內存

    if ((void*) -1 == ptr)
        perror("get share memory failed");

    printf("string int share memeory : %s\n",ptr);
    shmdt(ptr);
    return 0;
}

這裏寫圖片描述

首先得先執行write , 因爲shmget這個函數創建共享內存區域。IPC_CREAT這個參數是代表不存在則創建,所以第二次執行的話需要去掉這個參數

ipcs:查看當前共享資源。

這裏寫圖片描述

當然也可以釋放指定的共享內存:

ipcrm -m  0

這裏0是shimd的值


四、socket:

參考:服務器與多個用戶間的通信

發佈了64 篇原創文章 · 獲贊 114 · 訪問量 23萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章