【Linux学习】进程间通信 (1) —— 管道

下面是有关进程通信中管道的相关介绍,希望对你有所帮助!

小海编程心语录-CSDN博客

1. 进程通信的基本概念

1.1 概念

进程间通信简称 IPC ,指两个进程之间的通信。 IPC的方式通常有管道(包括无名管道和命名管道)、消息队列、信号量、共享存储、Socket、Streams等。其中 Socket和Streams支持不同主机上的两个进程IPC。

1.2 目的

  1. 数据传输:一个进程需要将它的数据发送给另一个进程。
  2. 资源共享:多个进程之间共享同样的资源。
  3. 通知事件:一个进程需要向另一个或一组进程发送消息,通知它们发生了某种事件。
  4. 进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),该控制进程希望能够拦截另一个进程的所有操作,并能够及时知道它的状态改变。

1.3 通信方式 

  1. UNIX IPC: 管道、FIFO、信号
  2. System V IPC: 信号量、消息队列、共享内存
  3. Socket IPC: 基于Socket进程间通信

2. 管道 

把一个进程连接到另一个进程的数据流称为管道,是一个半双工的通信方式。

管道分为有名管道无名管道两种,它们的区别是:

无名管道只能在有亲缘关系的进程之间进行通信。

有名管道又称为命名管道,可以在任意两个进程之间进行通信。

2.1 无名管道 

//函数原型
#include <unistd.h>

int pipe( int fd[2] );

匿名管道适用于一对一的、具有亲缘关系的进程间的通信

是一个半双工的通信方式,具有固定的读端和写端。

管道可以看做是一种特殊的文件,对管道的读写可以使用普通的read() ,write()函数,但管道不属于任何文件系统的,只存在于内存中。

2.1.1 如何操作无名管道

  1. 创建 :(pipe函数用来创建无名管道)
  2. 操作 :(read读;write写)  写是 fd[1],读是 fd[0]
  3. 关闭操作端口 :(close)

2.1.2 示例代码 

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

int main()
{
    //创建无名管道
    int fd[2];
    pipe(fd);
    
    if(fork() == 0)
    {
        close(fd[0]);   //读关闭
        char *buf = "hello world"; 
        write(fd[1], buf, strlen(buf)); //写操作
    }
    else
    {
        close(fd[1]);  //写关闭
        char showbuf[100];
        bzero(showbuf, 100);  //清空缓冲区

        read(fd[0], showbuf, 100); //读操作

        printf("txt is :%s\n", showbuf); //打印内容

        exit(0);
      
    }
}

代码运行结果

2.2 有名管道 

//函数原型
#include <sys/types.h>
#include <sys/stat.h>

int mkfifo(const char *pathname, mode_t mode);
//  pathname:具名管道的名称 
//  mode:文件权限模式,例如0666

具名管道通常又被称为FIFO,它是对无名管道的一种改进,其更接近普通文件,有文件名、可以open打开、支持read()/write()等读写操作

Linux在共享文件夹下,创建管道文件是创建不了的,运行结果为空,因此要将执行文件移到非共享文件夹下。因为共享文件夹和Windows是共享的,有些文件windows是没有的。

特性

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

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

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

2.2.1 如何操作具名管道

  1. 创建有名管道文件 :(mkfifo即是命令也是函数;mknod也可以创建管道文件)
  2. 打开有名管道 :(open)
  3. 读/写 :(read/write)
  4. 关闭 :(close)

具名管道一旦没有任何读者和写者,系统判定管道处于空闲状态,会释放管道中的所有数据

1.写者

  1. 创建有名管道文件 mkfifo()
  2. 打开有名管道 open()
  3. 创建缓冲区,写内容 buf
  4. 写操作 write()
  5. 关闭有名管道 close()

2.读者

  1. 打开有名管道 open()
  2. 创建缓冲区,清空缓冲区 buf bzero()
  3. 读操作,打印 read()
  4. 关闭有名管道 close()

2.2.2 示例代码 

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

int main()
{
    int fd = open("./fifo", O_RDWR);  //打开有名管道

    char showbuf[100];   //创建缓冲区
    bzero(showbuf, 100);  //清空缓冲区

    read(fd, showbuf, 100);  //读

    printf("%s\n", showbuf); //打印

    close(fd);  //关闭

    return 0;

}
//写者
#include <stdio.h> 
#include <unistd.h> 
#include <sys/stat.h> 
#include <string.h> 
#include <sys/types.h> 
#include <fcntl.h> 
#include <stdlib.h> 

int main()
{
    mkfifo("./fifo", 0666);    //创建有名管道文件

    int fd = open("./fifo", O_RDWR);  //打开有名管道

    char *buf = "hello world";   //写
    write(fd, buf, strlen(buf));

    close(fd);   //关闭

    return 0;

}

注意:

在Linux在共享文件夹下,创建管道文件是创建不了的,运行结果为空,因此要将执行文件移到非共享文件夹下,这里我把执行文件放到家目录的test文件夹下了。

打开两个终端执行命令,这里可用Ctrl+shift+n快速打开相同路径下的终端

代码运行结果

3. 相关练习

编写两个程序,一个客户端、一个服务器端,服务器端创建并且监视管道,发现如果有数据就把它保存到指定位置,客户端将当前系统时间和自己的PID写入管道 

3.1 客户端

//cilent 客户端
#include <stdio.h> 
#include <unistd.h> 
#include <sys/stat.h> 
#include <string.h> 
#include <sys/types.h> 
#include <fcntl.h> 
#include <stdlib.h> 
#include <time.h>
int main()
{
    int fd = open("./fifo", O_RDWR);  //打开有名管道

    char buf[1000];     //创建缓冲区
    time_t tim;

    while(1)
    {
        bzero(buf,1000);      //清空缓冲区
        time(&tim);         //时间
        sprintf(buf,"[%d]:  %s",getpid(),ctime(&tim)); //打印到buf指向的字符串中

        write(fd,buf,strlen(buf)); //写操作,在日志文件中写入数据
        
        sleep(2);

    }
    
    return 0;

}

3.2 服务器端 

//server 服务器端
#include <stdio.h> 
#include <unistd.h> 
#include <sys/stat.h> 
#include <string.h> 
#include <sys/types.h> 
#include <fcntl.h> 
#include <stdlib.h> 

int main()
{
    mkfifo("./fifo", 0666);    //创建有名管道文件
    int logfd = open("./log.txt",O_RDWR | O_CREAT,0777); //创建日志文件
    int fd = open("./fifo", O_RDWR);  //打开有名管道

    char buf[1000];     //创建缓冲区
    while(1)
    {
        bzero(buf,1000);      //清空缓冲区
        read(fd,buf,1000);     //读取缓冲区数据
        write(logfd,buf,strlen(buf)); //在日志文件中写入数据
    }
    
    return 0;

}

代码运行结果 


如果喜欢请不吝给予三连支持!

小海编程心语录-CSDN博客

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值