📅  最后修改于: 2021-01-18 06:39:37             🧑  作者: Mango
管道用于在相关过程之间进行通信。我们可以使用管道进行不相关的过程通信吗?例如,我们要从一个终端执行客户端程序,而从另一个终端执行服务器程序?答案是否定的。那么,如何实现不相关的流程通信,简单的答案就是命名管道。即使这适用于相关过程,也没有任何意义使用命名管道进行相关过程通信。
我们使用一根管道进行单向通信,而使用两条管道进行双向通信。同样的条件适用于命名管道吗?答案是否定的,因为命名管道支持双向通信,所以我们可以使用可用于双向通信(服务器和客户端之间的通信,以及客户端和服务器之间的通信)的单个命名管道。
命名管道的另一个名称是FIFO(先进先出) 。让我们看看系统调用(mknod())创建一个命名管道,这是一种特殊文件。
#include
#include
#include
#include
int mknod(const char *pathname, mode_t mode, dev_t dev);
该系统调用将创建一个特殊的文件或文件系统节点,例如普通文件,设备文件或FIFO。系统调用的参数是路径名,模式和dev。路径名以及模式和设备信息的属性。路径名是相对的,如果未指定目录,它将在当前目录中创建。指定的模式是文件模式,它指定文件类型,例如文件类型和下表中提到的文件模式。 dev字段用于指定设备信息,例如主要和次要设备编号。
File Type | Description | File Type | Description |
---|---|---|---|
S_IFBLK | block special | S_IFREG | Regular file |
S_IFCHR | character special | S_IFDIR | Directory |
S_IFIFO | FIFO special | S_IFLNK | Symbolic Link |
File Mode | Description | File Mode | Description |
---|---|---|---|
S_IRWXU | Read, write, execute/search by owner | S_IWGRP | Write permission, group |
S_IRUSR | Read permission, owner | S_IXGRP | Execute/search permission, group |
S_IWUSR | Write permission, owner | S_IRWXO | Read, write, execute/search by others |
S_IXUSR | Execute/search permission, owner | S_IROTH | Read permission, others |
S_IRWXG | Read, write, execute/search by group | S_IWOTH | Write permission, others |
S_IRGRP | Read permission, group | S_IXOTH | Execute/search permission, others |
文件模式也可以用八进制表示法表示,例如0XYZ,其中X表示所有者,Y表示组,Z表示其他。 X,Y或Z的值范围可以从0到7。读,写和执行的值分别为4、2、1。如果需要结合读,写和执行,则相应地添加值。
假设,如果我们提到0640,则意味着对所有者进行读写(4 + 2 = 6),对组进行读取(4),对其他用户则没有权限(0)。
成功时此调用将返回零,失败时将返回-1。要了解失败的原因,请使用errno变量或perror()函数。
#include
#include
int mkfifo(const char *pathname, mode_t mode)
该库函数创建一个FIFO特殊文件,该文件用于命名管道。该函数的参数是文件名和模式。文件名可以是绝对路径,也可以是相对路径。如果未提供完整路径名(或绝对路径),则将在执行进程的当前文件夹中创建文件。文件模式信息如mknod()系统调用中所述。
成功时此调用将返回零,失败时将返回-1。要了解失败的原因,请使用errno变量或perror()函数。
让我们考虑一个程序,该程序在一个终端上运行服务器,而在另一终端上运行客户端。该程序将仅执行单向通信。客户端接受用户输入并将消息发送到服务器,服务器将消息打印在输出上。继续该过程,直到用户输入字符串“ end”为止。
让我们通过一个例子来理解这一点-
步骤1-创建两个进程,一个是fifoserver,另一个是fifoclient。
步骤2-服务器进程执行以下操作-
如果未创建,则使用名称“ MYFIFO”创建一个命名管道(使用系统调用mknod())。
打开命名管道仅供只读。
在这里,创建了具有所有者读写权限的FIFO。阅读“组”信息,没有“其他”权限。
无限等待来自客户端的消息。
如果从客户端收到的消息不是“结束”,则打印该消息。如果消息是“结束”,则关闭FIFO并结束该过程。
步骤3-客户端进程执行以下操作-
打开命名管道仅用于写目的。
接受来自用户的字符串。
检查用户输入的是“结束”还是“结束”以外的内容。无论哪种方式,它都会向服务器发送一条消息。但是,如果字符串为“ end”,则这将关闭FIFO并结束该过程。
无限重复,直到用户输入字符串“ end”。
现在让我们看一下FIFO服务器文件。
/* Filename: fifoserver.c */
#include
#include
#include
#include
#include
#include
#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
#include
#include
#include
#include
#include
#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”,则打印该消息并反转字符串。反向的字符串将发送回客户端。如果消息是“结束”,则关闭FIFO并结束该过程。
步骤3-客户端进程执行以下操作-
打开命名管道以进行读写。
接受来自用户的字符串。
检查用户输入的是“结束”还是“结束”以外的内容。无论哪种方式,它都会向服务器发送一条消息。但是,如果字符串为“ end”,则这将关闭FIFO并结束该过程。
如果消息不是作为“ end”发送的,则它等待来自客户端的消息(反向字符串)并打印反向字符串。
无限重复,直到用户输入字符串“ end”。
现在,让我们看一下FIFO服务器示例代码。
/* Filename: fifoserver_twoway.c */
#include
#include
#include
#include
#include
#include
#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
#include
#include
#include
#include
#include
#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