当前位置: 代码迷 >> 综合 >> 进程间通信(一) 管道、FIFO
  详细解决方案

进程间通信(一) 管道、FIFO

热度:68   发布时间:2023-12-27 07:33:33.0

进程间通信,就是在不同进程之间传播或交换信息;而这些信息的传播又需要介质,所以我们想要了解进程间通信,首先必须了解这些介质。

UNIXUNIXUNIX系统中实现进程间通信的方法有很多,但不幸的是,极少方法能在所有的UNIXUNIXUNIX系统中进行移植(唯一一种是半双工的管道,这也是最原始的一种通信方式).

我们要学习的是LinuxLinuxLinux下的进程间通信,LinuxLinuxLinux几乎支持所有的UNIXUNIXUNIX下常用的进程间通信方法:管道、消息队列、共享内存、信号量、套接字等。其中,前面四种主要用于同一台机器上的进程间通信,而套接字则主要用于不同机器之间的网络通信。

socket在前面已经学习过,这里就不提及了,重点理解另外四种。

管道

在学习进程的时候相信应该了解到父进程与子进程并不共享数据段与堆栈段,它们之间是通过管道进行通信的。

管道是一种两个进程间进行单向通信的机制。因为管道传递数据的单向性,管道又被称为半双工管道,管道的这一特点决定了其使用的局限性。

管道的基本特点

管道是LinuxLinuxLinux支持的最初UNIXUNIXUNIX IPCIPCIPC形式之一,具有以下特点:

  • 1)数据只能由一个进程流向另一个进程(其中一个读管道,一个写管道);如果要进行双工通信,则需要建立两个管道
  • 2)管道只能用于父子进程或者兄弟进程间通信,也就是说管道只能用于具有亲缘关系的进程间通信
  • 3)除了以上局限性,管道还有其他一些不足,如管道没有名字(无名管道);管道的缓冲区大小是受限制的;管道所传输的是无格式的字节流等

使用管道进行通信

使用管道进行通信时,两端的进程向管道读写数据是通过创建管道时,系统设置的文件描述符进行的。

通过管道通信的两个进程,一个进程向管道写数据,另外一个从中读数据。写入的数据每次都添加到管道缓冲区的末尾,读数据的时候都是从缓冲区的头部读出数据的。

创建管道:

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

fd[0]fd[0]fd[0]为管道读端,fd[1]fd[1]fd[1]为管道写端

例1:利用管道实现在父子进程中实现通信

/*************************************************************************> File Name: pipe.cpp> Author: ersheng> Mail: ershengaaa@163.com > Created Time: Fri 01 Mar 2019 07:25:33 PM CST************************************************************************/#include <iostream>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
using namespace std;
#define INPUT 0
#define OUTPUT 1
int main() {
    int fd[2];//定义子进程号 pid_t pid;char buf[256];int returned_count;//创建无名管道 pipe(fd);//创建子进程 pid = fork();if (pid < 0) {
    printf("Error in fork\n");}else if (pid == 0) {
    printf("in the child process...\n");//子进程向父进程写数据,关闭管道的读端 close(fd[INPUT]);write(fd[OUTPUT], "hello world", strlen("hello world"));exit(0);}else {
    printf("in the parent process...\n");//父进程从管道读取子进程写的数据,关闭管道的写端 close(fd[OUTPUT]);returned_count = read(fd[INPUT], buf, sizeof(buf));printf("%d bytes of data received from child process: %s\n", returned_count, buf);}return 0;
}

在这里插入图片描述
在子进程中写数据,在父进程中读数据,两个进程之间实现了通信:父子进程分别拥有自己的读写管道,为了实现父子进程间的读写,只需把无关的读端或写端的文件描述符关闭即可。

有名管道(FIFO)

上面我们提到的管道又叫做无名管道,还有一种管道叫有名管道(FIFOFIFOFIFO),它不同于无名管道之处在于它提供一个路径名与之关联,以FIFOFIFOFIFO的形式存在于文件系统中.通过该路径名即使与FIFOFIFOFIFO的创建进程不存在血缘关系的进程,只要可以访问该路径,就能够通过FIFOFIFOFIFO相互通信;因此,通过FIFOFIFOFIFO不相关的进程也能交换数据

有名管道是对无名管道的一种改进,二者区别如下:
在这里插入图片描述

有名管道的特点

  • 1)它可以使互不相关的两个进程间实现彼此通信
  • 2)该管道可以通过路径名来指出,并且在文件系统中是可见的。在建立了管道之后,两个进程就可以把它当作普通文件一样进行读写操作,非常方便
  • 3)FIFOFIFOFIFO严格遵循先进先出规则

创建有名管道

有名管道由mkfifo()mkfifo()mkfifo()函数创建:

#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char * pathname, mode_t mode);
  • 第一个参数是一个普通的路径名,也就是创建FIFOFIFOFIFO的名字
  • 第二个参数与打开普通文件的open()open()open()函数中的modemodemode参数相同。
  • 如果mkfifomkfifomkfifo的第一个参数是一个已经存在的路径名时,会返回EEXISTEEXISTEEXIST错误,所有一般调用代码首先会检查是否返回该错误

例2:两个进程用有名管道进行通信

//读管道程序
/*************************************************************************> File Name: mkfifo_r.cpp> Author: ersheng> Mail: ershengaaa@163.com > Created Time: Fri 01 Mar 2019 08:03:08 PM CST************************************************************************/#include <iostream>
#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
using namespace std;
#define P_FIFO "/tmp/p_fifo"
int main(int argc, char *argv[]) {
    char cache[100];int fd;memset(cache, 0, sizeof(cache));  //初始化内存 if (access(P_FIFO, F_OK) == 0) {
      //管道文件存在 execlp("rm", "-f", P_FIFO, NULL);  //删掉 printf("access.\n");}if (mkfifo(P_FIFO, 0777) < 0) {
    printf("createnamed pipe failed.\n");} fd = open(P_FIFO, O_RDONLY | O_NONBLOCK);  //非阻塞方式打开,只读 while (1) {
    memset(cache, 0, sizeof(cache));if (read(fd, cache, 100) == 0) {
       //没有读到数据 printf("nodata: \n");} else {
    printf("getdata: %s\n", cache);  //读到数据,将其打印 }sleep(1);}close(fd);return 0;
}
//写管道程序
/*************************************************************************> File Name: mkfifo_w.cpp> Author: ersheng> Mail: ershengaaa@163.com > Created Time: Fri 01 Mar 2019 08:09:54 PM CST************************************************************************/#include <iostream>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
using namespace std;
#define P_FIFO "/tmp/p_fifo"
int main(int argc, char *argv[]) {
    int fd;if (argc < 2) {
    printf("please input the write data.\n");}  fd = open(P_FIFO, O_WRONLY | O_NONBLOCK);  //非阻塞方式 write(fd, argv[1], 100);  //将argv[1]写到fd里面去 close(fd);return 0;
}

在这里插入图片描述
在这里插入图片描述


以上仅仅简单的概述了无名管道及有名管道,通过这些知识点能对这两者有个大概的了解。

关于使用有名管道让一个服务器与多个客户端进行通信,一个解决办法就是每个客户在向服务器发送消息前都建立自己的读入管道,或让服务器在得到数据后再建立管道。
感兴趣的同学可以了解下这篇文章Linux-进程间通信(二): FIFO