Linux进程间通讯(匿名管道&命名管道)

本文详细介绍了Unix/Linux系统中管道通信的基本概念和技术细节,包括匿名管道和命名管道的创建、使用及区别,通过示例代码展示了如何在父子进程之间进行数据交换。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

匿名管道:

什么是管道?
管道是unix中最古老的进程间通讯方式。
具有亲缘关系的两个进程间连接的数据流称为一个管道。
这里写图片描述
注:yum进程与grep进程同为shell进程创建,所以为兄弟进程。

#include <unistd.h>

int pipe(int pipefd[2]);

说明:
此函数用于创建一个匿名管道。
参数:
pipefd[2]:输入输出型参数,pipe()调用成功后,pipefd[0]用于读管道、pipefd[1]用于写管道。
返回值:
成功:返回0。
失败:返回-1,并设置errno。
这里写图片描述

利用fork()共享管道原理
在父进程中打开的文件描述符会被fork()创建的子进程所继承,所以我们可以利用这个特性,在父子进程中关闭各自多余不用的描述符,达到进程间单向通讯
这里写图片描述
父进程读,子进程写
这里写图片描述
父进程写,子进程读
这里写图片描述
代码:

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




int main(){
  int fds[2];
  if(pipe(fds)){
    perror("pipe");
    return -1;
  }
  pid_t id = fork();
  if(id < 0){
    perror("fork");
    return -2;
  }else if(id == 0){
    //子进程
    close(fds[0]);
    char buf[100] = {0};
    printf("child write:$");
    fgets(buf,100,stdin);
    int len = strlen(buf);
    write(fds[1],buf,len);
    return -3;
  }else{
    //父进程
    wait(NULL);
    close(fds[1]);
    char buf[100] = {0};
    read(fds[0],buf,100);
    printf("father read:$%s",buf);
  }
  return 0;
}

结果:
这里写图片描述

管道的读写规则:
1、当管道中没有数据可读时:
阻塞方式:read()调用阻塞,直到有数据到来。
非阻塞方式:read()返回-1,errno值为EAGAIN。
2、当管道中数据满时:
阻塞方式:write()调用阻塞,直到有进程读走数据。
非阻塞方式:write()返回-1,errno值为EAGAIN。
3、如果管道所有写端被关闭,read()返回0。
4、如果管道所有读端被关闭,调用write()产生SIGPIPE信号,可能导致进程退出。
5、当每次写入的数据量不超过PIPE_BUF时,Linux保证写入的原子性,否则,不保证。
注:非阻塞方式可使用pipe2()函数创建并打开管道。

#include <fcntl.h>              
#include <unistd.h>

int pipe2(int pipefd[2], int flags);
//flags传为O_NONBLOCK为非阻塞方式创建并打开管道。

管道总结:
1、管道只能用于具有亲缘关系的进程间通讯,通常,是由父进程创建管道后,再调用fork()函数创建子进程,以达到共享管道从而通讯的目的。
2、管道提供流式服务。
3、管道的生命周期随进程。
4、内核会对管道的操作进行同步与互斥。
5、管道是半双工通讯,需要双向通讯时,需要建立两个管道。


命名管道:

命名管道是一种FIFO文件,此类文件是一种特殊类型的文件,可以用于没有亲缘关系的进程间通讯工作,因为匿名管道只能用于有亲缘关系的进程间通讯,所以便有了此类文件的产生,常称为命名管道。

命名管道的创建:
命名管道的创建有两种方式:一、在命令行通过mkfifo命令创建,二、在程序中通过mkfifo()函数创建。
通过命令创建:
mkfifo fifoname
通过函数创建

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

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

pathname: 命名管道的创建路径和名字。
mode: 命名管道的权限。
返回值:成功返回0,失败返回-1,并设置errno。

命名管道与匿名管道的区别:
命名管道与匿名管道的唯一区别就是创建和打开方式不同,其余均相同。
命名管道:
创建用mkfifo()函数,打开用open()函数。
匿名管道:
pipe()函数创建并打开。

命名管道的打开规则:
读的方式打开:
阻塞方式:阻塞,直到有进程以写的方式打开。
非阻塞方式:直接返回成功。
写的方式打开:
阻塞方式:阻塞,直到有进程以读的方式打开。
非阻塞方式:直接返回失败,errno为ENXIO。

用命名管道实现服务器与客户端通讯(本地通讯):
代码:
client.c:

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


#define ERR_EXIT(m) \
  do{\
    perror(m);\
    exit(EXIT_FAILURE);\
  }while(0)

int main(){
  //以写的方式打开命名管道
  int fifofd = open("tmp",O_WRONLY);
  if(fifofd == -1){
    ERR_EXIT("client:open");
  }
  char buf[1024];
  ssize_t len = 0;
  while(1){
    printf("please enter$");
    fflush(stdout);
    //读标准输出
    len = read(0,buf,1024);
    if(len <= 0){
      ERR_EXIT("client read");
    }else{
      //写入文件命名管道
      len = write(fifofd,buf,len);
      if(len <= 0){
        ERR_EXIT("client write");
      }
    }
  }
  return 0;

server.c:

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


//此宏作用在于,可以执行多条语句,还可防止;问题
#define ERR_EXIT(m) \
  do{\
    perror(m);\
    exit(EXIT_FAILURE);\
  }while(0)

int main(){
  //创建命名管道
  if(mkfifo("tmp",0644)){
    ERR_EXIT("mkfifo");
  }
  printf("please wait\n");
  //以读的方式打开命名管道
  int fifofd = open("tmp",O_RDONLY);
  if(fifofd == -1){
    ERR_EXIT("server:open");
  }
  char buf[1024];
  ssize_t len = 0;
  while(1){
    //读管道
    len = read(fifofd,buf,1024-1);
    if(len == -1){
      ERR_EXIT("server:read");
    }else if(len == 0){
      printf("client close! Disconnect\n");
      break;
    }else{
      buf[len] = '\0';
      printf("client:$%s",buf);
    }
  }
  //关闭文件描述符
  close(fifofd);
  //删除命名管道
  unlink("tmp");
  return 0;
}

结果:

这里写图片描述
注:启用时,必须先启用server,因为在server中创建命名管道。一个server可被多个client连接,server可收到每一个client发送的数据,关闭时,关闭所有client后,server端自动关闭。也可一个client连接多个server,连接后,client发送的数据随机的被多个server中的一个接收,关闭时,一旦关闭client,所有server均都关闭。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值