linux進程間通信之管道

下面幾節,將分別溫習下linux進程進通信的幾種機制

1:管道

管道是比較古老的進程間的通信方式。主要有有名管道和無名管道兩種。

2:無名管道

它的特點就是:
1:只能使用在具有親緣關係的進程之間的通信(父子進程或者兄弟進程之間)。因爲只有具有親緣關係的進程才能繼承其創建的文件描述符。
2:是一個半雙工的通信模式,具有固定的讀端和寫段。
3:可以將管道看着特殊的文件,也可以調用read()和write()操作。

管道是基於文件描述符的通信方式,當一個管道建立時,它會創建兩個文件描述符fd[0]和fd[1],其中fd[0]是固定用於讀管道, 而fd[1]是固定用於寫管道,構成一個半雙工通道。
這裏寫圖片描述
當一個管道共享多對文件描述符時,若將其中的一對讀寫文件描述符都刪除,則該管道將失效。

這裏寫圖片描述

上面插圖來自《linux應用程序開發標準教程》中,從圖中可以看到當fork()後 , 子進程會繼承父進程創建的管道,這樣父子進程都擁有了自己的讀寫通道, 從而實現了父子之間的讀寫,只需要把無關的讀端或寫段的文件描述符關閉即可。

 例如上圖中,父進程負責讀段,則把它的寫段fd[1]關閉, 子進程負責寫,則把它的讀段fd[0]關掉。這樣就建立起來一條“子進程寫父進程讀”的通道。

  下面來看一下無名管道的實例,直接上代碼:
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>


#define MAX_DATA_LEN 128
#define DELAY_TIME 1

int main()
{

    pid_t pid;
    int pipe_fd[2];
    char buf[MAX_DATA_LEN] = {0};
    const char data[] ="Pipe Test Program";
    int real_read, real_write;

    if( pipe(pipe_fd) < 0)
    {
        printf("Pipe create error\n");
        exit(1);
    }

    if((pid = fork()) == 0)
    {
        /* clode write fd */
        close(pipe_fd[1]);
        sleep(DELAY_TIME *3);

        if ((real_read = read(pipe_fd[0], buf, MAX_DATA_LEN)) > 0)
        {
            printf("Read data from pipe is %s\n", buf);
        }

        close(pipe_fd[0]);
        exit(0);
     } else if (pid > 0)
     {
         /*close read fd*/
         close(pipe_fd[0]);
         sleep(DELAY_TIME);

         if ((real_write = write(pipe_fd[1], data,  strlen(data))) != -1)
         {
             printf("Write to pipe is: %s\n", data);
         }
          {
        /* clode write fd */
        close(pipe_fd[1]);
        sleep(DELAY_TIME *3);

        if ((real_read = read(pipe_fd[0], buf, MAX_DATA_LEN)) > 0)
        {
            printf("Read data from pipe is %s\n", buf);
        }

        close(pipe_fd[0]);
        exit(0);
     } else if (pid > 0)
     {
         /*close read fd*/
         close(pipe_fd[0]);
         sleep(DELAY_TIME);

         if ((real_write = write(pipe_fd[1], data,  strlen(data))) != -1)
         {
             printf("Write to pipe is: %s\n", data);
         }
         close(pipe_fd[1]);
         waitpid(pid, NULL, 0); 
         exit(0);
      }   

      return 0;
}

  運行結果:
  Write to pipe is: Pipe Test Program
  Read data from pipe is Pipe Test Program

3:有名管道

有名管道突破了無名管道的限制,可以使兩個不相關的進程實現彼此之間的通信。相當於創建一個文件,建立管道之後,兩個進程可以把它當做普通文件一樣進行讀寫操作。嚴格遵循先進先出規則,對管道及FIFO的讀寫總是從開始出返回數據,對它們的寫則把數據添加到末尾,需要指出的是不支持lseek()等文件定位操作。
可以採用mkdfifo()函數來創建一個命名管道,創建成功後就可以使用open, read, write函數進行操作。

mkfifo函數原型:
int mkfifo(const char *filename, mode_t mode)
傳參:
     filename爲要創建的管道,包括路徑名
     mode只創建的管道權限,一般有O_RDONLY(讀管道), O_WRONLY(寫管道),O_RDWR(讀寫管道),O_NONBLOCK:非堵塞管道。 O_CREAT(如果文件不存在,則新建文件),O_EXCL:可以測試文件是否存在。

下面來看一個有名管道的例子,爲了方便看代碼,直接將命名管道應用在父子進程間通信:
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>

#define MYFIFO "myfifo"


#define MAX_DATA_LEN 128
#define DELAY_TIME 1

int main()
{

    pid_t pid;
    int pipe_fd[2];
    char buf[MAX_DATA_LEN] = {0};
    const char data[] ="Pipe Test Program";
    int real_read, real_write;
    int fd = 0;

   // memset((void *)buf, 0, sizeof(buf));
    if(access(MYFIFO, F_OK) == -1) 
    {   
        if ((mkfifo(MYFIFO, 0666) < 0) )
                  printf("Can not create fifo file\n");
            exit(1);
        }
    }

    if((pid = fork()) == 0)
    {
        /* open write fifo */
        fd= open(MYFIFO,O_WRONLY);
        if ( fd == -1)
        {
            printf("Open write file error\n");
            exit(1);
        }

        if(write(fd, data, strlen(data)) > 0)
        {
            printf("Write %s to FIFO\n", data);
        }

        close(fd);

        exit(0);
     } else if (pid > 0)
     {
         /*open write fifo */
         fd = open(MYFIFO, O_RDONLY);
         if (fd == -1)
         {
             printf("Open fifo error\n");
             exit(1);
         }

         if(read(fd, buf, MAX_DATA_LEN) > 0)
         {
             printf("Read %s from FIFO\n", buf);
         }

         close(fd);

         exit(0);
      }

      return 0;
}

運行結果:
Write Pipe Test Program to FIFO
Read Pipe Test Program from FIFO

下一節將溫習信號的使用

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