1. 程式人生 > >程序間通訊——管道,訊息佇列,共享記憶體

程序間通訊——管道,訊息佇列,共享記憶體



程序間通訊的本質是讓兩個不相干的程序看到同一份資源。這個資源是由作業系統提供的一個檔案。

程序間通訊的目的

1.資料傳輸:一個程序需要將它 的資料傳送給另一個程序。

2.資源共享:多個程序之間共享同樣的資源。

3.通知事件:一個程序需要向另一個(組)程序傳送訊息,通知它們發生了某種事件。(程序終止時通知父程序)

4.程序控制:有些程序希望完全控制另一個程序的執行,此時控制程序希望能夠攔截另一個程序的所有異常,並能夠及時知道它的狀態改變。

在這裡我主要介紹三種程序間通訊的方式:管道,訊息佇列,共享記憶體

管道: 通過fork()創建出子程序,父子程序共享資源,關閉讀/寫端以達到單向傳輸。

  管道分為匿名管道和命名管道。

匿名管道

#include<unistd.h>
功能:建立一無名管道
int pipe(int fd[2]);

fd:檔案描述符陣列。fd[0]表示讀端,fd[1]表示寫端。fd[1]的輸出是fd[1]的輸入。

通常,程序會先呼叫pipe建立管道,接著呼叫fork建立子程序,從而建立父程序到子程序的IPC通道。

fork之後做什麼取決於我們想要的資料流方向。對於父程序到子程序的管道,若父程序寫入,子程序讀出,則父程序關閉讀端fd[0],子程序關閉寫端fd[1]。

對於管道的一端被關閉,有兩條規則可以起作用:

(1).當read(讀)一個寫端已被關閉的管道時,在所有的資料都被讀取後,read返回0,表示檔案結束。

(2).如果write(寫)一個讀端已被關閉的管道,則產生訊號SIGPIPE。如果忽略該訊號或者捕捉該訊號並從其處理程式返回,則write返回-1,errno設定為EPIPE。

特點1.只能用於具有親緣關係的程序之間的通訊。

           2.管道在通訊的時候基於位元組流傳遞。

           3.管道的生命週期隨程序。

           4.管道自帶同步機制(程序按照某種順序訪問臨界資源)。

            5.半雙工通訊(單向通訊)。

下為一個從鍵盤讀取資料,寫入管道,讀取管道,寫到螢幕的例子。

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>

int main()
{
  int fds[2];
  char buf[100];
  int len;

  if(pipe(fds) == -1)
    perror("make pipe"),exit(1);

  while(fgets(buf,100,stdin)){
     len = strlen(buf);

     if(write(fds[1],buf,len)!=len){
        perror("write to pipe");
        break;
    }
   memset(buf,0x00,sizeof(buf));

   if((len = read(fds[0],buf,100))==-1){
       perror("read from pipe");
       break;
     }
    if(write(1,buf,len)!=len) {
       perror("write to stdout");
       break;
     }
  }
}



命名管道

如果我們想在不相關的程序之間交換資料,可以使用FIFO檔案來完成。它被叫做命名管道。

命名管道可以從命令列上建立。

$ mkfifo filename
也可以從程式裡建立。
int mkfifo(const char* filename,mode_t mode);

匿名管道和命名管道最大的區別是:匿名管道必須是有親緣關係才能通訊,而命名管道可以在兩個不相干的管道之間通訊。

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<errno.h>

#define ERR_EXIT(m)
do
{
  perror(m);
  exit(EXIT_FAILURE);
}while(0)

int main(int argc,char* argv[])
{
  mkfifo("tp",0644);
  int info;
  infd = open("abc",O_RDONLY);
  if(infd = =1) ERR_EXIT("open");

  int outfd;
  outfd = open("tp",O_ERONLY);
  if(outfd == -1)   ERR_EXIT("open");

  char buf[1024];
  int n;
  while((n = read(infd,buf,1024))>0){
     write(outfd,buf,n);
  }

 close(infd);
 close(outfd);
 return 0;
}

上圖是讀取檔案,寫入命名管道的例子:

訊息佇列:

