Liunx系统编程篇—进程通信(二)无名管道(原理、创建、实战)命名管道(原理、创建、实战)

一、无名管道

管道,通常指无名管道(之所以叫无名管道是因为,没有文件名),是 UNIX 系统IPC最古老的形式。

特点

(1)它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端
(2)它只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间)。
(3)它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。
(4)管道中的数据被读走就没了。

原理

当一个管道建立时,它会创建两个文件描述符:fd[0]为读而打开,fd[1]为写而打开。管道的建立存在与内核之中,如下图:
管道的建立
要关闭管道只需将这两个文件描述符关闭即可。
在这里插入图片描述

创建

单个进程中的管道几乎没有任何用处。所以,通常调用 pipe 的进程接着调用 fork,这样就创建了父进程与子进程之间的 IPC 通道。如下图所示:
在这里插入图片描述

1 #include <unistd.h>
2 int pipe(int fd[2]);    // 返回值:若成功返回0,失败返回-1

实例

#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
int main()
{
    
    
        int pid=0;
        int fd[2]={
    
    0};
        char buf[128]={
    
    0};
        if(pipe(fd) == -1){
    
    //建立管道
                printf("creat pipe failed\n");
        }
        pid=fork();//建立子进程
        if(pid<0){
    
    //建立子进程失败
                printf("creat failed\n");
        }
        else if(pid >0){
    
    //父进程运行空间,pid>0时
                sleep(3);
                printf("father\n");
                close(fd[0]);//父进程关闭读端文件描述符
                write(fd[1],"hello from father",strlen("hello from father"));//父进程对管道内写入
        }else{
    
    //子进程运行空间,pid=0时
                printf("child\n");
                close(fd[1]);//子进程关闭写端文件描述符
                read(fd[0],buf,128);//子进程从管道内读出数据到buf
                printf("read form father:%s\n",buf);
        }
        return 0;
}

实验结果:

在这里插入图片描述

二、命名管道(FIFO)

特点

(1)FIFO可以在无关的进程之间交换数据,与无名管道不同。
(2)FIFO 有路径名(无名管道没有)与之相关联,它以一种特殊设备文件形式存在于文件系统中。
(3)管道中的数据被读走就没了,同时保持==FIFO(先进先出)==的特点。

原理

与无名管道类似。

创建

1 #include <sys/stat.h>
2 int mkfifo(const char *pathname, mode_t mode);// 返回值:成功返回0,出错返回-1

其中的 mode 参数与open函数中的 mode 相同(0600-读写)(open函数)。一旦创建了一个 FIFO,就可以用一般的文件I/O函数操作它。
当 open 一个FIFO时,==是否设置非阻塞标志(O_NONBLOCK)==的区别:

若没有指定O_NONBLOCK(默认)

只读open 要阻塞到某个其他进程为写而打开此 FIFO。
只写open 要阻塞到某个其他进程为读而打开它。

若指定了O_NONBLOCK,则只读 open 立即返回。而只写 open 将出错返回 -1 如果没有进程已经为读而打开该 FIFO,其errno置ENXIO

实例

read.c

#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include <errno.h>
#include <fcntl.h>
int main()
{
    
    
        char buf[128]={
    
    0};
        if((mkfifo("./file",0600)==-1) && errno!=EEXIST){
    
    //建立管道
                printf("mkfifo failure\n");
                perror("why");
        }else{
    
    
                if(errno==EEXIST){
    
    
                        printf("file eexist\n");
                }else{
    
    
                        printf("mkfifo successed\n");
                }
        }
        int fd = open("./file",O_RDONLY);//打开管道,O_RDINLY-只读模式打开,如果没有另一个进程以只写模式打开管道,程序会阻塞此处
        printf("open file success\n");
        int nread=read(fd,buf,20);//从管道内读数据
        printf("read %d byte from fifo,context:%s\n",nread,buf);
        close(fd);//关闭管道
        return 0;
}

write.c

#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include <errno.h>
#include <fcntl.h>
int main()
{
    
    
// ssize_t read(int fd, void *buf, size_t count);
        char *buf="aioplo!!!!!!!!!!!!!!!!!";
        int fd = open("./file",O_WRONLY);//打开管道,O_WRONLY-只写模式打开,如果没有另一个进程读它,程序会阻塞此处
        printf("write file success\n");
        write(fd,buf,strlen(buf));//写入数据
        return 0;
}

实验结果:

先运行读部分,程序会阻塞,等待其他进程打开管道

写部分的进程写入数据

阻塞解除,读取成功
上述例子可以扩展成 客户进程—服务器进程 通信的实例,
write的作用类似于客户端,可以打开多个客户端向一个服务器发送请求信息,
read类似于服务器,它适时监控着FIFO的读端,当有数据时,读出并进行处理,
每一个客户端必须预先知道服务器提供的FIFO接口,如下图:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_44933419/article/details/113097639