In the preceding article, we describe an inter-process communication method: Using a signal, we create a notification event, and it causes a response, but the information passed is just a signal value. Here's another way to communicate between processes-anonymous pipelines, through which more useful data can be exchanged between processes.
first, what is the pipeline
If you have used Linux commands, you will not feel unfamiliar with the term pipe, because we usually use the symbol "|" To use pipelines, but what is the real definition of management? A pipeline is a process that connects a stream of data to another process, and it is typically used as input to connect the output of one process to another process through a pipeline.
For example, enter a command in the shell: ls-l | grep string, we know that the LS command (which is actually a process) lists the files in the current directory, but it does not output directly, but instead exports the data that would otherwise be output to the screen to the grep process as input to the grep process, The process then filters the input information and prints the string (in the behavior unit) of the information that exists in the string to the screen.
second, using the Popen () function
1, Popen () function and Pclose () function Introduction
There is static, there is the motion, and the same, 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 can pass data to it or receive data through it. command is the name of the program to run and the corresponding parameter. Open_mode can only be one of the "R (read only)" and "W (write only)". Note that the return value of the Popen () function is a pointer to the file type, and Linux treats everything as files, which means that we can manipulate it using the file handler function in the stdio I/O library.
If Open_mode is "R", the main invoker can use the output of the called program, through the file pointer returned by the function, can pass the stdio function (such as fread) to read the output of the program, if Open_mode is "w", The main invoker can send data to the callee, that is, by stdio functions (such as fwrite) to the called Program, and the callee can read the data in its own standard input.
The Pclose () function closes the associated file stream created by Popen. Pclose () returns only after the Popen-initiated process has ended, and if the called process is still running when Pclose () is called, the Pclose () call waits for the process to end. It returns the exit code for the process where the file stream is closed.
2. Example
Many times, we don't know the length of the output data, in order to avoid defining a very large array as a buffer, we can send the data in blocks, read one block of data at a time and send a block of data, until all the data is sent out. The following example is how data is read and sent 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 buffer memset (buffer, ' n ', sizeof (buffer));//Open LS and GRE P Process READ_FP = Popen ("Ls-l", "R"); WRITE_FP = Popen ("grep rwxrwxr-x", "w");//Two processes are open successfully if (READ_FP && write_fp) {//Read Take a block of data Chars_read = fread (buffer, sizeof (char), Bufsiz, READ_FP), while (Chars_read > 0) {buffer[chars_read] = ' + ';// It is written to the grep process fwrite (buffer, sizeof (char), Chars_read, WRITE_FP);//data is readable, and the data is cycled until all data is read Chars_read = fread (buffer, sizeof (char), Bufsiz, READ_FP);} Close file stream Pclose (READ_FP);p close (WRITE_FP); exit (exit_success);} Exit (exit_failure);}
The results of the operation are as follows:
From the running results, the purpose of information filtering is achieved. The program reads the data in the process LS and then sends the data to the process grep for filtering, which is equivalent to entering the command directly in the shell: ls-l | grep rwxrwxr-x.
3, Popen () of the realization of the way and advantages and disadvantages
When the request Popen () call runs a program, it first starts the shell, the SH command in the system, and then passes the command string to it as a parameter.
This brings with it an advantage and a disadvantage. The advantage is that all parameter extensions in Linux are done by the shell. So starting the shell to parse the command string before starting the program (command program) can also enable a variety of shell extensions (such as wildcards) to complete before the program starts, so that we can start very complex shell commands with Popen ().
And its disadvantage is: for each popen () call, not only to start a requested program, but also to start a shell, that is, each popen () call will start two processes, from the point of view of efficiency and resources, the Popen () function is slower than the normal way to call.
third, pipe () call
If Popen () is a high-level function, pipe () is an underlying invocation. Unlike the Popen () function, it does not need to start a shell to interpret the request command when it passes data between two processes, and it also provides more control over the read and write data.
The pipe () function is prototyped as follows:
#include <unistd.h>
int pipe(int file_descriptor[2]);
We can see that the definition of the pipe () function is very special, the function returns 0 after the two new file descriptor on the wall in the array, returns 1 if returned, and sets errno () to explain the reason for the failure.
The two file descriptors in the array are connected in a special way, the data is based on the FIFO principle, and all data written to file_descriptor[1] can be read back from File_descriptor[0. Since the data is based on the FIFO principle, the data read is consistent with the data being written.
Special Reminders:
1, from the function of the prototype we can see, it is a significant difference from the Popen function is that the Popen () function is based on file flow (files) work, and pipe is based on file descriptors, so after using the pipe, the data must be used in the underlying read () and write () Called to read and send.
2, do not use file_descriptor[0] to write data, do not use file_descriptor[1] read the data, its behavior is undefined, but on some systems may return 1 indicates that the call failed. Data can only be read from file_descriptor[0], and data can only be written to file_descriptor[1], not upside down.
Example:
First, we create a pipeline in the original process, then call Fork () to create a new process, and finally pass the data through the pipeline between two processes. 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 argc , char **argv) {ssize_t data_processed = 0;int filedes[2];const char data[] = "Hello pipe!\n"; char Buffer[bufsiz + 1];p id_t pid;//empties buffer memset (buffer, ' n ', sizeof (buffer)); if (pipe (filedes) = = 0) {//Create pipeline successfully//call fork to create child process PID = fork (); if (PID = = -1) {fprintf (stderr, "Fork failure\n"); exit (exit_failure);} if (PID = = 0) {//Sub-process//Read Data data_processed = Read (Filedes[0], buffer, bufsiz);p rintf ("read%ld bytes:%s\n", data_processed , buffer); exit (exit_success);} else{//parent process//write Data data_processed = Write (filedes[1], data, strlen (data));p rintf ("wrote%ld bytes:%s\n", data_processed, D ATA)////Hibernate for 2 seconds, mainly in order to wait for the end of the child process, this is purely for the sake of output good-looking//parent process does not really need to wait for the child process to end sleep (2); exit (exit_success);} else{fprintf (stderr, "Pipe failure\n"); exit (exit_failure);}}
The result of the operation is:
As can be seen, the child process reads the data that the parent process writes to Filedes[1], and if there is no sleep () statement in the parent process, the parent process may end before the child process ends, so you might see a command prompt separating the two inputs.
Iv. Use the pipe as standard input and standard output
Here's a more concise way to connect two processes with a pipeline, we can set the file descriptor to a known value, typically standard input 0 or standard output 1. The biggest benefit of this is the ability to invoke standard programs, which are those that do not require a file descriptor as a parameter.
In order to do this, we also need two helper functions, namely the DUP function or the DUP2 function, and their prototypes are 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 that points to the same file or pipeline as the existing file descriptor as its argument. For the DUP () function, the new file description always takes the smallest available value. The new file descriptor created by Dup2 () is either the same as int file_descriptor_two or the first available value larger than the parameter. So when we first close the file descriptor 0 and call the DUP (), then the new file descriptor will be the number 0.
Example
In the following example, first open the pipeline, then fork () a child process, then in the child process, the standard input to the read pipeline, and then close the child process in the read pipeline and the write pipeline, leaving only the standard input, and finally call the EXECLP () function to start a new process OD, But 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, then writes the data in the write pipeline, and then closes the write pipeline to complete its task. The source file is pipe2.c and the code is as follows:
#include <stdlib.h> #include <unistd.h> #include <stdio.h> #include <string.h>int main (int argc , char **argv) {ssize_t data_proccessed = 0;int pipes[2];const char data[] = "123";p id_t pid;if (pipe (pipes) = = 0) {pid = fo RK (); if (pid = =-1) {fprintf (stderr, "Fork failure\n"); exit (exit_failure);} if (PID = = 0) {//Sub-process//make standard input point to Filedes[0]close (0);d up (Pipes[0]),//close pipes[0] and pipes[1], only standard input close (pipes[0]); Close ( PIPES[1]);//Start new process ODEXECLP ("OD", "OD", "-C", 0); exit (exit_failure);} else{//Close Pipes[0] because the parent process does not have to read the data close (Pipes[0]);d ata_proccessed = Write (pipes[1], data, strlen (data));//After the data is finished, close pipes [1]close (pipes[1]);p rintf ("%d-worte%ld bytes\n", Getpid (), data_proccessed);}} else{fprintf (stderr, "Pipe failure\n"); exit (exit_failure);}}
The result of the operation is:
As you can see from the running results, the OD process has done its job correctly, just as you would enter OD-C and 123 directly in the shell.
v. Discussion on the reading operation after the pipe is closed
Now there is a problem, if the parent process file_pipe[1] write the data, and the child process reads the data in the pipeline file_pipe[0], when the parent process does not write the data to file_pipe[1], the child process does not have data readable, then what happens to the child process? Furthermore, if the parent process closes the file_pipe[1], how will the child process react?
When a pipe that writes data is not closed and no data is readable, the read () call is usually blocked, but when the pipe that writes the data is closed, the read () call returns 0 instead of blocking. Note that this is different from reading an invalid file descriptor, and read () An invalid file descriptor returns-1.
vi. defects in anonymous pipelines
See so much believe everyone also know that it is a disadvantage, is the process of communication, their relationship must be a parent-child process relationship, which makes its use is a bit limited, but we can use named pipes to solve the problem. Named pipes are described in the next article: Linux interprocess communication-using named pipes.
Reference:
http://blog.csdn.net/ljianhui/article/details/10168031
http://blog.csdn.net/zhouqi_2011/article/details/7039157
Linux inter-process communication-using anonymous pipelines