进程间通信之匿名命名管道

为什么要有进程间通信通信机制?

操作系统中,进程间是相互不可见的。操作系统在逻辑上将每个进程隔离开了。一个进程里是不可能看到真实的物理内存地址的。进程间的内存相互隔绝,操作系统这样做就是防止进程间的相互干扰,但是在一些具体的应用情况下需要多个进行相互配合,不同的进程需要进行信息的交互和状态的传递等,因此才会出现各种各样的通信机制。
比如内存映射文件,管道,邮槽等。

注:
通信目的数据传输 资源共享 通知事件 和进程控制


管道
一个进程连接到另一个进程的一个数据流称为管道,它是一种两个进程间进行单向通信的机制。
管道特点:
(1)内核缓冲区
(2) 由两个文件描述符引用,读端,写端。
(3) 数据从管道的写端流入管道,从读端流出。

管道又分为匿名管道和命名管道
匿名管道
(1)只适用于有亲缘关系的进程
(2)半双工(只能一个读一个写)
(3)生命周期随进程
(4)内置同步互斥机制
(5)面向字节流

#include<unistd.h>
功能:创建一无名管道
函数
int pipe(int fd[2]);
参数
fd:文件描述符数组,其中fd[0]表示读端,fd[1]表示写端
返回值:成功返回0,失败返回错误代码
函数调用成功返回r/w两个文件描述符。无需open,但需手动close

用法
1. 父进程调用pipe函数创建管道,得到两个文件描述符fd[0]、fd[1]指向管道的读端和写端。
2. 父进程调用fork创建子进程,那么子进程也有两个文件描述符指向同一管道。
3. 父进程关闭管道读端,子进程关闭管道写端。父进程可以向管道中写入数据,子进程将管道中的数据读出。由于管道是利用环形队列实现的,数据从写端流入管道,从读端流出,这样就实现了进程间通信。
这里写图片描述
这里写图片描述

#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
    int fd[2] = {0};
    if(pipe(fd) == -1){
        perror("pipe");
        exit(1);
}
while1)
{
    int pid = fork();
    if(pid > 0){
        //父进程
        close(fd[1]);
        char buf[1024] = {0};
        ssize_t tmp = read(fd[0],buf,sizeof(buf)-1);
        if(tmp == -1){
                perror("read");
                exit(1);
        }else if(tmp == 0){
            printf("read done\n");
            exit(0);
        }else{
            printf("father say : %s",buf);
        }
    }else if(pid == 0)
    {   //子进程
        close(fd[0]);
        char tmp[1024] = {0};
        printf("child say : ");
        fflush(stdout);
        read(0,tmp,sizeof(tmp)-1);
        if(write(fd[1],tmp,strlen(tmp)) == -1){
            perror("write");
            exit(1);
        }
        close(fd[1]);
        exit(0);
    }else
    {   //进程失败
        perror("fork");
        exit(1);
    }
}
    return 0;
}

这里写图片描述

管道读写规则

没有数据可读时
read调用阻塞,进程暂停执行,一直等到有数据来到为止
非阻塞情况下,read调用返回-1
当管道满的时候
write调用阻塞,直到有进程读走数据
非阻塞情况下,调用返回-1
如果所有管道写端对应文件描述符关闭,read返回0
如果所有管道读端对应的文件描述符关闭,则write操作会产生信号SIGPIPE,进而可能导致write进程退出

命名管道

命名管道是一种特殊类型的文件不同于管道之处在于它提供一个路径名与之关联,以FIFO的文件形式存储于文件系统中,通过网络来完成进程之间的通信。
(1)可以应用任意进程
(2)半双工
(3)生命周期随进程
(4)内置同步互斥机制
(5)面向字节流

创建一个命名管道

可以从命令行上创建
mkfifo filename
也可以从程序里创建
int mkfifo(const char* filename,mode_t mode);
参数:filename为所要创建的文件名,mode为文件权限.
返回值:成功返回0,失败返回-1
int main(int argc,char *argv[])
{
mkfifo("p2",0666)
return 0;
}

命名管道的打开规则

如果当前打开操作是为读而打开FIFO时
阻塞直到有相应进程为写而打开该FIFO
非阻塞情况下立刻返回成功
一读多写:任意一个写的内容会被这个进程读到
如果当前打开操作是为写而打开FIFO时
阻塞直到有相应的程序为读而打开FIFO
非阻塞情况下立刻返回失败
一写多读:写到管道的内容只会被某一个进程读到

用命名管道实现server&client通信

server部分
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<fcntl.h>

