linux基础编程-进程间通信

5.2 消息队列

消息队列的本质是一个存放消息的链表,该链表由内核来维护。一个消息队列由一个标识符(即队列key)来标识。
消息队列的通信机制传递的数据具有某种结构,而不是简单的字节流;
向消息队列中写数据,实际上是向这个数据结构中插入一个新结点;
从消息队列中读数据,实际上是从这个数据结构中删除一个结点;
消息队列提供了一个从一个进程向另外一个进程发送一块数据的方法;
消息队列具有和管道一样的不足,每个数据块的最大长度是有上限的,系统上全体队列的最大总长度也是有上限的。

Linux内核提供了4个系统调用:

int msgget(key_t key, int msgflg); //创建消息队列,返回值为该队列号int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);//发送消息ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);//接受信息,msgtyp需要指明接受的数据typekey_t ftok(const char *pathname, int proj_id);//为队列随机附加key,pathename为路径,id号可随意(1-255)int msgctl(int msqid, int cmd, struct msqid_ds *buf);//对指定消息队列进行控制

案例1  使用消息队列实现不同进程间的通信。

msgsend.c:消息发送端

msgrcv.c:消息接收端

代码如下:

msgrcv.c#include <stdio.h>#include <stdlib.h>#include <sys/msg.h>#define MAX_TEXT 512struct my_msg_st{long int my_msg_type;char anytext[MAX_TEXT];};int main(){int tempIdx = 1;int tempMsgid;struct my_msg_st tempData;long int tempMsgToRcv = 0;//rcv msgtempMsgid = msgget((key_t)1000, 0664 | IPC_CREAT);//获取消息队列if (tempMsgid == -1){perror("msgget err");exit(-1);}//of ifwhile (tempIdx < 5){//接收消息if (msgrcv(tempMsgid, (void*)&tempData, BUFSIZ, tempMsgToRcv, 0) == -1){perror("msgrcv err");exit(-1);}//of if//打印消息printf("msg type:%ld
", tempData.my_msg_type);printf("msg content is:%s", tempData.anytext);tempIdx ++;}//of while//删除消息队列if (msgctl(tempMsgid, IPC_RMID, 0) == -1){perror("msgctl err");exit(-1);}//of ifexit(0);}//of main
msgsend.c#include <stdio.h>#include <stdlib.h>#include <sys/msg.h>#include <string.h>#define MAX_TEXT 512//消息结构体struct my_msg_st{long int my_msg_type;       //消息类型char anytext[MAX_TEXT];     //消息数据};int main() {int tempIdx = 1;int tempMsgid;struct my_msg_st tempData;char tempBuf[BUFSIZ];//设置缓存变量tempMsgid = msgget((key_t)1000, 0664 | IPC_CREAT);//创建消息队列if (tempMsgid == -1){perror("msgget err");exit(-1);}//of ifwhile (tempIdx < 5){//发送消息printf("enter some text:");fgets(tempBuf, BUFSIZ, stdin);tempData.my_msg_type = rand() % 3 + 1;        //随机获取消息类型strcpy(tempData.anytext, tempBuf);//发送消息if (msgsnd(tempMsgid, (void*)&tempData, sizeof(tempData), 0) == -1){perror("msgsnd err");exit(-1);}//of iftempIdx ++;}//of whilereturn 0;}//of main

运行结果:

7864c87ca24245a8a881a54a4e804614.png

279b0ef8c7674471829791e26df6388b.png 

52e33a4a3e2f41fa910d50a93cf3ad01.png 

5.3  信号量

信号量是专门用来解决进程同步与互斥问题的一种通信机制,它与信号无关;
不同于管道、FIFO以及消息队列,一般不用来传输数据;
信号量包括:表示资源数量的非负整型变量、修改信号量的原子操作P和V、该信号量下等待资源的进程队列。
使用信号量进行通信时,通常需要如下步骤:

创建信号量/信号量集或者获取系统中已有的信号量/信号量集;
初始化信号量:早期信号量通常初始化为1,但有些进程一次需要多个同类的临界资源或多个不同类且唯一的临界资源,因此可能需要初始化信号量集;
信号量的P、V操作:根据进程请求修改信号量的数量,P操作使信号量-1,V操作使信号量+1;
从系统中删除不需要的信号量。

Linux内核提供了三个系统调用:

