管道(进程间通信方式)

目录

1.进程间通信方式

2.无名管道

3.有名管道

4.无名管道和有名管道的区别


1.进程间通信方式

    进程间的通信方式包括:无名管道,有名管道,信号;共享内存,消息队列,信号灯集;以及套接字。

2.无名管道(pipe)

(1)无名管道的特点

①只能用于具有亲缘关系的进程之间的通信;

②半双工的通信模式,具有固定的读端和写端;

③可以看作是一种特殊的文件,可以使用文件IO函数对其进行读写;

③通信方式基于文件描述符,管道建立时会产生两个文件描述符fd[0]和fd[1],其中fd[0]用于读管道,fd[1]用于写管道;

记忆方式:读写->01;

(2)函数接口

int pipe(int fd[2]);

功能:创建无名管道;

参数:文件描述符,fd[0],fd[1];

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

//使用示范
//1.创建存储文件描述符的数组
int fd[2] = {0};
//创建管道并错误判断
if(pipe(fd) < 0)
{
    perror("pipe create err");
    return -1;
}

(3)无名管道使用注意事项

①当管道中没有数据时,读操作阻塞;

②管道中装满数据时,写操作阻塞;管道内存为64K,即65536位,当管道存满后,不能继续写内容;当管道内有4K,即4096位的空间时,才能继续写内容。

③管道中必须有读端存在,写操作才有意义,没有读端时进行写操作的话,会造成管道破裂,内核传递SIGPIPE信号(管道破裂)。

代码演示1:使用代码验证管道内存大小。

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

int main(int argc, char const *argv[])
{
    //创建无名管道的读端fd[0]和写端fd[1];
    int fd[2] = {0};
    char buf[65536] = "";

    //创建无名管道
    if(pipe(fd) < 0)
    {
        perror("pipe create err");
        return -1;
    }
    //fd[0]和fd[1]是两个文件标识符,所以打印出来应该是3和4
    printf("%d %d\n",fd[0],fd[1]);

    //管道中写满64K->65536数据时,写堵塞
    write(fd[1],buf,65536);
    printf("full\n");
    //管道中至少有4K->4096的空间,才能继续写
    read(fd[0],buf,4096);
    write(fd[1],"a",1);//写入的参数是字符串,所以就算是只写一个a,也要用双引号括起

    close(fd[0]);//读端关闭,继续写的话,会导致管道破裂;
    
    return 0;
}

代码演示2:用管道实现,循环在父进程中终端输入,在子进程中终端输出,输入“quit”时循环结束;

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

int main(int argc, char const *argv[])
{
    char buf[32] = "";
    //先创建管道
    int fd[2] = {0};
    if(pipe(fd) < 0)
    {
        perror("pipe create err");
        return -1;
    }
    //再创建子进程
    pid_t pid;
    pid = fork();
    if(pid < 0)
    {
        perror("fork err");
        return -1;
    }
    else if(pid == 0) //子进程中读取管道内容并输出
    {
        //使用while实现循环
        while(1)
        {
            memset(buf,0,sizeof(buf));
            //从管道中读取内容后,读取的内容就不存在于管道中了
            read(fd[0],buf,32);
            //字符串的比较,使用函数strcmp();
            //quit后面加\n的原因是,fgets获取的是在终端中输入的所有字符和操作
            if(strcmp(buf,"quit\n") == 0)
            {
                break;
            }
            printf("buf:%s\n",buf);
        }
        //跳出循环后退出子进程
        exit(0);
    }
    else
    {
        while(1)
        {
            /*fgets()将在终端输入的字符串获取到buf数组中后,
            然后将buf数组中的数据写入管道时,但是buf数组中仍然存在原本输入的字符串,
            从管道中读出数据时,也是将数据读入到了buf数组中,然后打印,
            所以每次写入管道和读出的时候,都要将buf数组清空,清空使用函数memset()*/
            memset(buf,0,sizeof(buf));
            //fgets()可以获取输入的所有字符
            fgets(buf,32,stdin);
            write(fd[1],buf,strlen(buf));

            if(strcmp(buf,"quit\n") == 0)
            {
                break;
            }

        }
        //跳出循环后等待子进程结束;
        wait(NULL);
    }
    return 0;
}

