命名管道


管道是用於相關過程之間的通訊。 我們是否可以使用管道進行不相關的進程通訊,比方說,我們要從一個終端執行用戶端程式,從另一個終端執行伺服器程式? 答案是否定的。那麼怎樣才能實現不相關的進程通訊,簡單的答案就是使用 命名管道。 即使這適用於相關的進程,但是使用命名管道進行相關的進程通訊沒有任何意義。

我們使用一個管道進行單向通訊,兩個管道進行雙向通訊。 命名管道是否適用相同的條件。 答案是否定的,我們可以使用單一命名管道作為命名管道支援雙向通訊(伺服器和用戶端之間的通訊,同時還有用戶端和伺服器之間的通訊)。

命名管道的另一個名稱是FIFO(先進先出)。 讓我們看看系統呼叫(mknod())來建立一個命名管道,這是一種特殊的檔案。

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

int mknod(const char *pathname, mode_t mode, dev_t dev);

這個系統呼叫將建立一個特殊的檔案或檔案系統節點,如普通檔案,裝置檔案或FIFO。 系統呼叫的引數是路徑名,模式和dev。 路徑名以及模式和裝置資訊的屬性。 路徑名是相對的,如果沒有指定目錄,它將在當前目錄中建立。 指定的模式是指定檔案型別的檔案模式,如下表中所述的檔案型別和檔案模式。 開發領域是指定裝置資訊,如主要和次要裝置號碼。

檔案型別 描述 檔案型別 描述
S_IFBLK 指定塊 S_IFREG 普通檔案
S_IFCHR 指定字元 S_IFDIR 目錄
S_IFIFO 指定FIFO S_IFLNK 符號連結

檔案模式

檔案模式 描述 檔案模式 描述
S_IRWXU 所有者讀,寫,執行/搜尋 S_IWGRP 寫入許可權,組
S_IRUSR 讀取許可權,所有者 S_IXGRP 執行/搜尋許可權,組
S_IWUSR 寫入許可權,所有者 S_IRWXO 讀,寫,執行/由他人搜尋
S_IXUSR 執行/搜尋許可權,所有者 S_IROTH 讀取許可權,其他
S_IRWXG 讀,寫,執行/按組搜尋 S_IWOTH 寫許可權,其他
S_IRGRP 讀取許可權,組 S_IXOTH 執行/搜尋許可權,其他

檔案模式也可以用八進位制表示法表示,如0XYZ,其中X表示所有者,Y表示組,Z表示其他。 XYZ的取值範圍為0?7,讀,寫,執行的取值分別為4,2,1。 如果需要結合讀取,寫入和執行,則相應地新增值。

如果使用的是0640,那麼這意味著對所有者的讀寫(4 + 2 = 6),對於組讀取(4)和對其他人沒有許可權(0)。

這個呼叫在成功時將返回0,在失敗的情況下為-1。 要知道失敗的原因,請檢查errno變數或perror()函式。

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

int mkfifo(const char *pathname, mode_t mode)

這個庫函式建立一個FIFO專用檔案,用於命名管道。 這個函式的引數是檔案名和模式。 檔案名可以是絕對路徑或相對路徑。 如果未提供完整路徑名(或絕對路徑),則將在執行過程的當前檔案夾中建立該檔案。 檔案模式資訊如mknod()系統呼叫中所述。

這個呼叫在成功時將返回0,在失敗的情況下為-1。 要知道失敗的原因,請檢查errno變數或perror()函式。

讓我們考慮在一個終端上執行伺服器並在另一個終端上執行用戶端的程式。 該方案只會進行單向溝通。 用戶端接受使用者輸入並將訊息傳送到伺服器,伺服器在輸出上列印訊息。 這個過程一直持續到使用者輸入字串「end」。

讓我們通過一個例子來理解這一點 -

第1步 - 建立兩個進程,一個是fifoserver,另一個是fifoclient
第2步 - 伺服器進程執行以下操作 -

  • 建立一個名為「MYFIFO」的命名管道(使用系統呼叫mknod())。
  • 開啟命名管道為唯讀目的。
  • 在這裡,建立了具有對所有者的讀寫許可權的FIFO。讀取群組,沒有其他人的許可權。
  • 等待客戶的訊息。
  • 如果從用戶端收到的訊息不是「end」,則列印該訊息。 如果訊息是「end」,則關閉fifo並結束該進程。

