一、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\n",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\n",key);//通过键值获取消息队列的IDint msqid =msgget(key,IPC_CREAT);if(msqid==-1){perror("msgget");return 2;}printf("msgqid%d\n",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\n",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, '\0', sizeof(struct msgtype));msgrcv(msgid, &msg, sizeof(struct msgtype),2,0);printf("Client receive:%s\n",msg.buffer);
return 0;
}

(2)共享内存

和消息队列(使用msgget)一样,可以使用shmget(2)获取共享内存的ID。

#include <sys/types.h>

#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, int size, int flag);

void* shmat(int shmid, const void *addr, int flag); 
int shmdt(char *shmaddr);
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
shmget(2)

功能:

  用于开辟或指向一块共享内存,返回获得共享内存区域的ID,如果不存在指定的共享区域就创建相应的区域。

参数:
  keyt key: 共享内存的标识符。如果是父子关系的进程间通信的话,这个标识符用IPC_PRIVATE来代替。
    如果两个进程没有任何关系,所以就用ftok()算出来一个标识符(或者自己定义一个)使用了。
  int size: 以字节为单位指定需要共享的内存容量。
  int flag: 包含9个比特的权限标志,它是这块内存的模式(mode)以及权限标识。 模式可取如下值:
IPC_CREAT 新建(如果已创建则返回目前共享内存的id)
IPC_EXCL 与 IPC_CREAT结合使用,如果已创建则返回错误
  将“模式” 和“权限标识”进行或运算,做为第三个参数。如:IPC_CREAT | IPC_EXCL | 0640
其中0640为权限标识,4/2/1 分别表示读/写/执行3种权限,第一个0是UID,第一个6(4+2)表示拥有者的权限,第二个4表示同组权限,第3个0表示他人的权限。函数调用成功时返回共享内存的ID,失败时返回-1。
  注:创建共享内存时,shmflg参数至少需要 IPC_CREAT | 权限标识,如果只有IPC_CREAT 则申请的地址都是
k=0xffffffff,不能使用;

shmat(2)

void* shmat(int shmid, const void *addr, int flag);

功能:

  用于将共享内存映射到进程的虚拟地址空间中。用来允许本进程访问一块共享内存的函数。第一次创建共享内存时,它不能任何进程访问,要想启用对该共享内存的访问,必须将其连接到一个进程的地址空间中,。shmat函数就是用来完成此工作的。

参数:

  int shmid :共享内存的ID,即共享内存的标识。

  char *shmaddr: 共享内存连接到进程中的起始地址,如果shmaddr为NULL,内核会把共享内存映射到系统选定的地址空间中;如果shmaddr不为NULL,内核会把共享内存映射到shmaddr指定的位置。
  注:一般情况下我们很少需要控制共享内存连接的地址,通常都是让系统来选择一个地址,否则就会使应用程序对硬件的依赖性过高。所以一般把shmaddr设为NULL。
  int shmflag : 本进程对该内存的操作模式,可以由两个取值:SHM_RND和SHM_RDONLY。SHM_RND为读写模式,SHM_RDONLY是只读模式。需要注意的是,共享内存的读写权限由它的属主、它的访问权限和当前进程的属主共同决定。如果当shmflg & SM_RDONLY为true时,即使该共享内存的访问权限允许写操作,它也不能被写入。该参数通常会被设为0(进程有读写权限)。

返回值: 

  函数调用成功时,返回共享内存的起始地址,失败时返回-1。

shmdt函数

功能:

  解除进程与进程虚拟地址空间的的映射。用于函数删除本进程对这块内存的使用。shmdt()与shmat()相反,是用来禁止本进程访问一块共享内存的函数。
参数:

  char *shmaddr 是那块共享内存的起始地址。shmget(2)的返回值。

返回值:

  函数调用成功时返回0,失败时返回-1。

