ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

进程间通信

2021-07-01 18:30:04  阅读:172  来源: 互联网

标签:include 队列 间通信 管道 消息 进程 共享内存


进程间通信(Interprocess Communication, IPC):管道、FIFO、消息队列、信号量、共享存储、套接字

1. 管道

1.1 创建:

#include <unistd.h>
 
int pipe(int fd[2]);

// 返回值:若成功,返回0,若出错,返回-1.
// 函数调用成功返回r/w两个文件描述符。无需open,但需手动close。
// 规定:fd[0] → r; fd[1] → w,就像0对应标准输入,1对应标准输出一样
// 向管道文件读写数据其实是在读写内核缓冲区。

1.2 特点:

  • 其本质是一个伪文件(实为内核缓冲区)
  • 只适用于有血缘关系之间的进程
  • 数据一旦被读走,便不在管道中存在(不可反复读取)

1.3 局限性:

  • 半双工,数据只能在一个方向流动,只能单向通信
  • 管道只能在具有公共祖先之间的两个进程之间使用;

1.4 通信过程

管道创建成功以后,创建该管道的进程(父进程)同时掌握着管道的读端和写端。父子进程间通信的步骤:
在这里插入图片描述

  1. 父进程调用pipe函数创建管道,得到两个文件描述符fd[0]、fd[1]指向管道的读端和写端。

  2. 父进程调用fork创建子进程,那么子进程也有两个文件描述符指向同一管道。

  3. 父进程关闭管道读端,子进程关闭管道写端。父进程可以向管道中写入数据,子进程将管道中的数据读出。由于管道是利用环形队列实现的,数据从写端流入管道,从读端流出,这样就实现了进程间通信。

2. 命名管道(FIFO):

1.1 创建:

#include <sys/stat.h>

int mkfifo(const char *path, mode_t mode);

//返回值:若成功,返回0;若出错,返回-1。
// 参数pathname为路径名,创建管道的名字
// mode为创建fifo的读写权限。

1.2 特点:

  • 命名管道不同于匿名管道:它提供一个路径名与之关联,以FIFO的文件形式存储于文件系统中,即第一个被写入的数据将首先从管道中读出
  • 命名管道是一个设备文件,即使进程与创建FIFO的进程不存在亲缘关系,只要可以访问该路径,就能够通过FIFO相互通信。

1.3 与匿名管道对比

  • mkfifo函数的作用是在文件系统中创建一个真实存在文件,该文件用于提供FIFO功能,即命名管道。前边讲的那些管道都没有名字,因此它们被称为匿名管道,或简称管道。
  • 对文件系统来说,匿名管道是不可见的,它的作用仅限于在父进程和子进程两个进程间进行通信。而命名管道是一个可见的文件,因此,它可以用于任何两个进程之间的通信,不管这两个进程是不是父子进程,也不管这两个进程之间有没有关系。

3. 消息队列

1.1 创建:

第一步:获得key值。因为消息队列独立于进程而存在,所以为了区别不同的消息队列,需要以key值标记消息队列,这样两个不相关的进程可以通过事先约定的key值通过消息队列进行消息收发。

#include <sys/types.h>
#include <sys/ips.h>
 
key_t futon(char *pathname, int projid)

// pathname:文件名(含路径),通常设置为当前目录“.” 
// projid:子序号,虽然为int类型,但只使用了低8位,通常用一个字母表示
// 返回值:成功返回key值,失败返回-1;

第二步:创建或打开消息队列

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgget(key_t key, int msgflag)

/*
功能:用于创建一个新的或打开一个已经存在的消息队列,此消息队列与key相对应。
参数:
    key:函数ftok的返回值或IPC_PRIVATE,为IPC_PRIVATE时表示创建自己的消息队列,这样就只能用于和自己有亲缘关系的进程间通信。
    msgflag:
        IPC_CREAT:创建新的消息队列。
        IPC_EXCL:与IPC_CREAT一同使用,表示如果要创建的消息队列已经存在,则返回错误。
        IPC_NOWAIT:读写消息队列要求无法满足时,不阻塞。
返回值:
    调用成功返回队列标识符,否则返回-1.
*/

第三步:将消息添加到消息队列

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgsnd(int msqid,  struct msgbuf *msgp,  size_t msgsz,  int msgflag)
/*
功能:
    将新消息添加到队列尾端,即向消息队列中发送一条消息。
参数:
    msqid:消息队列的标识符。
    msgp:指向消息缓冲区的指针,此位置用来暂时存储发送和接收的消息,是一个用户可定义的通用结构,形态如下

struct msgbuf {
    long mtype;     /* 消息类型,必须 > 0 */
    char mtext[1];  /* 消息文本 */
};
    msgsz:消息的大小,消息的总大小不能超过8192字节,在<linux/msg.h>有个宏控制,#define MSGMAX 8192。
    msgflag:
         IPC_NOWAIT: 指明在消息队列没有足够空间容纳要发送的消息时,msgsnd立即返回。
         0:msgsnd调用阻塞直到条件满足为止.(一般选这个)