int main()
{
    //read write
    //int fd=open("./myfifo",O_RDONLY | O_NONBLOCK);//(阻塞方式O_RDONLY)
    int fd=open("./myfifo",O_RDONLY );

    if(fd<0){

        perror("open");
        return 1;
    }
    printf("open ok\n");
    while(1){

        char buf[1024]={0};
        ssize_t read_size = read(fd,buf,sizeof(buf)-1);
        if(read_size<0){
            perror("read error");
            return 1;
        }if(read_size==0)
        {
            printf("read done\n");
            sleep(3);
            continue;
            //return 0;
        }

        buf[read_size]='\0';
        printf("client:%s",buf);

    }
    close(fd);
    return 0;
}
client部分
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<fcntl.h>

int main()
{
    int fd = open("./myfifo",O_WRONLY);
    if(fd<0){

        perror("open");
        return 1;
    }
    printf("open ok\n");
    while(1){
        char buf[1024]={0};
       ssize_t read_size= read(0,buf,sizeof(buf)-1);
       if(read_size<0){

           perror("read");
           return 1;
       }
       if(read_size==0){
           printf("read done\n");
           return 0;
       }
       buf[read_size]='\0';
       write(fd,buf,strlen(buf));

    }
    close(fd);
    return 0;
}

这里写图片描述

匿名和命名管道的区别

(1)匿名管道由pipe函数创建并打开
(2)命名管道由mkfifo函数创建,打开用open
(3)FIFO与PIPE之间唯一的区别在它们创建与打开的方式不同,一旦这些工作完成之后,它们具有相同的语义。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. 实验目的 1) 加深对进程概念的理解,明确进程和程序的区别。 2) 进一步认识并发执行的实质。 3) 分析进程争用资源的现象,学习解决进程互斥的方法。 4) 学习解决进程同步的方法。 5) 了解Linux系统中进程通信的基本原理。   进程是操作系统中最重要的概念,贯穿始终,也是学习现代操作系统的关键。通过本次实验,要求理解进程的实质和进程管理的机制。在Linux系统下实现进程从创建到终止的全过程,从中体会进程的创建过程、父进程和子进程之间的关系、进程状态的变化、进程之间的互斥、同步机制、进程调度的原理和以管道为代表的进程间的通信方式的实现。 2. 内容及要求:   这是一个设计型实验,要求自行编制程序。   使用系统调用pipe()建立一条管道,两个子进程分别向管道写一句话:   Child process1 is sending a message!   Child process2 is sending a message!   父进程从管道读出来自两个子进程的信息,显示在屏幕上。   要求: 1) 父进程先接收子进程1发来的消息,然后再接收子进程2发来的消息。 2) 实现管道的互斥使用,当一个子进程正在对管道进行写操作时,另一子进程必须等待。使用系统调用lockf(fd[1],1,0)实现对管道的加锁操作,用lockf(fd[1],0,0)解除对管道的锁定。 3) 实现父子进程的同步,当子进程把数据写入管道后,便去睡眠等待;当父进程试图从一空管道中读取数据时,也应等待,直到子进程将数据写入管道后,才将其唤醒。 3.相关的系统调用 1) fork() 用于创一个子进程。 格式:int fork(); 返回值:在子进程中返回0;在父进程中返回所创建的子进程的ID值;当返回-1时,创建失败。 2) wait() 常用来控制父进程与子进程的同步。 在父进程中调用wait(),则父进程被阻塞,进入等待队列,等待子进程结束。当子进程结束时,父进程从wait()返回继续执行原来的程序。 返回值:大于0时,为子进程的ID值;等于-1时,调用失败。 3) exit() 是进程结束时最常调用的。 格式:void exit( int status); 其中,status为进程结束状态。 4) pipe() 用于创建一个管道 格式:pipe(int fd); 其中fd是一个由两个数组元素fd[0]和fd[1]组成的整型数组,fd[0]是管道的读端口,用于从管道读出数据,fd[1] 是管道的写端口,用于向管道写入数据。 返回值:0 调用成功;-1 调用失败。 5) sleep() 调用进程睡眠若干时间,之后唤醒。 格式:sleep(int t); 其中t为睡眠时间。 6) lockf() 用于对互斥资源加锁和解锁。在本实验中,该调用的格式为: lockf(fd[1],1,0);/* 表示对管道的写入端口加锁。 lockf(fd[1],0,0);/* 表示对管道的写入端口解锁。 7) write(fd[1],String,Length) 将字符串String的内容写入管道的写入口。 8) read(fd[0],String,Length) 从管道的读入口读出信息放入字符串String中。 4.程序流程 父进程: 1) 创建管道; 2) 创建子进程1; 3) 创建子进程2; 4) 等待从管道中读出子进程1写入的数据,并显示在屏幕上; 5) 等待从管道中读出子进程2写入的数据,并显示在屏幕上; 6) 退出。 子进程: 1) 将管道的写入口加锁; 2) 将信息“Child process n is sending message!”输入到变量OutPipe中,n=1,2; 3) 将OutPipe中信息写入管道; 4) 睡眠等待; 5) 将管道的写入口解锁; 6) 退出。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值