Документ взят из кэша поисковой машины. Адрес
оригинального документа
: http://www.sao.ru/hq/sts/linux/doc/ipc_guide/pipes.html
Дата изменения: Unknown Дата индексирования: Tue Oct 2 14:42:24 2012 Кодировка: Поисковые слова: arp 220 |
Since they're so very very easy, I shant spent much time on them. We'll just have some examples and stuff.
For example, stdin is file descriptor "0", stdout is
"1", and stderr is "2". Likewise, any files you open using
fopen() get their own file descriptor, although this detail is
hidden from you. (This file descriptor can be retrived from the
Basically, a call to the pipe() function returns a pair of file descriptors. One of these descriptors is connected to the write end of the pipe, and the other is connected to the read end. Anything can be written to the pipe, and read from the other end in the order it came in. On many systems, pipes will fill up after you write about 10K to them without reading anything out.
As a useless example, the following program creates, writes to, and reads from a pipe.
#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <unistd.h> int main() { int pfds[2]; char buf[30]; if (pipe(pfds) == -1) { perror("pipe"); exit(1); } printf("writing to file descriptor #%d\n", pfds[1]); write(pfds[1], "test", 5); printf("reading from file descriptor #%d\n", pfds[0]); read(pfds[0], buf, 5); printf("read \"%s\"\n", buf); }
As you can see, pipe() takes an array of two ints as an argument. Assuming no errors, it connects two file descriptors and returns them in the array. The first element of the array is the reading-end of the pipe, the second is the writing end.
From the above example, it's pretty hard to see how these would even be useful. Well, since this is an IPC document, let's put a fork() in the mix and see what happens. Pretend that you are a top federal agent assigned to get a child process to send the word "test" to the parent. Not very glamorous, but no one ever said computer science would be the X-Files, Mulder.
First, we'll have the parent make a pipe. Secondly, we'll fork(). Now, the fork() man page tells us that the child will receive a copy of all the parent's file descriptors, and this includes a copy of the pipe's file descriptors. Alors, the child will be able to send stuff to the write-end of the pipe, and the parent will get it off the read-end. Like this:
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <unistd.h> int main() { int pfds[2]; char buf[30]; pipe(pfds); if (!fork()) { printf(" CHILD: writing to the pipe\n"); write(pfds[1], "test", 5); printf(" CHILD: exiting\n"); exit(0); } else { printf("PARENT: reading from pipe\n"); read(pfds[0], buf, 5); printf("PARENT: read \"%s\"\n", buf); wait(NULL); } }
Please note, your programs should have a lot more error checking than mine do. I leave it out on occasion to help keep things clear.
Anyway, this example is just like the previous one, except now we fork() of a new process and have it write to the pipe, while the parent reads from it. The resultant output will be something similar to the following:
PARENT: reading from pipe CHILD: writing to the pipe CHILD: exiting PARENT: read "test"
In this case, the parent tried to read from the pipe before the child writes to it. When this happens, the parent is said to block, or sleep, until data arrives to be read. It seems that the parent tried to read, went to sleep, the child wrote and exited, and the parent woke up and read the data.
Hurrah!! You've just don't some interprocess communication! That was dreadfully simple, huh? I'll bet you are still thinking that there aren't many uses for pipe() and, well, you're probably right. The other forms of IPC are generally more useful and are often more exotic.
In an effort to make you think that pipes are actually reasonable
beasts, I'll give you an example of using pipe() in a more
familiar situation. The challenge: implement "
This requires usage of a couple more functions you may never have heard
of: exec() and dup(). The exec() family of
functions replaces the currently running process with whichever one is
passed to exec(). This is the function that we will use to run
ls and
Anyway, here is the code:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> int main() { int pfds[2]; pipe(pfds); if (!fork()) { close(1); /* close normal stdout */ dup(pfds[1]); /* make stdout same as pfds[1] */ close(pfds[0]); /* we don't need this */ execlp("ls", "ls", NULL); } else { close(0); /* close normal stdin */ dup(pfds[0]); /* make stdin same as pfds[0] */ close(pfds[1]); /* we don't need this */ execlp("wc", "wc", "-l", NULL); } }
I'm going to make another note about the close()/dup() combination since it's pretty weird. close(1) frees up file descriptor 1 (standard output). dup(pfds[1]) makes a copy of the write-end of the pipe in the first available file descriptor, which is "1", since we just closed that. In this way, anything that ls writes to standard output (file descriptor 1) will instead go to pfds[1] (the write end of the pipe). The wc section of code works the same way, except in reverse.
Copyright © 1997 by Brian "Beej" Hall. This guide may be reprinted in any medium provided that its content is not altered, it is presented in its entirety, and this copyright notice remains intact. Contact beej@ecst.csuchico.edu for more information.