命名管道

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

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

命名管道的另一個名稱是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