第3步 - 用戶端進程執行以下操作 -

  • 開啟命名管道僅用於寫入目的。
  • 接受來自使用者的字串。
    • 檢查如果使用者輸入是否為"end"。如果字串是「end」,則會關閉FIFO並結束進程。
  • 無限重複,直到使用者輸入字串「end」

現在我們來看看FIFO伺服器檔案的實現 -

/* Filename: fifoserver.c */
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

#define FIFO_FILE "MYFIFO"
int main() {
   int fd;
   char readbuf[80];
   char end[10];
   int to_end;
   int read_bytes;

   /* Create the FIFO if it does not exist */
   mknod(FIFO_FILE, S_IFIFO|0640, 0);
   strcpy(end, "end");
   while(1) {
      fd = open(FIFO_FILE, O_RDONLY);
      read_bytes = read(fd, readbuf, sizeof(readbuf));
      readbuf[read_bytes] = '\0';
      printf("Received string: \"%s\" and length is %d\n", readbuf, (int)strlen(readbuf));
      to_end = strcmp(readbuf, end);
      if (to_end == 0) {
         close(fd);
         break;
      }
   }
   return 0;
}

編譯和執行步驟

Received string: "this is string 1" and length is 16
Received string: "fifo test" and length is 9
Received string: "fifo client and server" and length is 22
Received string: "end" and length is 3

現在,來看看FIFO用戶端範例程式碼。

/* Filename: fifoclient.c */
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

#define FIFO_FILE "MYFIFO"
int main() {
   int fd;
   int end_process;
   int stringlen;
   char readbuf[80];
   char end_str[5];
   printf("FIFO_CLIENT: Send messages, infinitely, to end enter \"end\"\n");
   fd = open(FIFO_FILE, O_CREAT|O_WRONLY);
   strcpy(end_str, "end");

   while (1) {
      printf("Enter string: ");
      fgets(readbuf, sizeof(readbuf), stdin);
      stringlen = strlen(readbuf);
      readbuf[stringlen - 1] = '\0';
      end_process = strcmp(readbuf, end_str);

      //printf("end_process is %d\n", end_process);
      if (end_process != 0) {
         write(fd, readbuf, strlen(readbuf));
         printf("Sent string: \"%s\" and string length is %d\n", readbuf, (int)strlen(readbuf));
      } else {
         write(fd, readbuf, strlen(readbuf));
         printf("Sent string: \"%s\" and string length is %d\n", readbuf, (int)strlen(readbuf));
         close(fd);
         break;
      }
   }
   return 0;
}

我們來看一下收到的輸出。編譯和執行輸出結果如下 -

FIFO_CLIENT: Send messages, infinitely, to end enter "end"
Enter string: this is string 1
Sent string: "this is string 1" and string length is 16
Enter string: fifo test
Sent string: "fifo test" and string length is 9
Enter string: fifo client and server
Sent string: "fifo client and server" and string length is 22
Enter string: end
Sent string: "end" and string length is 3

使用命名管道雙向通訊

管道之間的通訊意味著是單向的。 一般情況下,管道僅限於單向通訊,至少需要兩根管道進行雙向通訊。 管道僅用於相關的進程。 管道不能用於不相關的進程通訊,例如,如果想從一個終端執行一個進程,而從另一個終端執行另一個進程,那麼管道是不可能的。 我們是否有任何簡單的方法在兩個進程之間進行通訊,用簡單的方式實現不相關的進程間的通訊? 答案是肯定的。 命名管道是用於兩個或更多不相關進程之間的通訊,也可以是雙向通訊。

我們已經看到了命名管道之間的單向通訊,即從用戶端到伺服器的訊息。 現在看看雙向通訊,即用戶端向伺服器傳送訊息,接收訊息的伺服器使用相同的命名管道向用戶端傳送另一條訊息。

以下是一個例子 -

第1步 - 建立兩個進程,一個是fifoserver_twoway,另一個是fifoclient_twoway
第2步 - 伺服器進程執行以下操作 -

  • 如果未建立,則在/tmp目錄中建立名為「fifo_twoway」的命名管道(使用庫函式mkfifo())。
  • 為讀寫目的開啟命名管道。
  • 在這裡,建立了具有對所有者的讀寫許可權的FIFO。讀取群組,沒有其他人的許可權。
  • 等待來自客戶的訊息。
  • 如果從用戶端收到的訊息不是"end"字串,則列印訊息並反轉字串。 反轉的字串被傳送回用戶端。 如果訊息是"end",則關閉fifo並結束該過程。

