天天看點

UNIX-Linux程序間通信基本概念傳統的程序間通信——管道XSI程序間tongxin共享記憶體消息隊列信号量

UNIX-Linux程序間通信

  • 基本概念
    • 程序間通信
    • 程序間為什麼需要通信
    • 程序間通信方式的分類
  • 傳統的程序間通信——管道
    • 有名管道(建立實體檔案)
    • 無名管道
  • XSI程序間tongxin
    • IPC辨別
    • IPC鍵值
    • IPC對象的建立用到的宏
    • IPC對象銷毀/控制用到的宏
  • 共享記憶體
  • 消息隊列
  • 信号量

基本概念

程序間通信

程序間通信是指兩個或多個程序之間交換資料的過程。

程序間為什麼需要通信

當需要多個程序協同工作高效率完成任務時,因為每個程序都是獨立個體(程序是資源機關),程序之間需要進行通信。

程序間通信方式的分類

1、簡單程序間通信:指令行參數、環境變量表、信号、檔案

2、傳統程序間通信:管道

3、XSI程序間通信:共享記憶體、消息隊列、信号量

4、網絡程序間通信:socket

傳統的程序間通信——管道

管道是UNIX系統最古老的程序間通信方式(基本上不再使用),曆史上的管道通常是半雙工(隻允許單項資料流動),現在的系統大都可以全雙工。資料可以雙向流動。

有名管道(建立實體檔案)

指令:mkfifo
函數:
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
功能:建立管道檔案
pathname:檔案路徑
mode:權限
傳回值:成功傳回0失敗傳回-1.
程式設計模型:
程序A					程序B
建立管道(mkfifo)			...
打開管道(open)			打開管道 
讀/寫資料(read)			讀寫資料
關閉管道(close)			關閉管道 
删除管道(unlink)			...
           

無名管道

#include <unistd.h>
int pipe(int pipefd[2]);
功能:建立無名管道
pipefd:用來存儲核心傳回的檔案描述符。
pipefd[0]:用于讀操作
pipefd[1]:用于寫操作
           

XSI程序間tongxin

x/open組織位UNIX系統設計一套程序間通信機制,有共享記憶體、消息隊列、信号量

IPC辨別

  • 核心會為每個XSI程序通信對象維護一個IPC對象(XSI對象)。
  • 該對象通過一個非負整數來引用(類似檔案描述符)。
  • 與檔案描述符不同的是,每用一個IPC對象辨別符就持續+1,達到最大值是再從零開始。
  • IPC辨別需要程式員自己建立。

IPC鍵值

建立IPC鍵值的依據(類似建立檔案時的檔案名),也是一個非負整數。

1、自定義(不建議,可能會沖突)

2、自動生成。

#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
注意:項目路徑一定要是有效路徑,生成IPC鍵值依靠的是路徑而不是字元串。
           

IPC對象的建立用到的宏

IPC_PRIVATE	建立IPC對象時永遠建立成功。
IPC_CREAT	對象存在則擷取,不存在則建立
IPC_EXCL	如果對象已經存在則建立失敗
           

IPC對象銷毀/控制用到的宏

IPC_STAT	擷取IPC對象的屬性
IPC_SET		設定IPC對象的屬性
IPC_RMID	删除IPC對象
           

共享記憶體

共享記憶體就是核心中開辟一塊記憶體由IPC對象管理,程序A和程序B都用自己的虛拟位址與它進行映射,這樣他就共享了同一塊記憶體,然後就可以通信了。

特點:

1、不需要複制資訊,是最快的一種程序間通信機制。

2、需要考慮同步問題(必須借助其他的機制,如信号)。

程式設計模型:

程序A								程序B
生成IPC 鍵值	ftok			生成IPC鍵值ftok
建立共享記憶體	shmget	擷取共享記憶體
映射共享記憶體	shmat		映射共享記憶體
使用共享記憶體	*ptr			使用共享記憶體
取消映射			shmdt		取消映射
删除共享記憶體	shmctl		...

#include <sys/types.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
功能:建立或擷取共享記憶體
key:IPC鍵值,由ftok函數生成
size:共享記憶體的大小,最好是4096的整數倍,擷取共享記憶體時,此值無效。
shmflg:
	0 擷取共享記憶體
	IPC_CREAT	建立
	IPC_EXCL	如果存在則建立失敗
傳回值:成功傳回共享記憶體辨別,失敗傳回-1

void *shmat(int shmid, const void *shmaddr, int shmflg);
功能:映射共享記憶體
shmid:共享記憶體辨別符,shmget函數的傳回值。
shmaddr:程序提供的虛拟位址,與核心中的記憶體映射用的,也可以是NULL(核心會自動選擇一個位址映射)。
shmflg:
	SHM_RDONLY	隻讀權限
	SHM_RND		當shmaddr不為空時自動選擇一個位址映射
傳回值:映射成功後的虛拟位址失敗傳回-1。

int shmdt(const void *shmaddr);
功能:取消虛拟位址與共享記憶體的映射
shmaddr:被映射過的虛拟位址
傳回值:成功傳回0失敗傳回-1

int shmctl(int shmid, int cmd, struct shmid_ds *buf);
功能:删除共享記憶體,擷取/設定共享記憶體的屬性
shmid:共享記憶體辨別符shmget的傳回值
cmd:
	IPC_STAT	擷取共享記憶體的屬性
	IPC_SET		設定共享記憶體的屬性
	IPC_RMID	删除共享記憶體