訊息佇列提供一個從一個程序向另一個程序傳送一個有資料型別的資料塊的方法。

訊息佇列是訊息的連結表,儲存在核心中,由訊息佇列識別符號標識。

訊息佇列的生命週期隨核心。

訊息佇列函式:

功能:建立和返回一個訊息佇列
原型:
int msgget(key_t key,int msgflg);
引數:
key:某個訊息佇列的名字
msgflg:由九個許可權標誌組成。
返回值:成功返回一個非負整數,即該訊息佇列的標識碼。失敗返回-1.
功能:訊息佇列的控制函式(增,刪,查,改)
原型:
int msgctl(int msqid,int cmd,struct msqid_ds* buf)
引數:
msqid:由msgget函式返回的訊息佇列標識碼
cmd:將要採取的動作。有三個可取。
返回值:成功返回0,失敗返回-1
功能:從一個訊息佇列中接收訊息
ssize_t msgrcv(int msqid,void* msgp,sizt_t msgsz,long msgtyp,int msgflg);
返回值:
成功返回實際放到接收緩衝區裡去的字元個數,失敗返回-1.

例項程式碼:

makefile:

.PHONY:all

all:client server

client:comm.c client.c
        gcc -o [email protected] $^
server:comm.c server.c
        gcc -o [email protected] $^

.PHONY:clean
clean:
        rm -f client server

comm.h:

#ifndef __COM_H__
#define __COM_H__

#include<stdio.h>
#include<string.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<sys/types.h>
#include<stdlib.h>
#include<unistd.h>

#define PATHNAME "."
#define PROJ_ID 0x6666

#define SERVER_TYPE 1
#define CLIENT_TYPE 2

struct msgbuf{
  long mtype;
  char mtext[1024];
};

int createMsgQueue();
int getMsgQueue();
int destroyQueue(int msgid);
int sendMsg(int msgid,int who,char* msg);
int recvMsg(int msgid,int recvType,char out[]);

#endif

comm.c:

#include"comm.h"

int commMsgQueue(int flags)
{
  key_t _key = ftok(".",0x6666);
  if(_key < 0 ){
    perror("ftok");
    return -1;
  }

  //int msgid = msgget(_key,IPC_CREAT|IPC_EXCL);
    int msgid = msgget(_key,flags);
    if(msgid < 0){
    perror("msgget");
  }
 return msgid;
}


int createMsgQueue()
{
  return commMsgQueue(IPC_CREAT|IPC_EXCL|0666);
}
int getMsgQueue()
{
  return commMsgQueue(IPC_CREAT);
}


int destroyMsgQueue(int msgid)
{
  if(msgctl(msgid,IPC_RMID,NULL) < 0){
    perror("msgctl");
    return -1;
  }
 return 0;
}


int sendMsg(int msgid,int who,char* msg)
{
 struct msgbuf buf;
 buf.mtype = who;
 strcpy(buf.mtext,msg);
 if(msgsnd(msgid,(void*)&buf,sizeof(buf.mtext),0) < 0){
     perror("msgsnd");
     return -1;
  }
return 0;
}


int recvMsg(int msgid,int recvType,char out[])
{
  struct msgbuf buf;
  if(msgrcv(msgid,(void*)&buf,sizeof(buf.mtext),recvType,0)<0){
     perror("msgrcv");
     return -1;
  }
 strcpy(out,buf.mtext);
 return 0;
}

server.c:

#include"comm.h"

