进程间通讯——消息队列、信号量以及共享内存
来源:互联网 发布:淘宝如何选择快递公司 编辑:程序博客网 时间:2024/06/10 17:23
本篇博客见《unix环境高级编程》的进程通信那一节,看到那一节就把这个写到博客里面啦,加深记忆。方便以后复习用……
消息队列,信号量,共享内存这三种我们称为XSI IPC,它们之间有很多相似之处。
在了解这三个之前,我们先来了解标识符和键。
每个内核中的IPC结构(这里指消息队列,信号量和共享存储段)都用一个非负整数的标识符加以引用。例如,为了对一个消息队列发送或读消息时,只需要知道
其队列的标识符。
标识符是IPC对象的内部名。为使多个合作进程能够在同一IPC对象上会和,需要提供一个外部名,因此使用了键(key),每个IPC对象都与一个键相关联,于是键
就用作为该对象的外部名。
无论何时创建IPC结构(调用 msgget、semget 或shmget),都应指定一个键(key),键的数据类型由系统规定为 key_t,通常在头文件<sys/types.h>中被规定为长整型。
键由内核变换成标识符。
三个get函数(msgget、semget和shmget)都有两个类似的参数:一个key和一个整型flag。如若满足下列两个条件之一,则创建一个新的IPC结构(服务器进程创建)
(1)key是IPC_PRIVATE
(2)key当前未与特定类型的IPC结构相结合,并且flag中指定了IPC_CREAT位。
为访问现存的队列,key必须等于创建该队列时所指定的键,并且不应该指定IPC_CREAT。
注意,访问一个现存的队列,决不能指定IPC_PRIVATE作为键。因为这是一个特殊的键值,它总是用于创建一个新队列。为了访问一个用IPC_PRIVATE键创建的现存队列,
一定要知道与该队列相结合的标识符,然后在其他IPC调用中(如msgsnd和msgrcv)使用该标识符。
权限:: XSI IPC权限
接下来我们来看消息队列:
消息队列是消息的链接表,存放在内核中并由消息队列标识符的标识。
消息队列的函数:
#include <sys/msg.h>
(1)msgget用于创建新队列或打开一个现存的队列。
int msgget(key_t key, int flag);
返回值:成功返回消息队列的ID,出错返回-1;此后该值就可被用于其他三个消息队列的函数。
参数:key:键,可以看上面的介绍,这个我们一般给一个非负整数,
flag:权限位
(2)msgctl函数对队列执行多种操作。
int msgctl(int msqid,int cmd,struct msqid_ds *buf);
返回值:成功返回0,出错-1
参数:msqid:msgget的返回值
cmd
(3)调用msgsnd将数据放到消息队列中。
int msgsnd(int msqid,const void *ptr,size_t nbyte,int flag);成功返回0,出错-1
每个消息都有三部分组成,正长整型类型字段,非负长度(nbyte),以及实际数据字节(对应于长度)。消息总是放在队列尾端。
ptr参数指向一个长整型数,它包含了正的整型消息类型,在其后紧跟消息数据,若发送的最长消息是128字节,则可定义下列结构:
struct mymesg
{
long mtype; //正的整型消息类型
char mtext[128]; //消息数据
};
ptr就是指向mymesg结构的指针。接受者可以使用消息类型以非先进先出的次序去消息。
参数flag的值可以指定为IPC_NOWAIT.这类似于文件I/O的非阻塞I/O标志。一般给0的话,即忽略。
当msgsnd成功返回,与消息队列相关的msqid_ds结构得到更新,以标明发出该调用的进程ID(msg_lspid),
进行该调用的时间(msg_stime),并指示队列中加了一条消息(msg_qnum)
(4)msgrcv从队列中取消息;
ssize_t msgrcv(int msqid,void *ptr,size_t nbytes, long type , int flag);
成功返回消息的数据部分的长度,出错返回-1;
ptr和msgsnd的prt一样;
nbyte:数据缓冲区的长度
type:我们可以指定想要哪一种消息:type == 0 返回队列中的第一个消息。
type > 0 返回队列中消息类型为type的第一个消息
type < 0 返回队列中消息类型值小于或等于type绝对值的消息,如果这种消息有若干个,则取类型值最小的消息。
当msgrcv成功返回,内核更新与消息队列相关的msqid_ds结构,以指示调用者进程ID(msg_lspid),
和调用的时间(msg_stime),并将队列中的消息数减一(msg_qnum)
下面简单代码实现消息队列:
创建消息队列,写入消息:
#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <assert.h>#include <string.h>#include <sys/msg.h>struct mymesg //ptr指针指向的结构{long mtype;char mtext[512];};void main(){int msgid = msgget((key_t)1234, 0664|IPC_CREAT); //打开消息队列,没有创建的话则创建assert(msgid != -1);struct mymesg buf;buf.mtype = 1000; //消息类型1000strcpy(buf.mtext, "hello"); //消息数据hellomsgsnd(msgid, &buf, strlen(buf.mtext), 0); //将数据放到消息队列中buf.mtype = 2000; //消息类型2000strcpy(buf.mtext, "word");//消息数据wordmsgsnd(msgid, &buf, strlen(buf.mtext), 0);//将数据放到消息队列中}
从消息队列中取消息:
#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <assert.h>#include <string.h>#include <sys/msg.h>struct node{long type;char buff[128];};void main(){int msgid = msgget((key_t)1234, 0664|IPC_CREAT);assert(msgid != -1);struct node buf;memset(&buf, 0, sizeof(buf));msgrcv(msgid, &buf, sizeof(buf) - 1, 2000, 0); //从消息队列中取类型为2000的消息printf("buf.type: %d\n", buf.type);printf("buf.buff: %s\n", buf.buff);}
执行结果:
接下来我们看看信号量::
信号量与其他IPC不同的是,它是一个计数器,用于多进程对共享数据对象的访问。
为了获得共享资源,进程需要执行的操作:
(1)测试控制该资源的信号量。
(2)信号量的值为正时,则进程可以使用该资源。使用了,就将信号量减一(p操作)。
(3)信号量的值为0,则进程进入休眠状态,直至信号量的大于0。进程被唤醒后,返回至第(1)步
当进程不再使用该资源时,信号量的值加一(v操作)。如果有进程正在休眠等待此信号量,则唤醒进程。
信号量的测试及加一减一操作都是原子操作,信号量通常是在内核中实现。
常用的信号量形式被称为二元信号量或双态信号量,控制单个资源,初始值为1,。但是一般而言,信号量的初值可以是任意正数值,这说明
有多少个共享资源单位可供共享应用。
XSI的信号量要复杂一些:
(1)信号量并非是单个非负值,而必须将信号量定义为含有一个或多个信号量值的集合。当创建一个信号量时,要指定集合中信号量值的数量。
(2)创建信号量与对其赋初值是分开的。
(3)即使没有进程正在使用各种形式的XSI IPC,它们仍然是存在的。有些程序在终止时并没有释放已经分配给它的信号量。
关于信号量的函数::
(1)要获得一个信号量的ID,要调用的函数semget
#include <sys/sem.h>
int semget(key_t key, int nsems, int flag);
返回值:成功返回信号量的ID,若出错则返回-1
nsems是该集合中的信号量数。如果引用一个现存的集合,则将nsems指定为0。如果是创建新集合,则必须指定nsems
(2)semctl函数包含了多种信号量的操作
int semctl(int semid, int semnum ,int cmd , /* union semun arg */);
第四个参数是可选的,如果使用该参数,则其类型是semun,它是多个特定命令参数的联合(union);
cmd参数指定下列10种命令的一种, 在semid指定的信号量集合上执行此命令,其中有5条命令是针对一个特定的信号量值
(3)函数semop自动执行信号量集合上的操作数组,这是个原子操作。
int semop(int semid,struct sembuf semoparray[ ] ,size_t nops);
返回值:成功返回0,出错返回-1
参数semoparray是一个指针,它指向一个信号量操作数组,信号量操作由sembuf结构表示:
#include <stdio.h>#include <stdlib.h>#include <assert.h>#include <string.h>#include <unistd.h>#include <sys/sem.h>union semun{int val;};void seminit(); //初始化void semp(); //减一操作void semv(); //加一操作void semdel(); //删除操作
#include "sem.h"int semid = 0;void seminit() //初始化操作{semid = semget((key_t)1234, 1, 0666);if(semid == -1) //打开现有的失败,创建信号量{semid = semget((key_t)1234, 1, 0666 | IPC_CREAT);if(semid == -1){perror("");exit(0);}union semun v;v.val = 0; //设定初始值为0semctl(semid, 0, SETVAL, v);}}void semp() //减一操作{struct sembuf buf;buf.sem_num = 0;buf.sem_op = -1;buf.sem_flg = SEM_UNDO;semop(semid, &buf, 1);}void semv() //加一操作{struct sembuf buf;buf.sem_num = 0;buf.sem_op = 1;buf.sem_flg = SEM_UNDO;semop(semid, &buf, 1);}void semdel() //删除信号量{semctl(semid, 0, IPC_RMID, NULL);}
#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <assert.h>#include <string.h>#include <fcntl.h>#include "sem.h"void main(){int fd = open("a.txt", O_CREAT | O_WRONLY | O_TRUNC, 0664);assert(fd != -1);seminit(); //初始化信号量while(1){char buff[128] = {0};printf("please input data: ");fflush(stdout);fgets(buff, 128, stdin);buff[strlen(buff) - 1] = 0;write(fd, buff, strlen(buff));semv(); //写完给信号量值加一if(strncmp(buff, "end", 3) == 0){break;}}close(fd);}
#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <assert.h>#include <string.h>#include <fcntl.h>#include "sem.h"void main(){int fd = open("a.txt", O_CREAT | O_RDONLY , 0664);assert(fd != -1);seminit(); //初始化信号量while(1){char buff[128] = {0};int n = 1, flag = 0;semp(); //减一操作while(1){n = read(fd, buff, 10);if(n == 0){break;}if(strncmp(buff, "end", 3) == 0){flag = 1;break;}printf("n = %d, buff = %s\n", n, buff);memset(buff, 0, 128);}if(flag){break;}}close(fd);}
共享存储::
共享存储允许两个或更多进程共享一给定的存储区。因为数据不需要在客户进程和服务器进程之间复制,所以共享存储是进程间通讯最快的一种。
使用共享存储必须是多进程之间同步访问共享存储区
即就是当我有一A进程正在将数据放入共享存储区,这个时候另一B进程是不能从共享存储区中去取数据,直到进程A写这一操作执行完
这里我用了信号量来实现进程的同步,信号量的操作利用上面信号量的代码::
简单代码实现如下:
#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <assert.h>#include <string.h>#include <sys/shm.h>#include "sem.h"void main(){int shmid = shmget((key_t)1234, 128, 0664 | IPC_CREAT); //获得一个共享存储标识符assert(shmid != -1);char *ptr = (char *)shmat(shmid, NULL, 0); //将共享存储连接到内核分配的一块空间上assert(ptr != (char *)-1);seminit(); //初始化信号量while(1){printf("please input: ");fflush(stdout);fgets(ptr, 128, stdin);ptr[strlen(ptr) - 1] = 0;semv(); //信号量值加一if(strncmp(ptr, "end", 3) == 0){break;}}shmdt(ptr); //对共享存储的操作已结束}
#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <assert.h>#include <string.h>#include <sys/shm.h>#include "sem.h"void main(){int shmid = shmget((key_t)1234, 128, 0664 | IPC_CREAT);assert(shmid != -1);char *ptr = (char *)shmat(shmid, NULL, 0);assert(ptr != (char *)-1);seminit();while(1){semp();if(strncmp(ptr, "end", 3) == 0){break;}printf("s = %s\n", ptr);}shmdt(ptr);shmctl(shmid, IPC_RMID, NULL); //从系统中删除共享存储段semdel(); //删除信号量}
- 进程间通讯——消息队列、信号量以及共享内存
- Linux--进程间通讯IPC(信号量,消息队列,共享内存)
- 进程间通信(IPC)——信号量、共享内存、消息队列
- 进程间通信(IPC)——信号量、共享内存、消息队列
- IPC—进程间的通信(信号量,共享内存,消息队列)
- 进程间通信:管道,信号量,共享内存,消息队列
- 进程间通信:消息队列&信号量&共享内存
- Linux程序设计——信号量、共享内存和消息队列
- 《Linux程序设计》——信号量、共享内存和消息队列
- 信号量 消息队列 共享内存
- 信号量、消息队列、共享内存
- Linux进程间通信--进程,信号,管道,消息队列,信号量,共享内存
- Linux进程间通信--进程,信号,管道,消息队列,信号量,共享内存
- Linux进程间通信--进程,信号,管道,消息队列,信号量,共享内存
- Linux进程间通信--进程,信号,管道,消息队列,信号量,共享内存
- Linux进程间通信--进程,信号,管道,消息队列,信号量,共享内存
- Linux进程间通信--进程,信号,管道,消息队列,信号量,共享内存
- Linux进程间通信--进程,信号,管道,消息队列,信号量,共享内存
- activemq理解
- Hadoop项目实战---黑马论坛日志分析
- 我的电路实践
- MySQL初步学习4:处理大数据对象
- Java内部类和匿名内部类
- 进程间通讯——消息队列、信号量以及共享内存
- 欢迎使用CSDN-markdown编辑器
- C#实现List
- D
- PHP实现汉字转拼音
- E
- MySQL高级篇(一)——索引
- 测量Python代码运行时间
- hive 配置