进程间通信

c/c++

浏览数:280

2019-6-30

进程间通信(IPC,InterProcess Communication)是指在不同进程之间传播或交换信息。进程间通信的方式有如下几种:

1.管道通信

特点:

  1. 管道只允许具有血缘关系的进程间通信,如父子进程间的通信。

  2. 它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端。
  3. 管道并非是进程所有的资源,而是和套接字一样,归操作系统所有。可以将它看成文件系统,但该文件系统只存在于内存当中。

原型

#include <unistd.h>
/* Create a one-way communication channel (pipe).
   If successful, two file descriptors are stored in PIPEDES;
   bytes written on PIPEDES[1] can be read from PIPEDES[0].
   Returns 0 if successful, -1 if not.  */
extern int pipe (int fd[2]) __THROW __wur;

参数的说明:

​ 字符数组fd是管道传输或者接收时用到的文件描述符,其中fd[0]是接收的时候使用的文件描述符,即管道出口;而fd[1]是传输的时候用到的文件描述符,即管道入口。

​ 为了使数据可以双向传递,可以使用两个管道,一个管道负责进程1的写和进程2的读,另一个管道负责一个进程1的读和进程2的写。测试程序如下:

#include <iostream>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#define BUF_SIZE 30

int main(){
    int fds1[2], fds2[2];
    /*
     *注意:
     * 此处不能写char* str1 = "Who are you?";
     * 得到的sizeof(str1)等于8,该值实际上是指针的大小,并不是字符串数组的大小
     * 这个时候要用strlen()函数(strlen的唯一标准是找‘\0’)
     * 系统函数返回值是char *(#include <string.h>)类型的往往会在末尾加上'\0'。
     * 要注意的是未初始化的情况下,用strlen是不可行的
     **/
    char str1[] = "Who are you?";
    char str2[] = "Thank you for your message.";
    char buf[BUF_SIZE];
    pipe(fds1);     //创建两个管道
    pipe(fds2);

    pid_t pid = fork();
    if(pid == 0){
        write(fds1[1], str1, sizeof(str1));
        read(fds2[0], buf, BUF_SIZE);
        printf("Child process copy the message: %s\n", buf);
    }else {
        read(fds1[0], buf, BUF_SIZE);
        printf("Parent Process copy the message: %s\n", buf);
        write(fds2[1], str2, sizeof(str2));
    }
    return 0;
}

2.FIFO

​ FIFO即命名管道,在磁盘上有对应的节点,但没有数据块—换言之,只是拥有一个名字和相应的访问权限,通过mknode()系统调用或者mkfifo()函数来建立的。一旦建立,任何进程都可以通过文件名将其打开和进行读写,而不局限于父子进程,当然前提是进程对FIFO有适当的访问权。当不再被进程使用时,FIFO在内存中释放,但磁盘节点仍然存在。

/* Create a new FIFO named PATH, with permission bits MODE.  */
extern int mkfifo (const char *__path, __mode_t __mode)
     __THROW __nonnull ((1));

其中的 mode 参数与open函数中的 mode 相同。一旦创建了一个 FIFO,就可以用一般的文件I/O函数操作它。

当 open 一个FIFO时,是否设置非阻塞标志(O_NONBLOCK)的区别:

  • 若没有指定O_NONBLOCK(默认),以只读方式打开的FIFO要阻塞到其他的某个程序以写打开这个FIFO。同样以只写方式打开的FIFO要阻塞到其他某个进程以读方式打开该FIFO。
  • 若指定了O_NONBLOCK,则以只读方式打开会立刻返回而不阻塞(不是出错返回)。而以只写方式打开,若之前没有进程以读方式打开这个FIFO则立刻出错返回。

示例代码:一个进程发送消息给另一个进程

writefifo.cpp

#include <iostream>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>  // O_WRONLY
#include <time.h>   //time
#include <unistd.h>
using namespace std;

int main(){
    int fd;
    int n;
    char buf[1024];
    time_t tp;

    if( mkfifo("fifo1",  0666) < 0 && errno != EEXIST) //创建FIFO管道
    {
        perror("Create FIFO Faileed");
    }

    printf("I am %d process.\n", getpt());  //说明进程的ID

    if((fd = open("fifo1",O_WRONLY )) < 0){   //以只写方式打开FIFO
        perror("Open FIFO failed");
        exit(1);
    }
    for (int i = 0; i < 10; ++i) {
        time(&tp);  //获取当前系统时间
        n = sprintf(buf, "Process %d's time is %s",getpid(), ctime(&tp));
        printf("send message: %s", buf);
        if(write(fd, buf, n+1) < 0)
        {
            perror("write FIFO Failed");
            close(fd);
            exit(1);
        }
        sleep(1);
    }
    close(fd);
    return 0;
}

readfifo.cpp

#include <stdio.h>
#include <iostream>
#include <stdlib.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>  // O_WRONLY
#include <time.h>   //time
#include <unistd.h>
using namespace std;

int main(){
    int fd;
    int len;
    char buf[1024];
    if((fd = open("fifo1",  O_RDONLY)) < 0){   //以只读方式打开FIFO
        perror("Open FIFO failed");
        exit(1);
    }
    while ((len = read(fd, buf ,1024)) > 0)   //读取FIFO管道
    {
       printf("Read message: %s", buf);
    }

    close(fd);
    return 0;
}

如果在writefifo.cpp中修改如下,设置非阻塞标志:

if((fd = open("fifo1",O_WRONLY | O_NONBLOCK)) < 0){   //以只写方式打开FIFO

如果先运行writefifo,在运行readfifo,则会出错。

3. 消息队列

​ 消息队列,就是一个消息的链表,是一系列保存在内核的列表。用户进程可以向消息队列添加消息,也可以向消息队列读取消息。

特点:

  • 队列独立于发送与接收进程。进程终止时,消息队列及其内容并不删除。
  • 消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。

4.共享内存

​ 共享内存(Shared Memory),指两个或多个进程共享一个给定的存储区。

5. 信号量

​ 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。

6.套接字

​ 套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。

参考资料

进程间通信的方式——信号、管道、消息队列、共享内存

进程间的五种通信方式介绍

作者:一盏淡酒、醉了夕阳