Linux inter-process communication-using anonymous Pipelines

Source: Internet
Author: User
Previously, we introduced a communication method between processes: Using signals, we create a notification event and send a response through it, but the transmitted information is only a signal value. Here we will introduce another way of inter-process communication-An anonymous pipeline through which more useful data can be exchanged between processes. 1. What is a pipeline if you have used Linux commands, you will not feel unfamiliar with the term "Pipeline", because we usually use pipelines through the symbol "|, but what is the real definition of management? A pipeline is a channel through which a process connects data streams to another process. It is usually used to connect the output of a process to the input of another process through a pipeline. For example, input the command LS-L | grep string in shell. We know that the LS command (actually a process) will list all the files in the current directory, however, it does not directly output data, but outputs the data originally to the screen to the grep process through the pipeline, as the input of the grep process, then, this process filters the input information and prints the string (in behavior units) with the string information on the screen. Ii. Use the popen function 1. the popen function and the pclose function introduce static, dynamic, and open functions. Similarly, the function corresponding to the popen function is the pclose function, their prototype is as follows:
#include <stdio.h>FILE* popen (const char *command, const char *open_mode);int pclose(FILE *stream_to_close);
The poen function allows a program to start another program as a new process and transmit data to it or receive data through it. Command is the program name to run and the corresponding parameter. Open_mode can only be one of "R (read-only)" and "W (write only. Note that the return value of the popen function is a pointer of the file type, while Linux regards everything as a file. That is to say, we can use the File Processing Function in the stdio I/O library to operate it. If open_mode is "r", the main caller can use the output of the called program. The file pointer returned by the function can read the output of the program through the stdio function (such as fread; if open_mode is "W", the main caller can send data to the called program, that is, write data to the called program through the stdio function (such as fwrite, the called program can read the data in its own standard input. The pclose function is used to close the associated file stream created by popen. Pclose is returned only after the process started by popen ends. If the called process is still running when pclose is called, The pclose call will wait until the process ends. It returns the exit code of the process where the closed file stream is located. 2. In many cases, we do not know the length of the output data. To avoid defining a very large array as a buffer, we can send data in blocks, read data from one block at a time and send data from one block until all data is sent. The following example shows how to read and send data in this way. The source file is named popen. C and the code is as follows:
# Include <unistd. h> # include <stdlib. h> # include <stdio. h> # include <string. h> int main () {file * read_fp = NULL; file * write_fp = NULL; char buffer [bufsiz + 1]; int chars_read = 0; // initialize the memset buffer (buffer, '\ 0', sizeof (buffer); // enable the LS and grep processes read_fp = popen ("ls-L", "R "); write_fp = popen ("grep rwxrwxr-X", "W"); // both processes are successfully enabled if (read_fp & write_fp) {// read a data block chars_read = fread (buffer, sizeof (char), bufsiz, read_fp); While (chars_read> 0) {buffer [chars_read] = '\ 0 '; // write data to the grep process fwrite (buffer, sizeof (char), chars_read, write_fp); // read data in a readable manner and read data cyclically, until all data is read chars_read = fread (buffer, sizeof (char), bufsiz, read_fp) ;}// close the file stream pclose (read_fp); pclose (write_fp ); exit (exit_success);} exit (exit_failure );}
The running result is as follows: from the running result, information filtering is achieved. The program reads data in process ls and sends the data to process grep for filtering. It is equivalent to directly entering the command in shell: ls-L | grep rwxrwxr-X. 3. popen implementation method and advantages and disadvantages when requesting popen to call and run a program, it first starts shell, that is, the sh command in the system, and then passes the command string as a parameter to it. This brings both advantages and disadvantages. Advantage: All parameter extensions in Linux are implemented by shell. Therefore, before starting a program (the command program in the Command), start the shell to analyze the command string, so that various shell extensions (such as wildcards) can be completed before the program starts, in this way, we can start very complex shell commands through popen. But its disadvantage is: For each popen call, not only need to start a Requested program, but also start a shell, that is, each popen call will start two processes, from the perspective of efficiency and resources, popen function calls are slower than normal calls. 3. Pipe call if popen is an advanced function, pipe is an underlying call. Unlike the popen function, it does not need to start a shell to explain the Request command to transmit data between two processes. It also provides more control over data reading and writing. The pipe function is prototype as follows:
#include <unistd.h>int pipe(int file_descriptor[2]);
We can see that the pipe function has a very special definition. This function returns 0 after two new file descriptors on the wall of the array. If-1 is returned, errno is set to indicate the cause of the failure. The two file descriptors in the array are connected in a special way. Based on the first-in-first-out principle, all data written to file_descriptor [1] can be read back from file_descriptor [0. Because the data is based on the first-in-first-out principle, the read data is consistent with the written data. Note: 1. From the prototype of the function, we can see that a major difference between the function and the popen function is that the popen function works based on file, pipe Works Based on file descriptors. Therefore, after using pipe, data must be read and sent using underlying read and write calls. 2. Do not use file_descriptor [0] to write data or use file_descriptor [1] to read data. The behavior is undefined, but in some systems,-1 may be returned, indicating that the call failed. Data can only be read from file_descriptor [0], and can only be written to file_descriptor [1. Example: First, we create an pipeline in the original process, call fork to create a new process, and then pass data between two processes through the pipeline. The source file is named pipe. C and the code is as follows:
# Include <unistd. h> # include <stdlib. h> # include <stdio. h> # include <string. h> int main () {int data_processed = 0; int filedes [2]; const char data [] = "Hello pipe! "; Char buffer [bufsiz + 1]; pid_t PID; // clear the buffer memset (buffer, '\ 0', sizeof (buffer); If (pipe (filedes) = 0) {// pipeline created successfully // call fork to create a subprocess pid = fork (); If (pid =-1) {fprintf (stderr, "Fork failure"); exit (exit_failure);} If (pid = 0) {// read data in a sub-process // data_processed = read (filedes [0], buffer, bufsiz); printf ("read % d Bytes: % s \ n", data_processed, buffer); exit (exit_success );} else {// In the parent process // write data data_processed = write (filedes [1], Data, strlen (data); printf ("wrote % d Bytes: % s \ n ", data_processed, data); // sleep for 2 seconds, mainly to wait until the child process ends first, this is just to make the output look good. // The parent process is not necessary. Wait until the child process ends sleep (2); exit (exit_success);} exit (exit_failure );}
The running result is: visible, the child process reads data written by the parent process to filedes [1]. If the parent process does not have a sleep statement, the parent process may end before the child process ends, in this way, you may see that there is a command prompt to separate the two inputs. 4. The following describes a more concise method for connecting two processes using pipelines as standard input and standard output. We can set the file descriptor to a known value, generally, the standard input is 0 or the standard output is 1. The biggest advantage of this is that you can call standard programs, that is, programs that do not need to take file descriptors as parameters. To do this, we also need the assistance of two functions: DUP function or dup2 function. Their prototype is as follows:
#include <unistd.h>int dup(int file_descriptor);int dup2(int file_descriptor_one, int file_descriptor_two);
The DUP call creates a new file descriptor and the existing file descriptor that acts as its parameter points to the same file or pipeline. For the DUP function, the new file description always takes the smallest available value. The new file descriptor created by dup2 is the same as int file_descriptor_two, or the first one is greater than the available value of this parameter. So when we first disable file descriptor 0 and call dup, the new file descriptor will be number 0. in the following example, first open the MPs queue, fork a sub-process, and then point the standard input to the read MPs queue in the sub-process, then close the Read and Write pipelines in the sub-process, leave only the standard input, and finally call the execlp function to start a new process OD, however, OD does not know whether its data source is a pipe or a terminal. The parent process is relatively simple. It first closes the read pipeline, writes data to the write pipeline, and then closes the write pipeline to complete its tasks. The source file is pipe2.c, and the code is as follows:
# Include <unistd. h> # include <stdlib. h> # include <stdio. h> # include <string. h> int main () {int data_processed = 0; int pipes [2]; const char data [] = "123"; pid_t PID; If (pipe (pipes) = 0) {pid = fork (); If (pid =-1) {fprintf (stderr, "Fork failure! \ N "); exit (exit_failure);} If (pid = 0) {// sub-process // point the standard input to Fildes [0] Close (0 ); DUP (pipes [0]); // close pipes [0] and pipes [1], with only the standard input close (pipes [0]) left. close (pipes [1]); // start the new process odexeclp ("OD", "OD", "-c", 0); exit (exit_failure );} else {// close pipes [0] because the parent process does not need to read data close (pipes [0]); data_processed = write (pipes [1], data, strlen (data); // After writing data, close pipes [1] Close (pipes [1]); printf ("% d-wrote % d bytes \ n ", getpid (), data_processed) ;}exit (exit_success );}
The running result is as follows: the running result shows that the OD process has completed its tasks correctly, which is the same as inputting OD-C and 123 directly in the shell. 5. The discussion on read operations after the MPs queue is closed now has the following problem: if the parent process writes data to the file_pipe [1] In the MPs queue, the child process reads data in the file_pipe [0] pipeline. When the parent process does not write data to file_pipe [1], the child process is not readable, what will happen to the child process? In addition, what happens to the child process when the parent process closes file_pipe [1? When the data writing pipeline is not closed and there is no data readable, the read call is usually blocked, but when the Data Writing pipeline is closed, the read call will return 0 instead of blocking. Note that, unlike reading an invalid file descriptor, reading an invalid file descriptor returns-1. 6. The defects of the anonymous pipeline I believe that everyone knows one of its shortcomings is the communication process. The relationship between them must be the relationship between the parent and child processes, this imposes some restrictions on its use, but we can use the named pipeline to solve this problem. The named pipe will be introduced in the next article: Linux inter-process communication-Using Named Pipes.

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.