03Linux进程间的通信-无名与有名管道

本文详细介绍了无名管道和有名管道(FIFO)在进程间通信(IPC)中的应用。无名管道适用于亲缘进程之间的半双工通信,不具备原子性,而有名管道通过文件系统实现,允许任何具有权限的进程通信,支持多写入者。文中提供了示例代码,展示了如何创建和使用这两种管道进行数据传输。
摘要由CSDN通过智能技术生成

进程间的通信(IPC)方式,总归起来主要有如下这些:
1,无名管道(PIPE)和有名管道(FIFO)。
2,信号(signal)。
3,system V-IPC 之共享内存。
4,system V-IPC 之消息队列。
5,system V-IPC 之信号量。
6,套接字。

1、无名管道:

在这里插入图片描述
注意:

  • pipe中的参数是一个具有两个整形数据的数组,用来存放文件描述符,一个是读端、另一个是写端

Pipe的特性:

  • 没有名字,因此无法用open函数打开
  • 只能用于亲缘进程(如:父子进程、兄弟进程、祖孙进程…)通信
  • 半双工工作方式:不能同时读写
  • 写入操作不具有原子性(写操作可以被打断),因此只能用于一对一的简单通信
  • 不能使用sleek来定位读写的信息

在这里插入图片描述
无名管道例子:

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

int main()
{
    int pipefd[2];
    if(pipe(pipefd) < 0)
    {
        printf("pipe error\n");
        return 0;
    }
	//创建pipe必须在父进程创建子进程之前
    pid_t pid = fork();

    if(pid > 0)
    {
        printf("父进程\n");
        write(pipefd[1], "hello world", sizeof("hello world"));//往pipe[1]管道中写入数据
    }
    else if(pid == 0)
    {
        char buf[32] = {0};
        read(pipefd[0], buf, sizeof(buf));//从pipe[0]管道中写入数据
        printf("子进程\n");
        printf("%s", buf);
    }
    else
    {
        printf("c创建失败\n");
    }
}

练习:
尝试实现一下父子进程之间互相收发数据。

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

char *buf = NULL;
int pipefd[2];


void father()
{
    while(1)
    {
        printf("父进程请输入数据:\n");
        fgets(buf, 1024, stdin);
        int ret = write(pipefd[1], buf, strlen(buf));
        printf("成功写入:%d字节 \n", ret );//数据写完之后最好等待一会,避免父子进程不同步
        
        ret = read( pipefd[0] , buf , 1024 );
        printf("父进程*成功读取: %d 字节 内容:%s \n" ,ret,  buf );
    }
}


void sun()
{
    while(1)
    {
        int ret_val = read( pipefd[0] , buf , 1024 );
        printf("子进程*成功读取:%d字节  内容:%s \n" , ret_val , buf );

        printf("子进程*请输入:\n");
        fgets(buf , 1024 , stdin );
        ret_val = write( pipefd[1] , buf , strlen(buf));
        printf("成功写入:%d字节 \n" , ret_val );
    }
}

int main()
{
    
    if(pipe(pipefd))
    {
        printf("error\n");
        return -1;
    }

    buf = calloc(1, 1024);
	//需要继承的东西都要在创建子进程之前
    int pid = fork();

    if(pid == 0)
    {
        sun();
    }
    else if(pid > 1)
    {
        father();
    }   
    return 0;
}

注意:需要在写入之后等待一会,让另外一个进程读取,不然会造成不同步现象

2、有名管道

在这里插入图片描述
有名管道FIFO的特性:

  • 有名字,存储于普通文件系统之中
  • 任何具有相应权限的进程都可以使用open()来获取FIFO的文件描述符
  • 跟普通文件一样,使用统一的read()\write()来读写
  • 跟普通文件不同,不能使用lseek()来定位
  • 具有写入原子性,支持多写入者同时进行写操作,而不会出现数据相互践踏
  • First In First Out,最先被写进去的数据,最先被读取出来
    在这里插入图片描述

读者:

  • 持有管道文件读取权限的进程

写者:

  • 持有管道文件写入权限的进程

练习:
尝试使用有名管道实现两个进程互相通信。

//fifo_write.c

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

