进程间通信(IPC):管道

进程间通信 IPC管道、信号量、共享内存、消息队列、套接字

进程间通信,两个进程间传递信息

除了套接字,前面几个主要是在同一台主机上两个进程间通信

进程间通信(IPC):信号量_♚陌上花开的博客-CSDN博客_ipc 信号量

进程间通信(IPC):共享内存_♚陌上花开的博客-CSDN博客

进程间通信( IPC):消息队列_♚陌上花开的博客-CSDN博客

管道

管道分为有名管道和无名管道

管道文件的内存大小永远为0,打开文件写入数据,实际上是写入内存中,管道在内核中

向管道写入的数据在内存中

有名管道

mkfifo fifo

使用 | , bash 可以自动创建一个有名管道

如何使用管道

还是可以使用open(打开管道,分配的空间在内存中)read(读取数据) write(写入数据)close(关闭管道)

eg:文件a和文件b,文件a负责向管道中写数据,文件b负责读数据

a.c文件

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

int main()
{
    int fd = open("fifo",O_WRONLY);//以只写方式打开管道
    printf("fd=%d\n",fd);
    if(fd==-1)
    {
        exit(1);
    }

    write(fd,"hello",5);//写入5个数据

    close(fd);//关闭管道

    exit(0);

}
                                                                

b.c文件

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

int main()
{
    int fd = open("fifo",O_RDONLY);//以只读方式打开管道
    printf("fd r =%d\n",fd);
    if(fd ==-1)
    {
        exit(1);
    }

    char buff[128]={0};
    
    int n = read(fd,buff,127);//读取管道中的数据,管道分配的空间在内存中。
    printf("read:%s,n=%d\n",buff,n);

    close(fd);//关闭管道

    exit(0);

}                                                   

a,b文件单独执行时会被阻塞住

 管道必须在两个进程间执行,单个进程是没法执行的

要求创建时必须两个进程同时打开。

 将a.c文件改为从键盘获取数据

int main()
{
    int fd = open("fifo",O_WRONLY);//以只写方式打开管道
    printf("fd=%d\n",fd);
    if(fd==-1)
    {
        exit(1);
    }

    printf("input:\n");
    char buff[128]={0};
    fgets(buff,128,stdin);//从键盘获取数据

    write(fd,buff,strlen(buff)-1);//写入数据

    close(fd);//关闭管道

    exit(0);

}
                                                                
                                                                

a(写端)没有输入数据,b(读端)就读不到数据

 a(写端)输入数据之后b(读端)才可以读取到数据,a,b正常读写数据后结束

 a(写端)没有输入数据,就结束时,b(读端)也会结束

 一旦写端关闭了管道,就算没有数据,读端也不会继续阻塞了,读端也会关闭(写端都关闭了,读端再也不可能收到数据,就关闭了)

read的返回值n为0,是唯一一个可以判断管道写端被关闭的条件

循环持续输入数据

b.c文件

int main()
{
    int fd = open("fifo",O_RDONLY);//以只读方式打开管道
    printf("fd r =%d\n",fd);
    if(fd ==-1)
    {
        exit(1);
    }


    while(1)
    {
        char buff[128]={0};
        int n = read(fd,buff,127);//读取管道中的数据,管道分配的空间在内存
        if(n==0)//若输入的数据为空,结束读取数据,结束程序
        {
            break;
        }
        printf("read:%s,n=%d\n",buff,n);

    }
    close(fd);//关闭管道

    exit(0);

}

a.c文件

int main()
{
    int fd = open("fifo",O_WRONLY);//以只写方式打开管道
    printf("fd=%d\n",fd);
    if(fd==-1)
    {
        exit(1);
    }

    while(1)
    {
        printf("input:\n");
        char buff[128]={0};
        fgets(buff,128,stdin);//从键盘获取数据
        if(strncmp(buff,"end",3)==0)
        {
            break;
        }

        write(fd,buff,strlen(buff)-1);//写入数据

    }
    close(fd);//关闭管道

    exit(0);

}