semget

semctl

semop

semget函数

#include <sys/sem.h>int semget(key_t key, int nsems, int semflg);

 semctl函数

#include <sys/sem.h>int semctl(int semid, int semnum, int cmd, ...);

semop函数

#include <sys/sem.h>int semop(int semid, struct sembuf *sops, unsigned nsops);

案例 使用信号量实现父子进程同步,防止父子进程抢夺CPU

代码段:

#include <stdio.h>#include <stdlib.h>#include <sys/sem.h>//自定义共用体union semu{int val;struct semid_ds* buf;unsigned short* array;struct seminfo* _buf;};static int semId;//设置信号量值static int setSemValue() {union semu tempSemUnion;tempSemUnion.val = 1;if (semctl(semId, 0, SETVAL, tempSemUnion) == -1){return 0;}//of ifreturn 1;}//of setSemValue//p操作,获取信号量static int semaphoreP() {struct sembuf tempSemBuf;tempSemBuf.sem_num = 0;tempSemBuf.sem_op = -1;tempSemBuf.sem_flg = SEM_UNDO;if (semop(semId, &tempSemBuf, 1) == -1){perror("sem_p err");return 0;}//of ifreturn 1;}//of semaphoreP//V操作,释放信号量static int semaphoreV() {struct sembuf tempSemBuf;tempSemBuf.sem_num = 0;tempSemBuf.sem_op = 1;tempSemBuf.sem_flg = SEM_UNDO;if (semop(semId, &tempSemBuf, 1) == -1){perror("sem_v err");return 0;}//of ifreturn 1;}//of semaphoreV//删除信号量static void delSemValue() {union semu tempSemUnion;if (semctl(semId, 0, IPC_RMID, tempSemUnion) == -1){perror("del err");}//of if}//of delSemValueint main() {int i;pid_t temPid;char tempChar = 'C';semId= semget((key_t)1000, 1, 0664 | IPC_CREAT);//创建信号量if (semId== -1){perror("sem_c err");exit(-1);}//of ifif (!setSemValue()){//设置信号量值perror("init err");exit(-1);}//of iftemPid = fork();//创建子进程if (temPid== -1){//若创建失败delSemValue();//删除信号量exit(-1);} else if (temPid == 0){     //设置子进程打印的字符tempChar = 'Z';} else {//设置父进程打印的字符tempChar = 'C';}//of ifsrand((unsigned int)getpid());//设置随机数种子for (i = 0; i < 8; i++){//循环打印字符semaphoreP();//获取信号量printf("%c", tempChar);fflush(stdout);//将字符打印到屏幕sleep(rand() % 4);//沉睡printf("%c", tempChar);fflush(stdout);//再次打印到屏幕sleep(1);semaphoreV();//释放信号量}//of for iif (temPid > 0){wait(NULL);//回收子进程delSemValue();//删除信号量}//of ifprintf("
process %d finished.
", getpid());return 0;}//of main

运行截图:

1d9d40c93b494ad6b386057cb11fa9b3.png

5.4 共享内存:

共享内存允许两个或多个进程访问给定的同一块存储区域。它是效率最高的一种进程通信方式,节省了不同进程间多次读写的时间;
在写进程的操作尚未完成时,不应有进程从共享内存中读取数据。共享内存自身不限制对共享内存的读写次序,程序开发人员应自觉遵循读写规则;
一般情况下,共享内存与信号量一起使用,由信号量帮它实现读写操作的同步;

Linux提供了如下系统调用来实现共享内存的申请、管理与释放:
– shmget

#include <sys/shm.h>int shmget(key_t key, size_t size, int shmflg);
  • key:传入参数,共享内存的键值,通常为一个整数;
  • size:设置共享内存的大小;
  • shmflg:设置shmget函数的创建条件(一般设置为IPC_CREAT或IPC_EXCL)及进程对共享内存的读写权限。

– shmat

#include <sys/shm.h>void *shmat(int shmid, const void *shmaddr, int shmflg);

shmid:共享内标识符,通常为shmget函数的返回值;
shmaddr:指针类型的传入参数,用于指定共享内存映射到虚拟内存时的虚拟地址,设置为NULL时,映射地址由系统决定;
shmflg:设置共享内存的使用方式,如果shmflg = SHM_RDONLY,则共享内存将以只读的方式进行映射,当前进程只能从共享内存中读取数据。

