$ ls | pr | lprpoveže izhod iz ukaza ls (ki lista datoteke direktorija) na standardni vhod ukaza pr (ki oblikuje ta izpis v strani). Končno je izhod ukaza pr preusmerjen na standardni vhod ukaza lpr, ki izpiše rezultat na tiskalnik. Cevi so torej enosmerni tokovi bajtov, ki povezujejo standardni izhod enega procesa s standardnim vhodom nekega drugega procesa. Procesi se take preusmeritve ne zavedajo in se obnašajo tako, kot bi se v vsakem primeru. Začasne cevovode med njimi vzpostavi lupina.
V Linuxu je cev implementirana v obliki dveh datotečnih struktur, ki obe kažeta na isti začasni VFS vozel., ta pa kaže na fizično stran v pomnilniku. Slika kaže, da ima vsaka od obeh datotečnih struktur (file) kazalce na različne vektorje (sezname) datotečnih rutin, ena na pisanje v cev, druga na branje iz cevi.
Tako skrijemo razlike med splošnim sistemskim klicem za branje oziroma pisanje v datoteke (read oziroma write). Ko pišoči proces piše v cev, se bajti kopirajo v skupno podatkovno stran. Ko bralni proces bere iz cevi, pride do kopiranja bajtov it te skupne podatkovne strani. Linux mora poskrbeti za sinhronoziran dostop do cevi. Pri tej sinhronizaciji uporablja zaklepanje, čakalne vrste in signale.
Ko oba procesa prenehata uporabljati cev, se vozel, dodeljen cevi, in souporabljena podatkovna stran sprostita.
Linux podpira tudi imenovane cevi (named pipes), ki delujejo po principu FIFO (First In, First Out). V razliko od navadnih cevi imenovane cevi niso le začasni objekti in jih tvorimo z ukazom mkfifo. Procesi lahko tako cev uporabljajo, če imajo za to pravico. Odpiranje imenovane cevi (ki ji pravimo tudi FIFO) je drugačno od odpiranja navadnih cevi, saj pred uporabo že obstaja in jo moramo le odpreti in na koncu zapreti. Linux mora tudi upoštevati, da morda najprej odpremo bralne procese in šele nato pisalne.
Primer:
#include <sys/types.h> #include <sys/wait.h> #include <stdio.h> #include <errno.h> #include <unistd.h> int main(int argc, char *argv[]){ int status; int pid[2]; int pipe_fd[2]; char *prog1_argv[4]; char *prog2_argv[2]; /* Build argument list */ prog1_argv[0] = "/usr/local/bin/ls"; prog1_argv[1] = "-l"; prog1_argv[2] = "/"; prog1_argv[3] = NULL; prog2_argv[0] = "/usr/ucb/more"; prog2_argv[1] = NULL; /* Create the pipe */ if (pipe(pipe_fd) < 0){ perror ("pipe failed"); exit (errno); } /* Create a process space for the ls */ if ((pid[0]=fork()) < 0){ perror ("Fork failed"); exit(errno); } if (!pid[0]){ /* Set stdout to pipe */ close (pipe_fd[0]); dup2 (pipe_fd[1], 1); close (pipe_fd[1]); /* Execute the ls */ execvp (prog1_argv[0], prog1_argv); } if (pid[0]) { /* We're in the parent */ /* Create a process space for the more */ if ((pid[1]=fork()) < 0){ perror ("Fork failed"); exit(errno); } if (!pid[1]){ /* We're in the child */ /* Set stdin to pipe */ close (pipe_fd[1]); dup2 (pipe_fd[0], 0); close (pipe_fd[0]); /* Execute the more */ execvp (prog2_argv[0], prog2_argv); } /* This is the parent */ close(pipe_fd[0]); close(pipe_fd[1]); waitpid (pid[1], &status, 0); printf ("Done waiting for more.\n"); } }