Linux基础 ——“进程间通信” 沟通是怎么连接的呢

本文详细介绍了Linux进程间通信的多种方式,包括无名管道(PIPE)、命名管道(FIFO)、消息队列、共享内存和信号。无名管道适用于父子进程,命名管道可在无关进程间通信,消息队列支持随机查询和优先级,共享内存则提供快速通信但需同步,信号用于处理异步事件。文章通过实例代码展示了各种通信方式的使用方法和注意事项。
摘要由CSDN通过智能技术生成

进程间通信(IPC)篇

一、进程间通信概述

进程间通信是指在不同进程之间传播或交换信息,IPC的方式常有管道(包括无名管道和命名管道)消息队列,信号量,共享存储,Socket,Stream等。其中Socket和Stream支持不同主机上的两个进程IPC。
在这里插入图片描述
在这里插入图片描述

单机通信 多机通信
半双工管道(FIFO) 套接字
全双工管道 STPEAMS
消息队列
信号量
共享存储

二、半双工管道

管道是一个流,读走数据后,这个数据就不在缓存中了

PIPE(无名管道 ):

管道,通常指 无名管道 ,是UNIX系统IPC最古老的形式。只存在于内存中,当父子进程退出后,管道就消失了。

1.特点:

  1. 它是 半双工 的(即数据只能在一个方向上流动),具有固定的读端和写端。在这里插入图片描述

  2. 它只是用于具有 亲缘关系 的进程之间 通信(也是父子进程或者兄弟进程之间【两个由同一个父进程创建的子进程】)

  3. 它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read,write等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且 只存在于内存中 。当父子进程退出后,管道就消失了。

2.原型:

#include<unistd.h>
int pipe(int fd[2]);    //返回值 :若成功返回0   失败返回-1

3.返回值: 若成功返回0 失败返回-1

当一个管道建立时候,它会创建两个文件描述符: fd[0] 为读而打开fd[1] 为写而打开。

pipefd[2]:文件描述符数组,fd[0]表示读端,fd[1]表示写端

read(fd[0] , buff , 128);
write(fd[1] , “hello world \n” , 12);

在这里插入图片描述

要关闭管道,只需要将这两个文件描述符关闭即可。

close(fd[0]); //关闭读端
close(fd[1]); //关闭写端

例如:单个进程中的管道几乎没有任何用处。所以,通常调用pipe的进程接着调用fork,这样就创建了父进程与子进程之间的IPC通道,
在这里插入图片描述

2. 管道 编程的实战:

无名管道:父进程往管道里写,子进程从管道外读:
父——>子的管道,父亲要发送数据,所以要关闭读端,儿子要接收数据,所以要关闭写端。

#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>


int main()
{
   
     int fd [2];   //两个文件描述符
     int  pid;
     char buff [128];
      if(pipe(fd) == -1){
     //创建管道
            printf("Create pipe error! \n"); //创建管道错误  
      }
       pid = fork();    //创建子进程
       if(pid <0 ){
   
             printf("Fork  error  \n");   //fork错误
       }else  if(pid > 0){
      //父进程
                sleep(3);
                printf("this is father\n");
                close(fd[0]);    //关闭读端
                 write(fd[1],"hello from father\n",strlen("hello from father"));
                wait();
       }else{
   
                 printf("this is child\n");
                 close(fd[1]);    //关闭写端
                 read(fd[0],buff,128);
                 printf("read from father:%s\n",buff);
                 exit(0);
        }
         return 0;
}

父进程等待3秒写入的时候,发现read 没读到数据 ,read会阻塞,等到父进程写入后,子进程执行read读出数据,

通过测试结果可以发现,一开始是父亲先输入信息,原因是儿子一开始是先read(),而read()函数是一个阻塞函数,当读到信息的时候才结束,没读到数据就阻塞。儿子收到信息后,儿子就执行完read(),并接着执行write(),同时父亲发完信息后也就是执行完write(),并接着执行read(),等待儿子发信息。就这样循环反复的收发信息,就实现了父子进程间的通信。

三、FIFO (命名管道)

FIFO,也称为命名管道,它是一种文件类型。
命名管道是一种特殊类型的文件,它以文件的形式存在,但不是真正的文件,并且不保留数据,只是起到一个数据传输的作用。

1.特点:

  1. FIFO可以在无关的进程之间交换数据,与无名管道不同。

  2. FIFO有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中。

2.原型:

#include<sys/stat.h>
#include<sys/types.h>
//返回值:成功返回0 ,失败返回-1
int mkfifo(char *pathname , mode_t mode);

返回值:成功返回0 ,失败返回-1

创建命名管道代码:

int mkfifo("./file",0600);