int main()
{
  int msgid = createMsgQueue();

  char buf[1024];
  while(1){
    buf[0] = 0;
    recvMsg(msgid,CLIENT_TYPE,buf);
    printf("client:%s\n",buf);

    printf("please enter:");
    fflush(stdout);
    ssize_t s = read(0,buf,sizeof(buf));
    if(s>0){
        buf[s-1] = 0;
        sendMsg(msgid,SERVER_TYPE,buf);
        //printf("send down,wait recv..\n");
     }
  }
 destroyMsgQueue(msgid);
 return 0;
#include"comm.h"

int main()
{
  int msgid = createMsgQueue();

  char buf[1024];
  while(1){
    buf[0] = 0;
    recvMsg(msgid,CLIENT_TYPE,buf);
    printf("client:%s\n",buf);

    printf("please enter:");
    fflush(stdout);
    ssize_t s = read(0,buf,sizeof(buf));
    if(s>0){
        buf[s-1] = 0;
        sendMsg(msgid,SERVER_TYPE,buf);
        //printf("send down,wait recv..\n");
     }
  }
 destroyMsgQueue(msgid);
 return 0;
}

client.c:

#include"comm.h"

int main()
{
  int msgid = getMsgQueue();

  char buf[1024];
  while(1){
    buf[0] = 0;
    printf("please enter:");
    fflush(stdout);
    ssize_t s = read(0,buf,sizeof(buf));
    if(s>0){
        buf[s-1] = 0;
        sendMsg(msgid,CLIENT_TYPE,buf);
        printf("send done,wait recv..\n");
     }
    recvMsg(msgid,SERVER_TYPE,buf);
    printf("server:%s\n",buf);
  }
 return 0;
}


共享記憶體:

共享記憶體是最快的IPC形式。它是在實體記憶體上開闢一塊空間讓兩個程序看到一份公共的資源。一旦這樣的記憶體對映到共享它的程序的地址空間,這些程序間資料傳遞不再涉及到核心,換句話是說程序不再通過執行進入核心的系統呼叫來傳遞彼此的資料。

1.共享記憶體的效率高是因為避免了來回拷貝。

2.生命週期隨核心,需手動刪除。

3.共享記憶體沒有同步與互斥機制,要使用必須自己實現互斥和同步。

共享記憶體函式:

shmget函式:
 功能:建立共享記憶體
 原型:
   int shmget(key_t key,size_t size,int shmflg);
返回值:成功返回一個非負整數,即該共享記憶體段的標識碼。失敗返回-1.

shmat函式:
 功能:將共享記憶體段連線到程序地址空間。g關聯。
 原型:
  void* shmat(int shmid,const void* shmaddr,int shmflg);
 返回值:
  成功返回一個指標,指向共享記憶體第一節。失敗返回-1.

shmdt:
  功能:將共享記憶體段與當前程序脫離。去關聯
  原型:
  int shmdt(const void* shmaddr);
  返回值:成功返回0,失敗返回-1.

shmctl函式:
  功能:控制共享記憶體。
  原型:
  ing shmctl(int shmid,int cmd,struct shmid_ds* buf);
  返回值:成功返回0,失敗返回-1.
例項程式碼:

makefile:

.PHONY:all
all:server client

client:client.c comm.c
        gcc -o [email protected] $^
server:server.c comm.c
        gcc -o [email protected] $^
.PHONY:clean
clean:
        rm -f client server

comm.h:

#ifndef __COMM_H__
#define __COMM_H__

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


int createShm(int size);
int destroyShm(int shmid);
int getShm(int size);


#endif

comm.c:

#include"comm.h"

int commShm(int size,int flags)
{
  key_t _key = ftok(".",0x6666);
  if(_key < 0 ){
     perror("ftok");
     return -1;
  }
 int shmid = 0;
 if((shmid = shmget(_key,size,flags)) < 0){
     perror("shmget");
     return -2;
  }
 return shmid;
}

int destroyShm(int shmid)
{
  if(shmctl(shmid,IPC_RMID,NULL) < 0){
     perror("shmctl");
     return -1;
  }
 return 0;
}
int createShm(int size)
{
  return commShm(size,IPC_CREAT|IPC_EXCL|0666);
}

int getShm(int size)
{
  return commShm(size,IPC_CREAT);
}

server.c:

#include"comm.h"

int main()
{
  int shmid = createShm(4096);

  char* addr = shmat(shmid,NULL,0);
  sleep(2);
  int i=0;
  while(i++ < 26){
    printf("client: %s\n",addr);
    sleep(1);
  }
 shmdt(addr);
 sleep(2);
 destroyShm(shmid);
 return 0;
}

client.c:

#include"comm.h"

int main()
{
  int shmid = getShm(4096);
  sleep(1);
  char* addr = shmat(shmid,NULL,0);
  sleep(2);
  int i=0;
  while(i<26){
    addr[i] = 'A'+i;
    i++;
    addr[i] = 0;
    sleep(1);
  }
 shmdt(addr);
 sleep(2);
 return 0;
}

值得注意的是,ctrl+c終止程序,再次啟動後,需要ipcs -m檢視共享記憶體。再用ipcrm -m shmid刪掉共享記憶體id。

相關推薦

程序程式設計之程序通訊-管道訊息佇列

1.程序間通訊 Linux作為一種新興的作業系統,幾乎支援所有的Unix下常用的程序間通訊方法:管道、訊息佇列、共享記憶體、訊號量、套介面等等。 2.管道 管道是程序間通訊中最古老的方式,它包括無名管道(或者匿名管道)和有名管道兩種,前者用於父程序和

程序通訊---管道訊息佇列

程序間通訊的目的:資料傳輸:一個程序需要將它的資料傳送給另一個程序資源共享:對個程序之間共享同樣的資源通知事件:一個程序需要向另一個或一組程序傳送訊息,通知它們發生了什麼事件程序控制:有些程序希望完全控制另一個程序的執行(如:Debug程序)程序間通訊的發展:管道:Syste

程序通訊---管道訊息佇列共享記憶體

程序通訊分為低階通訊和高階通訊。 低階通訊是指程序互斥與同步,包括訊號、訊號量、管程等。 高階通訊方式有管道、訊息佇列、共享記憶體以及網路通訊中的套接字。 匿名管道PIPE: 管道是連線兩個程序的檔案,

程序通訊的方式——訊號、管道訊息佇列共享記憶體

多程序: 首先,先來講一下fork之後,發生了什麼事情。 由fork建立的新程序被稱為子程序(child process)。該函式被呼叫一次,但返回兩次。兩次返回的區別是子程序的返回值是0,而父程序的返回值則是新程序(子程序)的程序 id。將子程序id返回給父程序的理由是

人工智慧(PythonNet)—— 程序通訊管道訊息佇列共享記憶體、訊號、訊號量、套接字)