int main()
{
    //创建管道文件,文件存在返回0, 不存在返回-1
    if(access("/tmp/my_fifo", F_OK))
    {
        if(mkfifo("/tmp/my_fifo", 0666))//创建
        {
            perror("Mkfifo\n");
            return -1;
        }
    }   
    printf("管道文件创建成功!! \n") ;

    //打开文件
    int fd = open("/tmp/my_fifo", O_WRONLY);

    if(fd == -1)
    {
        printf("文件打开失败\n");
        return -1;
    }
    printf("文件打开成功\n");

    //写入信息
    int ret = write(fd, "Hello Even", sizeof("Hello Even"));
    printf("写入成功\n");
	//关闭文件
    close(fd);
}

//fifo_read.c

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

int main()
{
    //创建管道文件,文件存在返回0, 不存在返回-1
    if(access("/tmp/my_fifo", F_OK))
    {
        if(mkfifo("/tmp/my_fifo", 0666))//创建
        {
            perror("Mkfifo\n");
            return -1;
        }
    }
    printf("管道文件创建成功!! \n") ;

    //打开
    int fd = open("/tmp/my_fifo", O_RDONLY);

    if(fd == -1)
    {
        printf("文件打开失败\n");
        return -1;
    }
    printf("文件打开成功\n");

    //写入信息
    char buf[32] = {0};
    int ret = read(fd, buf, sizeof(buf));
    printf("读取成功:%s\n", buf);
    //关闭文件
    close(fd);
}

注意:

  • 管道文件在打开的时候,如果只有一方(读者\写者)则阻塞,需要等待对方到达后同时打开文件。这是因为在有名管道中,如果打开管道的一方没有对应的对端进程,则打开操作会阻塞。这是因为有名管道是一种同步的进程间通信机制。

作业:

  • 使用有名管道实现类似系统中日志文件写入的操作,使用多个进程在不同的间隔时间(随机)
    对同一个文件进行写入(进程号 + 时间 + 消息),检查是否能确保各个进程写入的数据不会被互相践踏。

    在这里插入图片描述
    客户端(client)采用父子进程同时对FIFO管道发送消息,服务端(server)从FIFO管道中接收数据并存入到日志文件中。

//client.c

#include "stdio.h"
#include <unistd.h>
#include <fcntl.h>          
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <time.h>

#define PATH_FIFO   "/tmp/Logs"

int main()
{
    //如果没有文件返回-1,有文件返回0
    if(access(PATH_FIFO, F_OK))
    {
        if(mkfifo(PATH_FIFO, 0666) == -1)//创建文件失败返回-1
        {
            printf("创建失败\n");
            return -1;
        }
    }
    printf("创建成功\n");
    int fd = open(PATH_FIFO, O_WRONLY);//可以让父子进程同用一个文件描述父,因为有名管道有同步机制
    if(fd == -1)
    {
        printf("error\n");
        return -1;
    }

    pid_t pid = fork();//创建子进程

    if(pid == 0)//子进程
    {
        time_t timep;//获取时间
        while(1)
        {
            char buf[64] = {0};
            time(&timep);

            sprintf(buf, "%d:son Time:%s", getpid(), ctime(&timep));
            write(fd, buf, strlen(buf));
        }
        close(fd);
    }
    else if(pid > 0)//父进程
    {
        time_t time_p;//获取时间
        while(1)
        {
            char buf[64] = {0};
            time(&time_p);
            sprintf(buf, "%d:father   Time:%s", getpid(), ctime(&time_p));
            write(fd, buf, strlen(buf));
        }
        close(fd);
    }
    else
    {
        printf("error\n");
        return -1;
    }
}

//server.c

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


#define PATH_FIFO   "/tmp/Logs"
#define PATH_LOGS   "./Logs_fifo.txt"
int main()
{
    if(access(PATH_FIFO, F_OK))
    {
        if(mkfifo(PATH_FIFO, 0666) == -1)//创建失败返回-1
        {
            printf("error\n");
            return -1;
        }
    }
    printf("创建成功\n");

    int fd_fifo = open(PATH_FIFO, O_RDONLY);//打开管道文件
    int fd_logs = open(PATH_LOGS, O_WRONLY);//打开日志文件

    if(fd_fifo == -1 || fd_logs == -1)
    {
        printf("打开失败\n");
        return -1;
    }
    printf("打开文件成功\n");
    while(1)
    {
        char buf[64] = {0};
        int ret = read(fd_fifo, buf, sizeof(buf));//读取管道文件中的数据
        write(fd_logs, buf, ret);//读到的数据写入日志文件中
    }
    close(fd_fifo);
    close(fd_logs);
}

文章对无名管道和有名管道进行了详细的介绍和解释,包括它们的特性、使用方法和示例代码。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值