给予共享内存通信代码A

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/shm.h>
#define MSG_FILE "/home/tarena/进程A.c"
#define BUFFER 4096
#define PERM S_IRUSR|S_IWUSR
int main()
{key_t key;
if((key=ftok(MSG_FILE,'a'))==-1){perror("Creat Key Error");exit(1);}
int shmid = shmget (key, BUFFER, PERM | IPC_CREAT | IPC_EXCL);
if (shmid == -1){perror ("shmget");exit (EXIT_FAILURE);}
void* shmaddr = shmat (shmid, NULL, 0);
if (shmaddr == (void*)-1){perror ("shmat");exit (EXIT_FAILURE);}strcpy (shmaddr, "这是放入共享内存的内容!");
if (shmdt (shmaddr) == -1){perror ("shmdt");exit (EXIT_FAILURE);}sleep(10);
if (shmctl (shmid, IPC_RMID, NULL) == -1){perror ("shmctl");exit (EXIT_FAILURE);}
return 0;
}

基于共享内存通讯进程B代码

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/shm.h>
#define MSG_FILE "/home/tarena/进程A.c"
#define BUFFER 4096
#define PERM S_IRUSR|S_IWUSR
int main(int argc,char **argv)
{key_t key;
if((key=ftok(MSG_FILE,'a'))==-1){perror("Creat Key Error");exit(1);}
int shmid = shmget (key, BUFFER, PERM);
if (shmid == -1){perror ("shmget");exit (EXIT_FAILURE);}
void* shmaddr = shmat (shmid, NULL, 0);
if (shmaddr == (void*)-1){perror ("shmat");exit (EXIT_FAILURE);}printf ("%s\n", (char*)shmaddr);
if (shmdt (shmaddr) == -1){perror ("shmdt");exit (EXIT_FAILURE);}
return 0;
}

(3)信号量集

  略

3、通过标识符操作这块内存

共享内存操作
shmctl函数

功能:

   控制对这块共享内存的使用。

参数:
  int shmid: 共享内存的ID,即共享内存标识。
  int cmd : 控制命令,表示要采取的动作,可取值如下:
IPC_STAT 得到共享内存的状态:把shmid_ds结构中的数据设置为共享内存的当前关联值
IPC_SET 改变共享内存的状态:把共享内存的当前关联值设置为shmid_ds结构中给出的值
IPC_RMID 删除共享内存段
  shmid_ds结构至少包含以下成员:
  struct shmid_ds {
  uid_t shm_perm.uid;
  uid_t shm_perm.gid;
  uid_t shm_perm.mode;
  }
  struct shmid_ds *buf: 一个结构体指针。IPC_STAT的时候,取得的状态放在这个结构体中。如果要改变共享内存的状态,用这个结构体指定。

返回值:

  函数调用成功时返回0,失败时返回-1。

使用ipcrm命令删除IPC通讯对象

 

转载于:https://www.cnblogs.com/ptfe/p/11031452.html

