进程间的通讯
上一篇笔记描述了system v ipc 有三种进程间通讯的方式。
消息队列
可以通过ftok(2)获得一个进程的键值,可以通过msgget(2)获得一个消息队列的ID。
现在可以通过消息队列的ID来对消息队列进行写入数据和读取数据。
系统提供以下函数来操作这个过程:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
功能:发送消息到消息队列
参数:
msqid:将消息添加到msqid指定的消息队列中
msgp:需要调用者自定义的结构体变量的地址
msgsz:指定了msgp指向的mtext成员的大小
msgflg:
0:阻塞
IPC_NOWAIT:不阻塞
返回值:
error:-1,errno被设置
success:0
msgp指向这样一个结构体类型变量的地址
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[1]; /* message data */
};
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
功能:从消息队列移除一条消息,存放到msgp指定的地址空间里
参数:
msqid:指定消息队列,从这个消息队列中移除一条消息
msgp:指定存放消息的结构体的地址空间
msgsz:指定了msgp指向的变量的mtext成员的最大字节数
msgtyp:指定消息的类型,结构体变量的第一个成员变量的值。
msgflg:
参数:
IPC_NOWAIT:非阻塞,请求的消息不存在立即返回错误。不等待
0:阻塞,请求的消息不存在,等待。
返回值:
错误 -1 errno被设置
成功 返回实际拷贝的mtext的成员字节数
通过以下例子来演示上述函数:
/*send*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
typedef struct{
long mtype; /* message type, must be > 0 */
char mtext[128]; /* message data */
}msg_t;
int main(void){
msg_t msg;
//获取键值
key_t key = ftok(".",34);
if(key == -1){
perror("ftok");
return -1;
}
//获取队列ID
int flag_msg = msgget(key,IPC_CREAT|0644);
if(flag_msg == -1){
perror("msgget");
return -1;
}
msg.mtype = 3;
strcpy(msg.mtext,"hello kitty!\n");
//向队列发送一条消息
int flag_snd = msgsnd(flag_msg,&msg,strlen(msg.mtext),0);
if(flag_snd == -1){
perror("msgsnd");
return -1;
}
return 0;
}
-------------------------------------------------------------------
/*rcv*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
typedef struct{
long mtype; /* message type, must be > 0 */
char mtext[128]; /* message data */
}msg_t;
int main(void){
msg_t msg;
//获取键值
key_t key = ftok(".",34);
if(key == -1){
perror("ftok");
return -1;
}
//获取队列ID
int flag_msg = msgget(key,IPC_CREAT|0644);
if(flag_msg == -1){
perror("msgget");
return -1;
}
//从队列移除一条消息
int flag_rcv = msgrcv(flag_msg,&msg,128,3,0);
if(flag_rcv == -1){
perror("msgsnd");
return -1;
}
printf("%s",msg.mtext);
return 0;
}
在send函数中,向队列发送一条消息,在rcv函数中,接受队列发送的消息。
可以在程序的末尾加入getchar(),让程序暂停,然后通过ipcs来观察队列的连接数,和队列里的消息。
共享内存
共享内存的方式,是将内核中的一块内存映射到进程的虚拟地址空间上,通过操作进程的虚拟地址空间来完成进程间的通讯。
系统提供以下函数用于操作:
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
功能:分配一块(system v )共享内存段
参数:
key:ftok(2)的返回值,获取跟key值相关的共享内存段的id
size:指定了共享内存段的大小(系统为其分配页的整数倍)
shmflg:
IPC_CREAT:如果指定了这个标记,如果我的共享内存段不存在就创建一个。
IPC_EXCL:和open(2)一样
mode:指定了共享内存段的权限
返回值:
error:-1 errno被设置
success:共享内存段的ID
举例说明 使用shmget(2) 分配一块共享内存段
shmat(2)
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
功能:关联共享内存段到进程的地址空间
参数:
shmid:指定了共享内存段的id,将这个共享内存段关联到进程空间
shmaddr:NULL,由系统决定共享内存段关联的地址
shmflg:0
返回值:
error (void *) -1返回,errno被设置
success 共享内存段关联到进程的地址
shmdt(2)
int shmdt(const void *shmaddr);
功能:解除共享内存段和当前进程的地址空间的关联
参数:
shmaddr:shmat的返回值,指定了共享内存段在当前进程的指定地址
返回值:
success 0
error -1 errno被设置
通过以下两个程序来演示上述程序:
/*send*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
int main(void){
//获取键值
key_t key = ftok(".",35);
if(key == -1){
perror("ftok");
return -1;
}
printf("key:%x\n",key);
//获取队列ID
int flag_shm = shmget(key,4096,IPC_CREAT|0644);
if(flag_shm == -1){
perror("shmget");
return -1;
}
printf("shmid:%d\n",flag_shm);
printf("shmger success!\n");
//关联共享内存段
void *p_shmat = shmat(flag_shm,NULL,0);
if(p_shmat == (void *)-1){
perror("p_shmat");
return -1;
}
//解除关联
strcpy((char *)p_shmat,"hello kitty!\n");
getchar();
shmdt(p_shmat);
}
--------------------------------------------
/*rcv*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
int main(void){
//获取键值
key_t key = ftok(".",35);
if(key == -1){
perror("ftok");
return -1;
}
printf("key:%x\n",key);
//获取队列ID
int flag_shm = shmget(key,4096,IPC_CREAT|0644);
if(flag_shm == -1){
perror("shmget");
return -1;
}
printf("shmid:%d\n",flag_shm);
printf("shmger success!\n");
//关联共享内存段
void *p_shmat = shmat(flag_shm,NULL,0);
if(p_shmat == (void *)-1){
perror("p_shmat");
return -1;
}
//解除关联
printf("%s\n",(char *)p_shmat);
getchar();
shmdt(p_shmat);
}
共享内存和消息队列不同,共享内存方式下,读取数据的进程,读取数据之后,并不会移除内存中的数据。
网络基础