interprocess communication (4)-piping (pipe)

Source: Internet
Author: User
Tags syslog

1. PrefaceAll examples of this post are based on the RHEL6.5 platform. This article covers only pipelines (anonymous pipes/normal pipelines), and named Pipes are described in subsequent articles.

2. Piping characteristics

The pipeline is one of the original UNIX IPC forms supported by Linux and has the following features:
* * The pipe is half-duplex, the data can only flow in one direction, the other end input, the other end output. There are two pipelines that need to be built when both parties need to communicate.
* * Pipelines are divided into ordinary pipes and named pipes. Normal pipes are located in memory and can only be used between parent-child processes or sibling processes (affinity processes). Named Pipes are located in the file system, and there is no affinity between processes as long as the pipe name is known to communicate.
* * Pipeline is also a file. The pipe size is 4096 bytes.
* * A separate file system: A pipeline is a file for the process at both ends of the pipe, but it is not an ordinary file, it is not a file system, it is a separate file system, and only exists in memory.
* * Read and Write data: a process that writes to the pipeline is read out by the process at the other end of the pipeline. The content that is written is added to the end of the pipe buffer every time, and the data is read from the head of the buffer each time.
* * When the pipe is full, write block, the pipe empty, read block.

Pipelines can only be used between processes that are genetically related. This is because the pipeline does not have a name, so it cannot be used across the process's address space. This sentence here is not absolute, because it is technically possible to pass a pipeline descriptor between processes, so that communication between unrelated processes can be implemented through pipelines. However, pipelines are often used for inter-process communication with common ancestors.

3. Build Piping Pipe

#include <unistd.h>
int pipe (int filedest[2])//successfully returned 0, failed to return-1

The pipe function is used to create a pipeline, and FD is an outgoing parameter that holds the returned two file descriptors, which are used to identify both ends of the pipeline, Fd[0] can only be used for reading, and fd[1] can only be used for writing.
So if we go to Fd[0] what is the result of writing the data in the end? ?
Here is the test code:

#include <iostream>  #include <cstring>  #include <unistd.h>  #include <errno.h> int main ()  {      int fd[2];        if (pipe (FD) < 0)      {          std::cout<< "create pipe failed." <<std::endl;        return-1;      }        char *temp = "Hello World";        if (write (fd[0], temp, strlen (temp) + 1) < 0)      {          std::cout<< "write pipe failed:" <<strerror ( errno) <<std::endl;      }      return 0;  }
Output Result:
Write pipe Failed:bad file descriptor

as you can see from this result, the kernel Fd[0] The descriptor opens in a read-only manner. Fd[1] is opened in write-only mode, so pipelines can only guarantee one-way data communication.

Shows the data flow for an in-process pipeline:


From we can see the pipeline in the kernel, the process through two file descriptors for data transmission, of course, a single in-process pipeline is not necessary, just to more vividly indicate how the pipeline works, the general pipeline is used in a way that is: the parent process creates a pipeline, and then fork produces a child process, Because a child process has a copy of the parent process, the parent-child process can communicate through the pipeline process. This usage is shown in the following way:


As shown, when a parent process creates a child process through fork, the parent-child process has a file descriptor for the pipeline operation, at which time the parent-child process closes the corresponding read-write side, which forms a one-way pipeline between the parent and child processes. Which side to close depends on the specific data flow.

4. Parent-child process one-way communication

It says that when the parent process creates a child process by fork, the parent-child process can communicate through the pipeline, and the direction of the data flow is determined by the specific application. We all know that in the shell, the data flow of a pipe flows from the parent process to the child process, that is, the parent process closes the read end, and the child process closes the write end. As shown in the following:


The test code is as follows:

#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys    /wait.h>int Main (int argc, char *argv[]) {int pfd[2];//Save two file descriptor after opening pipeline pid_t cpid;//save child process identifier char buf;        if (argc! = 2)//Determines whether the command-line arguments conform to {fprintf (stderr, "Usage:%s <string>\n", argv[0]);    Exit (0);        if (pipe (PFD) = =-1)//build Pipeline {perror ("pipe");    Exit (Exit_failure);    } cpid = fork ();        if (cpid = =-1) {perror ("fork");    Exit (Exit_failure);          } if (cpid = = 0)//Sub-process {close (pfd[1]); Close pipe Write, reference count-1 while (read (pfd[0], &buf, 1) > 0)//read data from Pipe loop write (Stdout_fileno, &AMP;BUF, 1)  ;      Output read Data write (Stdout_fileno, "\ n", 1);         Outputs the data read from the pipe close (pfd[0]);    Close pipe Read, reference count-1 exit (0); } else <span style= "font-family: the song Body;"        >//Parent Process </span> {close (pfd[0]); Close pipe Read write (pfd[1], argv[1], strlen (aRGV[1]);//write to the pipe command line parameter 1 close (pfd[1]);           Wait (NULL);    Wait for the child process exit exit (0); }}

Description: Each time a fork is called, the process descriptor is closed
Execute this line command: #./a.out www
Program output: WWW

