pipe chains the STDOUT of one command and put it to the STDIN of another command. Typically, we want to do pipe per direction. command pipelines span two child processes create a pipe to allow the two processes to communicate connect the first child’s STDOUT to the pipe + the second child’s STDIN to the pipe pipe() pipe() gives us back two file descriptors, such that whatever is written to one can be read from another. Interface: int pipes[2]; // create the pipes int ret = pipe(pipes); // an so int read_from_here = ret[0]; int write_to_here = ret[1]; // i.e. ret[1] writes to => ret[0] read // fork! pid_t pid_p = fork(); if(pid_p == 0) { // child subroutine // because child is READING, and not READINg // we want to close the write close(write_to_here); // we want to then make a buffer char buf[num_bytes]; // if the child reads before the parents write // it will block until some data is available // if the write ends are closed globally, read // will also stop. read(read_from_here, buffer, sizeof(buffer)); close(read_from_here); return 0; } // parent subroutine // because parent is WRITING and not READING // we don't want the read to block, we will // close the parent immediately. close(read_from_here); // write some data write(write_to_here, "msg", num_bytes); // close now we are done writing close(write_to_here); // clean up child waitpid(pid_p, NULL, 0); pipes have to be closed twice, and opened before the fork. dup2() dup2() lets you REWIRE fire descriptors: dup2(scrfd, destft); for instance: dup2(fds[0], STDIN_FILENO); close(fds[0]); copy the underlying open file pointer which fds[0] points to the FD STDIN, meaning STDIN will now refer to the underlying file of fds[0]. stalling if you don’t close the right ends of your pipes, it STALLS. read() BLOCKS UNTIL ALL WRITES ARE CLOSED!