– shmdt

#include <sys/shm.h>int shmdt(const void *shmaddr);
  • shmaddr:shmat函数返回的虚拟空间地址。

– shmctl

#include <sys/shm.h>int shmctl(int shmid, int cmd, struct shmid_ds *buf);//为了方便对共享内存进行管理,由内核维护的存储共享内存属性信息的结构体:struct shmid_ds{struct ipc_perm shm_perm;/* 操作权限*/size_t shm_segsz;                   /*段的大小(以字节为单位)*/time_t shm_atime;          /*最后一个进程附加到该段的时间*/time_t shm_dtime;          /*最后一个进程离开该段的时间*/time_t shm_ctime;          /*最后一个进程修改该段的时间*/unsigned short shm_cpid;   /*创建该段进程的pid*/unsigned short shm_lpid;   /*在该段上操作的最后1个进程的pid*/short shm_nattch;          /*当前附加到该段的进程的个数*//*下面是私有的*/unsigned short shm_npages;  /*段的大小(以页为单位)*/unsigned long *shm_pages;   /*指向frames->SHMMAX的指针数组*/struct vm_area_struct *attaches; /*对共享段的描述*/};
  • shmid:共享内存标识符;cmd:要执行的操作,常用的设置为IPC_RMID,功能为删除共享内存;buf:对共享内存的管理信息进行设置,该参数是结构体shmid_ds的指针。
    shm_w.c#include <stdio.h>#include <sys/ipc.h>#include <sys/shm.h>#include <sys/types.h>#include <unistd.h>#include <string.h>#define SEGSIZE 4096//定义共享内存容量typedef struct{//读写数据结构体char name[8];int age;} Stu;int main() {int tempShmId, i;key_t tempKey;char tempName[8];Stu *tempSmap;/*ftok函数的作用:系统建立IPC通讯(如消息队列、共享内存时)必须指定一个ID值。通常情况下,该id值通过ftok函数得到。*/tempKey = ftok("/", 0);//获取关键字if (tempKey == -1) {perror("ftok error");return -1;}//of ifprintf("key=%d
    ", tempKey);//创建共享内存tempShmId= shmget(tempKey, SEGSIZE, IPC_CREAT | IPC_EXCL | 0664);if (tempShmId == -1) {perror("create shared memory error
    ");return -1;}//of ifprintf("shm_id=%d
    ", tempShmId);tempSmap = (Stu*)shmat(tempShmId, NULL, 0);//将进程与共享内存绑定memset(tempName, 0x00, sizeof(tempName));strcpy(tempName, "Jhon");tempName[4] = '0';for (i = 0; i < 3; i++){//写数据tempName[4] += 1;strncpy((tempSmap+ i)->name, tempName, 5);(tempSmap + i)->age = 20 + i;}//of for iif (shmdt(tempSmap) == -1){//解除绑定perror("detach error");return -1;}//of ifreturn 0;}//of mainshm_r.c#include <stdio.h>#include <string.h>#include <sys/ipc.h>#include <sys/shm.h>#include <sys/types.h>#include <unistd.h>typedef struct{char name[8];int age;} Stu;int main() {int tempShmId, i;key_t tempKey;Stu *tempSmap;struct shmid_ds tempBuf;tempKey = ftok("/", 0);//获取关键字if (tempKey == -1) {perror("ftok error");return -1;}//of ifprintf("key=%d
    ", tempKey);tempShmId = shmget(tempKey, 0, 0);//创建共享内存if (tempShmId == -1) {perror("shmget error");return -1;}//of ifprintf("shm_id=%d
    ", tempShmId);tempSmap = (Stu*)shmat(tempShmId, NULL, 0);//将进程与共享内存绑定for (i = 0; i < 3; i++){//读数据printf("name:%s
    ", (*(tempSmap + i)).name);printf("age :%d
    ", (*(tempSmap + i)).age);}//of for iif (shmdt(tempSmap) == -1){//解除绑定perror("detach error");return -1;}//of ifshmctl(tempShmId, IPC_RMID, &tempBuf);//删除共享内存return 0;}//of main

    c9f106c0befd40eb81c2ba6c62fbae31.png

    3f0de143b49d4e3a988be88ad6eb8a3a.png 

     

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值