鍍金池/ 教程/ Linux/ 命名管道
命名管道
消息隊(duì)列
進(jìn)程創(chuàng)建與終止
信號量
進(jìn)程組,會話和作業(yè)控制
共享內(nèi)存
進(jìn)程間通信簡介
子進(jìn)程監(jiān)視
其他進(jìn)程
覆蓋進(jìn)程映像
進(jìn)程信息
進(jìn)程映像
內(nèi)存映射
相關(guān)系統(tǒng)調(diào)用(System V)
進(jìn)程資源
System V & Posix
信號
進(jìn)程間通信教程
管道

命名管道

管道是用于相關(guān)過程之間的通信。 我們是否可以使用管道進(jìn)行不相關(guān)的進(jìn)程通信,比方說,我們要從一個(gè)終端執(zhí)行客戶端程序,從另一個(gè)終端執(zhí)行服務(wù)器程序? 答案是否定的。那么怎樣才能實(shí)現(xiàn)不相關(guān)的進(jìn)程通信,簡單的答案就是使用 命名管道。 即使這適用于相關(guān)的進(jìn)程,但是使用命名管道進(jìn)行相關(guān)的進(jìn)程通信沒有任何意義。

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

命名管道的另一個(gè)名稱是FIFO(先進(jìn)先出)。 讓我們看看系統(tǒng)調(diào)用(mknod())來創(chuàng)建一個(gè)命名管道,這是一種特殊的文件。

#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);

這個(gè)系統(tǒng)調(diào)用將創(chuàng)建一個(gè)特殊的文件或文件系統(tǒng)節(jié)點(diǎn),如普通文件,設(shè)備文件或FIFO。 系統(tǒng)調(diào)用的參數(shù)是路徑名,模式和dev。 路徑名以及模式和設(shè)備信息的屬性。 路徑名是相對的,如果沒有指定目錄,它將在當(dāng)前目錄中創(chuàng)建。 指定的模式是指定文件類型的文件模式,如下表中所述的文件類型和文件模式。 開發(fā)領(lǐng)域是指定設(shè)備信息,如主要和次要設(shè)備號碼。

文件類型 描述 文件類型 描述
S_IFBLK 指定塊 S_IFREG 普通文件
S_IFCHR 指定字符 S_IFDIR 目錄
S_IFIFO 指定FIFO S_IFLNK 符號鏈接

文件模式

文件模式 描述 文件模式 描述
S_IRWXU 所有者讀,寫,執(zhí)行/搜索 S_IWGRP 寫入權(quán)限,組
S_IRUSR 讀取權(quán)限,所有者 S_IXGRP 執(zhí)行/搜索權(quán)限,組
S_IWUSR 寫入權(quán)限,所有者 S_IRWXO 讀,寫,執(zhí)行/由他人搜索
S_IXUSR 執(zhí)行/搜索權(quán)限,所有者 S_IROTH 讀取權(quán)限,其他
S_IRWXG 讀,寫,執(zhí)行/按組搜索 S_IWOTH 寫權(quán)限,其他
S_IRGRP 讀取權(quán)限,組 S_IXOTH 執(zhí)行/搜索權(quán)限,其他

文件模式也可以用八進(jìn)制表示法表示,如0XYZ,其中X表示所有者,Y表示組,Z表示其他。 X,YZ的取值范圍為0?7,讀,寫,執(zhí)行的取值分別為4,2,1。 如果需要結(jié)合讀取,寫入和執(zhí)行,則相應(yīng)地添加值。

如果使用的是0640,那么這意味著對所有者的讀寫(4 + 2 = 6),對于組讀取(4)和對其他人沒有權(quán)限(0)。

這個(gè)調(diào)用在成功時(shí)將返回0,在失敗的情況下為-1。 要知道失敗的原因,請檢查errno變量或perror()函數(shù)。

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

int mkfifo(const char *pathname, mode_t mode)

這個(gè)庫函數(shù)創(chuàng)建一個(gè)FIFO專用文件,用于命名管道。 這個(gè)函數(shù)的參數(shù)是文件名和模式。 文件名可以是絕對路徑或相對路徑。 如果未提供完整路徑名(或絕對路徑),則將在執(zhí)行過程的當(dāng)前文件夾中創(chuàng)建該文件。 文件模式信息如mknod()系統(tǒng)調(diào)用中所述。

這個(gè)調(diào)用在成功時(shí)將返回0,在失敗的情況下為-1。 要知道失敗的原因,請檢查errno變量或perror()函數(shù)。

讓我們考慮在一個(gè)終端上運(yùn)行服務(wù)器并在另一個(gè)終端上運(yùn)行客戶端的程序。 該方案只會進(jìn)行單向溝通。 客戶端接受用戶輸入并將消息發(fā)送到服務(wù)器,服務(wù)器在輸出上打印消息。 這個(gè)過程一直持續(xù)到用戶輸入字符串“end”。