一、程序間通訊        程序間通訊(IPC,InterProcess Communication)是指在不同程序之間傳播或交換資訊。        由於每個程序的空間是互相獨立的,程序之間無法互相直接獲取彼此的資源,故引入程序間通訊來實現程序間的資源互動。       

程序通訊(IPC)-管道訊息佇列共享記憶體、訊號、訊號量、套接字

多程序:首先,先來講一下fork之後,發生了什麼事情。由fork建立的新程序被稱為子程序(child process)。該函式被呼叫一次,但返回兩次。兩次返回的區別是子程序的返回值是0,而父程序的返回值則是新程序(子程序)的程序 id。將子程序id返回給父程序的理由是:因為一

作業系統(11)程序--程序通訊:訊號、管道訊息佇列共享記憶體

文章目錄 1. 程序通訊相關概念 1. 通訊流程、屬性、鏈路 2. 程序通訊方式:直接通訊、間接通訊 2. 程序通訊的機制 1. 訊號 2. 管道 3. 訊息佇列

程序通訊方式總結——訊息佇列

        Linux/Unix系統IPC是各種程序間通訊方式的統稱,但是其中極少能在所有Linux/Unix系統實現中進行移植。隨著POSIX和Open Group(X/Open)標準化的推進

Linux程序通訊之POSIX訊息佇列

訊息佇列可認為是一個訊息連結串列,它允許程序之間以訊息的形式交換資料。有足夠寫許可權的程序或執行緒可往佇列中放置訊息,有足夠讀許可權的程序或執行緒可從佇列中取走訊息。每個訊息都是一個記錄,它由傳送者賦予一個優先順序。與管道不同,管道是位元組流模型,沒有訊息邊界。

程序通訊(IPC)之訊息佇列

