1. 程式人生 > >【程序通訊】無名管道 and 有名管道

【程序通訊】無名管道 and 有名管道

  管道通訊分為無名管道有名管道

  無名管道用於父程序和子程序間的通訊,有名管道可用於運行於同一系統中的任意兩個程序間的通訊

管道的特點:

 ①半雙工,資料只能向一個方向流動;需要雙方通訊時,要建立兩個管道。

 ②只能用於具有親緣關係的程序。

 ③單獨構成一種獨立的檔案系統,且只存在於記憶體中。

 ④資料的讀寫:一個程序向管道中寫的內容被管道另一端的程序讀出。寫入的內容每次都新增在管道緩衝區的末尾,且總是從緩衝區的頭部讀出資料。

一、建立無名管道

1. pipe

函式的作用:建立管道

函式的原型:int pipe(int filedes[2]);

標頭檔案:#include <unistd.h>

返回值:成功——返回0

失敗——返回-1

函式的說明:pipe()會建立管道,並將檔案描述詞由引數filedes陣列返回。filedes[0]為管道里的讀取端,filedes[1]則為管道的寫入端。

  管道用於不同程序間通訊。通常先建立一個管道,再通過fork函式建立一個子程序,該子程序會繼承父程序所建立的管道。必須在系統呼叫fork( )前呼叫pipe( ),否則子程序將不會繼承檔案描述符

示例1

#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
    int pipe_fd[2];

    if(pipe(pipe_fd) < 0)
    {
        printf("pipe create error!\n");
        return -1;
    }
    else
    { 
        printf("pipe create success!\n");
    }

    close(pipe_fd[0]);
    close(pipe_fd[1]);
}

執行結果:

pipe create success!

示例2

/*父程序借管道將字串"hello!\n"傳給子程序並顯示*/
#include <stdio.h>
#include <unistd.h>

int main()
{
    int filedes[2];
    char buffer[80];
    pipe(filedes);
    if(fork() > 0)
    {
        /*父程序*/
        char s[] = "hello!\n";
        write(filedes[1],s,sizeof(s));
    }
    else
    {
        /*子程序*/
        read(filedes[0],buffer,80);
        printf("%s",buffer);
    }

    return 0;
}

執行結果:

hello!

二、讀寫無名管道

  管道兩端可分別用描述符fd[0]fd[1]來描述,管道的兩端固定了任務,即fd[0]只能讀,fd[1]只能寫。

示例:

#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>

int main()
{
    int pipe_fd[2];
    pid_t pid;
    char buf_r[100];
    int r_num;

    memset(buf_r,0,sizeof(buf_r));

    /*建立管道*/
    if(pipe(pipe_fd) < 0)
    {
        printf("pipe creat error.\n");
        return -1;
    }

    /*建立子程序*/
    if(0 == (pid = fork()))    //子程序
    {
        printf("\n");
        close(pipe_fd[1]);     //關閉寫
        sleep(2);              //確保關閉寫端
        if((r_num = read(pipe_fd[0],buf_r,100)) > 0)
        {
            printf("%d numbers read from the pipe is %s.\n",r_num,buf_r);
        }
        close(pipe_fd[0]);
        exit(0);
    }
    else if(pid > 0)
    {
        close(pipe_fd[0]);
        if(write(pipe_fd[1],"hello",5) != -1)
        {
            printf("parent write1 hello!\n");
        }
        if(write(pipe_fd[1],"pipe",5) != -1)
        {
            printf("parent write2 pipe!\n");
        }
        close(pipe_fd[1]);      //關閉寫
        sleep(3);               //確保關閉寫
        waitpid(pid,NULL,0);    //等待子程序結束
        exit(0);
    }

    return 0;
}

執行結果:

parent write1 hello!
parent write2 pipe!
10 numbers read from the pipe is hellopipe.

三、建立有名管道

1. mkfifo

函式的作用:建立有名管道

函式的原型:int mkfifo(const char * pathname, mode_t mode);

引數:filename:有名管道的名稱、路徑

mode:開啟方式

標頭檔案:#include<sys/types.h>

#include<sys/stat.h>

mode:

    O_NONBLOCKFIFO開啟的時候

    O_RDONLY:只讀

    O_WRONLY:只寫

    O_RDWR:可讀寫

返回值:成功——返回0

             失敗——返回-1

示例(有名管道的建立):

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

#define FIFO "/tmp/2"

int main()
{
    char buffer[80];
    int fd;

    unlink(FIFO);
    mkfifo(FIFO,0666);

    if(fork() > 0)
    {
        char s[] = "hello!\n";
        fd = open(FIFO,O_WRONLY);
        write(fd,s,sizeof(s));
        close(fd);
    }
    else
    {
        fd = open(FIFO,O_RDONLY);
        read(fd,buffer,80);
        printf("%s",buffer);
        close(fd);
    }

    return 0;
}

執行結果:

hello!
讀有名管道:
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define FIFO "myfifo"

int main(int argc, char **argv)
{
    char buf_r[100];
    int fd;
    int nread;

    /*建立管道*/
    if((mkfifo(FIFO, O_CREAT | O_EXCL) < 0) && (errno != EEXIST))
    {
        printf("cannot cret fifoserver.\n");
    }

    printf("Preparing for reading bytes...\n");

    memset(buf_r,0,sizeof(buf_r));

    /*開啟管道*/
    fd = open(FIFO, O_RDONLY | O_NONBLOCK, 0);
    if(-1 == fd)
    {
        perror("open");
        exit(1);
    }
    while(1)
    {
        memset(buf_r,0,sizeof(buf_r));

        if(-1 == (nread = read(fd,buf_r,100)))
        {
            if(errno == EAGAIN)
            {
                printf("no data yet\n");
            }
        }
        printf("read %s from FIFO\n",buf_r);
        sleep(1);
    }

    puse();
    unlink(FIFO);
}
寫有名管道:
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define FIFO_SERVER "myfifo"

int main(int argc, char **argv)
{
    char w_buf[100];
    int fd;
    int nwrite;

    /*開啟管道*/
    fd = open(FIFO_SERVER, O_WRONLY | O_NONBLOCK, 0);
    if(1 == argc)
    {
        printf("Please send something.\n");
        exit(-1);
    }

    strcpy(w_buf,argv[1]);

    if((nwrite = write(fd,w_buf,100)) == -1)
    {
        printf("The FIFO has not been read yet.Please try later.\n");
    }
    else
    {
        printf("write %s to the FIFO.\n",w_buf);
    }
}
同時執行讀和寫,執行結果如下: 寫:
/*命令列引數hello*/
./write_mkfifo hello
執行結果:
write hello to the FIFO.
讀:
Preparing for reading bytes...
read  from FIFO
read  from FIFO
read  from FIFO
read hello from FIFO
read  from FIFO
read  from FIFO
read  from FIFO
read  from FIFO
read  from FIFO
read  from FIFO

管道的應用場景:

1. shell中時常用到管道(作為輸入輸出的重定向)

2. 用於具有親緣關係的程序間通訊,使用者自己建立管道,並完成讀寫操作。