Linux pipeline (anonymous PIPE), linux pipeline anonymous pipe
Basic concepts of Pipelines
Pipelines are the oldest form of inter-process communication in Unix.
We call a data stream that connects a process to another process as a "Pipeline"
For example: ps aux | grep httpd | awk '{print $2 }'
MPs queue
Essence of Pipelines
Fixed-size kernel buffer
MPs queue restrictions
1) pipelines are half-duplex and data can only flow in one direction. Two pipelines need to be established when both parties need to communicate;
2) an anonymous pipeline can only be used for communication between processes with common ancestor (for example, parent processes and fork child processes). [generally, one pipeline is created by one process, then the process calls fork, and then the Parent and Child processes share the pipeline]
Anonymous pipeline pipe
SYNOPSIS #include <unistd.h> int pipe(int pipefd[2]);
Function
Create an unknown MPs queue
Parameters
Pipefd: an array of file descriptors. pipefd [0] indicates the read end and pipefd [1] indicates the write end.
MPs queue Creation
// Self-implemented pipe void err_exit (string str); int main () {int pipefd [2]; if (pipefd) =-1) err_exit ("pipe error"); pid_t pid; if (pid = fork () <0) err_exit ("fork error"); if (pid = 0) // In Child, Write pipe {close (pipefd [0]); // point STDOUT_FILENO to pipefd [1], that is, the output of the ls command will be printed to dup2 (pipefd [1], STDOUT_FILENO) in the pipeline; // close the pipeline write end (pipefd [1]) at this time; execlp ("/bin/ls", "/bin/ls", NULL); // if the process image replacement fails, print the following error message fprintf (stderr, "Child: execlp error "); exit (0);} // In Parent close (pipefd [1]); // point STDIN_FILENO to pipefd [2], that is, the wc command reads the input dup2 (pipefd [0], STDIN_FILENO) from the pipeline; // close the pipeline read end (pipefd [0]) at this time; execlp ("/usr/bin/wc", "/usr/bin/wc", "-w", NULL); // If process image replacement fails, print the following error message fprintf (stderr, "Parent: execlp error"); return 0;} void err_exit (string str) {perror (str. c_str (); exit (EXIT_FAILURE );}
Example: Pipeline programming practices
void err_exit(string str);int main(){ int pipefd[2]; int ret; if ((ret = pipe(pipefd)) != 0) { err_exit("pipe error"); } pid_t pid = fork(); if (pid == -1) { err_exit("fork error"); } if (pid == 0) //In Child, Write pipe { close(pipefd[0]); //Close Read pipe string str("I Can Write Pipe from Child!"); write(pipefd[1],str.c_str(),str.size()); //Write to pipe close(pipefd[1]); exit(0); } //In Parent, Read pipe close(pipefd[1]); //Close Write pipe char buf[1024]; memset(buf,0,sizeof(buf)); read(pipefd[0],buf,sizeof(buf)); //Read from pipe cout << "Read from pipe: " << buf << endl; close(pipefd[0]); return 0;}void err_exit(string str){ perror(str.c_str()); exit(EXIT_FAILURE);}
Anonymous MPs queue read/write rules
1) when the MPs queue is empty
O_NONBLOCK disable: The read call is blocked, that is, the process is paused until data arrives.
O_NONBLOCK enable: The read call returns-1, and the errno value is EAGAIN.
2) When the MPs queue is full
O_NONBLOCK disable: The write call is blocked until a process reads data.
O_NONBLOCK enable: The call returns-1, and the errno value is EAGAIN.
3) pipelines are constantly written and full
O_NONBLOCK disable: write call blocking (Block)
O_NONBLOCK enable: The call returns-1, and the errno value is EAGAIN.
// Example: Set the parent process to Unblock read PIPEint main () {int pipefd [2]; int ret; if (ret = pipe (pipefd ))! = 0) {err_exit ("pipe error");} pid_t pid = fork (); if (pid =-1) {err_exit ("fork error ");} if (pid = 0) // In Child, Write pipe {sleep (10); close (pipefd [0]); // Close Read pipe string str ("I Can Write Pipe from Child! "); Write (pipefd [1], str. c_str (), str. size (); // Write to pipe close (pipefd [1]); exit (0);} // In Parent, Read pipe close (pipefd [1]); // Close Write pipe char buf [1024]; memset (buf, 0, sizeof (buf); // Set Read pipefd UnBlock! Int flags = fcntl (pipefd [0], F_GETFL); flags | = O_NONBLOCK; ret = fcntl (pipefd [0], F_SETFL, flags); if (ret! = 0) {err_exit ("Set UnBlock error");} int readCount = read (pipefd [0], buf, sizeof (buf )); // Read from pipe if (readCount <0) {// read returns immediately, no longer waiting for the sub-process to send data err_exit ("read error ");} cout <"Read from pipe:" <buf <endl; close (pipefd [0]); return 0 ;}
4) if the file descriptor corresponding to the write end of all pipelines is disabled, read returns 0
int main(){ int pipefd[2]; int ret; if ((ret = pipe(pipefd)) != 0) { err_exit("pipe error"); } pid_t pid = fork(); if (pid == -1) { err_exit("fork error"); } if (pid == 0) //In Child { //close all close(pipefd[0]); close(pipefd[1]); exit(0); } //In Parent sleep(1); close(pipefd[1]); //Close Write pipe, Now all pipefd[1] Closed!!! char buf[1024]; memset(buf,0,sizeof(buf)); int readCount = read(pipefd[0],buf,sizeof(buf)); //Read from pipe if (readCount == 0) { cout << "OK, read 0 byte" << endl; } close(pipefd[0]); return 0;}
5) if the file descriptor corresponding to the reading end of all pipelines is disabled, the write operation will generate the signal SIGPIPE
void onSignalAction(int signalNumber){ switch(signalNumber) { case SIGPIPE: cout << "receive signal SIGPIPE: " << signalNumber << endl; break; default: cout << "other signal" << endl; break; }}int main(){ if (signal(SIGPIPE,onSignalAction) != 0) { err_exit("signal error"); } int pipefd[2]; int ret; if ((ret = pipe(pipefd)) != 0) { err_exit("pipe error"); } pid_t pid = fork(); if (pid == -1) { err_exit("fork error"); } if (pid == 0) //In Child, Write pipe { //Wait Parent Close pipefd[0] sleep(1); close(pipefd[0]); string str("I Can Write Pipe from Child!"); write(pipefd[1],str.c_str(),str.size()); //Write to pipe close(pipefd[1]); exit(0); } //In Parent, Close All Pipe close(pipefd[1]); close(pipefd[0]); wait(NULL); return 0;}
Linux PIPE features
1) when the data volume to be written is not greater than PIPE_BUF, Linux ensures the atomicity of writing.
2) When the data volume to be written is greater than PIPE_BUF, Linux will no longer guarantee the atomicity of writing.
// Example: test the PIPE_BUF size int main () {int pipefd [2]; int ret = pipe (pipefd); if (ret <0) {err_exit ("pipe error");} int flags = fcntl (pipefd [1], F_GETFL); flags | = O_NONBLOCK; ret = fcntl (pipefd [1], F_SETFL, flags); if (ret <0) {err_exit ("fcntl error");} // Write test unsigned int countForTestPipe = 0; while (true) {ret = write (pipefd [1], "a", 1); if (ret <0) {break; }++ countForTestPipe;} cout <"size = "<CountForTestPipe <endl;}/** Test Result: Ubuntu 14.04X64 xiaofang @ xiaofang-Lenovo-G470 :~ /Apue/it $./main size = 65536 */
Appendix-MPs queue capacity Query
Man 7 pipe
Appendix-in-depth understanding of file descriptors
int main(){ close(STDIN_FILENO); if (open("readfile.txt",O_RDONLY) == -1) { err_exit("open read error"); } close(STDOUT_FILENO); if (open("writefile.txt",O_WRONLY|O_TRUNC|O_CREAT,0644) == -1) { err_exit("open write error"); } if (execlp("/bin/cat","/bin/cat",NULL) == -1) { err_exit("execlp error"); } return 0;}