*/

第四步:从消息队列读取消息:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

ssize_t msgrcv(int msqid,  struct msgbuf *msgp,  size_t msgsz,  long msgtype,  int msgflag)

/*
功能:
    从队列中接收消息
参数:
    msqid:已打开的消息队列id
    msgp:存放消息的结构体指针。msgp->mtype与第四个参数是相同的。
    msgsz:消息的字节数,指定mtext的大小。
    msgtype:消息类型,消息类型 mtype的值。如果为0,则接受该队列中的第一条信息,如果小于0,则接受小于该值的绝对值的消息类型,如果大于0,接受指定类型的消息,即该值消息。
    msgflag:函数的控制属性。
    msgflag:
        MSG_NOERROR:若返回的消息比msgsz字节多,则消息就会截短到msgsz字节,且不通知消息发送进程.
        IPC_NOWAIT:调用进程会立即返回.若没有收到消息则返回-1.
        0:msgrcv调用阻塞直到条件满足为止.
在成功地读取了一条消息以后,队列中的这条消息将被删除。
返回值:成功执行时,msgrcv()返回0, 失败返回-1
*/

1.2 特点:

  • 消息队列是消息的链表,具有特定的格式,存放在内存中,并由消息队列标识符标识.
  • 消息队列允许一个或多个进程向它写入与读取消息.
  • 管道和命名管道都是通信数据都是先进先出的原则,而消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取;比FIFO更有优势

4. 共享存储

共享存储允许一个或多个进程共享一个给定的存储区,因为数据不需要在客户端进程和服务器进程之间进行复制,所以这是最快的一种IPC,对于像管道和消息队列等通信方式,则需要在内核和用户空间进行四次的数据拷贝,而共享内存则只拷贝两次数据:一次从输入文件到共享内存区,另一次从共享内存区到输出文件。

不同进程之间共享的内存通常安排为同一段物理内存。进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址,同一块物理内存被映射到进程A、B各自的进程地址空间。进程A可以即时看到进程B对共享内存中数据的更新,反之亦然。由于多个进程共享同一块内存区域,必然需要某种同步机制——互斥锁和信号量都可以。

4.1 相关函数

第一步:创建/打开共享内存

#include <sys/ipc.h>
#include <sys/shm.h>
 
int shmget(key_t key, size_t size, int shmflg);
 
/*
    功能:分配一块共享内存
  
    参数:
         key: 函数ftok的返回值或IPC_PRIVATE,为IPC_PRIVATE时创建的共享内存只能用于有亲缘关系的进程通信;
         size:建立共享内存的大小(单位字节),通常取系统页长的整数倍,若size值不是系统页的整数倍,则最后一页剩余部分是不可用的;
         shmflg:
             0:取共享内存标识符,若不存在则函数会报错
             IPC_CREAT:如果内核中不存在键值与key相等的共享内存,则新建一个共享内存;如果存在这样的共享内存,返回此共享内存的标识符
             IPC_CREAT|IPC_EXCL:如果内核中不存在键值与key相等的共享内存,则新建一个消息队列;如果存在这样的共享内存则报错  16    返回值:成功,返回共享内存标识符,失败返回-1;      
*/

第二步:映射共享内存

#include <sys/types.h>
#include <sys/shm.h>

void *shmat(int shmid, const void *shmaddr, int shmflg)

/*
    功能:连接共享内存标识符为shmid的共享内存,连接成功后把共享内存区对象映射到调用进程的地址空间,随后可像本地空间一样访问

    参数:
          msqid:共享内存标识符
          shmaddr:指定共享内存出现在进程内存地址的什么位置,直接指定为NULL让内核自己决定一个合适的地址位置
          shmflg:SHM_RDONLY:为只读模式,否则为读写模式

    返回值:
            成功,返回共享内存地址,出错返回-1
*/

4.2 共享存储的特点:

  • 速度快,效率高;

  • 需要有同步机制;

4.3 共享存储操作流程

  1. 创建/打开共享内存

  2. 映射共享内存,即把即把指定的共享内存映射到进程的地址空间用于访问

  3. 撤销共享内存映射

  4. 删除共享内存

5. UNIX域套接字

  • UNIX域套接字用于在同一台机器上运行的进程之间的通信,虽然因特网域套接字可用于同一目的,但UNIX域套接字的效率更高。
  • UNIX域套接字仅仅复制数据;它们并不执行协议处理,不需要添加或删除网络报头,无需计算检验和,不要产生顺序号,无需发送确认报文。
  • UNIX域套接字有两种类型的套接字:字节流套接字和数据包套接字,字节流套接字类似于TCP,数据包套接字类似于UDP。

标签:include,队列,间通信,管道,消息,进程,共享内存
来源: https://blog.csdn.net/m0_38140207/article/details/118386980

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有