赵老师笔记----消息队列-共享内存
二、搭建开发环境
1.将window设置静态ip
172.20.128.XX,子网掩码255.255.0.0,默认网关172.20.128.1
2.将虚拟机设置静态iP
172.20.128.XX+100,子网掩码255.255.0.0,默认网关172.20.128.1
3.利用Xshell连接开发板,
选择串口serial,然后设置serial,com1,115200,8,1,none
4.设置开发板的网卡地址
ifconfig eth0 172.20.128.100 netmask 255.255.0.0
ifconfig eth0 up
5.利用NFS挂载虚拟机下的某个文件,建议该文件就是通过samba共享给windows的目录
mount -o nolock,wsize=1024,rsize=1024 你的虚拟机的ip:/home/edu/share /mnt
以上4.5.需要每次进行设置不便。
6.利用开发板引导脚本自动配置网络并挂载
将以上4步及5步,写入到开发板的引导脚本/etc/profile
vi /etc/profile
下拉到最后
ifconfig eth0 172.20.128.100 netmask 255.255.0.0
ifconfig eth0 up
mount -o nolock,wsize=1024,rsize=1024 你的虚拟机的ip:/home/edu/share /mnt
然后保存,再重启,以后每次自动设置网络地址并自动挂载。
三、消息队列
1.经典IPC:
消息队列、共享内存、信号量
2.利用ipcs命令可以查看三种经典IPC
ipcs -m:命令显示共享内存(share memory)
ipcs -q:命令显示消息队列(message queue)
ipcs -s:命令显示信号量(semaphore)
删除 ipcrm -q msgid
ipcrm -m shmid
3.什么是消息队列
消息队列是消息的链表,存放在内存中,由内核维护
4.消息队列的特点
1、消息队列中的消息是有类型的。
2、消息队列中的消息是有格式的。
3、消息队列可以实现消息的随机查询。消息不一定要以先进先出的次序读取,编程时可以按消息的类型读取。
4、消息队列允许一个或多个进程向它写入或者读取消息。
5、与无名管道、命名管道一样,从消息队列中读出消息,消息队列中对应的数据都会被删除。
6、每个消息队列都有消息队列标识符,消息队列的标识符在整个系统中是唯一的。
7、只有内核重启或人工删除消息队列时,该消息队列才会被删除。若不人工删除消息队列,消息队列会一直存在于系统中。
5.经典IPC的标识符(key)的设定
key(键值)是经典IPC资源的关键要素
只要不同的进程拥有相同的key,那么他们通过同样的XXXget()函数返回id就会自动关联到一起,就可以实现不同进程间的数据或者资源的共享
有三种方式进行设定
①直接设置一个绝对的数字0x12341234
②利用ftok()函数获得
③设置为IPC_PRIVATE
6.ftok函数
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
功能:
获得项目相关的唯一的IPC键值。
参数:
pathname:路径名
proj_id:项目ID,非0整数(只有低8位有效),1~255
返回值:
成功返回key值,失败返回 -1。
7.创建消息队列msgget
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
功能:
创建一个新的或打开一个已经存在的消息队列。不同的进程调用此函数,只要用相同的key值就能得到同一个消息队列的标识符。
参数:
key:IPC键值。
msgflg:标识函数的行为及消息队列的权限。
IPC_CREAT:创建消息队列
IPC_EXCL:检测消息队列是否存在
位或权限位:对消息队列的访问权限
IPC_CREAT|0666:表示创建消息队列并是所以用户具有读写权限
返回值:
成功:消息队列的标识符(msgid),失败:返回-1
8.删除消息队列
删除 ipcrm -q msgid
9.消息队列的格式
必须以结构体定义消息队列的信息
第一个成员必须是消息类型,且消息类型为长整型(long)
后面可以有一个或多个成员表示消息正文,类型不限
typedef struct _msg
{
long mtype; /*消息类型*/
char mtext[100]; /*消息正文*/
... /*消息的正文可以有多个成员*/
}MSG;
10.消息发送msgsnd
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
功能:
将新消息添加到消息队列。
参数:
msqid:消息队列的标识符。
msgp:待发送消息结构体的地址。
msgsz:消息正文的字节数。sizeof(MSG)-sizeof(long)
msgflg:0为调用阻塞直到条件满足,IPC_NOWAIT,不阻塞直接返回
返回值:
成功:0;失败:返回-1。
11.消息接收msgrcv
#include <sys/msg.h>
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
功能:
从标识符为msqid的消息队列中接收一个消息。一旦接收消息成功,则消息在消息队列中被删除。
参数:
msqid:消息队列的标识符,代表要从哪个消息列中获取消息。
msgp: 存放消息结构体的地址。
msgsz:消息正文的字节数。sizeof(MSG)-sizeof(long)
msgtyp:消息的类型、可以有以下几种类型
msgtyp = 0:返回队列中的第一个消息
msgtyp > 0:返回队列中消息类型为msgtyp的消息
msgtyp < 0:返回队列中消息类型值小于或等于msgtyp绝对值的消息,如果这种消息有若干个,则取类型值最小的消息
msgflg:函数的控制属性
0:msgrcv调用阻塞直到接收消息成功为止。
MSG_NOERROR:若返回的消息字节数比nbytes字节数多,则消息就会截短到nbytes字节,且不通知消息发送进程。
IPC_NOWAIT:调用进程会立即返回。若没有收到消息则立即返回-1。
返回值:
成功返回读取消息的长度,失败返回-1。
11.消息队列的控制函数msgctl
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
功能:
对消息队列进行各种控制,如修改消息队列的属性,或删除消息消息队列。
参数:
msqid:消息队列的标识符。
cmd:函数功能的控制。 IPC_RMID:删除由msqid指示的消息队列,将它从系统中删除并破坏相关数据结构。等价ipcrm -q msgid IPC_STAT:将msqid相关的数据结构中各个元素的当前值存入到由buf指向的结构中。 IPC_SET:将msqid相关的数据结构中的元素设置为由buf指向的结构中的对应值。
buf:msqid_ds数据类型的地址,用来存放或更改消息队列的属性。
返回值:成功:返回 0;失败:返回 -1
举例:在一个父进程不断获得键盘的输入,发送到消息队列里,在子进程里不断读取消息队列的内容并送屏幕显示。可添加退出条件,当输入“quit”时退出。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
typedef struct _msg
{
long mtype;
char mtext[100];
}MSG;
int main(int argc, char *argv[])
{
key_t key;
int msgqid;
MSG msg;
#if 0
//1.直接指定key值
msgqid = msgget(0x123456, IPC_CREAT|0666);
//3.申请私有键值
msgqid = msgget(IPC_PRIVATE, IPC_CREAT|0666);
#endif
//2.通过ftok()获得
key = ftok("./", 'a');
msgqid = msgget(key, IPC_CREAT|0666);
if(msgqid == -1)
{
perror("msgget");
exit(-1);
}
pid_t pid=fork();
if (pid<0)
{
perror("fork");
return 0;
}
if (0==pid)
{//子进程
while(1)
{
bzero(msg.mtext,100);
msg.mtype=22;//指定消息类型,在接收消息时可以指定接收该类型消息
read(0,msg.mtext,99);//从键盘输入信息
msgsnd(msgqid,&msg,sizeof(MSG)-sizeof(long),0);//发送不成功阻塞
if (!strncasecmp(msg.mtext,"quit",strlen("quit")))
break;//用户输入了,quit
}
exit(0);
}else
{//父进程
while(1)
{
sleep(1);//停一秒
bzero(msg.mtext,100);
if (msgrcv(msgqid,&msg,sizeof(msg.mtext),22,0)<=0)
{
printf("消息队列被释放\n");
break;//接收返回值非正,表示消息队列已经被释放
}
printf("rcv=%s\n", msg.mtext);//打印从消息队列读入的信息
if (!strncasecmp(msg.mtext,"quit",strlen("quit")))
break;//用户输入了,quit
}
}
return 0;
}
晚上作业:
消息队列实现多人聊天程序
四、共享内存(shared memory)
1.什么是共享内存
共享内存允许两个或者多个进程共享给定的存储区域
2.共享内存的特点
1、共享内存是进程间共享数据的一种最快的方法。是一种最高效IPC通信方式 一个进程向共享的内存区域写入了数据,共享这个内存区域的所有进程就可以立刻看到其中的内容。
2、使用共享内存要注意的是多个进程之间对一个给定存储区访问的互斥。 若一个进程正在向共享内存区写数据,则在它做完这一步操作前,别的进程不应当去读、写这些数据。
3.获得key(三种方法,同上所述)
4.创建共享内存shmget()
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size,int shmflg);
功能:创建或打开一块共享内存区
参数:
key:IPC键值
size:该共享存储段的长度(字节)
shmflg:标识函数的行为及共享内存的权限。
成功:返回共享内存标识符。
失败:返回-1。
5.删除命令ipcrm -m shmid
6.映射共享内存attach,即把指定的共享内存映射到进程的逻辑地址空间用于访问
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr,
int shmflg);
功能:
将一个共享内存段映射到调用进程的数据段中。
参数:
shmid:共享内存标识符。
shmaddr:共享内存映射地址(若为NULL则由系 统自动指定),推荐使用NULL。
shmflg:共享内存段的访问权限和映射条件
0:共享内存具有可读可写权限。
SHM_RDONLY:只读。
SHM_RND:(shmaddr非空时才有效)
没有指定SHM_RND则此段连接到shmaddr所指定的地址上(shmaddr必需页对齐)。
指定了SHM_RND则此段连接到shmaddr- shmaddr%SHMLBA 所表示的地址上。
成功:返回共享内存段映射地址
失败:返回 -1
7.解除(撤销)共享内存映射detach
#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
功能:
将共享内存和当前进程分离(仅仅是断开联系并不删除共享内存)。
参数:
shmaddr:共享内存映射地址。
返回值:
成功返回 0,失败返回 -1。
8.删除共享内存对象(借用共享内存控制函数shmctl)
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd,
struct shmid_ds *buf);
功能:共享内存空间的控制。
参数:
shmid:共享内存标识符。
cmd:函数功能的控制。
IPC_RMID:删除。
IPC_SET:设置shmid_ds参数。
IPC_STAT:保存shmid_ds参数。
SHM_LOCK:锁定共享内存段(超级用户)。 SHM_LOCK用于锁定内存,禁止内存交换。并不代表共享内存被锁定后禁止其它进程访问。其真正的意义是:被锁定的内存不允许被交换到虚拟内存中。
这样做的优势在于让共享内存一直处于内存中,从而提高程序性能。
SHM_UNLOCK:解锁共享内存段。
buf:shmid_ds数据类型的地址,用来存放或修改共享内存的属性。
返回值:
成功返回 0,失败返回 -1
9.总结共享内存的使用步骤
①创建/打开共享内存(shmget),利用ipc键值(key),key三种方法
②映射共享内存(shmat)
③利用(shmat)返回的地址对共享内存进行读写
④解除,撤销共享内存映射(shmdt)
⑤删除共享内存对象(shmctl,带的参数IPC_RMID)
练习:一个程序不断从键盘读入信息,写入共享内存,另一个程序不断从共享内存里读取数据并显示,碰到quit时结束程序。