struct shmid_ds {
    struct ipc_perm shm_perm;    /* 記憶體所有者及權限 */
    size_t          shm_segsz;   /* 記憶體的大小,以位元組數為機關 */
    time_t          shm_atime;   /* 最後映射時間 */
    time_t          shm_dtime;   /* 最後取消映射的時間 */
    time_t          shm_ctime;   /* 最後修改時間 */
    pid_t           shm_cpid;    /* 建立者的程序id */
    pid_t           shm_lpid;    /* 最後映射/取消映射的程序ID */
    shmatt_t        shm_nattch;  /* 映射的次數 */
    ...
    };
struct ipc_perm {
    key_t          __key;    /* 建立共享記憶體IPC的鍵值 */
    uid_t          uid;      /* 有效使用者ID */
    gid_t          gid;      /* 有效組ID */
    uid_t          cuid;     /* 建立者的使用者ID */
    gid_t          cgid;     /* 建立者的組ID */
    unsigned short mode;     /* 權限 */
    unsigned short __seq;    /* 對象ID */
       };
           

消息隊列

消息隊列就是由核心管理的一個管道,可以按順序發送消息包(消息類型+消息内容),可以全雙工工作,可以不按消息的順序接收。

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
功能:建立/擷取消息隊列
key:IPC鍵值,由ftok函數生成
msgflg:建立的标志位和權限
	0 擷取消息隊列
	IPC_CREAT	不存在則建立消息隊列,存在則擷取
	IPC_EXCL	如果存在則建立失敗
傳回值:消息隊列辨別,類似檔案描述符

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
功能:向消息隊列發送消息
msqid:消息隊列辨別,msgget函數的傳回值
msgp:結構指針
	struct msgbuf {
        long mtype;       /* 消息類型 */
        char mtext[1];    /* 消息内容 */
       };
msgsz:消息的長度,不包括消息類型,sizeof(msgbuf)-sizeof(msgbuf.mtype).
msgflg:
	0 阻塞,當消息隊列滿時,等待
	IPC_NOWAIT=1 不阻塞,當消息隊列滿時,不等待
	
傳回值:成功發送傳回0失敗傳回-1(傳回-1的兩種情況1、id有問題。2、隊列滿了)

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
功能:從消息隊列中按消息類型擷取消息擷取消息
msqid:消息隊列辨別
msgp:消息結構指針
	struct msgbuf {
        long mtype;       /* 要接收消息類型 */
        char mtext[1];    /* 消息内容 */
       };
msgsz:要接收消息長度,可以長一些。
msgtyp:要接收消息類型
	0	辨別接收任意類型的消息(接收隊列中的第一個消息)
	>0	隻接收msgtyp類型的消息
	<0	接收消息隊列中小于等于msgtyp絕對值的消息,取最小的那個。
msgflg:
	0 阻塞,消息隊列中是否有對應類型的消息,沒有則等待。
	1 不阻塞,消息隊列中沒有對應類型的消息,傳回。
	MSG_NOERROR:
		消息類型正确,而消息的實際長度大于msgsz,則不接受消息,并傳回-1
		如果msgflg帶MSG_NOERROR标志,則把多餘的消息截取,成功接收
	IPC_NOWAIT:如果消息隊列中沒有要接收的消息,則不等待,傳回-1.
	MSG_EXCEPT:接收消息隊列中第一個消息類型不是msgtyp的消息,編譯時添加參數 -D_GNU_SOURCE (gcc -D_GNU_SOURCE b.c)
傳回值:成功接收消息的位元組數

int msgctl(int msqid, int cmd, struct msqid_ds *buf);
功能:删除消息隊列,設定或擷取消息隊列的屬性
msqid:消息隊列辨別
cmd:
	IPC_STAT	擷取消息隊列的屬性
	IPC_SET		設定消息隊列的屬性
	IPC_RMID	删除消息隊列
傳回值:成功傳回0失敗傳回-1.
struct msqid_ds {
    struct ipc_perm msg_perm;     /* 權限 */
    time_t          msg_stime;    /* 最後一個消息發送時間 */
    time_t          msg_rtime;    /* 最後一次接收消息的時間 */
    time_t          msg_ctime;    /* 最後一次修改時間 */
    unsigned long   __msg_cbytes; /* 消息隊列中的位元組數 */
    msgqnum_t       msg_qnum;     /* 消息隊列中消息的個數 */
    msglen_t        msg_qbytes;   /* 消息隊列中能容納的最大位元組數 */
    pid_t           msg_lspid;    /* 最後一次發送消息的程序 */
    pid_t           msg_lrpid;    /* 最後一次接收消息的程序 */
    };
struct ipc_perm {
    key_t          __key;    /* 建立共享記憶體IPC的鍵值 */
    uid_t          uid;      /* 有效使用者ID */
    gid_t          gid;      /* 有效組ID */
    uid_t          cuid;     /* 建立者的使用者ID */
    gid_t          cgid;     /* 建立者的組ID */
    unsigned short mode;     /* 權限 */
    unsigned short __seq;    /* 對象ID */
       };
           

信号量

信号量是核心維護的計數器,用于管理多程序之間共享資源。

程式設計模型:

程序A										程序B
建立信号量				semget	擷取信号量
初始化信号量的值	semctl		...
加減信号量				semop		加減信号量
删除信号量				semctl		...

注意:信号量是用來計數的,一定要與資源對應。
           

繼續閱讀