第3步 - 用戶端進程執行以下操作 -

  • 為讀寫目的開啟命名管道。
  • 接受來自使用者的字串。
  • 檢查如果使用者輸入是否為"end"。它傳送一條訊息到伺服器。 但是,如果字串是"end",則會關閉FIFO並結束進程。
  • 如果訊息傳送不是"end",則等待來自用戶端的訊息(反向字串)並列印反向字串。
  • 無限重複,直到使用者輸入字串"end"

現在,讓我們來看看FIFO伺服器範例程式碼。

/* Filename: fifoserver_twoway.c */
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

#define FIFO_FILE "/tmp/fifo_twoway"
void reverse_string(char *);
int main() {
   int fd;
   char readbuf[80];
   char end[10];
   int to_end;
   int read_bytes;

   /* Create the FIFO if it does not exist */
   mkfifo(FIFO_FILE, S_IFIFO|0640);
   strcpy(end, "end");
   fd = open(FIFO_FILE, O_RDWR);
   while(1) {
      read_bytes = read(fd, readbuf, sizeof(readbuf));
      readbuf[read_bytes] = '\0';
      printf("FIFOSERVER: Received string: \"%s\" and length is %d\n", readbuf, (int)strlen(readbuf));
      to_end = strcmp(readbuf, end);

      if (to_end == 0) {
         close(fd);
         break;
      }
      reverse_string(readbuf);
      printf("FIFOSERVER: Sending Reversed String: \"%s\" and length is %d\n", readbuf, (int) strlen(readbuf));
      write(fd, readbuf, strlen(readbuf));
      /*
      sleep - This is to make sure other process reads this, otherwise this
      process would retrieve the message
      */
      sleep(2);
   }
   return 0;
}

void reverse_string(char *str) {
   int last, limit, first;
   char temp;
   last = strlen(str) - 1;
   limit = last/2;
   first = 0;

   while (first < last) {
      temp = str[first];
      str[first] = str[last];
      str[last] = temp;
      first++;
      last--;
   }
   return;
}

編譯和執行輸出結果 -

FIFOSERVER: Received string: "LINUX IPCs" and length is 10
FIFOSERVER: Sending Reversed String: "sCPI XUNIL" and length is 10
FIFOSERVER: Received string: "Inter Process Communication" and length is 27
FIFOSERVER: Sending Reversed String: "noitacinummoC ssecorP retnI" and length is 27
FIFOSERVER: Received string: "end" and length is 3

現在,我們來看看FIFO用戶端範例程式碼。

/* Filename: fifoclient_twoway.c */
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

#define FIFO_FILE "/tmp/fifo_twoway"
int main() {
   int fd;
   int end_process;
   int stringlen;
   int read_bytes;
   char readbuf[80];
   char end_str[5];
   printf("FIFO_CLIENT: Send messages, infinitely, to end enter \"end\"\n");
   fd = open(FIFO_FILE, O_CREAT|O_RDWR);
   strcpy(end_str, "end");

   while (1) {
      printf("Enter string: ");
      fgets(readbuf, sizeof(readbuf), stdin);
      stringlen = strlen(readbuf);
      readbuf[stringlen - 1] = '\0';
      end_process = strcmp(readbuf, end_str);

      //printf("end_process is %d\n", end_process);
      if (end_process != 0) {
         write(fd, readbuf, strlen(readbuf));
         printf("FIFOCLIENT: Sent string: \"%s\" and string length is %d\n", readbuf, (int)strlen(readbuf));
         read_bytes = read(fd, readbuf, sizeof(readbuf));
         readbuf[read_bytes] = '\0';
         printf("FIFOCLIENT: Received string: \"%s\" and length is %d\n", readbuf, (int)strlen(readbuf));
      } else {
         write(fd, readbuf, strlen(readbuf));
         printf("FIFOCLIENT: Sent string: \"%s\" and string length is %d\n", readbuf, (int)strlen(readbuf));
         close(fd);
         break;
      }
   }
   return 0;
}

編譯和執行輸出結果 -

FIFO_CLIENT: Send messages, infinitely, to end enter "end"
Enter string: LINUX IPCs
FIFOCLIENT: Sent string: "LINUX IPCs" and string length is 10
FIFOCLIENT: Received string: "sCPI XUNIL" and length is 10
Enter string: Inter Process Communication
FIFOCLIENT: Sent string: "Inter Process Communication" and string length is 27
FIFOCLIENT: Received string: "noitacinummoC ssecorP retnI" and length is 27
Enter string: end
FIFOCLIENT: Sent string: "end" and string length is 3