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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章