讓我們通過一個(gè)例子來理解這一點(diǎn) -

第1步 - 創(chuàng)建兩個(gè)進(jìn)程,一個(gè)是fifoserver,另一個(gè)是fifoclient。
第2步 - 服務(wù)器進(jìn)程執(zhí)行以下操作 -

  • 創(chuàng)建一個(gè)名為“MYFIFO”的命名管道(使用系統(tǒng)調(diào)用mknod())。
  • 打開命名管道為只讀目的。
  • 在這里,創(chuàng)建了具有對所有者的讀寫權(quán)限的FIFO。讀取群組,沒有其他人的權(quán)限。
  • 等待客戶的消息。
  • 如果從客戶端收到的消息不是“end”,則打印該消息。 如果消息是“end”,則關(guān)閉fifo并結(jié)束該進(jìn)程。

第3步 - 客戶端進(jìn)程執(zhí)行以下操作 -

  • 打開命名管道僅用于寫入目的。
  • 接受來自用戶的字符串。
    • 檢查如果用戶輸入是否為"end"。如果字符串是“end”,則會關(guān)閉FIFO并結(jié)束進(jìn)程。
  • 無限重復(fù),直到用戶輸入字符串“end”。

現(xiàn)在我們來看看FIFO服務(wù)器文件的實(shí)現(xiàn) -

/* 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;
}

編譯和執(zhí)行步驟

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

現(xiàn)在,來看看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;
}

我們來看一下收到的輸出。編譯和執(zhí)行輸出結(jié)果如下 -

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

使用命名管道雙向通信

管道之間的通信意味著是單向的。 一般情況下,管道僅限于單向通信,至少需要兩根管道進(jìn)行雙向通信。 管道僅用于相關(guān)的進(jìn)程。 管道不能用于不相關(guān)的進(jìn)程通信,例如,如果想從一個(gè)終端執(zhí)行一個(gè)進(jìn)程,而從另一個(gè)終端執(zhí)行另一個(gè)進(jìn)程,那么管道是不可能的。 我們是否有任何簡單的方法在兩個(gè)進(jìn)程之間進(jìn)行通信,用簡單的方式實(shí)現(xiàn)不相關(guān)的進(jìn)程間的通信? 答案是肯定的。 命名管道是用于兩個(gè)或更多不相關(guān)進(jìn)程之間的通信,也可以是雙向通信。

我們已經(jīng)看到了命名管道之間的單向通信,即從客戶端到服務(wù)器的消息。 現(xiàn)在看看雙向通信,即客戶端向服務(wù)器發(fā)送消息,接收消息的服務(wù)器使用相同的命名管道向客戶端發(fā)送另一條消息。

以下是一個(gè)例子 -

第1步 - 創(chuàng)建兩個(gè)進(jìn)程,一個(gè)是fifoserver_twoway,另一個(gè)是fifoclient_twoway。
第2步 - 服務(wù)器進(jìn)程執(zhí)行以下操作 -

  • 如果未創(chuàng)建,則在/tmp目錄中創(chuàng)建名為“fifo_twoway”的命名管道(使用庫函數(shù)mkfifo())。
  • 為讀寫目的打開命名管道。
  • 在這里,創(chuàng)建了具有對所有者的讀寫權(quán)限的FIFO。讀取群組,沒有其他人的權(quán)限。
  • 等待來自客戶的消息。
  • 如果從客戶端收到的消息不是"end"字符串,則打印消息并反轉(zhuǎn)字符串。 反轉(zhuǎn)的字符串被發(fā)送回客戶端。 如果消息是"end",則關(guān)閉fifo并結(jié)束該過程。

第3步 - 客戶端進(jìn)程執(zhí)行以下操作 -

  • 為讀寫目的打開命名管道。
  • 接受來自用戶的字符串。
  • 檢查如果用戶輸入是否為"end"。它發(fā)送一條消息到服務(wù)器。 但是,如果字符串是"end",則會關(guān)閉FIFO并結(jié)束進(jìn)程。
  • 如果消息發(fā)送不是"end",則等待來自客戶端的消息(反向字符串)并打印反向字符串。
  • 無限重復(fù),直到用戶輸入字符串"end"。

現(xiàn)在,讓我們來看看FIFO服務(wù)器示例代碼。

/* 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;
}

編譯和執(zhí)行輸出結(jié)果 -

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

現(xiàn)在,我們來看看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;
}

編譯和執(zhí)行輸出結(jié)果 -

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

上一篇:內(nèi)存映射下一篇:管道