进程间通信 - 管道

管道是两个或多个相关或相互关联的流程之间的沟通媒介.它可以在一个进程内,也可以在子进程和父进程之间进行通信.通信也可以是多层次的,例如父母,孩子和孙子之间的通信等.通过写入管道的一个过程和管道的其他读取来实现通信.要实现管道系统调用,请创建两个文件,一个用于写入文件,另一个用于从文件中读取.

可以使用填充等实时方案查看管道机制用管子把水塞进一个容器里,比如说一个水桶,然后有人拿回来用杯子说.填充过程只不过是写入管道,读取过程只不过是从管道中取出.这意味着一个输出(水)输入另一个(桶).

管道与一个

 
 #include< unistd.h> 
 int pipe(int pipedes [2]);

这个系统调用会创建一个单向通信的管道,即它创建两个描述符,第一个连接到管道读取,另一个是连接写入管道.

Descriptor pipedes [0]用于读取,pipedes [1]用于写入.无论写入pipedes [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./p>

此系统调用将返回用于读/写/搜索(lseek)的进一步文件操作的文件描述符.通常文件描述符从3开始,并随着文件数量的增加而增加一个数字.

传递给开放系统调用的参数是路径名(相对路径或绝对路径),标志提到目的打开文件(例如,打开读取,O_RDONLY,写入,O_WRONLY,读取和写入,O_RDWR,附加到现有文件O_APPEND,创建文件,如果不存在O_CREAT等)和提供权限的所需模式用户或所有者/组/其他人的读/写/执行.模式可以用符号来表示.

读 -  4,写 -  2和执行 -  1.

例如:八进制值(以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()函数进行检查.

示例程序

以下是一些示例程序.

示例程序1 : 使用管道编写和读取两条消息的程序.

算法

步骤1 : 创建一个管道.

第2步 : 发送消息到管道.

第3步 : 从管道中检索消息并将其写入标准输出.

步骤4 : 发送另一条消息到管道.

第5步 : 从管道中检索消息并将其写入标准输出.

注意 : 在发送所有消息后也可以检索消息.

源代码: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;
}

注意 : 理想情况下,需要为每个系统调用检查返回状态.为了简化流程,不对所有调用进行检查.

执行步骤

编译

 
 gcc -o simplepipe simplepipe.c

执行/输出

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

示例程序2 : 使用父进程和子进程通过管道写入和读取两条消息的程序.

算法

步骤1  : 去;创建一个管道.

第2步 : 创建子进程.

步骤3 : 父进程写入管道.

步骤4 : 子进程从管道中检索消息并将其写入标准输出.

步骤5 : 再次重复步骤3和步骤4.

源代码: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;
}

执行步骤

编译

 
 gcc pipewithprocesses.c -o pipewithprocesses

执行

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

使用管道的双向通信

管道通信被视为仅单向通信,即父进程写入和子进程读取,反之亦然,但不是两者.但是,如果父节点和子节点需要同时写入和读取管道,解决方案是使用管道进行双向通信.需要两个管道来建立双向通信.

以下是实现双向通信的步骤 :

步骤1 : 创建两个管道.第一个是父母写和孩子读,比如说pipe1.第二个是孩子写和父母读,比如说pipe2.

第2步 : 创建子进程.

步骤3 : 关闭不需要的结尾,因为每次通信只需要一端.

步骤4 : 在父进程中关闭不需要的结尾,读取pipe1的结尾并写入pipe2的结尾.

步骤5 : 关闭子进程中不需要的结尾,写入pipe1的结尾并读取pipe2的结尾.

步骤6 : 根据需要执行通信.

管道有两个

示例程序

示例程序1 : 使用管道实现双向通信.

算法

步骤1 : 为要写入的父进程和要读取的子进程创建pipe1.

步骤2 : 为要写入的子进程和要读取的父进程创建pipe2.

步骤3 : 从父母和子女一侧关闭不需要的管道末端.

步骤4 : 父进程编写消息和子进程以在屏幕上阅读和显示.

步骤5 : 子进程写入消息和父进程以在屏幕上阅读和显示.

源代码: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;
}

执行步骤

编译

 
 gcc twowayspipe.c -o twowayspipe

执行

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