linux 无名管道和有名管道fifo

无名管道简介:

管道是半双工的,数据只能向一个方向流动,需要双方通信时,需要建立起两个管道。管道只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程):管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,并且只存在于内存中。管道的读写规则:fifo先进先出规则,写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。

无名管道相关函数说明:

#include <unistd.h>
int pipe(int fd[2])

返回值:若成功,返回0;若出错,返回-1。
函数传入值 fd[2]:管道的两个文件描述符,之后就可以直接操作这两个文件描述符。
调用此函数之后,返回两个文件描述符,fd[0]用于读管道,fd[1]用于写管道。

管道的运用:

通常先是创建一个管道,再通过 fork()函数创建一子进程,该子进程会继承父进程所创建的管道。这个时候,子进程和父进程都有两个文件描述符,fd[0]用于读管道,fd[1]用于写管道。我们想要让其通信,只需要一个进程保留写端,一个进程保留读端。

示例程序:

/* pipe.c*/

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

int main ()   
{   
    pid_t pid; 
    int count=0;  
    int pipe_fd[2];
    char read_pipe_buf[100];
    char read_pipe_buf_last[100];
    char write_pipe_buf[] = "hello";

    int read_num;
    //创建管道,pipe_fd[0]用于读管道,pipe_fd[1]用于写管道
    if(pipe(pipe_fd)<0)
    {
        perror("pipe create error\n");
        exit(-1);
    }

    if ((pid = fork()) < 0)
    {
        perror("fork");
        exit(-1);
    }
    else if (pid == 0) //子进程
    {
        //关闭pipe_fd[1]写管道,表明要读取管道的数据
        close(pipe_fd[1]);
        sleep(1);
        //子进程读取管道内容
        if((read_num=read(pipe_fd[0],read_pipe_buf,5))>0)
        {
            printf("this is child, %d numbers first read from  pipe is %s\n",read_num,read_pipe_buf);
        }
        if((read_num=read(pipe_fd[0],read_pipe_buf_last,5))>0)
        {
            printf("this is child, %d numbers second read from  pipe is %s\n",read_num,read_pipe_buf_last);
        }
        //关闭子进程读描述符
        close(pipe_fd[0]);
        exit(0);
    }
    else //父进程
    {
        //关闭pipe_fd[0]读管道,表明要向管道写数据
        close(pipe_fd[0]);
        /*if(write(pipe_fd[1],write_pipe_buf,sizeof(write_pipe_buf)) == -1)
        {
            perror("write pipe");
            exit(-1);
        }*/
        if(write(pipe_fd[1],"hello",5) == -1)
        {
            perror("write pipe");
            exit(-1);
        }
        if(write(pipe_fd[1],"world",5) == -1)
        {
            perror("write pipe");
            exit(-1);
        }
        //关闭父进程写描述符
        close(pipe_fd[1]);
        //等待子进程退出
        waitpid(pid,NULL,0);
        exit(0);
    }
    return 0;  
}

实验结果:

ubuntu:~/test/process_test$ gcc pipe.c -o pipe
ubuntu:~/test/process_test$ ./pipe            
this is child, 5 numbers first read from  pipe is hello
this is child, 5 numbers second read from  pipe is world

以上程序,先是调用pipe(pipe_fd),得到pipe_fd[0]用于读管道,pipe_fd[1]用于写管道。然后调用fork,创建出子进程,在子进程保留读端,用于读取管道的数据,在父进程保留写端,用于写管道,把字符串”hello”、”world”先后写入管道,接着子进程分两次读取管道的内容,并把它打印出来。子进程先读取的字符串是父进程先写入的字符串”hello”,这也体现了管道数据fifo先进先出的原则。

有名管道:

无名管道只能用在亲缘关系的进程之间,并且管道是存储在内存中,进程一终止,数据就没有了。而有名管道没有这种限制,可以用在互不相关的两个进程之间,文件也能保存在文件系统中。有名管道和无名管道都遵循先进先出规则。

有名管道函数说明:

#include <sys/state.h>
int mkfifo(const char *filename,mode_t mode)

返回值:若成功,返回文件流指针;若出错,返回-1
参数:

  • filename:要创建的管道
  • mode:
    • O_RDONLY:读管道
    • O_WRONLY:写管道
    • O_RDWR:读写管道
    • O_NONBLOCK:非阻塞
    • O_CREAT:如果该文件不存在,那么就创建一个新的文件,并为其设置权限
    • O_EXCL:如果使用 O_CREAT 时文件存在,那么可返回错误消息。这一参数可测试文件是否存在

可把有名管道的操作类似于对普通文件的操作,创建使用函数 mkfifo(),该函数类似普通文件中的 open()操作,然后在创建管道成功之后,就可以使用 open、read、write 这些函数了。
接下来创建两个程序,一个用于写管道,一个用于读管道,这两个程序是互不相关的。

写管道示例程序:

/* write_fifo.c*/

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

#define FIFO "myfifo"

int main(int argc,char** argv)
{
    /*创建有名管道*/
    if(mkfifo(FIFO,O_CREAT|O_EXCL|777)<0)
        perror("cannot create fifoserver\n");
    /*打开管道*/
    int fd=open(FIFO,O_RDWR|O_NONBLOCK,0);
    if(fd==-1)
    {
        perror("open");
    }

    char write_buf[128];
    int i = 0,write_num = 0;
    while(1)
    {
        printf("enter something to the myfifo:");
        scanf("%s",write_buf);
        if (write_num = write(fd,write_buf,sizeof(write_buf)) < 0)
        {
            perror("write");
        }
        printf("This is a write fifo....write %s to the FIFO\n",write_buf);
    }
    return 0;
}

读管道示例程序:

/* read_fifo.c*/

#include <stdio.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#define FIFO "myfifo"

int main(int argc,char** argv)
{
    /*创建有名管道*/
    if(mkfifo(FIFO,O_CREAT|O_EXCL|777)<0)
        perror("cannot create fifoserver\n");
    /*打开管道,默认阻塞模式*/
    int fd = open(FIFO,O_RDONLY,0);
    //int fd = open(FIFO,O_RDONLY|O_NONBLOCK,0);//非阻塞模式
    if(fd == -1)
    {
        perror("open");
    }

    char read_buf[128];
    int i = 0,read_num;
    while(1)
    {
        memset(read_buf,0,sizeof(read_buf));
        if (read_num = read(fd,read_buf,sizeof(read_buf)) < 0)
        {
            if(errno==EAGAIN)
                printf("no data yet\n");
        }
        printf("This is a read fifo....read %s from FIFO\n",read_buf);
        //sleep(2);
    }
    return 0;
}

我们在A终端运行写管道的程序,在B终端运行读管道的程序,根据A终端的提示,输入任意字符串,然后在B终端看看是否有读到相应的字符串。如果读不到,B终端会一直阻塞,因为我们程序设置了打开管道的默认属性阻塞,我们也可以设置非阻塞模式。从下面的实验结果,可以发现有名管道可以运用在没有亲缘关系的进程中。

//A终端
ubuntu:~/test/process_test$ gcc write_fifo.c -o write_fifo
ubuntu:~/test/process_test$ ./write_fifo
enter something to the myfifo:helloworld
This is a write fifo....write helloworld to the FIFO
enter something to the myfifo:
//B终端
ubuntu:~/test/process_test$ gcc read_fifo.c -o read_fifo
ubuntu:~/test/process_test$ ./read_fifo                 
cannot create fifoserver
: File exists
This is a read fifo....read helloworld from FIFO
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值