The code flow above is that the child process waits for the parent process to send the data through the pipeline, and then outputs the received data, and the read in the code blocks until there is data in the pipeline.

5. Parent-Child process bidirectional communication

As we know, a pipeline can only support one-way communication between kinship processes, i.e. half-duplex communication. If you want to support two-way communication through a pipeline, then you need to create two pipelines, fd1,fd2; close fd1[0],fd2[1] in the parent process, and close fd1[1],fd2[0 in the child process]. This communication pattern is as follows:


Here is the test code for two-way communication:

 #include <iostream> #include <unistd.h> int main () {int fd1[2], fd2[2]        ; if (pipe (FD1) < 0 | | pipe (FD2) < 0) {std::cout<< "Create pipe failed."          <<std::endl;      return-1;      } Char buf[256];        char *temp = "Hello World";          if (fork () = = 0) {close (fd1[1]);            Close (fd2[0]);          Read (Fd1[0], buf, sizeof (BUF));            std::cout<< "child:receive Message from Pipe 1:" <<buf<<std::endl;          Write (fd2[1], temp, strlen (temp) + 1);      Exit (0);      } close (fd1[0]);        Close (fd2[1]);      Write (fd1[1], temp, strlen (temp) + 1);      Read (Fd2[0], buf, sizeof (BUF));        std::cout<< "parent:receive Message from Pipe 2:" <<buf<<std::endl;  return 0; }

The result of the code execution is as follows:
Child:receive Message from pipe 1:hello world
Parent:receive Message from pipe 2:hello world

Where the process of code is that the parent process creates two pipelines, which, in Fd1,fd2, fd1 the parent process to send data to the child process, fd2 the child process to send the data to the parent process. After the process starts, the child process waits for the parent process to send the data through the pipeline Fd1, when the child process receives the data from the parent process, outputs the message, and FD2 the parent process back through the pipeline, and then the child process exits, and the parent process receives the response from the child process and exits the message.

As mentioned earlier, the read and write rules of the pipeline will be described later in this article, which will block the data in the pipeline.

6.popen and pclose functions

As an example of a pipeline, the Popen function is provided by the standard I/O function library, which creates a pipeline and fork a child process, which closes the corresponding end of the pipeline according to the parameters passed in by Popen, then executes the incoming shell command and waits for termination.

A pipeline is formed between the calling process and the child process of the fork. The pipeline communication between the calling process and the child processes executing the shell command is implemented indirectly through the file* returned by Popen, which invokes the process through standard file I/O to write to or read the pipeline.

#include <stdio.h>
FILE *popen (const char *command, const char *type); The standard file I/O pointer was successfully returned and failed to return null
Command: The incoming parameter is a shell command line, which is handled by the shell.
Type: This parameter determines the processing of the command to be executed by the calling process, type has the following two cases:
Type = "R", the calling process will read the command execution standard output, the standard output through the return of the file* to operate;
Type = "W", the calling process will write the standard input during the execution of the command;

int Pclose (FILE *stream); Successfully returns the Shell's terminating state, failed return-1
The Pclose function closes the standard I/O stream created by Popen, waits for the command to terminate, and then returns the execution state of the shell.

Here is the test code for Popen 1:

#include <iostream>  #include <cstdio>  #include <unistd.h>    using namespace std;    int main ()  {      char *cmd = "ls/usr/include/sys*.h";        FILE *p = popen (cmd, "R");      Char buf[256];        while (Fgets (BUF, p)! = NULL)      {          cout<<buf;      }          Pclose (p);        return 0;  }
output:
/usr/include/syscall.h/usr/include/sysexits.h/usr/include/syslog.h

The execution flow of the program is as follows: When the calling process executes Popen, a pipeline is created, and then fork generates a child process that executes the Popen incoming "ls/usr/local/bin" shell command, which passes the execution result through the pipeline to the calling process. The calling process reads the data in the pipeline through standard file I/O and outputs the display.

Test Code 2:

#include <stdlib.h> #include <stdio.h> #include <string.h> #include <unistd.h>int main (void) {   file *stream;//stream    char   buf[1024];//read/write buffer    memset (buf, ' + ', sizeof (BUF));//empty    stream = Popen ("Ls/usr/include/sys*.h", "w");    for (;;)    {        memset (buf,0x00,sizeof (BUF));        scanf ("%s", buf);//Accept input if        (strcmp (buf, "k") = = 0)//if K exits        {break            ;        }        fprintf (Stream, "%s\n", buf);//write    }    pclose (stream);//close    return 0;}
output:
[Email protected] csdnblog]#./a.out
/usr/include/syscall.h/usr/include/sysexits.h/usr/include/syslog.h
7. Pipe reading and writing rules

The two ends of the pipe can be described by the description character Fd[0] and fd[1], and it is important to note that the ends of the pipe are fixed on the task. That is, one end can only be used for reading, represented by the description word fd[0], which is called the pipe reading end, and the other end can only be used for writing, by the description word fd[1] to be said to be the pipe write end. If you attempt to read data from the pipe write end, or write data to the pipe read end, it will cause an error to occur. I/O functions for general files can be used for pipelines such as close, read, write, and so on.

