Pipes
管道是两个或多个相关或相互关联的过程之间的通信媒介。 它可以在一个进程内,也可以在子进程和父进程之间进行通信。 通信也可以是多层次的,例如父母,孩子和孙子之间的通信等。通过写入管道的一个过程和管道的其他读取来实现通信。 要实现管道系统调用,请创建两个文件,一个用于写入文件,另一个用于从文件中读取。
管道机制可以通过实时场景来查看,例如用管道将水倒入某个容器,比如水桶,还有人用杯子取出它。 填充过程只不过是写入管道,读取过程只不过是从管道中取出。 这意味着一个输出(水)输入另一个(桶)。
#include<unistd.h>
int pipe(int pipedes[2]);
该系统调用将创建用于单向通信的管道,即,它创建两个描述符,第一个连接到管道读取而另一个连接以写入管道。
描述符pipedes [0]用于读取,pipedes [1]用于写入。 无论写入管道[1]是什么都可以从pipedes [0]中读取。
此调用在成功时返回零,在失败时返回-1。 要了解失败的原因,请使用errno variable或perror()函数进行检查。
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
即使文件的基本操作是读写,也必须在执行操作之前打开文件,并在完成所需操作后关闭文件。 通常,默认情况下,为每个进程打开3个描述符,分别用于输入(标准输入 - 标准输入),输出(标准输出 - 标准输出)和错误(标准错误 - 标准错误),文件描述符分别为0,1和2。
该系统调用将返回用于读/写/查找(lseek)的进一步文件操作的文件描述符。 通常,文件描述符从3开始,并随着打开的文件数增加一个数字。
传递给开放系统调用的参数是路径名(相对路径或绝对路径),提示打开文件的目的(例如,打开读取,O_RDONLY,写入,O_WRONLY,读取和写入,O_RDWR,附加到现有文件) O_APPEND,创建文件,如果不存在,则使用O_CREAT等)以及为用户或所有者/组/其他人提供读/写/执行权限的所需模式。 可以用符号来提及模式。
读 - 4,写 - 2和执行 - 1。
例如:Octal值(以0开头),0764表示所有者具有读取,写入和执行权限,组具有读写权限,其他具有读取权限。 这也可以表示为S_IRWXU | S_IRGRP | S_IWGRP | S_IROTH,暗示或操作0700 | 0040 | 0020 | 0004→0764。
成功时,此系统调用将返回新文件描述符id和-1,以防出现错误。 可以使用errno variable或perror()函数识别错误原因。
#include<unistd.h>
int close(int fd)
上面的系统调用已经打开了文件描述符。 这意味着文件不再使用,并且任何其他进程都可以重用关联的资源。 此系统调用在成功时返回零,在出现错误时返回-1。 可以使用errno variable或perror()函数识别错误原因。
#include<unistd.h>
ssize_t read(int fd, void *buf, size_t count)
上面的系统调用是从指定文件中读取文件描述符fd的参数,具有已分配内存的适当缓冲区(静态或动态)和缓冲区的大小。
文件描述符id用于标识相应的文件,该文件在调用open()或pipe()系统调用后返回。 在从文件读取之前需要打开该文件。 它会在调用pipe()系统调用时自动打开。
此调用将返回成功时读取的字节数(如果遇到文件末尾则为零),如果失败则返回-1。 返回字节可以小于请求的字节数,以防万一没有数据或文件关闭。 如果发生故障,则设置正确的错误编号。
要了解失败的原因,请使用errno variable或perror()函数进行检查。
#include<unistd.h>
ssize_t write(int fd, void *buf, size_t count)
上面的系统调用是使用文件描述符fd的参数,具有已分配内存(静态或动态)的适当缓冲区和缓冲区大小来写入指定文件。
文件描述符id用于标识相应的文件,该文件在调用open()或pipe()系统调用后返回。
在写入文件之前需要打开该文件。 它会在调用pipe()系统调用时自动打开。
此调用将返回成功时写入的字节数(如果没有写入则为零),如果失败则返回-1。 如果发生故障,则设置正确的错误编号。
要了解失败的原因,请使用errno variable或perror()函数进行检查。
示例程序
以下是一些示例程序。
Example program 1 - 使用管道写入和读取两条消息的程序。
算法 (Algorithm)
Step 1 - 创建管道。
Step 2 - 向管道发送消息。
Step 3 - 从管道中检索消息并将其写入标准输出。
Step 4 - 向管道发送另一条消息。
Step 5 - 从管道中检索消息并将其写入标准输出。
Note - 在发送所有消息后也可以检索消息。
Source Code: simplepipe.c
#include<stdio.h>
#include<unistd.h>
int main() {
int pipefds[2];
int returnstatus;
char writemessages[2][20]={"Hi", "Hello"};
char readmessage[20];
returnstatus = pipe(pipefds);
if (returnstatus == -1) {
printf("Unable to create pipe\n");
return 1;
}
printf("Writing to pipe - Message 1 is %s\n", writemessages[0]);
write(pipefds[1], writemessages[0], sizeof(writemessages[0]));
read(pipefds[0], readmessage, sizeof(readmessage));
printf("Reading from pipe – Message 1 is %s\n", readmessage);
printf("Writing to pipe - Message 2 is %s\n", writemessages[0]);
write(pipefds[1], writemessages[1], sizeof(writemessages[0]));
read(pipefds[0], readmessage, sizeof(readmessage));
printf("Reading from pipe – Message 2 is %s\n", readmessage);
return 0;
}
Note - 理想情况下,需要为每个系统调用检查返回状态。 为了简化过程,不对所有呼叫进行检查。
执行步骤 (Execution Steps)
编译 (Compilation)
gcc -o simplepipe simplepipe.c
Execution/Output
Writing to pipe - Message 1 is Hi
Reading from pipe – Message 1 is Hi
Writing to pipe - Message 2 is Hi
Reading from pipe – Message 2 is Hell
Example program 2 - 使用父进程和子进程通过管道写入和读取两条消息的程序。
算法 (Algorithm)
Step 1 - 创建管道。
Step 2 - 创建子进程。
Step 3 - 父进程写入管道。
Step 4 - 子进程从管道中检索消息并将其写入标准输出。
Step 5 - 再次重复步骤3和步骤4。
Source Code: pipewithprocesses.c
#include<stdio.h>
#include<unistd.h>
int main() {
int pipefds[2];
int returnstatus;
int pid;
char writemessages[2][20]={"Hi", "Hello"};
char readmessage[20];
returnstatus = pipe(pipefds);
if (returnstatus == -1) {
printf("Unable to create pipe\n");
return 1;
}
pid = fork();
// Child process
if (pid == 0) {
read(pipefds[0], readmessage, sizeof(readmessage));
printf("Child Process - Reading from pipe – Message 1 is %s\n", readmessage);
read(pipefds[0], readmessage, sizeof(readmessage));
printf("Child Process - Reading from pipe – Message 2 is %s\n", readmessage);
} else { //Parent process
printf("Parent Process - Writing to pipe - Message 1 is %s\n", writemessages[0]);
write(pipefds[1], writemessages[0], sizeof(writemessages[0]));
printf("Parent Process - Writing to pipe - Message 2 is %s\n", writemessages[1]);
write(pipefds[1], writemessages[1], sizeof(writemessages[1]));
}
return 0;
}
执行步骤 (Execution Steps)
Compilation
gcc pipewithprocesses.c –o pipewithprocesses
Execution
Parent Process - Writing to pipe - Message 1 is Hi
Parent Process - Writing to pipe - Message 2 is Hello
Child Process - Reading from pipe – Message 1 is Hi
Child Process - Reading from pipe – Message 2 is Hello
使用管道进行双向通信
管道通信被视为仅单向通信,即父进程写入和子进程读取,反之亦然,但不是两者。 但是,如果父节点和子节点都需要同时写入和读取管道,解决方案是使用管道进行双向通信。 需要两根管道来建立双向通信。
以下是实现双向沟通的步骤 -
Step 1 - 创建两个管道。 第一个是父母写和孩子读,比如说pipe1。 第二个是孩子写和父母读,比如说pipe2。
Step 2 - 创建子进程。
Step 3 - 关闭不需要的结束,因为每次通信只需要一端。
Step 4 - 在父进程中关闭不需要的结尾,读取pipe1的结尾并写入pipe2的结尾。
Step 5 - 关闭子进程中不需要的结尾,写入pipe1的结尾并读取pipe2的结尾。
Step 6 - 根据需要执行通信。
示例程序
Sample program 1 - 使用管道实现双向通信。
算法 (Algorithm)
Step 1 - 为要写入的父进程和要读取的子进程创建pipe1。
Step 2 - 为要写入的子进程和要读取的父进程创建pipe2。
Step 3 - 从父级和子级侧关闭管道的不需要的末端。
Step 4 - 父进程编写消息和子进程以在屏幕上阅读和显示。
Step 5 - 子进程写入消息和父进程以在屏幕上阅读和显示。
Source Code: twowayspipe.c
#include<stdio.h>
#include<unistd.h>
int main() {
int pipefds1[2], pipefds2[2];
int returnstatus1, returnstatus2;
int pid;
char pipe1writemessage[20] = "Hi";
char pipe2writemessage[20] = "Hello";
char readmessage[20];
returnstatus1 = pipe(pipefds1);
if (returnstatus1 == -1) {
printf("Unable to create pipe 1 \n");
return 1;
}
returnstatus2 = pipe(pipefds2);
if (returnstatus2 == -1) {
printf("Unable to create pipe 2 \n");
return 1;
}
pid = fork();
if (pid != 0) // Parent process {
close(pipefds1[0]); // Close the unwanted pipe1 read side
close(pipefds2[1]); // Close the unwanted pipe2 write side
printf("In Parent: Writing to pipe 1 – Message is %s\n", pipe1writemessage);
write(pipefds1[1], pipe1writemessage, sizeof(pipe1writemessage));
read(pipefds2[0], readmessage, sizeof(readmessage));
printf("In Parent: Reading from pipe 2 – Message is %s\n", readmessage);
} else { //child process
close(pipefds1[1]); // Close the unwanted pipe1 write side
close(pipefds2[0]); // Close the unwanted pipe2 read side
read(pipefds1[0], readmessage, sizeof(readmessage));
printf("In Child: Reading from pipe 1 – Message is %s\n", readmessage);
printf("In Child: Writing to pipe 2 – Message is %s\n", pipe2writemessage);
write(pipefds2[1], pipe2writemessage, sizeof(pipe2writemessage));
}
return 0;
}
执行步骤 (Execution Steps)
编译 (Compilation)
gcc twowayspipe.c –o twowayspipe
Execution
In Parent: Writing to pipe 1 – Message is Hi
In Child: Reading from pipe 1 – Message is Hi
In Child: Writing to pipe 2 – Message is Hello
In Parent: Reading from pipe 2 – Message is Hello