Linux-IPC进程间通信(day11)相关推荐

  1. Linux IPC进程间通信(三):信号量

    系列文章: Linux IPC进程间通信(一):管道 Linux IPC进程间通信(二):共享内存 Linux IPC进程间通信(三):信号量 Linux IPC进程间通信(四):消息队列 文章目录 ...

  2. Linux IPC 进程间通信——消息队列message

    消息队列是消息的连接表,存储在内核中.本实例主要实现消息队列方式进行进程间通信,接收端收到消息之后,立马转发给发送端:发送端发出消息之后,立马监听接收端回馈的消息,实现一个双向通信示例. 一.示例 发 ...

  3. Linux进程+进程间通信IPC

    一 Linux进程 1) 进程的内存映像 2)解释 BSS段:在采用段式内存管理的架构中,BSS段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域.BSS是英文Bloc ...

  4. Linux 3.进程间通信(IPC)(pipe 无名管道、mkfifo 有名管道、ftok、msgget、msgrcv、msgsnd、msgctl 消息队列)

    Linux 3.进程间通信(IPC) 进程间通信: 进程间方式: pipe 管道(无名管道) 头文件及原型 特点 pipe 示例 FIFO(有名管道) 管道文件的创建 mkfifo 头文件及原型 mk ...

  5. 音视频:06.linux系统-IPC进程间通信

    linux系统-IPC进程间通信 1.进程间通信的方式 2.管道(pipe) 3.共享映射区 4.信号 1.进程间通信的方式 文件,管道,信号.信号量,共享映射区(共享内存),消息队列,管道,套接字( ...

  6. linux c 进程间通信

    进程间通信概述 进程间通信(InterProcess Communication,IPC)是指在不同进程之间传播或交换信息. Linux的进程间通信方法有管道(Pipe)和有名管道(FIFO).信号( ...

  7. Linux的进程间通信-消息队列

    Linux的进程间通信-消息队列 微博ID:orroz 微信公众号:Linux系统技术 前言 Linux系统给我们提供了一种可以发送格式化数据流的通信手段,这就是消息队列.使用消息队列无疑在某些场景的 ...

  8. Linux下进程间通信的六种机制详解

    linux下进程间通信的几种主要手段:        1.管道(Pipe)及有名管道(named pipe):管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具 ...

  9. Linux下进程间通信概述

    1. Linux下进程间通信概述 P83-P84 将第一页和第二页合并起来讲了 引言:前面我们学习了一下进程,我们知道多,进程间的地址空间相对独立.进程与进程间不能像线程间通过全局变量通信. 如果想进 ...

  10. Android开发之IPC进程间通信-AIDL介绍及实例解析

    一.IPC进程间通信 IPC是进程间通信方法的统称,Linux IPC包括以下方法,Android的进程间通信主要采用是哪些方法呢? 1. 管道(Pipe)及有名管道(named pipe):管道可用 ...

最新文章

  1. QS世界大学学科排名公布,清华22个学科进入世界50强
  2. 写的py_ping 可以跑了
  3. c++和c语言的区别_C 语言和 C++ 有什么区别?老程序员居然这样理解,不怕你不懂...
  4. java+arrayblockquene_java集合(五)Queue集合之ArrayBlockingQueue 详解
  5. mongodb添加多条数据_分析了一万多条拼车数据,看看北上广深的各位都回哪过年...
  6. oracle 创建新库时报错:enterprise manager 配置失败
  7. 数据结构:点之间的最短距离--Floyd算法
  8. 12.2 新特性:锁信息获取之在线删除索引
  9. fragment中嵌套viewpager,vierpager中用fragment不显示数据
  10. 50、BGP配置实验之社团属性community
  11. 转载:东拉西扯:产业链
  12. FZU 2128 最长子串
  13. KT:一款可提升Kubernetes开发效率的免费工具
  14. 美了哭了,AduSkin是我见过最好看的WPF控件库
  15. 小程序 VS APP优缺点
  16. 自学Python+Selenium自动化测试
  17. shell遍历ip执行特定功能
  18. 如何通过linux的终端命令远程登陆windows主机
  19. 开启和关闭远程Windows系统3389端口
  20. Linux的文件和目录管理

热门文章

  1. php截取字符串utf8,php自定义截取中文字符串-utf8版
  2. 蓝桥杯2011--2--歌赛新规则
  3. utf8_unicode_ci和utf8_general_ci的区别,联系及使用
  4. 实时计算在天猫双十一大屏中的应用
  5. Java语言中:switch语句经典习题
  6. Javascript图像处理之平滑处理
  7. 从FindBugs中学Java【四】
  8. Uploading File using Ajax and receiving binary data in Asp.net (C#)[转]
  9. 华为鸿蒙 os 适配机型曝光,华为鸿蒙OS 2.0首批适配机型曝光
  10. java 泛型的问题_java 泛型问题?