参数说明:
pathname:文件路径
mode:文件权限

mode与open函数中的mode相同。一旦创建一个FIFO,就可以用一般的文件I/O函数操作它(如 open ,read ,write)。

1. 打开命名管道文件FIFO

1、FIFO和通过pipe调用创建的管道不同,它是一个有名字的文件而表示一个打开的文件描述符。

2、在对它进行读或写操作之前必须先打开它。

3、FIFO文件也要用open和close函数来打开或关闭,除了一些额外的功能外,整个操作过程与文件操作是一样的。

4、传递给open调用的是一个FIFO文件的路径名,而不是一个正常文件的路径名。

2. 创建FIFO有名管道代码:

#include<sys/types.h>
#include<sys/stat.h>
#include<stdio.h>
#include<errno.h>

// int mkfifo(const char *pathname ,mode_t mode);

int main(){
   
      if((mkfifo("./file",0600)== -1)&& errno!=EEXIST){
   
              printf("mkfifo failuer\n");
              perror("why");
      }
      return 0;
}

在这里插入图片描述
管道读取:

#include<sys/types.h>
#include<sys/stat.h>
#include<stdio.h>
#include<errno.h>
#include<fcntl.h>
// int mkfifo(const char *pathname ,mode_t mode);

int main(){
   
      if((mkfifo("./file",0600)== -1)&& errno!=EEXIST){
   
              printf("mkfifo failuer\n");
              perror("why");
      }


      int fd = open("./file",O_RDONLY);     //O_RDONLY读数据
      printf("open success\n");

      return 0;
}

结果显示:
创建一个管道后, 打开管道后printf(“open success\n”); 没有执行。
在这里插入图片描述

3.命名管道的打开规则

1、如果当前打开操作是为读而打开FIFO时,若已经有相应进程为写而打开该FIFO,则当前打开操作将成功返回;否则,可能阻塞直到有相应进程为写而打开该FIFO(当前打开操作设置了阻塞标志,即只设置了O_RDONLY),反之,如果当前打开操作没有设置了非阻塞标志,即O_NONBLOCK,则返回成功

2、如果当前打开操作是为写而打开FIFO时,如果已经有相应进程为读而打开该FIFO,则当前打开操作将成功返回;否则,可能阻塞直到有相应进程为读而打开该FIFO(当前打开操作设置了阻塞标志);或者,返回ENXIO错误(当前打开操作没有设置阻塞标志)。

3、通俗的说,要打开一个FIFO命名管道文件,需要一个进程以写打开,并且另一个进程要以读打开,只有满足了有读打开和有写打开,命名管道才算打开成功。就像水管一样,水管的进口和出口需要同时打开,水才能流过去。

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

  • 没有指定O_NONBLOCK(默认)只读open要阻塞 到某个 其他进程为写而打开此FIFO ,类似的,只写open要阻塞到某个其他他进程为读而打开它。

  • 指定了O_NOBLOCK,则只读open立即返回。而只写open将出错误返回-1 如果没有进程已经为读而打开该FIFO,其errno置ENXIO

所以说上面代码堵塞,再一个进程用写的方式打开管道,就不会堵塞了

3. 命名管道的数据通信编程实现:

读的方式打开管道:
./read

#include<sys/types.h>
#include<sys/stat.h>
#include<stdio.h>
#include<errno.h>
#include<fcntl.h>
// int mkfifo(const char *pathname ,mode_t mode);
int main(){
   

      int buf[30]={
   0};

      if((mkfifo("./file",0600)== -1)&& errno!=EEXIST){
      //创建管道
              printf("mkfifo failuer\n");
              perror("why");
      }

      int fd = open("./file",O_RDONLY);   //O_RDONLY 读数据
      printf("read open success\n");
      return 0;
}

写的方式打开管道:
./write

#include<sys/types.h>
#include<sys/stat.h>
#include<stdio.h>
#include<errno.h>
#include<fcntl.h>

int main(){
   

      int fd = open("./file",O_WRONLY); //O_WRONLY 写数据
      printf("write open success\n");
      return 0;
}

当用读的方式打开 会堵塞, 再用写的方式打开,就不会阻塞,就像水管两头打开流通。
在./read堵塞的时候,在执行./write ,就可以获取数据
在这里插入图片描述

也可以实现两个进程间通信:多次写入:
./read

#include<sys/types.h>
#include<sys/stat.h>
#include<stdio.h>
#include<errno.h>
#include<fcntl.h>

int main(){
   

      int buf[30]={
   0};
      int nread=0;

      int fd = open("./file",O_RDONLY);
      printf("read open success\n");

      while(1){
   
         nread = read(fd,buf,30);
         printf(
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值