To read data from the pipeline:
--If the write end of the pipeline does not exist, it is considered to have read the end of the data, and the Read function returns the number of read bytes0;
--If the number of bytes requested is greater than when the write end of the pipe is presentPipe_buf, the number of existing data bytes in the pipeline is returned, if the requested number of bytes is not greater thanPipe_buf, the number of existing data bytes in the pipeline is returned (at this point the amount of data in the pipeline is less than the requested amount of data), or the number of bytes requested is returned (at this point, the amount of data in the pipeline is not less than the requested amount of data). Note: (Pipe_bufin theInclude/linux/limits.h, different kernel versions may vary. posix.1RequirementsPipe_bufat least for +bytes,Red Hat 7.2in the4096).

To write data to the pipeline:
--When writing data to the pipeline, Linux there will be no guarantee of the atomicity of the writes, and the write process will attempt to write data to the pipeline if there is an idle area in the pipeline buffer. If the read process does not read the data in the pipeline buffer, the write operation will be blocked.

Note: Writing data to a pipeline is only meaningful if the read side of the pipeline exists. Otherwise, the process that writes data to the pipeline receives the sifpipe signal from the kernel, which the application can process or ignore (the default action is the application termination).

Validation of write rules for pipelines 1: write-side dependence on read-side existence

#include <unistd.h> #include <sys/types.h>int main () {int pipe_fd[2];p id_t pid;char r_buf[4];char* w_buf; int Writenum;int cmd;memset (r_buf, 0, sizeof (R_BUF)), if (pipe (PIPE_FD) <0) {printf ("Pipe Create error\n"); return-1;} if (PID = fork ()) = = 0) {close (pipe_fd[0]), close (pipe_fd[1]), sleep (); exit ();} else if (pid>0) {sleep (1);  Wait for the child process to finish closing the read operation Close (pipe_fd[0]);//writew_buf = "111"; if ((Writenum = Write (pipe_fd[1], W_BUF, 4)) = =-1) printf ("Write To pipe error\n "); elseprintf (" The bytes write to pipe is%d \ n ", writenum); Close (pipe_fd[1]);} return 0;}

Output:
Broken pipe

validation of the write rules for pipelines 2 : Linux does not guarantee atomic validation of the write pipeline

 #include <unistd.h> #include <sys/types.h> #include <errno.h>int main (int argc, CHAR**ARGV) {int pipe_fd[2];p id_t pid;char r_buf[4096];char w_buf[4096 * 2];int writenum;int rnum;memset (r_buf, 0, sizeof (R_BUF)); if (Pipe (PIPE_FD) <0) {printf ("Pipe Create error\n"); return-1;}  if (PID = fork ()) = = 0) {close (pipe_fd[1]), while (1) {sleep (1); rnum = Read (Pipe_fd[0], r_buf, +);p rintf ("Child:readnum Is%d\n ", rnum);} Close (pipe_fd[0]); exit ();} else if (pid>0) {close (pipe_fd[0]),//writememset (r_buf, 0, sizeof (R_BUF)), if (Writenum = Write (pipe_fd[1], w_buf, ) = =-1) printf ("Write to Pipe error\n"), elseprintf ("The bytes write to pipe is%d \ n", writenum); writenum = Write (pip E_fd[1], w_buf, 4096); Close (pipe_fd[1]);} return 0;} 
output:
[[email protected] csdnblog]#./a.out
The bytes Write to Pipe is a child:readnum
is $
Child:readnum is   //Note that this line of output illustrates the non-atomic nature of the write
Child:readnu M is-Child:readnum is Child:readnum-Child:readnum is    //Note that this line of output illustrates the non-atomicity of the write
child:readnum is 0
child:readnum are 0
child:readnum is 0
child:readnum are 0
child:readnum is 0
... ...

Conclusion:
number of writes less than 4096 when writing non-atomic!
If you change the number of two write bytes in the parent process to the , it is easy to draw the following conclusions:
The amount of data written to the pipeline is greater than 4096 bytes, the free space of the buffer is written to the data until all the data has been written, and if no process reads the data, it is blocked.

8.dup

#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys /wait.h>int Main () {int pfds[2];if (pipe (PFDS) = = 0) {    if (fork () = = 0)//Sub-process    {        close (1);//Turn off standard output        dup2 (Pfds[1], 1);//The Write file descriptor of the pipeline is copied to the output of the process        close (pfds[0]);//shutdown pipe read        execlp ("ls", "ls", "-l", NULL);//execute ls-l output write pipeline    }    Else    {        close (0);        Dup2 (Pfds[0], 0);//pipe Read file descriptor copied to process input        close (pfds[1]);        EXECLP ("WC", "WC", "-l", NULL),//execute wc-l to read the data as input to the WC command     }}return 0;}
[email protected] csdnblog]# ls-a
.  .. A.out pipe.c

After executing the a.out, the output is as follows:
[Email protected] csdnblog]# a.out
3

Where the Linux EXECLP function is equivalent to executing
# Ls-l | Wc-l
Count the number of files in the current directory

interprocess communication (4)-piping (pipe)

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.