Linux環境下的程序間的通訊
程序間通訊的方式
管道
- 管道是Linux作業系統下的一種檔案型別,該檔案型別的檔案就是為了給不同程序之間提供可以進行通訊的檔案資源。
- 一個管道只能單向通訊。
為什麼一個管道只能進行單向通訊?
一個程序可以對一個管道檔案進行讀或寫操作,當兩個程序通過一個管道進行通訊時,一定是有一個程序對管道進行寫操作,另一個管道進行讀操作,這樣就可以實現程序之間的單向通訊,但假如兩個程序同時將讀寫埠都開啟時,兩個程序都可以進行讀或寫操作,這樣就會造成程序之間讀取資訊紊亂,所以一個管道只能進行單向通訊,如果想要以管道的方式實現兩個程序之間雙向通訊,就必須實現兩個管道。
管道操作
圖解管道操作(以匿名管道為例):
1.父程序建立管道
2.父程序fork出子程序
3.子程序關閉寫端,父程序關閉讀端
所以我們可以將管道視為檔案,並且使用檔案操作的方式去操作管道。
匿名管道
- 匿名管道只能用於有親緣關係的兩個程序之間的通訊,通常用於子程序與父程序。
- 匿名管道屬於管道的一種,它同樣只可以單向通訊。
- 匿名管道的生命週期隨程序。
- 匿名管道是面向位元組流的。
- 作業系統核心會對匿名管道進行同步於互斥。
介面
#include<unistd.h>
int pipe(int fd[2]);
介面功能:建立一個匿名管道檔案
引數:檔案描述符陣列,fd[0]表示讀端,fd[1]表示寫端
程式碼實現匿名管道
/*
*子程序向管道內寫入10次,i am child...
*父程序每隔1s從管道本讀出一句i am child...
*該程序的子程序與父程序未設定退出邏輯,需要使用[Ctrl+C]終止程序
*/
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
#include <string.h>
void test_pipe()
{
int fd[2];
if(-1 == pipe(fd))//匿名管道建立失敗
{
exit(EXIT_FAILURE);
}
pid_t id = fork();
char buf[64];
if(-1 == id)//子程序建立失敗
{
exit(EXIT_FAILURE);
}
else if(0 == id)//子程序
{
int i = 0;
close(fd[0]);//關閉子程序讀端
strcpy(buf, "i am child...\n");
while(i < 10)
{
write(fd[1], buf, sizeof(buf));
i += 1;
}
while(1);//子程序寫入完成時不退出,管道寫端不關閉
close(fd[1]);
}
else//父程序
{
close(fd[1]);//關閉父程序的寫端
while(1)
{
read(fd[0], buf, sizeof(buf));//子程序從管道讀資料
printf("%s\n", buf);
}
while(wait(NULL)!=id);//等待子程序退出
close(fd[0]);//關閉讀端
}
}
int main()
{
test_pipe();
return 0;
}
執行結果:
i am child...
i am child...
i am child...
i am child...
i am child...
i am child...
i am child...
i am child...
i am child...
i am child...
命名管道
- 命名管道可以應用於兩個毫不相干的程序之間的通訊。
- 命名管道是一種特殊型別的檔案。
介面
#include<sys/types.h>
#include<sys/stat.h>
int mkfifo(const char* pathname, mode_t mode);
功能:在pathname路徑下,建立一個命名管道檔案
引數:
pathname:路徑資訊,表示管道檔案將會建立在pathname路徑下,不支援~
mode:管道檔案許可權資訊,通常配合umask(mode_t mode);使用
命名管道與匿名管道的區別
- 匿名管道只能用於具有親緣關係的程序之間的通訊,命名管道可以用於任意兩個程序之間的通訊。
- 匿名管道用pipe()函式建立並開啟,命名管道通過mkfifo()函式建立,由open()函式開啟
程式碼實現命名管道
通過命名管道實現client程序向service程序傳送文字資料,該管道由service建立並銷燬,client只打開並寫入文字資料即可。client.c service.c pipe.h必須放在同一級目錄下。
模程式碼結構
$ ls
service.c client.c pipe.h Makefile
$ cat Makefile
.PHONY:all
all:service client
service:service.c
gcc $^ -o [email protected]
client:client.c
gcc $^ -o [email protected]
.PHONY:clean
clean:
rm -f service client
/*
*pipe.h
*/
#ifndef _PIPE_H_
#define _PIPE_H_
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>
#define PATHNAME "/tmp/pipe_withname"
#define BUF_SIZE 1024
#denif
/*
*service.c
*/
#include"./pipe.h"
int main()
{
int fd;
char buf[BUF_SIZE] = {0};
umask(0);
if(-1 == mkfifo(PATHNAME, 0644))//建立管道檔案,檔案許可權為-rw-r--r--
{
exit(EXIT_FAILURE);//若管道建立失敗,則失敗退出
}
if(-1 == (fd = open(PATHNAME, O_RDONLY)))//只讀開啟管道檔案
{
exit(EXIT_FAILURE);//如果開啟失敗,則失敗退出
}
while(1)
{
ssize_t s;
printf("please wait...\n");
s = read(fd, buf, sizeof(BUF_SIZE));
if(s <= 0)//s小於0,則讀取出現異常
break;
if(0 == strcmp(buf, "quit\n"))//若client傳送quit文字,則正常退出
{
printf("quit signal, Bye...\n");
break;
}
printf("client said: %s", buf);
fflush(stdout);
}
close(fd);
return 0;
}
/*
*client.c
*/
#include"./pipe.h"
int main()
{
int fd;
char buf[BUF_SIZE] = {0};
if(-1 == (fd = open(PATHNAME, O_WRONLY)))//只寫開啟管道檔案
{
exit(EXIT_FAILURE);//如開啟失敗,則失敗退出
}
while(1)
{
printf("client: ");
fflush(stdout);
fgets(buf, sizeof(buf), stdin);//從鍵盤獲取字串
write(fd, buf, sizeof(buf));//將獲取到的字串寫進管道
if(strcmp(buf, "quit\n"))
break;
}
close(fd);
}
管道讀寫規則
-
當沒有資料可讀時:
- O_NONBLOCK disable:read呼叫阻塞,程序暫停執行,一直等到有資料來為止。
- O_NONBLOCK enable:read返回-1,errno值為EAGAIN。
-
當管道寫滿時:
- O_NONBLOCK disable:write呼叫阻塞,直到有程序讀走資料。
- O_NONBLOCK enable:write呼叫返回-1,errno值為EAGAIN。
-
如果所有管道對應的寫端檔案描述符被關閉,則read返回0。
-
如果所有管道對應的讀端檔案描述符被關閉,則write操作會產生訊號SIGPIPE,可能會導致write退出。
-
當要寫入的資料不大於PIPE_BUF時,Linux保證寫入資料的原子性。
-
當要寫入的資料大於PIPE_BUF時,Linux不再保證寫入資料的原子性。
訊息佇列
什麼是訊息佇列
- 訊息佇列是作業系統核心提供的一個用於程序雙方通訊的佇列(連結串列)
- 程序向訊息佇列中傳送的資料塊中包含一個特有的型別,類似於標籤,用來表明該資料塊來自哪個程序。(因為程序向訊息佇列中傳送的資料塊不能被自己拿走)
- 訊息佇列每個訊息的最大長度是有上限的(MSGMAX),每個訊息佇列的總的位元組數是有上限的(MSGMNB),系統中訊息佇列的總數也是有上限的(MSGMNI)。
- 訊息佇列的生命週期是隨系統的。
IPC物件資料結構 /usr/include/linux/ipc.h
struct ipc_perm{
key_t _key; /* Key supplied to xxxget(2) */
uid_t uid; /* Effective UID of owner */
gid_t gid; /* Effective GID of owner */
uid_t cuid; /* Effective UID of Creater */
gid_t cgid; /* Effective GID of Creater */
unsigned short mode; /* Permissions */
unsigned short _seq; /* Sequeue number */
};
- _key:用來判斷兩個程序看到的訊息佇列是否為同一個訊息佇列,每一個訊息佇列的_key值是唯一的。
- 進行通訊的兩個程序獲取訊息佇列_key值的方式必須是相同的。
- ipc資源的生命週期跟隨核心的。
- ipcs -q :檢視系統中現存的訊息佇列
- ipcrm -q msgid :刪除訊息佇列
訊息佇列資料結構 /usr/include/linux/msg.h
struct msqid_ds {
struct ipc_perm msg_perm;
struct msg *msg_first; /* first message on queue,unused */
struct msg *msg_last; /* last message in queue,unused */
__kernel_time_t msg_stime; /* last msgsnd time */
__kernel_time_t msg_rtime; /* last msgrcv time */
__kernel_time_t msg_ctime; /* last change time */
unsigned long msg_lcbytes; /* Reuse junk fields for 32 bit */
unsigned long msg_lqbytes; /* ditto */
unsigned short msg_cbytes; /* current number of bytes on queue */
unsigned short msg_qnum; /* number of messages in queue */
unsigned short msg_qbytes; /* max number of bytes on queue */
__kernel_ipc_pid_t msg_lspid; /* pid of last msgsnd */
__kernel_ipc_pid_t msg_lrpid; /* last receive pid */
};
訊息佇列中訊息塊的資料結構(自己定義)
struct msgbuf{
long mytype;
char mtext[1];
};
- mtype:指明訊息型別,它必須大於0,類似於標籤,用來表明該資料塊來自哪個程序。
- mtext[1]:存放訊息資料,該陣列的大小可以自行定義,但不能超過系統規定的上限值。
介面:
//建立/開啟訊息佇列
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
int msgget(key_t key, int msgflg);
- key:用來判斷兩個程序看到的訊息佇列是否為同一個訊息佇列,每一個訊息佇列的_key值是唯一的。通常該處呼叫key_t ftok()函式來設定唯一鍵值。
- msgflg:
- IPC_CREAT|IPC_EXEL:建立訊息佇列,如果想要建立的訊息佇列在底層已經存在,則出錯返回;如果要建立的訊息佇列在底層不存在,則建立該訊息佇列,但不開啟。
- IPC_CREAT:開啟訊息佇列,如果想要開啟的訊息佇列在底層已經存在,則開啟該訊息佇列。
- 返回值:成功返回訊息佇列的識別符號;失敗返回-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:表示要對訊息佇列所做的操作
- IPC_RMID:立即刪除訊息佇列
- IPC_STAT:
- buf:屬性資訊,若要刪除訊息佇列,此處可以傳遞NULL
//向訊息佇列傳送資料
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
int msgsnd(int msqid, const void* msgp, size_t msgsz, int msgflg);
- msqid:訊息佇列識別符號。
- msgp:是一個指標,指向準備傳送的訊息。
- msgsz:是msgp指向的訊息⻓度,這個⻓度不含儲存訊息型別的那個long int⻓整型。
- msgflg:控制當佇列存滿或達到系統上限時要做的事情
- IPC_NOWAIT表示佇列滿不等待,返回EAGAIN錯誤
- 返回值:成功返回0;失敗返回-1。
//從訊息佇列取出資料
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
ssize_t msgrcv(int msgid, void* msgp, size_t msgsz, long msgtyp, int msgflg);
- msgid:訊息佇列識別符號。
- msgp:是⼀個指標,指標指向準備接收的訊息。
- msgsz:是msgp指向的訊息⻓度,這個⻓度不含儲存訊息型別的那個long int⻓整型
- msgtype:它可以實現接收優先順序的簡單形式。
- msgtype=0返回佇列第⼀條資訊
- msgtype>0返回佇列第⼀條型別等於msgtype的訊息
- msgtype<0返回佇列第⼀條型別⼩於等於msgtype絕對值的訊息,並且是滿⾜條件的訊息型別最⼩的訊息
- msgflg:控制著佇列中沒有相應型別的訊息可供接收時將要發⽣的事
- msgflg=IPC_NOWAIT,佇列沒有可讀訊息不等待,返回ENOMSG錯誤
- msgflg=MSG_NOERROR,訊息⼤⼩超過msgsz時被截斷
- msgtype>0且msgflg=MSG_EXCEPT,接收型別不等於msgtype的第⼀條訊息
- 返回值:成功返回實際放到接收緩衝區⾥去的字元個數,失敗返回-1
封裝訊息佇列介面
/*
* 注意:1.接受訊息時,如果訊息佇列裡沒有符合要求的訊息,則會發生堵塞,直至訊息佇列中出現符和要求的訊息後並讀取它
*/
#ifndef _MESSAGE_H_
#define _MESSAGE_H_
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/stat.h>
#include<stdio.h>
#include<string.h>
#define PATHNAME "."
#define PRO_ID 0x6666
/*
* type定義的值必須大於0
*/
#define SERVICE_TYPE 1
#define CLIENT_TYPE 2
struct msg_buf
{
long type;
char date[1024];
};
int CreatMsg();//建立訊息佇列,成功返回訊息佇列msgid,失敗返回-1
int DestoryMsg(const int msgid);//刪除訊息佇列,成功返回0,失敗返回-1
int OpenMsg();//開啟訊息佇列,開啟成功返回訊息佇列msgid,失敗返回-1
int SendMsg(int msgid,long who ,char* msg);//向訊息佇列發訊息
int RecMsg(int msgid, long who,char msg[]);//從訊息佇列接受訊息,成功返回0,並將訊息寫入msg,失敗返回-1
#endif
//message.c
#include"./message.h"//使用時需確保標頭檔案與message.c檔案在同一級目錄下
int CreatMsg()//建立訊息佇列,成功返回訊息佇列msgid,失敗返回-1
{
umask(0);
return msgget(ftok(PATHNAME, PRO_ID),IPC_CREAT | IPC_SET | 0666);
}
int OpenMsg()//開啟訊息佇列
{
return msgget(ftok(PATHNAME, PRO_ID),IPC_CREAT);
}
int SendMsg(int msgid,long who ,char* msg)
{
struct msg_buf buf;
buf.type = who;
strcpy(buf.date, msg);
if(0 > msgsnd(msgid, (void*)&buf,sizeof(buf.date),0))
{
perror("msgsnd");
return -1;
}
return 0;
}
int RecMsg(int msgid, long who, char msg[])//從訊息佇列接受訊息
{
struct msg_buf buf;
buf.type = who;
printf("Please wait...\n");
if(-1 == msgrcv(msgid, (void*)&buf, sizeof(buf.date), buf.type,0))
{
return -1;
}
strcpy(msg, buf.date);
return 0;
}
int DestoryMsg(const int msgid)//刪除訊息佇列,成功返回0,失敗返回-1
{
return msgctl(msgid, IPC_RMID, NULL);
}
通過封裝好的訊息佇列介面實現client&service互相通訊
-
service.c & client.c 需要和標頭檔案在同一級目錄下
目錄結構
$ ls service.c client.c message.c message.h Makefile $ cat Makefile .PHONY:all all:service client service:service.c message.c gcc $^ -o [email protected] client:client.c message.c gcc $^ -o [email protected] .PHONY:clean clean: rm -f service client
//service.c
#include"./message.h"
int main()
{
int msgid;
char rec_msg[1024] = {0};
char msg[1024] ={0};
if(-1 != (msgid = CreatMsg()))
{
printf("CreatMsg success...\n");
}
if(-1 != (msgid = OpenMsg()))
{
printf("OpenMsg success...\n");
}
while(1)
{
RecMsg(msgid, CLIENT_TYPE, rec_msg);
if(0 == strcmp(rec_msg, "quit\n"))
{
printf("quit signal,Bye...\n");
break;
}
printf("client$ %s",rec_msg);
memset(rec_msg, 0, sizeof(rec_msg));
printf("service# ");
fflush(stdout);
memset(msg, 0, sizeof(msg));
fgets(msg, sizeof(msg), stdin);
SendMsg(msgid, SERVICE_TYPE, msg);
}
DestoryMsg(msgid);
return 0;
}
//client.c
#include"./message.h"
int main()
{
int msgid;
char msg[1024] = {0};
char rec_msg[1024] = {0};
if(-1 != (msgid = OpenMsg()))
{
printf("OpenMsg success...\n");
}
while(1)
{
printf("client# ");
fflush(stdout);
memset(msg, 0, sizeof(msg));
fgets(msg, sizeof(msg)
相關推薦
Linux系統下-程序間通訊(訊息佇列-詳解)
Linux下程序間通訊方式:
# 管道( pipe ):管道是一種半雙工的通訊方式,資料只能單向流動,而且只能在具有親緣關係的程序間使用。程序的親緣關係通常是指父子程序關係。
# 有名管道 (named pipe) : 有名管道也是半雙工的通訊方式,但是它允許無親緣關係程序
Linux環境程式設計程序間通訊機制理解
一、Linux系統呼叫主要函式
二、建立程序
1、建立子程序系統呼叫fork()
2、驗證fork()建立子程序效果
3、系統呼叫fork()與掛起系統呼叫wait()
三、模擬程序管道通訊
四、pipe()下生產者與消費者問題
總結
一、Linux系統呼叫主要函式
首先,認識一下Linux下系統
Linux下程序間通訊方式 - UNIX Domain Socket
概述 Linux下程序通訊方式有很多,比較典型的有套接字,平時比較常用的套接字是基於TCP/IP協議的,適用於兩臺不同主機上兩個程序間通訊, 通訊之前需要指定IP地址. 但是如果同一臺主機上兩個程序間通訊用套接字,還需要指定ip地址,有點過於繁瑣. 這個時候就需要用到UNIX Domain Sock
linux下程序間通訊的幾種主要方式簡介
共享記憶體是執行在同一臺機器上的程序間通訊最快的方式,因為資料不需要在不同的程序間複製。通常由一個程序建立一塊共享記憶體區,其餘程序對這塊記憶體區進行讀寫。共享記憶體往往與其它通訊機制,如訊號量結合使用,來達到程序間的同步及互斥。
首先要用的函式是shmget,它獲得一個共享儲存識別符號。
Linux 下程序間通訊機制(六) 訊號燈集 Semaphore Arrays
訊號燈集函式定義如下:
#include <sys/sem.h>
int semctl(int sem_id, int sem_num, int command, ...);
int semget(key_t key, int num_sems, int sem
Linux下程序間通訊的六種機制詳解
linux下程序間通訊的幾種主要手段:
1.管道(Pipe)及有名管道(named pipe):管道可用於具有親緣關係程序間的通訊,有名管道克服了管道沒有名字的限制,因此,除具有管道所具
Linux下程序間通訊方式
概述
Linux下程序通訊方式有很多,比較典型的有套接字,平時比較常用的套接字是基於TCP/IP協議的,適用於兩臺不同主機上兩個程序間通訊, 通訊之前需要指定IP地址. 但是如果同一臺主機上兩個程序間通訊用套接字,還需要指定ip地址,有點過於繁瑣. 這個時候就需要用到UNIX
Linux下程序間通訊方式之管道、訊號、共享記憶體、訊息佇列、訊號量、套接字
/*
1,程序間通訊 (IPC ) Inter-Process Communication
比較好理解概念的就是程序間通訊就是在不同程序之間傳播或交換資訊。
2,linux下IPC機制的分類:管道、訊號、共享記憶體、訊息佇列、訊號量、套接字
3,這篇主要說說管
linux通過訊號程序間通訊例項理解
在linux系統程式設計的學習中,通過訊號進行程序簡編譯是一大重點 本文通過一個例項加強對sigemptyset、sigprocmask的理解
int sigaction(int signum, const struct sigaction *act,struct sigactio
Linux系統程式設計——程序間通訊:共享記憶體
概述
共享記憶體是程序間通訊中最簡單的方式之一。共享記憶體允許兩個或更多程序訪問同一塊記憶體,就如同 malloc() 函式向不同程序返回了指向同一個實體記憶體區域的指標。當一個程序改變了這塊地址中的內容的時候,其它程序都會察覺到這個更改。
共享記憶體的特點:
1)共
Linux系統程式設計——程序間通訊:訊息佇列
概述
訊息佇列提供了一種在兩個不相關的程序之間傳遞資料的簡單高效的方法,其特點如下:
1)訊息佇列可以實現訊息的隨機查詢。訊息不一定要以先進先出的次序讀取,程式設計時可以按訊息的型別讀取。
2)訊息佇列允許一個或多個程序向它寫入或者讀取訊息。
3)與無名管道、命名管道一
Linux系統程式設計——程序間通訊:管道(pipe)
管道的概述
管道也叫無名管道,它是是 UNIX 系統 IPC(程序間通訊) 的最古老形式,所有的 UNIX 系統都支援這種通訊機制。
無名管道有如下特點:
1、半雙工,資料在同一時刻只能在一個方向上流動。
2、資料只能從管道的一端寫入,從另一端讀出。
Linux系統程式設計——程序間通訊:訊號中斷處理
什麼是訊號?
訊號是 Linux 程序間通訊的最古老的方式。訊號是軟體中斷,它是在軟體層次上對中斷機制的一種模擬,是一種非同步通訊的方式 。訊號可以導致一個正在執行的程序被另一個正在執行的非同步程序中斷,轉而處理某一個突發事件。
“中斷”在我們生活中經常遇到,譬如,我正在
Linux系統程式設計——程序間通訊概述
程序是一個獨立的資源分配單元,不同程序(這裡所說的程序通常指的是使用者程序)之間的資源是獨立的,沒有關聯,不能在一個程序中直接訪問另一個程序的資源(例如開啟的檔案描述符)。
但是,程序不是孤立的,不同的程序需要進行資訊的互動和狀態的傳遞等,因此需要程序間通訊( IPC:Inter Pr
Linux環境下程序和計劃任務管理
程式時儲存在外部儲存介質(如硬碟)中的可執行機器程式碼和資料的靜態集合,而程序實在CPU及記憶體中處於動態執行狀態的計算機程式。在Linux系統中,每個程式啟動後可以建立一個或多個程序。
1
ps命令——檢視靜態的程序統計資訊
ps命令是Linux系統中最為常用的程
【linux開發】程序間通訊命名管道-共享記憶體-記憶體對映-訊息佇列-訊號量
程序間通訊命名管道-共享記憶體-記憶體對映-訊息佇列-訊號量
在Unix平臺上,建立命名管道是建立了一個fifo檔案,和在shell下面用mkfifo命令的效果是一樣的。看起來這個管道檔案就是一個普通的檔案系統瓜掛載點,但是它只不過是作為一個名稱存在,實際的內容是一塊系統
Linux socket本地程序間通訊
使用套接字除了可以實現網路間不同主機間的通訊外,還可以實現同一主機的不同程序間的通訊,且建立的通訊是雙向的通訊。socket程序通訊與網路通訊使用的是統一套介面,只是地址結構與某些引數不同。
一、建立socket流程
(1)建立socket,型別為AF_LOCAL或A
Linux Pipe (程序間通訊,生產者消費者)
PIPE是Linux下可以用來實現程序間通訊的一種手段,當我們呼叫pipe系統呼叫時,將會產生一對檔案描述符,fd[0]可以用來讀,fd[1]用來寫,fd[1]寫的資料將會在fd[0]中讀到,我們稱之為管道。程序之間可以依賴管道的方式實現程序間通訊,pipe是半
linux (五)程序間通訊(匿名管道,命名管道,訊息佇列)
程序間通訊
程序間通訊的目的
資料傳輸:一個程序需要將他的資料傳送給另一個程序
資源共享:多個程序之間共享同樣的資源
通知事件:一個程序需要向另一個或一組程序傳送訊息,通知它發生了某種事件(如程序終止時要通知父程序)
程序控制:有寫程序希望完全控制另一
《Linux程式設計》——程序間通訊:管道
一、管道
1、管道:當從一個程序連結資料到另一個程序時,我們使用術語管道。通常把一個程序的輸出通過管道連線到另一個程序的輸入。
2、程序管道
1)、popen函式
#include<stdio.h>
FILE *