3.有名管道(mkfifo)

(1)有名管道的特点

①有名管道支持两个互不相关的进程之间相互通信;

②有名管道可以通过路径名指出文件位置,并且管道文件存在于文件系统中,但是管道文件的内存为0,文件内容存储在进程的3G到4G的内核内存中;

③有名管道同样通过文件IO函数来操作;

④有名管道遵循先进先出的规则;

⑤不支持lseek()定位操作;

(2)函数接口

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

功能:创建有名管道

参数:filename->管道文件名,mode->权限

返回值:成功为0,返回为-1并设置errno号;

//使用示范
//管道创建时,返回值为-1,则创建失败
if(mkfifo("./fifo",0666) < 0)
{
    //如果返回的错误码是管道文件已存在,则打印信息
    if(errno == EEXIST)
    {
        printf("fifo file exist\n");
    }
    else
    {
        //否则管道创建失败
        perror("mkfifo err");
        return -1;
    }
}
//打印管道创建成功
printf("mkfifo sucess\n");

(3)有名管道使用注意事项

使用open()函数打开管道文件时,

①只写方式打开,写阻塞,直到另一个进程将读打开;

②只读方式打开,读阻塞,直到另一个进程将写打开;

③可读可写的方式打开,如果管道中没有数据,读阻塞。

代码演示1:在一个进程中打开管道,实现写入数据,读取数据并在终端打印的操作;

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

int main(int argc, char const *argv[])
{
    //创建管道文件
    if(mkfifo("./fifo",0666)<0)
    {
        if(errno == EEXIST)
        {
            printf("fifo file exist\n");
        }
        else
        {
            perror("mkfifo err");
            return -1;            
        }
    }
    printf("mkfifo sucess\n");

    
    char buf[32] = "";
    //以可读可写的方式打开管道文件
    int fd;
    fd = open("./fifo",O_RDWR);
    if(fd < 0)
    {
        printf("fifo open err");
        return -1;
    }
    //向管道中写入数据
    write(fd,"hello",5);
    //从管道中读取数据并输出
    read(fd,buf,32);
    printf("%s\n",buf);
    return 0;
}

代码演示2:实现在一个文件中对管道写入字符串hello,在另一个文件中读取管道内的字符串hello并输出;

//写操作程序
int main(int argc, char const *argv[])
{
    //创建管道文件
    if(mkfifo("./fifo",0666)<0)
    {
        if(errno == EEXIST)
        {
            printf("fifo file exist\n");
        }
        else
        {
            perror("mkfifo err");
            return -1;            
        }
    }
    printf("mkfifo sucess\n");


    char buf[32] = "";
    int fd;
    //以只写方式打开管道文件
    fd = open("./fifo",O_WRONLY);
    if(fd < 0)
    {
        printf("fifo open err");
        return -1;
    }
    //向管道中写入字符串hello
    write(fd,"hello",5);
    return 0;
}

//读操作程序
//程序中不需要再创建管道文件,直接在文件中打开相同的管道文件即可
int main(int argc, char const *argv[])
{
    char buf[32] = "";
    //以只读方式打开管道文件
    int fd;
    fd = open("./fifo",O_RDONLY);
    if(fd < 0)
    {
        printf("fifo open err");
        return -1;
    }
    //读取管道中的内容并打印
    read(fd,buf,32);
    printf("%s\n",buf);
    return 0;
}

执行过程:

打开两个终端,运行程序,

 写:

读:

4.无名管道和有名管道的区别

1.无名管道用于具有亲缘关系之间的进程通信,有名管道可以用于没有亲缘关系进程之间的通信;

2.无名管道具有固定的读端和写端,有名管道不需要固定的读端和写端;

3.无名管道不需要路径来创建管道文件,有名管道需要指定路径来创建管道文件,文件大小为0,文件的内容存储在进程的3G-4G的内核存储空间中。

如果本文中存在概念错误或代码错误的情况,请批评指正。 

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值