Piping Concepts
Pipelines are the oldest form of interprocess communication in Unix, and we refer to a data flow from one process to another as a "pipe", the essence of which is a fixed-size kernel buffer ;
such as: PS aux | grep httpd | awk ' {print $} '
Pipe limits
1) The pipe is half-duplex , the data can only flow in one direction, need to establish two pipelines when the two sides communicate;
2) Anonymous pipelines can only be used to communicate between processes that have a common ancestor , such as the parent process and the fork-out child process, because the pipe creates two file descriptors that are directly inaccessible to different processes; [Typically, a pipeline is created by a process, and then the process calls fork, and then the parent-child process shares the pipeline]
Anonymous piping pipe
#include <unistd.h>int pipe (int pipefd[2]);
Create a nameless pipe
Parameters
PIPEFD: An array of file descriptors, where pipefd[0] represents the read end, Pipefd[1] represents the write end
Pipeline creation
/** Example: Send a data pipeline from a child process to a parent process such as the second image above **/int main () {int fd[2]; if (pipe (FD) = =-1) err_exit ("Pipe error"); pid_t pid = fork (); if (PID = =-1) err_exit ("fork Error"); if (PID = = 0)//Sub-process: Writes data to the pipeline {close (fd[0]); Turn off read-only String str ("Message from Child process!"); Write (Fd[1], str.c_str (), str.size ()); Write data to write end fd[1] Close (fd[1]); Exit (exit_success); }//Parent process: Read data from the pipeline close (fd[1]); Close write-end char Buf[bufsiz] = {0}; Read (Fd[0], buf, sizeof (BUF)); Close (fd[0]); cout << buf << Endl;}
/** Example: Pipe emulation: ls | Wc-w Run 1. The child process runs the LS2. The parent process runs WC-W3. Through the pipeline, the output of the child process is sent to the input of the WC **/int main () { int pipefd[2]; if (pipe (PIPEFD) = =-1) err_exit ("pipe error"); pid_t pid = fork (); if (PID = =-1) err_exit ("fork Error"); if (PID = = 0) //Sub -process { close (pipefd[0]); Turn off read //Make Stdout_fileno also point to pipefd[1], that is, the output of the LS command will print to the pipeline dup2 (pipefd[1], Stdout_fileno);//You can close the pipe write end at this time Close (pipefd[1]); EXECLP ("/bin/ls", "ls", NULL); If the process image substitution fails, the following error message is printed cerr << "Child EXECLP Error" << Endl;; Exit (exit_failure); } Parent process Close (pipefd[1]); Close the Write end //Make Stdin_fileno also point to pipefd[2], i.e. the WC command will read the input dup2 (pipefd[0], Stdin_fileno) from the pipeline; Close (pipefd[0]); EXECLP ("/USR/BIN/WC", "WC", "-w", NULL); Cerr << "Parent EXECLP error" << Endl; Exit (exit_failure);}
Anonymous pipe Read and write rules
Rule 1) When the pipe is empty
O_nonblock Disable:read Call blocking, that is, the process pauses execution until the data arrives.
the O_nonblock enable:read call returns a -1,errno value of eagain.
Verify that int main () {int pipefd[2]; if (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 (exit_success); }//in Parent, Read pipe Close (pipefd[1]); Close Write pipe char buf[1024] = {0}; Set Read PIPEFD unblock! See what the difference is between the following four lines of statement comments//INT flags = FCNTL (PIPEFD[0],F_GETFL, 0);//Flags |= o_nonblock;//if (Fcntl (pipefd[0],f_set fl,flags) = =-1)//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 child process to send data err_exit ("read error"); cout << "Read from pipe:" << buf << Endl; Close (pipefd[0]);}
Rule 2) when the pipe is full
O_nonblock Disable:write Call blocked until a process reads the data.
O_nonblock Enable: Call returns -1,errno value of Eagain
/** Validation Rule 2) simultaneously tests the capacity of the pipeline **/int main () { if (signal (sigpipe, handler) = = Sig_err) err_exit ("Signal error"); int pipefd[2]; if (pipe (PIPEFD)! = 0) err_exit ("pipe error"); Set the write end of the pipeline to non-blocking mode //After the following three lines of comments see the effect int flags = FCNTL (pipefd[1], F_GETFL, 0); if (Fcntl (pipefd[1], F_SETFL, flags| O_nonblock) = =-1) err_exit ("Fcntl set Error"); int count = 0; while (true) { if (write (pipefd[1], "A", 1) = =-1) { cerr << "Write pipe error:" << strer Ror (errno) << Endl; break; } + + count; } cout << "Pipe size =" << count << Endl;}
3) If the file descriptor for all the pipe writes is closed, read returns 0
Validation Rule 3) int main () { int pipefd[2]; if (pipe (PIPEFD)! = 0) err_exit ("pipe error"); pid_t pid = fork (); if (PID = =-1) err_exit ("fork Error"); else if (PID = = 0) { close (pipefd[1]); Exit (exit_success); } Close (pipefd[1]); Sleep (2); Char buf[2]; if (read (pipefd[0], buf, sizeof (BUF)) = = 0) cout << "sure" << Endl;}
4) The write operation generates a signal if the file descriptor corresponding to the read end of all the pipes is closed sigpipe
Validation rule 4) int main () { if (signal (sigpipe, handler) = = Sig_err) err_exit ("Signal error"); int pipefd[2]; if (pipe (PIPEFD)! = 0) err_exit ("pipe error"); pid_t pid = fork (); if (PID = =-1) err_exit ("fork Error"); else if (PID = = 0) { close (pipefd[0]); Exit (exit_success); } Close (pipefd[0]); Sleep (2); char test; if (write (pipefd[1], &test, sizeof (test)) < 0) err_exit ("write Error");}
Linux Pipe Features
1) When the amount of data to be written is not greater than pipe_buf , Linux guarantees the atomicity of the write.
2) Linux will no longer guarantee the atomicity of writes when the amount of data to be written is greater than pipe_buf.
Man Description:
posix.1-2001 says that write (2) s of less than Pipe_buf bytes must is atomic:
The output data is written to the pipe as a contiguous sequence.
Writes of more than Pipe_buf bytes could be nonatomic:
The kernel may interleave the data with data written by other processes.
POSIX.1-2001 requires PIPE_BUF to is at least bytes.
(On Linux, Pipe_buf is 4096 bytes. In Linux, Pipe_buf is 4 bytes).
The precise semantics depend on whether the file descriptor is non-blocking (O_nonblock),
Whether there is multiple writers to the pipe, and on N, the number of bytes to be written:
O_nonblock Disabled (blocked), n <= pipe_buf
all n bytes is written atomically; Write (2) may block if there was not a guest for n bytes to be written immediately
O_nonblock enabled (non-blocking), n <= pipe_buf
If There is a to write n bytes to the pipe and then write (2) succeeds immediately, writing all n bytes;
otherwise write (2) fails, with errno set to Eagain (note: If there is not enough space to write data, a byte is not written and a direct error is returned).
O_nonblock disabled, n > Pipe_buf
The Write is nonatomic:the data given to write (2) may be interleaved with write (2) s by other process;
The Write (2) blocks until n bytes has been written.
O_nonblock enabled, n > Pipe_buf
If The pipe is full and then write (2) fails, with errno set to Eagain (which is also not a character write pipeline at this time).
Otherwise, from 1 to n bytes is written (i.e., a "partial write" may occur;
The caller should check the return value from write (2) to see how many bytes were actually written),
And these bytes is interleaved with the writes by other processes.
/** Verify: The Pipe_buf of the known pipeline is 4K, we start two process A, B to the pipeline each write 68K content, then we use 4 K as a group, the last byte of the pipeline to view the content, run the program several times, you will find that 68K of data will have cross-write situation **/int Main () {const int TEST_BUF = 68 * 1024;//sets the amount of data written to 68K Char Bufa[test_buf]; Char Bufb[test_buf]; memset (Bufa, ' A ', sizeof (BUFA)); memset (BUFB, ' B ', sizeof (BUFB)); int pipefd[2]; if (pipe (PIPEFD)! = 0) err_exit ("Pipe error"); pid_t pid; if (PID = fork ()) = =-1) err_exit ("First fork Error"); else if (PID = = 0)//first child process A, write Bufa {close (pipefd[0]) to the pipeline; int writebytes = write (pipefd[1], Bufa, sizeof (BUFA)); cout << "A Process" << getpid () << ", write" << writebytes << "bytes to pipe" << Endl; Exit (exit_success); } if (pid = fork ()) = =-1) err_exit ("Second fork Error"); else if (PID = = 0)//second child process B, write BUFB {close (pipefd[0]) to the pipeline; int writebytes = write (pipefd[1], BUFB, sizeof (BUFB)); cout << "B Process" << getpid () << ", write" << writebytes << "bytes to pipe" << Endl; Exit (exit_success); }//Parent process close (pipefd[1]); Sleep (2); Wait for two sub-processes to finish writing char buf[4 * 1024]; Apply for a 4K buf int fd = open ("Save.txt", o_wronly| o_trunc| O_creat, 0666); if (fd = =-1) err_exit ("File open error"); while (true) {int readbytes = read (Pipefd[0], buf, sizeof (BUF)); if (readbytes = = 0) break; if (Write (FD, buf, readbytes) = =-1) err_exit ("Write file Error"); cout << "Parent Process" << getpid () << "read" << readbytes << "bytes from P Ipe, buf[4095] = "<< buf[4095] << Endl; }}
Attached-Pipeline capacity query
Man 7 pipe
Note: The capacity of the pipe is not necessarily equal to pipe_buf, such as in Ubuntu, the pipe capacity is 64K, and Pipe_buf is 4K.
Linux IPC Practice (2)--Anonymous pipe