★IPC方法包括管道(PIPE)、訊息佇列(Message_Queue)、旗語、共用記憶體(ShareMemory)以及套接字(Socket)。進 程間通訊主要包括了管道、系統IPC(包括了訊息佇列、

Linux---程序通訊IPC之訊息佇列

**程序間通訊(IPC):**是指在不同程序之間傳播或交換資訊。 **IPC的方式:**通常有管道(無名管道、命名管道)、訊息佇列、訊號量、共享儲存、Socket、Streams等(Socket和Streams支援不同主機上的兩個程序IPC) 程序間通訊的目

管道訊息佇列共享記憶體、訊號量的特點

在之前我們已經瞭解過了程序間通訊的幾種方式:管道、訊息佇列、共享記憶體以及訊號量。今天我們就來總結一下這幾種方式的各自的特點分別是什麼,和他們相應的應用場景。也是對於這個知識點的一個複習和鞏固。 Q1

管道訊息佇列共享記憶體之間的區別與聯絡

 管道和訊息佇列的區別 管道(PIPE)      管道通訊方式的中間介質是檔案,通常稱這種檔案為管道檔案。兩個程序利用管道檔案進行通訊時,一個程序為寫程序,另一個程序為讀程序。寫程序通過寫

Linux:程序通訊(匿名管道命名管道)(共享記憶體訊息佇列訊號量)

目錄 程序間通訊的介紹 管道 匿名管道 原理: 程式碼實現 匿名管道特性 實現管道符 |  命名管道 命名管道特性 程式碼實現 管道讀寫規則 作業系統中ipc的相關命令 共享記憶體(重點) 生命週期: 程式碼實現 程式碼實現獲

Linux程序通訊--訊號管道訊息佇列訊號量共享記憶體,socket

Linux 傳統的程序間通訊有很多,如各類管道、訊息佇列、記憶體共享、訊號量等等。但它們都無法介於核心態與使用者態使用,原因如表 通訊方法 無法介於核心態與使用者態的原因 管道(不包括命名管道) 侷限於父子程序間的通訊。 訊息佇列 在硬、軟中斷中無法無阻塞地接收資料。 訊號量 無法介於核

程序通訊——管道訊息佇列共享記憶體

程序間通訊的本質是讓兩個不相干的程序看到同一份資源。這個資源是由作業系統提供的一個檔案。程序間通訊的目的:1.資料傳輸:一個程序需要將它 的資料傳送給另一個程序。2.資源共享:多個程序之間共享同樣的資源。3.通知事件:一個程序需要向另一個(組)程序傳送訊息,通知它們發生了

嵌入式Linux併發程式設計程序通訊方式System V IPC訊息佇列開啟/建立msgget(), 傳送訊息msgsnd()格式接收訊息msgrcv()控制訊息佇列 msgctl()

文章目錄 1,訊息佇列 2,訊息佇列結構 3,訊息佇列使用步驟 3.1,開啟/建立訊息佇列 msgget() 3.1.1,開啟/建立訊息佇列---示例msgget() 3.2,向訊息佇列傳送訊息 msgs

程序通訊訊息佇列共享記憶體方式的實現

共享記憶體方式使用QSharedMemory 和QSystemSemaphore兩個類實現 一個程序往共享記憶體空間中寫,一個程序往共享記憶體空間中讀 兩程序通訊時: 向共享記憶體中提供資料的一方: 1,定義QSharedMemory shareMemory,並設定標誌名shareMemory.setKey(

管道訊息佇列共享記憶體之間的區別和聯絡

程序間通訊的目的:                  資料傳輸:一個程序需要將它的資料傳送給另一個程序,傳送的資料量在一個位元組到幾兆位元組之間。        共享資料:多個程序想要操作共享資料,一個程序對共享資料的修改,別的程序應該立刻看到。        通知事件:一

Linux-程序通訊-訊息佇列/訊號燈/共享記憶體

訊息佇列     訊息佇列提供了程序間傳送資料塊的方法,每個資料塊都可以被認為是有一個型別,接受者接受的資料塊可以有不同的型別;我們可以通過傳送訊息來避免命名管道的同步和阻塞問題;訊息佇列與命名管道一樣,每個資料塊都有一個最大長度的限制;我們可以將每個資料塊當作是一