进程间通信-system-v
文章目錄
- System V IPC概述
- 消息隊列
- 創建或打開消息隊列
- 消息控制
- 發送消息
- 接收消息
- 發送消息示例:
- 共享內存
- 創建共享內存
- 共享內存控制
- 共享內存映射和解除映射
- 共享內存使用示例:
- 信號量
- 進程間信號量
- 創建信號量集
- 信號量集控制
- 信號量集操作
- 信號量模塊封裝示例:
System V IPC概述
- System V引入了三種高級進程間通信機制
-
IPC對象(消息隊列、共享內存和信號量)存在于內核而不是文件系統中,由用戶控制釋放(用戶管理IPC對象的生命周期),不像管道的釋放由內核控制。
-
IPC對象通過其標識來引用和訪問,所有的IPC對象在內核空間中唯一性標識ID,在用戶空間中的唯一性標識稱為Key.
-
IPC對象是全局對象,可以通過ipcs,ipcrm等命令來查看或刪除
-
每個IPC對象都由get函數創建
消息隊列
-
消息隊列是內核中的一個鏈表
-
用戶進程將數據傳輸到內核后,內核重新添加一些如用戶ID,組ID,讀寫進程的ID和優先級等相關信息后并打成一個數據包稱為消息
-
允許一個進程或者多個進程往消息隊列中寫消息和讀消息,但一個消息只能被一個進程讀取,讀取完畢后就自動刪除
-
消息隊列具有一定的FIFO的特性,消息可以按照順序發送到隊列中,也可以幾種不同的方式從隊列中讀取,每一個消息對壘在內核中用一個唯一的IPC標識ID表示
-
消息隊列的實現包括創建和打開隊列、發送消息、讀取消息和控制消息隊列四中操作
創建或打開消息隊列
#include <sys/msg.h> int msgget(key_t key, int flag); 返回:成功返回內核中消息隊列的標識ID,出錯返回-1消息控制
#include <sys/msg.h> int msgctl(int msgid, intcmd, struct msqid_ds *buf);; 返回:成功返回0,出錯返回-1發送消息
#include <sys/msg.h> int msgsnd(int msgqid, const void *ptr, size_t nbytes,int flag); 返回:成功返回0,出錯返回-1 ptr: struct mymesg{long mytype;char mtext[512]; }- nbytes 指定消息的大小,不包括mtype的大小
- mtype指消息的類型,它由一個整數來代表,并且它只能是大于0的整數
- mtext是消息數據本身
- 在linux中,消息的最大長度是4056個字節,其中包括mtype,它占有4個字節
- 結構體mymesg用戶可自定義,但第一個成員必須是mytype
接收消息
#include <sys/msg.h> ssize_t msgrcv(int msgqid, void *ptr,size_t nbytes,long type, int flag); 返回:成功返回消息的數據部分長度,出錯返回-1type : 消息類型type == 0 : l獲得消息隊列中的第一個消息type > 0 : 獲得消息隊列中類型為type的第一個消息type < 0 : 獲得消息中小于或等于type和絕對值的消息`發送消息示例:
#include <string.h> #include <stdio.h> #include <stdlib.h> #include <sys/msg.h>typedef struct{long type; //消息類型int start; //消息數據本身int end; }MSG;/*** 往消息隊列中發送消息*/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);//創建消息隊列int msq_id;if((msq_id = msgget(key, IPC_CREAT | IPC_EXCL | 0777)) < 0){perror("msgget error");}printf("msg id: %d\n", msq_id);//定義要發送的消息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};//發型消息到消息隊列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");}//獲取發送消息的總數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); }執行結果
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--------- 消息隊列 ----------- 鍵 msqid 擁有者 權限 已用字節數 消息 0x00000020 0 andrew 777 40 5 # 同樣的key值發送之后只能發送一次,等待接受之后才能再發送 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--------- 消息隊列 ----------- 鍵 msqid 擁有者 權限 已用字節數 消息 0x00000020 0 andrew 777 40 5 0x00000024 32769 andrew 777 40 5 # 刪除消息隊列 andrew@andrew-Thurley:/work/linux-sys/TEK$ ipcs -q --------- 消息隊列 ----------- 鍵 msqid 擁有者 權限 已用字節數 消息 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 --------- 消息隊列 ----------- 鍵 msqid 擁有者 權限 已用字節數 消息- 說明:消息隊列中的消息被接收完之后消息隊列還是存在的,由用戶刪除
共享內存
-
共享內存區域是被多個進程共享的一部分物理內存。
-
多個進程都可以把該共享內存映射到自己的虛擬內存空間,所有用戶空間的進程若要操作共享內存,都要將其映射到自己的虛擬內存空間中,通過映射的虛擬內存空間地址去操作共享內存,從而達到進程間的數據通信。
-
共享內存是進程間共享數據的一種最快的方法,一個進程向共享內存區域寫入數據,共享內存中的所有進程就可以立刻看到其中的內容
-
本身不提供同步機制,可以通過信號量進行同步
-
提升數據處理效率,一種效率最高的IPC機制
-
shmget創建共享內存函數
-
shmat函數映射共享內存,將這段創建共享的共享內存映射到具體的進程虛擬內存空間
創建共享內存
#include <sys/ipc.h> #include <sys/shm.h>int shmget(key_t key, size_t size, int shmflg);共享內存控制
#include <sys/ipc.h> #include <sys/shm.h>int shmctl(int shmid, int cmd, struct shmid_ds *buf);共享內存映射和解除映射
#include <sys/types.h> #include <sys/shm.h>void *shmat(int shmid, const void *shmaddr, int shmflg); 返回: 成功返回共享內存映射到進程虛擬內存空間中的地址,失敗返回-1 int shmdt(const void *shmaddr); 返回: 如果失敗,則返回-1- 父子進程通過管道創建共享內存案例
共享內存使用示例:
telll.c
//< 使用管道進行同步共享內存的創建 #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h>#include "tell.h"//< 管道文件描述符數組 static int fd[2];//管道初始化 void init() {if(pipe(fd) < 0){perror("perror");}}//利用管道進行等待 void wait_pipe() {char c;//管道讀寫默認是阻塞性的if(read(fd[0], &c, 1) < 0){perror("wait pipe error");}}//利用管道進行通知 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();//利用管道進行等待 extern void wait_pipe(); //利用管道進行通知 extern void notify_pipe(); //銷毀管道 extern void destroy_pipe();#endif //TELLcal_shm.c
//< 使用管道進行同步共享內存的創建 #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[]) {//創建共享內存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){//進行共享內存映射int *pi;pi = (int *)shmat(shmid, 0, 0);if(pi == (int *)-1){//<因為pi是指針類型的數據perror("shmat error");exit(1); }//往共享內存中寫入數據(通過操作映射的地址即可)*pi = 100;*(pi +1) =200;//操作完,解除共享內存映射shmdt(pi);//< 通知子進程讀取數據notify_pipe();destroy_pipe();wait(0);}else{// child process//< 子進程阻塞,等待父進程往共享內存中寫入數據wait_pipe();//子進程從共享內存中讀取數據//子進程進行共享內存的映射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));//< 讀取完數據,解除映射 shmdt(pi);//< 刪除共享內存 共享內存只有一個,在子進程和父進程中只有一個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信號量
進程間信號量
- 進程信號量本質上就是共享資源的數目,用來控制對共享資源的訪問
- 用于進程間資源的互斥和同步
- 每種共享資源對應一個信號量,為了便于大量共享資源的操作引入了信號量集,可對所有的信號量一次性操作。對信號量集中的所有操作可以要求全部陳宮,也可以部分成功。
- 二元信號量(信號燈)值為和
- 對信號量做PV操作
創建信號量集
#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h>int semget(key_t key, int nsems, int semflg);返回:成功返回信號量集ID, 出錯返回-1- key:用戶指定的信號量集鍵值
- nsems:信號量集中信號量個數
- flag:IPC_CREAT、IPC_EXECL等權限的組合
信號量集控制
#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:防止獲取或設置信號量集中某個信號量值
- buf:信號量集屬性指針
- array:放置獲取或設置信號量集中所有信號量的值
信號量集操作
#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個信號燈/信號量的值(value) int I(int semnums, int value) {//創建信號量集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:標示要初始化的所有信號集*/ 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類型的結構體數組,防止若干個新結構體變量 , ops中有多少個信號量決定//防止要操作的信號量、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類型的結構體數組,防止若干個新結構體變量 , ops中有多少個信號量決定//防止要操作的信號量、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個信號燈/信號量的值(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總結
以上是生活随笔為你收集整理的进程间通信-system-v的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【2017年第2期】深度学习在化学信息学
- 下一篇: 作者:石勇(1956-),男,中国科学院