Linux系统编程——管道通信

管道通信

1.无名管道

无名管道,是 UNIX 系统IPC最古老的形式

(1)特点

1.它是半双工的(即数据只能在一个方向上流动),具有固定读端和写端
2.它只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间)。
3.它可以看成是一种特殊的文件,对于它的读写也可以使用普通的readwrite 等函数。但是它不是普通的文件,并不属于其他任何文件系统并且只存在于内存中。(其字节大小为0)

(2)无名管道的创建与关闭

无名管道是基于文件描述符的通信方式。当一个管道创建时,它会创建两个文件描述符:fd[0]fd[1] 。其中 fd[0] 固定用于读管道,而 fd[1] 固定用于写管道,如下图,这样就构成了一个单向的数据通道:
在这里插入图片描述
管道关闭时只需要用 close() 函数将这两个文件描述符关闭即可。

(3)函数原型

#include <unistd.h>//所需头文件
int pipe(int fd[2]);    
  • 参数:fd ,包含两个元素的整型数组,存放管道对应的文件描述符

  • 函数返回值:成功:0,出错:-1
    (4)用法说明
    用pipe() 函数创建的管道两端处于一个进程中。由于管道主要是用于不同进程间的通信,通常是先创建一个管道,再调用 fork () 函数创建一个子进程,该子进程会继承父进程所创建的管道

    需要注意的是,无名管道是单工的工作方式,即进程要么只能读管道,要么只能写管道。父子进程虽然都拥有管道的读端和写端,但是只能使用其中一个,若要数据流从父进程流向子进程,则关闭父进程的读端(fd[0])与子进程的写端(fd[1]);反之,则可以使数据流从子进程流向父进程。

在这里插入图片描述
示例:

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
int main()
{
    int fd[2];//存放文件描符的数组
    char readBuf[128];
    pid_t pid;
    int readSize;
    if(pipe(fd) == -1){
        printf("creat pipe faild!");
        exit(0);
    }
    pid = fork();
    if(pid > 0){//父进程
        printf("This is father progress the pid is %d \n",getpid());
        close(fd[0]);//关闭父进程的读
        write(fd[1],"hello this is father",strlen("hello this is father"));//利用管道向子进程发送信息
        wait(NULL);//等待子进程退出

    }
    if(pid == 0){//子进程
        printf("This is child progress the pid is %d \n",getpid());
        close(fd[1]);//关闭子进程的写
        read(fd[0],readBuf,128);
        printf("the message from farherr is %s \n",readBuf);
        exit(1);

    }

    return 0;
}

在这里插入图片描述

2.有名管道

有名管道(FIFO)是对无名管道的一种改进

(1)特点

1.它可以使互不相关的两个进程实现彼此通信;

2.该管道可以通过路径名来指出,并且在文件系统中是可见的。在建立了管道之后,两个进程就可以把它当做普通文件一样进行读写操作,使用非常方便;

3.FIFO严格地遵循先进先出规则,对管道及 FIFO 的读总是从开始处返回数据,对它们的写则把数据添加到末尾有名管道不支持如lseek()等文件定位操作

(2)函数原型

#include <sys/stat.h>//需要引入的头文件
int mkfifo(const char *pathname, mode_t mode);
  • 参数 :
    pathname参数:为所创建管道的路径名称
    mode 参数:与open函数中的 mode 相同。一旦创建了一个 FIFO,就可以用一般的文件I/O函数操作它。
  • 返回值:成功返回0,出错返回-1

(3)有名管道用法

有名管道(FIFO)的创建可以使用 mkfifo() 函数,该函数类似文件中的open() 操作,可以指定管道的路径和访问权限 (用户也可以在命令行使用 “mknod <管道名>”来创建有名管道)。
在创建管道成功以后,就可以使用open()、read() 和 write() 这些函数了。与普通文件一样,对于为读而打开的管道可在 open() 中设置 O_RDONLY,对于为写而打开的管道可在 open() 中设置O_WRONLY

  • 对于读进程

    缺省情况下,如果当前FIFO内没有数据,读进程将一直阻塞到有数据写入或是FIFO写端都被关闭

  • 对于写进程

    只要FIFO有空间,数据就可以被写入。若空间不足,写进程会阻塞,知道数据都写入为止;

  • 关于是否设置阻塞标志:
    当 open 一个FIFO时,是否设置非阻塞标志(O_NONBLOCK)的区别:
    若没有指定O_NONBLOCK(默认),只读 open 要阻塞到某个其他进程为写而打开此 FIFO。类似的,只写 open 要阻塞到某个其他进程为读而打开它。
    若指定了O_NONBLOCK,则只读 open 立即返回。而只写 open 将出错返回 -1 如果没有进程已经为读而打开该 FIFO,其errno置ENXIO

  • 示例

writefifo.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
int main(int argc, const char *argv[])
{
    int fd;
    int n;
    char buf[1024] = {0};
    if(argc < 2){
        printf("输入需要创建或打开的文件名!\n");
        exit(1);
    }

    fd =  open(argv[1],O_WRONLY);//以只写的方式打开fifo


    if(fd < 0){
        printf("opem fail\n");
        perror("why");
        exit(1);
    }
    while(1){
        printf("输入要发送的内容:");
        scanf("%s",buf);
        n= write(fd,buf,strlen(buf));//写入fifo的内容
        if(n == -1){
            perror("write fails");
        }
    }
    return 0;

}

openfifo.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
int main(int argc, const char *argv[])
{
    int fd;
    int nread;
    char buf[128] = {0};
    if(argc < 2){
        printf("输入需要创建或打开的文件名!\n");
        exit(1);
    }

    if((mkfifo(argv[1], 0600)<0)&& errno != EEXIST){//其中 errno 表示错类型,这种写法可以在文件已经存在时不报错
        printf("creat fifo fail\n");
        perror("why");
        exit(1);
    }
    fd =  open(argv[1],O_RDONLY);

    if(fd < 0){
        printf("open fail\n");
        perror("why");
        exit(1);
    }
    while(1){
        nread = read(fd,buf,128);//读取fifo的内容
        if(nread == -1){
            perror("read fails");
            exit(1);
        }else if(nread ==0){
            printf("none from fifowrite\n");
            exit(1);
        }else
        {
            buf[nread] = '\0';//将读出的最后一个字节添加一个结束符
            printf("read %d bytes from fifo:%s\n",nread,buf);
        }

    }
    return 0;

}

  • 执行结果
    写端:
    在这里插入图片描述
    读端:
    在这里插入图片描述
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值