一、IPC进程间通讯(system v IPC)
二、网络基础知识
一、IPC进程间通讯(system v IPC)
包含三个方面:
1、消息队列
2、共享内存
3、信号量集
可以使用ipcs查看system v IPC的对象
IPC的操作主要有以下几个步骤:
1、获取一个键值
ftok(3)
#include<sys/types.h>
#include<sys/ipc.h>
key_t ftok(const char *pathname,int proj_id);
功能:
转换pathname和proj_id为一个键值
参数:
pathname:指定文件名字
proj_id:整数,不能为0
返回值:
成功:key值返回
错误:-1,errno被设置
#include<sys/types.h> #include<sys/ipc.h> #inlcude<stdio.h> int main(void){ key_t key; key=ftok("hello",31); if(key==-1){ perror("ftok"); return 1; } printf("key=%d ",key); return 0; }
键值是由文件名和proj_id决定的,且是唯一不变的。
2、通过键值获取一块内存,并返回这块内存的ID(IPC的ID,即标识符)
(1)消息队列
msgget(2)(获取内存ID)
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key,int magflg);
功能:
获取一个和key关联的消息队列的ID,(其中key是通过ftok获取的)
参数:
key
0(IPC_PRIVATE):会建立新的消息队列
大于0的32位整数:视参数msgflg来确定操作。通常要求此值来源于ftok返回的IPC键值
msgflg
0:取消息队列标识符,若不存在则函数会报错
IPC_CREAT:当msgflg&IPC_CREAT为真时,如果内核中不存在键值与key相等的消息队列,则新建一个消息队列;如果存在这样的消息队列,返回此消息队列的标识符
IPC_EXCL:如果内核中不存在键值与key相等的消息队列,则新建一个消息队列;如果存在这样的消息队列则报错
返回值:
失败:-1,errno被设置
成功:消息队列的ID
错误代码
EACCES:指定的消息队列已存在,但调用进程没有权限访问它
EEXIST:key指定的消息队列已存在,而msgflg中同时指定IPC_CREAT和IPC_EXCL标志
ENOENT:key指定的消息队列不存在同时msgflg中没有指定IPC_CREAT标志
ENOMEM:需要建立消息队列,但内存不足
ENOSPC:需要建立消息队列,但已达到系统的限制
例:
#include<sys/types.h> #include<sys/ipc.h> #inlcude<stdio.h> int main(void){ key_t key; key=ftok("hello",31); if(key==-1){ perror("ftok"); return 1; } printf("key=%d ",key); //通过键值获取消息队列的ID int msqid =msgget(key,IPC_CREAT); if(msqid==-1){ perror("msgget"); return 2; } printf("msgqid%d ",msqid); return 0; }
此时可以使用ipcs查看IPC相关信息,看到key值可ID已经绑定。
消息队列
1 获取键值 ftok(3)
2 获取消息队列的id msgget(2)
3 向消息队列发送消息 msgsnd(2)
4 从消息队列获取消息 msgrcv(2)
msgsnd(2) (将消息写入到消息队列)
所需头文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
函数原型
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg)
功能:
将msgp消息写入到标识符为msqid的消息队列
参数:
msqid:要操作的消息队列,函数传入值
msgp:消息队列标识符,发送给队列的消息。msgp可以是任何类型的结构体,但第一个字段必须为long类型,即表明此发送消息的类型,msgrcv根据此接收消息。msgp定义的参照格式如下:
struct s_msg{ /*msgp定义的参照格式*/
long type; /* 必须大于0,消息类型 */
char mtext[256]; /*消息正文,可以是其他任何类型*/
} msgp;
msgsz
要发送消息的大小,不含消息类型占用的4个字节,即mtext的长度
msgflg
0:当消息队列满时,msgsnd将会阻塞,直到消息能写进消息队列
IPC_NOWAIT:当消息队列已满的时候,msgsnd函数不等待立即返回
IPC_NOERROR:若发送的消息大于size字节,则把该消息截断,截断部分将被丢弃,且不通知发送进程。
函数返回值
成功:0
出错:-1,错误原因存于error中
错误代码
EAGAIN:参数msgflg设为IPC_NOWAIT,而消息队列已满
EIDRM:标识符为msqid的消息队列已被删除
EACCESS:无权限写入消息队列
EFAULT:参数msgp指向无效的内存地址
EINTR:队列已满而处于等待情况下被信号中断
EINVAL:无效的参数msqid、msgsz或参数消息类型type小于0
msgrcv (从消息队列读取消息)
所需头文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
功能:
从标识符为msqid的消息队列读取消息并存于msgp中,读取后把此消息从消息队列中删除
函数原型
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
参数:
msqid
消息队列标识符,同msgsnd
msgp
存放消息的结构体,结构体类型要与msgsnd函数发送的类型相同
msgsz
要接收消息的大小,不含消息类型占用的4个字节
msgtyp
0:接收第一个消息
>0:接收类型等于msgtyp的第一个消息
<0:接收类型等于或者小于msgtyp绝对值的第一个消息
msgflg
0: 阻塞式接收消息,没有该类型的消息msgrcv函数一直阻塞等待
IPC_NOWAIT:如果没有返回条件的消息调用立即返回,此时错误码为ENOMSG
IPC_EXCEPT:与msgtype配合使用返回队列中第一个类型不为msgtype的消息
IPC_NOERROR:如果队列中满足条件的消息内容大于所请求的size字节,则把该消息截断,截断部分将被丢弃
函数返回值
成功:实际读取到的消息数据长度
出错:-1,错误原因存于error中
错误代码
E2BIG:消息数据长度大于msgsz而msgflag没有设置IPC_NOERROR
EIDRM:标识符为msqid的消息队列已被删除
EACCESS:无权限读取该消息队列
EFAULT:参数msgp指向无效的内存地址
ENOMSG:参数msgflg设为IPC_NOWAIT,而消息队列中无消息可读
EINTR:等待读取队列内的消息情况下被信号中断
msgrcv()解除阻塞的条件有以下三个:
① 消息队列中有了满足条件的消息。
② msqid代表的消息队列被删除。
③ 调用msgrcv()的进程被信号中断。
进程A代码:
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <errno.h> #include <unistd.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/stat.h> #include <sys/msg.h> #define MSG_FILE "/home/tarena/server.c" #define BUFFER 255 #define PERM S_IRUSR|S_IWUSR struct msgtype { long mtype; char buffer[BUFFER+1]; }; int main() { struct msgtype msg; key_t key; int msgid; if((key=ftok(MSG_FILE,'a'))==-1) { perror("Creat Key Error"); exit(1); } if((msgid=msgget(key,PERM | IPC_CREAT | IPC_EXCL))==-1) { perror("Creat Message Error "); exit(1); } while(1) { msgrcv(msgid, &msg, sizeof(struct msgtype), 1,0); printf("Server Receive:%s ",msg.buffer); msg.mtype=2; msgsnd(msgid,&msg,sizeof(struct msgtype),0); } return 0; }
可再次使用ipcs查看相关信息,可以看到权限用户等已经被写入。
进程B代码:
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <errno.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <sys/stat.h> #define MSG_FILE "/home/tarena/server.c" #define BUFFER 255 #define PERM S_IRUSR|S_IWUSR struct msgtype { long mtype; char buffer[BUFFER+1]; }; int main(int argc,char **argv) { struct msgtype msg; key_t key; int msgid; if((key=ftok(MSG_FILE,'a'))==-1) { perror("Creat Key Error"); exit(1); } if((msgid=msgget(key,PERM))==-1) { perror("Creat Message Error"); exit(1); } msg.mtype=1; strcpy(msg.buffer, "这是客户端发出的消息内容"); msgsnd(msgid, &msg, sizeof(struct msgtype), 0); memset(&msg, '