循环获取数据,a输入一个,b输出一个

 

  读端关闭,写端就不能再输入数据了,一旦输入数据,就会引发异常,系统会发出信号(SIGPIPE)终止程序。

 写端结束,读端依旧可以将残留的数据读取出来

读端结束,写端再写会触发异常

管道 写端关闭,读 返回值为0

管道 读端关闭,写 异常(信号SIGPIPE)

管道 写满 写操作会阻塞

管道 为空 读操作会阻塞

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

void fun(int sig)
{
    printf("sig=%d\n",sig);//改变信号SIGPIP的响应方式
}
int main()
{
    signal(SIGPIPE,fun);
    int fd = open("fifo",O_WRONLY);//以只写方式打开管道
    printf("fd=%d\n",fd);
    if(fd==-1)
    {
        exit(1);
    }

    while(1)
    {
        printf("input:\n");
        char buff[128]={0};
        fgets(buff,128,stdin);//从键盘获取数据
        if(strncmp(buff,"end",3)==0)
        {
            break;
        }

        write(fd,buff,strlen(buff)-1);//写入数据

    }
    close(fd);//关闭管道

    exit(0);

}
                                                      

我们可以看到,当读端关闭时,写端再写入数据本该引发异常终止,但我们改变了引发异常的信号量(SIGPIPE),读端关闭,写端再数据数据并没有引发异常终止,而是打印信号量代号

三种通讯方式

单工

数据的发送方式是单向的,数据只在一个方向上传输,不能实现双方通信。

eg:电视,

半双工

允许数据在两个方向上传输,但是同一时间数据只能在一个方向上传输,其实际上是切换的单工。

eg:对讲机

全双工

允许数据在两个方向上同时传输。

eg:打电话

管道通讯方式是 半双工

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

有名管道可以在任意两个进程间通信,无名只能在父子进程间通信

无名管道

pipe

#include <unistd.h>

/* On Alpha, IA-64, MIPS, SuperH, and SPARC/SPARC64; see NOTES */
struct fd_pair {
    long fd[2];
};
struct fd_pair pipe();

/* On all other architectures */
int pipe(int pipefd[2]);//传入两个字符,一个读,一个写

#define _GNU_SOURCE             /* See feature_test_macros(7) */
#include <fcntl.h>              /* Obtain O_* constant definitions */
#include <unistd.h>

int pipe2(int pipefd[2], int flags);

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

 On  success,  zero  is returned.  On error, -1 is returned, errno is set appropriately, and pipefd is left unchanged.

fd[0] 读   fd[1] 写

单个进程获得读写能力

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

int main()
{
    int fd[2];
    int res = pipe(fd);//fd[0] 读 fd[1] 写 固定的
    if(res==-1)
    {
        exit(1);
    }

    write(fd[1],"hello",5);
    char buff[128]={0};
    read(fd[0],buff,127);
    printf("buff=%s\n",buff);

    close(fd[1]);
    close(fd[0]);

    exit(0);


}
                                                              

父子进程间无名管道

fork一个子进程,子进程复制了父进程的读写能力,此时父子进程都具有读写能力,我们可以关闭其中一个能力,使其变为一个读一个写

 

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

int main()
{
    int fd[2];
    int res = pipe(fd);//fd[0] 读 fd[1] 写 固定的
    if(res==-1)
    {
        exit(1);
    }
    pid_t pid = fork();
    if(pid==-1)
    {
        exit(1);
    }
    if(pid==0)//子进程
    {
        close(fd[1]);//关闭写端
        char buff[128]={0};
        read(fd[0],buff,127);
        printf("buff=%s\n",buff);
        close(fd[0]);//读取完数据,关闭读端
    }
    else//父进程
    {
        close(fd[0]);//关闭读端
        write(fd[1],"hello",5);
        close(fd[1]);//写完数据,关闭写端
    }

    exit(0);


}

管道如何实现

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值