进程间通信-system-v
文章目錄
- System V IPC概述
- 消息隊(duì)列
- 創(chuàng)建或打開消息隊(duì)列
- 消息控制
- 發(fā)送消息
- 接收消息
- 發(fā)送消息示例:
- 共享內(nèi)存
- 創(chuàng)建共享內(nèi)存
- 共享內(nèi)存控制
- 共享內(nèi)存映射和解除映射
- 共享內(nèi)存使用示例:
- 信號量
- 進(jìn)程間信號量
- 創(chuàng)建信號量集
- 信號量集控制
- 信號量集操作
- 信號量模塊封裝示例:
System V IPC概述
- System V引入了三種高級進(jìn)程間通信機(jī)制
-
IPC對象(消息隊(duì)列、共享內(nèi)存和信號量)存在于內(nèi)核而不是文件系統(tǒng)中,由用戶控制釋放(用戶管理IPC對象的生命周期),不像管道的釋放由內(nèi)核控制。
-
IPC對象通過其標(biāo)識來引用和訪問,所有的IPC對象在內(nèi)核空間中唯一性標(biāo)識ID,在用戶空間中的唯一性標(biāo)識稱為Key.
-
IPC對象是全局對象,可以通過ipcs,ipcrm等命令來查看或刪除
-
每個(gè)IPC對象都由get函數(shù)創(chuàng)建
消息隊(duì)列
-
消息隊(duì)列是內(nèi)核中的一個(gè)鏈表
-
用戶進(jìn)程將數(shù)據(jù)傳輸?shù)絻?nèi)核后,內(nèi)核重新添加一些如用戶ID,組ID,讀寫進(jìn)程的ID和優(yōu)先級等相關(guān)信息后并打成一個(gè)數(shù)據(jù)包稱為消息
-
允許一個(gè)進(jìn)程或者多個(gè)進(jìn)程往消息隊(duì)列中寫消息和讀消息,但一個(gè)消息只能被一個(gè)進(jìn)程讀取,讀取完畢后就自動刪除
-
消息隊(duì)列具有一定的FIFO的特性,消息可以按照順序發(fā)送到隊(duì)列中,也可以幾種不同的方式從隊(duì)列中讀取,每一個(gè)消息對壘在內(nèi)核中用一個(gè)唯一的IPC標(biāo)識ID表示
-
消息隊(duì)列的實(shí)現(xiàn)包括創(chuàng)建和打開隊(duì)列、發(fā)送消息、讀取消息和控制消息隊(duì)列四中操作
創(chuàng)建或打開消息隊(duì)列
#include <sys/msg.h> int msgget(key_t key, int flag); 返回:成功返回內(nèi)核中消息隊(duì)列的標(biāo)識ID,出錯(cuò)返回-1消息控制
#include <sys/msg.h> int msgctl(int msgid, intcmd, struct msqid_ds *buf);; 返回:成功返回0,出錯(cuò)返回-1發(fā)送消息
#include <sys/msg.h> int msgsnd(int msgqid, const void *ptr, size_t nbytes,int flag); 返回:成功返回0,出錯(cuò)返回-1 ptr: struct mymesg{long mytype;char mtext[512]; }- nbytes 指定消息的大小,不包括mtype的大小
- mtype指消息的類型,它由一個(gè)整數(shù)來代表,并且它只能是大于0的整數(shù)
- mtext是消息數(shù)據(jù)本身
- 在linux中,消息的最大長度是4056個(gè)字節(jié),其中包括mtype,它占有4個(gè)字節(jié)
- 結(jié)構(gòu)體mymesg用戶可自定義,但第一個(gè)成員必須是mytype
接收消息
#include <sys/msg.h> ssize_t msgrcv(int msgqid, void *ptr,size_t nbytes,long type, int flag); 返回:成功返回消息的數(shù)據(jù)部分長度,出錯(cuò)返回-1type : 消息類型type == 0 : l獲得消息隊(duì)列中的第一個(gè)消息type > 0 : 獲得消息隊(duì)列中類型為type的第一個(gè)消息type < 0 : 獲得消息中小于或等于type和絕對值的消息`發(fā)送消息示例:
#include <string.h> #include <stdio.h> #include <stdlib.h> #include <sys/msg.h>typedef struct{long type; //消息類型int start; //消息數(shù)據(jù)本身int end; }MSG;/*** 往消息隊(duì)列中發(fā)送消息*/int main(int argc, char *argv[]) {if(argc < 2){printf("usage: %s key \n", argv[0]);exit(1);}key_t key = atoi(argv[1]);printf("key: %d\n", key);//創(chuàng)建消息隊(duì)列int msq_id;if((msq_id = msgget(key, IPC_CREAT | IPC_EXCL | 0777)) < 0){perror("msgget error");}printf("msg id: %d\n", msq_id);//定義要發(fā)送的消息MSG m1 = {4, 4, 400};MSG m2 = {2, 2, 200};MSG m3 = {1, 1, 100};MSG m4 = {6, 6, 600};MSG m5 = {6, 60, 6000};//發(fā)型消息到消息隊(duì)列if(msgsnd(msq_id, &m1, sizeof(MSG) - sizeof(long), IPC_NOWAIT) < 0){perror("msgsnd error");}if(msgsnd(msq_id, &m2, sizeof(MSG) - sizeof(long), IPC_NOWAIT) < 0){perror("msgsnd error");}if(msgsnd(msq_id, &m3, sizeof(MSG) - sizeof(long), IPC_NOWAIT) < 0){perror("msgsnd error");}if(msgsnd(msq_id, &m4, sizeof(MSG) - sizeof(long), IPC_NOWAIT) < 0){perror("msgsnd error");}if(msgsnd(msq_id, &m5, sizeof(MSG) - sizeof(long), IPC_NOWAIT) < 0){perror("msgsnd error");}//獲取發(fā)送消息的總數(shù)struct msqid_ds ds;if(msgctl(msq_id, IPC_STAT, &ds) < 0){perror("msgctl error");}printf("msg total : %ld\n", ds.msg_qnum);exit(0); }執(zhí)行結(jié)果
andrew@andrew-Thurley:/work/linux-sys/TEK$ ./a.out 32 key: 32 msg id: 0 msg total : 5 andrew@andrew-Thurley:/work/linux-sys/TEK$ ipcs -q--------- 消息隊(duì)列 ----------- 鍵 msqid 擁有者 權(quán)限 已用字節(jié)數(shù) 消息 0x00000020 0 andrew 777 40 5 # 同樣的key值發(fā)送之后只能發(fā)送一次,等待接受之后才能再發(fā)送 andrew@andrew-Thurley:/work/linux-sys/TEK$ ./a.out 32 key: 32 msgget error: File exists msg id: -1 msgsnd error: Invalid argument msgsnd error: Invalid argument msgsnd error: Invalid argument msgsnd error: Invalid argument msgsnd error: Invalid argument msgctl error: Invalid argument msg total : 0 andrew@andrew-Thurley:/work/linux-sys/TEK$ ./a.out 36 key: 36 msg id: 32769 msg total : 5 andrew@andrew-Thurley:/work/linux-sys/TEK$ ipcs -q--------- 消息隊(duì)列 ----------- 鍵 msqid 擁有者 權(quán)限 已用字節(jié)數(shù) 消息 0x00000020 0 andrew 777 40 5 0x00000024 32769 andrew 777 40 5 # 刪除消息隊(duì)列 andrew@andrew-Thurley:/work/linux-sys/TEK$ ipcs -q --------- 消息隊(duì)列 ----------- 鍵 msqid 擁有者 權(quán)限 已用字節(jié)數(shù) 消息 0x00000024 32769 andrew 777 40 5 andrew@andrew-Thurley:/work/linux-sys/TEK$ ipcrm -q 32769 andrew@andrew-Thurley:/work/linux-sys/TEK$ ipcs -q --------- 消息隊(duì)列 ----------- 鍵 msqid 擁有者 權(quán)限 已用字節(jié)數(shù) 消息- 說明:消息隊(duì)列中的消息被接收完之后消息隊(duì)列還是存在的,由用戶刪除
共享內(nèi)存
-
共享內(nèi)存區(qū)域是被多個(gè)進(jìn)程共享的一部分物理內(nèi)存。
-
多個(gè)進(jìn)程都可以把該共享內(nèi)存映射到自己的虛擬內(nèi)存空間,所有用戶空間的進(jìn)程若要操作共享內(nèi)存,都要將其映射到自己的虛擬內(nèi)存空間中,通過映射的虛擬內(nèi)存空間地址去操作共享內(nèi)存,從而達(dá)到進(jìn)程間的數(shù)據(jù)通信。
-
共享內(nèi)存是進(jìn)程間共享數(shù)據(jù)的一種最快的方法,一個(gè)進(jìn)程向共享內(nèi)存區(qū)域?qū)懭霐?shù)據(jù),共享內(nèi)存中的所有進(jìn)程就可以立刻看到其中的內(nèi)容
-
本身不提供同步機(jī)制,可以通過信號量進(jìn)行同步
-
提升數(shù)據(jù)處理效率,一種效率最高的IPC機(jī)制
-
shmget創(chuàng)建共享內(nèi)存函數(shù)
-
shmat函數(shù)映射共享內(nèi)存,將這段創(chuàng)建共享的共享內(nèi)存映射到具體的進(jìn)程虛擬內(nèi)存空間
創(chuàng)建共享內(nèi)存
#include <sys/ipc.h> #include <sys/shm.h>int shmget(key_t key, size_t size, int shmflg);共享內(nèi)存控制
#include <sys/ipc.h> #include <sys/shm.h>int shmctl(int shmid, int cmd, struct shmid_ds *buf);共享內(nèi)存映射和解除映射
#include <sys/types.h> #include <sys/shm.h>void *shmat(int shmid, const void *shmaddr, int shmflg); 返回: 成功返回共享內(nèi)存映射到進(jìn)程虛擬內(nèi)存空間中的地址,失敗返回-1 int shmdt(const void *shmaddr); 返回: 如果失敗,則返回-1- 父子進(jìn)程通過管道創(chuàng)建共享內(nèi)存案例
共享內(nèi)存使用示例:
telll.c
//< 使用管道進(jìn)行同步共享內(nèi)存的創(chuàng)建 #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h>#include "tell.h"//< 管道文件描述符數(shù)組 static int fd[2];//管道初始化 void init() {if(pipe(fd) < 0){perror("perror");}}//利用管道進(jìn)行等待 void wait_pipe() {char c;//管道讀寫默認(rèn)是阻塞性的if(read(fd[0], &c, 1) < 0){perror("wait pipe error");}}//利用管道進(jìn)行通知 void notify_pipe() {char c ='c';if(write(fd[1], &c, 1) != 1){perror("notify pipe error");}}//銷毀管道 void destroy_pipe() {close(fd[0]);close(fd[1]);}tell.h
#ifndef __TELL_H__ #define __TELL_H__//管道初始化 extern void init();//利用管道進(jìn)行等待 extern void wait_pipe(); //利用管道進(jìn)行通知 extern void notify_pipe(); //銷毀管道 extern void destroy_pipe();#endif //TELLcal_shm.c
//< 使用管道進(jìn)行同步共享內(nèi)存的創(chuàng)建 #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/shm.h> #include <sys/wait.h> #include "tell.h"int main(int argc, char *argv[]) {//創(chuàng)建共享內(nèi)存int shmid;if((shmid = shmget(IPC_PRIVATE, 1024, IPC_CREAT | IPC_EXCL | 0777)) < 0){perror("shmget error");exit(1);}pid_t pid;init(); //初始化管道if((pid = fork()) < 0){perror("fork error !");exit(1);}else if(pid > 0){//進(jìn)行共享內(nèi)存映射int *pi;pi = (int *)shmat(shmid, 0, 0);if(pi == (int *)-1){//<因?yàn)閜i是指針類型的數(shù)據(jù)perror("shmat error");exit(1); }//往共享內(nèi)存中寫入數(shù)據(jù)(通過操作映射的地址即可)*pi = 100;*(pi +1) =200;//操作完,解除共享內(nèi)存映射shmdt(pi);//< 通知子進(jìn)程讀取數(shù)據(jù)notify_pipe();destroy_pipe();wait(0);}else{// child process//< 子進(jìn)程阻塞,等待父進(jìn)程往共享內(nèi)存中寫入數(shù)據(jù)wait_pipe();//子進(jìn)程從共享內(nèi)存中讀取數(shù)據(jù)//子進(jìn)程進(jìn)行共享內(nèi)存的映射int *pi = (int *)shmat(shmid, 0, 0);if(pi == (int *)-1){perror("shmat error");exit(1);}printf("start : %d end : %d \n", *pi, *(pi+1));//< 讀取完數(shù)據(jù),解除映射 shmdt(pi);//< 刪除共享內(nèi)存 共享內(nèi)存只有一個(gè),在子進(jìn)程和父進(jìn)程中只有一個(gè)shmctl(shmid, IPC_RMID, NULL);destroy_pipe();}exit(0); }Makefile
all:cal_shmcal_shm: cal_shm.c tell.cgcc -o cal_shm -I. tell.c cal_shm.c信號量
進(jìn)程間信號量
- 進(jìn)程信號量本質(zhì)上就是共享資源的數(shù)目,用來控制對共享資源的訪問
- 用于進(jìn)程間資源的互斥和同步
- 每種共享資源對應(yīng)一個(gè)信號量,為了便于大量共享資源的操作引入了信號量集,可對所有的信號量一次性操作。對信號量集中的所有操作可以要求全部陳宮,也可以部分成功。
- 二元信號量(信號燈)值為和
- 對信號量做PV操作
創(chuàng)建信號量集
#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h>int semget(key_t key, int nsems, int semflg);返回:成功返回信號量集ID, 出錯(cuò)返回-1- key:用戶指定的信號量集鍵值
- nsems:信號量集中信號量個(gè)數(shù)
- flag:IPC_CREAT、IPC_EXECL等權(quán)限的組合
信號量集控制
#include <sys/types.h>#include <sys/ipc.h>#include <sys/sem.h>int semctl(int semid, int semnum, int cmd, ...);DESCRIPTIONsemctl() performs the control operation specified by cmd on the System V sema‐phore set identified by semid, or on the semnum-th semaphore of that set. (Thesemaphores in a set are numbered starting at 0.)This function has three or four arguments, depending on cmd. When there arefour, the fourth has the type union semun. The calling program must definethis union as follows:union semun {int val; /* Value for SETVAL */struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */unsigned short *array; /* Array for GETALL, SETALL */struct seminfo *__buf; /* Buffer for IPC_INFO(Linux-specific) */};The semid_ds data structure is defined in <sys/sem.h> as follows:struct semid_ds {struct ipc_perm sem_perm; /* Ownership and permissions */time_t sem_otime; /* Last semop time */time_t sem_ctime; /* Last change time */unsigned long sem_nsems; /* No. of semaphores in set */};The ipc_perm structure is defined as follows (the highlighted fields are set‐table using IPC_SET):struct ipc_perm {key_t __key; /* Key supplied to semget(2) */uid_t uid; /* Effective UID of owner */gid_t gid; /* Effective GID of owner */uid_t cuid; /* Effective UID of creator */gid_t cgid; /* Effective GID of creator */unsigned short mode; /* Permissions */unsigned short __seq; /* Sequence number */};- semid:信號量集ID
- semnum: 0表歐式所有信號量操作,信號量編號從開始。
- val:防止獲取或設(shè)置信號量集中某個(gè)信號量值
- buf:信號量集屬性指針
- array:放置獲取或設(shè)置信號量集中所有信號量的值
信號量集操作
#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h>int semop(int semid, struct sembuf *sops, size_t nsops);int semtimedop(int semid, struct sembuf *sops, size_t nsops,const struct timespec *timeout);信號量模塊封裝示例:
pv.c
//< 信號量操作PV原語 #include <sys/sem.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> #include <assert.h>union semun{int val;struct semid_ds *buf;unsigned short *array; };//< 初始化semnums個(gè)信號燈/信號量的值(value) int I(int semnums, int value) {//創(chuàng)建信號量集int semid = semget(IPC_PRIVATE, semnums, IPC_CREAT | IPC_EXCL | 0777);if(semid < 0){perror("semget faild!");return -1;}union semun un;unsigned short *array = (unsigned short *)calloc(semnums, sizeof(unsigned short));int i;for(i = 0; i < semnums; i ++){array[i] = value;}un.array = array;/** 初始化信號集中所有的信號燈的初值0;* 0:標(biāo)示要初始化的所有信號集*/ if (semctl(semid, 0, SETALL, un) < 0){perror("semctl error");return -1;}free(array);return semid; }//< 對信號量集(semid)中的信號燈(semnum)做P(value)操作 void P(int semid, int semnum, int value) {assert(value >= 0);//定義sembuf類型的結(jié)構(gòu)體數(shù)組,防止若干個(gè)新結(jié)構(gòu)體變量 , ops中有多少個(gè)信號量決定//防止要操作的信號量、P或V操作struct sembuf ops[] = {{semnum, -value, SEM_UNDO}};if(semop(semid, ops, sizeof(ops)/sizeof(struct sembuf)) < 0){perror("semop error");}}//< 對信號量集(semid)中的信號燈(semnum)作V(value)操作 void V(int semid, int semnum, int value) { assert(value >= 0);//定義sembuf類型的結(jié)構(gòu)體數(shù)組,防止若干個(gè)新結(jié)構(gòu)體變量 , ops中有多少個(gè)信號量決定//防止要操作的信號量、P或V操作struct sembuf ops[] = {{semnum, -value, SEM_UNDO}};if(semop(semid, ops, sizeof(ops)/sizeof(struct sembuf)) < 0){perror("semop error");} }//< 銷毀信號量集 void D(int semid) {if(semctl(semid, 0, IPC_RMID, NULL) < 0){perror("semctl error");}}pv.h
//< 信號量操作PV原語 #ifndef __PV_H__ #define __PV_H__//< 初始化semnums個(gè)信號燈/信號量的值(value) extern int I(int semnums, int value);//< 對信號量集(semid)中的信號燈(semnum)做P(value)操作 extern void P(int semid, int semnum, int value);//< 對信號量集(semid)中的信號燈(semnum)作V(value)操作 extern void V(int semid, int semnum, int value);//< 銷毀信號量集 extern void D(int semid);#endif //PV_H總結(jié)
以上是生活随笔為你收集整理的进程间通信-system-v的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【2017年第2期】深度学习在化学信息学
- 下一篇: 作者:石勇(1956-),男,中国科学院