En los sistemas operativos de tipo Unix , una canalización es un mecanismo de comunicación entre procesos mediante el paso de mensajes. Una canalización es un conjunto de procesos encadenados entre sí por sus flujos estándar , de modo que el texto de salida de cada proceso ( stdout ) se pasa directamente como entrada ( stdin ) al siguiente. El segundo proceso se inicia mientras el primero todavía se está ejecutando, y se ejecutan simultáneamente .
El concepto de pipelines fue defendido por Douglas McIlroy en Bell Labs , la casa ancestral de Unix , durante el desarrollo de Unix, dando forma a su filosofía de caja de herramientas . Se lo denomina así por analogía con un pipeline físico . Una característica clave de estos pipelines es que "ocultan los elementos internos". Esto, a su vez, permite una mayor claridad y simplicidad en el sistema.
Las tuberías de la tubería son tuberías anónimas (a diferencia de las tuberías con nombre ), donde los datos escritos por un proceso se almacenan en el búfer del sistema operativo hasta que son leídos por el siguiente proceso, y este canal unidireccional desaparece cuando se completan los procesos. La sintaxis estándar del shell para las tuberías anónimas es enumerar varios comandos, separados por barras verticales (" tuberías " en el lenguaje común de Unix).
El concepto de pipeline fue inventado por Douglas McIlroy [1] y descrito por primera vez en las páginas de manual de la versión 3 de Unix . [2] [3] McIlroy se dio cuenta de que la mayor parte del tiempo los shells de comandos pasaban el archivo de salida de un programa como entrada a otro. El concepto de pipelines fue defendido por Douglas McIlroy en la casa ancestral de Unix , Bell Labs , durante el desarrollo de Unix, dando forma a su filosofía de caja de herramientas . [4] [5]
Sus ideas se implementaron en 1973 cuando ("en una noche febril", escribió McIlroy) Ken Thompson agregó la pipe()
llamada al sistema y las tuberías al shell y varias utilidades en la versión 3 de Unix. "Al día siguiente", continuó McIlroy, "vimos una orgía inolvidable de frases ingeniosas mientras todos se unían a la emoción de la plomería". McIlroy también le atribuye a Thompson la |
notación, que simplificó enormemente la descripción de la sintaxis de las tuberías en la versión 4. [ 6] [2]
Aunque se desarrollaron de forma independiente, las tuberías de Unix están relacionadas con, y fueron precedidas por, los "archivos de comunicación" desarrollados por Ken Lochner [7] en la década de 1960 para el Sistema de tiempo compartido de Dartmouth . [8]
Esta característica de Unix fue tomada prestada por otros sistemas operativos, como MS-DOS y el paquete CMS Pipelines en VM/CMS y MVS , y eventualmente llegó a ser designada como el patrón de diseño de tuberías y filtros de la ingeniería de software .
En los procesos secuenciales de comunicación (CSP) de Tony Hoare , las tuberías de McIlroy se desarrollan aún más. [9]
Un mecanismo de pipeline se utiliza para la comunicación entre procesos mediante el paso de mensajes. Un pipeline es un conjunto de procesos encadenados entre sí por sus flujos estándar , de modo que el texto de salida de cada proceso ( stdout ) se pasa directamente como entrada ( stdin ) al siguiente. El segundo proceso se inicia mientras el primero todavía se está ejecutando, y se ejecutan simultáneamente . Se denomina así por analogía a un pipeline físico . Una característica clave de estos pipelines es su "ocultamiento de los elementos internos". [10] Esto, a su vez, permite una mayor claridad y simplicidad en el sistema.
En la mayoría de los sistemas tipo Unix, todos los procesos de una tubería se inician al mismo tiempo, con sus flujos conectados apropiadamente y administrados por el planificador junto con todos los demás procesos que se ejecutan en la máquina. Un aspecto importante de esto, que distingue a las tuberías de Unix de otras implementaciones de tuberías, es el concepto de almacenamiento en búfer : por ejemplo, un programa de envío puede producir 5000 bytes por segundo , y un programa receptor puede solo ser capaz de aceptar 100 bytes por segundo, pero no se pierden datos. En cambio, la salida del programa de envío se mantiene en el búfer. Cuando el programa receptor está listo para leer datos, el siguiente programa en la tubería lee desde el búfer. Si el búfer está lleno, el programa de envío se detiene (se bloquea) hasta que el receptor elimine al menos algunos datos del búfer. En Linux, el tamaño del búfer es de 65.536 bytes (64 KiB). Hay disponible un filtro de terceros de código abierto llamado bfr para proporcionar búferes más grandes si es necesario.
Herramientas como netcat y socat pueden conectar tuberías a sockets TCP/IP .
Todos los shells de Unix de uso generalizado tienen una sintaxis especial para la creación de pipelines. En todos los casos, los comandos se escriben en secuencia, separados por el carácter de barra vertical ASCII (que, por este motivo, suele denominarse "carácter de canalización"). El shell inicia los procesos y organiza las conexiones necesarias entre sus flujos estándar (incluida cierta cantidad de almacenamiento en búfer ).|
El pipeline utiliza canales anónimos . En el caso de los canales anónimos, los datos escritos por un proceso se almacenan en el búfer del sistema operativo hasta que son leídos por el siguiente proceso, y este canal unidireccional desaparece cuando se completan los procesos; esto difiere de los canales con nombre , en los que los mensajes se pasan hacia o desde un canal que se nombra convirtiéndolo en un archivo, y permanece después de que se completan los procesos. La sintaxis estándar del shell para los canales anónimos es enumerar varios comandos, separados por barras verticales ("canales" en el lenguaje común de Unix):
comando1 | comando2 | comando3
Por ejemplo, para listar archivos en el directorio actual ( ls ), retener sólo las líneas de salida de ls que contienen la cadena "clave" ( grep ) y ver el resultado en una página desplazable ( less ), un usuario escribe lo siguiente en la línea de comando de una terminal:
ls -l | grep clave | menos
El comando ls -l
se ejecuta como un proceso, cuya salida (stdout) se canaliza a la entrada (stdin) del proceso for grep key
; y lo mismo ocurre con el proceso for less
. Cada proceso toma la entrada del proceso anterior y produce la salida para el siguiente proceso a través de flujos estándar . Cada uno |
le indica al shell que conecte la salida estándar del comando de la izquierda con la entrada estándar del comando de la derecha mediante un mecanismo de comunicación entre procesos llamado canalización (anónimo) , implementado en el sistema operativo. Las canalizaciones son unidireccionales; los datos fluyen a través de la canalización de izquierda a derecha.
A continuación se muestra un ejemplo de una secuencia de comandos que implementa un tipo de corrector ortográfico para el recurso web indicado por una URL . A continuación se ofrece una explicación de lo que hace.
curl "https://es.wikipedia.org/wiki/Pipeline_(Unix)" | sed 's/[^a-zA-Z ]/ /g' | es 'AZ' 'az\n' | grep '[az]' | ordenar -u | comm -23 - < ( ordenar /usr/share/dict/words ) | menos
curl
obtiene el contenido HTML de una página web (podría usarse wget
en algunos sistemas).sed
reemplaza todos los caracteres (del contenido de la página web) que no sean espacios o letras, con espacios. ( Se conservan las nuevas líneas ).tr
cambia todas las letras mayúsculas a minúsculas y convierte los espacios en las líneas de texto en nuevas líneas (cada 'palabra' ahora está en una línea separada).grep
incluye sólo líneas que contienen al menos un carácter alfabético en minúscula (eliminando cualquier línea en blanco).sort
ordena la lista de 'palabras' en orden alfabético y el -u
conmutador elimina los duplicados.comm
encuentra líneas en común entre dos archivos, -23
suprime las líneas exclusivas del segundo archivo y las que son comunes a ambos, dejando solo las que se encuentran solo en el primer archivo nombrado. El -
en lugar de un nombre de archivo hace comm
que se use su entrada estándar (de la línea de canalización en este caso). sort /usr/share/dict/words
ordena el contenido del words
archivo alfabéticamente, como comm
se espera, y <( ... )
envía los resultados a un archivo temporal (a través de la sustitución de procesos ), que comm
lee. El resultado es una lista de palabras (líneas) que no se encuentran en /usr/share/dict/words.less
Permite al usuario navegar a través de los resultados.De manera predeterminada, los flujos de error estándar (" stderr ") de los procesos en una tubería no se pasan a través de la tubería; en su lugar, se fusionan y se dirigen a la consola . Sin embargo, muchos shells tienen una sintaxis adicional para cambiar este comportamiento. En el shell csh , por ejemplo, usar |&
en lugar de |
significa que el flujo de error estándar también debe fusionarse con la salida estándar y enviarse al siguiente proceso. El shell Bash también puede fusionar el error estándar con |&
desde la versión 4.0 [11] o usando 2>&1
, así como redirigirlo a un archivo diferente.
En las tuberías simples más utilizadas, el shell conecta una serie de subprocesos a través de tuberías y ejecuta comandos externos dentro de cada subproceso. Por lo tanto, el shell en sí no realiza ningún procesamiento directo de los datos que fluyen a través de la tubería.
Sin embargo, es posible que el shell realice el procesamiento directamente, utilizando un denominado molino o pipemill (ya que se utiliza un while
comando para "moler" los resultados del comando inicial). Esta construcción generalmente se parece a algo como esto:
command | while read -r var1 var2 ... ; do # procesa cada línea, usando variables como se analizan en var1, var2, etc. # (tenga en cuenta que esto puede ser un subshell: var1, var2, etc. no estarán disponibles # después de que finalice el bucle while; algunos shells, como zsh y versiones más nuevas de Korn shell, procesan los comandos a la izquierda del operador de tubería # en un subshell) done
Es posible que un pipemill de este tipo no funcione como se espera si el cuerpo del bucle incluye comandos, como cat
y ssh
, que leen desde stdin
: [12] en la primera iteración del bucle, un programa de este tipo (llamémoslo el drenaje ) leerá la salida restante de command
, y el bucle luego terminará (con resultados que dependen de los detalles del drenaje). Hay un par de formas posibles de evitar este comportamiento. Primero, algunos drenajes admiten una opción para deshabilitar la lectura de stdin
(por ejemplo, ssh -n
). Alternativamente, si el drenaje no necesita leer ninguna entrada de stdin
para hacer algo útil, se puede dar < /dev/null
como entrada.
Como todos los componentes de una tubería se ejecutan en paralelo, un shell normalmente bifurca un subproceso (un subshell) para manejar su contenido, lo que hace imposible propagar cambios de variables al entorno del shell externo. Para solucionar este problema, el "pipemill" puede alimentarse en cambio desde un documento here que contenga una sustitución de comando , que espera a que la tubería termine de ejecutarse antes de procesar el contenido. Alternativamente, se puede utilizar una tubería con nombre o una sustitución de proceso para la ejecución en paralelo. GNU bash también tiene una lastpipe
opción para deshabilitar la bifurcación para el último componente de la tubería. [13]
Las tuberías se pueden crear bajo el control del programa. La pipe()
llamada al sistema Unix solicita al sistema operativo que construya un nuevo objeto de tubería anónimo . Esto da como resultado dos descriptores de archivo nuevos y abiertos en el proceso: el extremo de solo lectura de la tubería y el extremo de solo escritura. Los extremos de la tubería parecen ser descriptores de archivo anónimos normales , excepto que no tienen capacidad de búsqueda.
Para evitar bloqueos y aprovechar el paralelismo, el proceso Unix con una o más tuberías nuevas generalmente llamará fork()
para crear nuevos procesos. Luego, cada proceso cerrará los extremos de la tubería que no usará antes de producir o consumir datos. Alternativamente, un proceso podría crear nuevos subprocesos y usar la tubería para comunicarse entre ellos.
También se pueden crear canales con nombremkfifo()
utilizando omknod()
y luego se presentan como archivo de entrada o salida a los programas a medida que se los invoca. Permiten crear canales de múltiples rutas y son especialmente eficaces cuando se combinan con la redirección de error estándar o contee
.
El robot en el ícono de Automator de Apple , que también utiliza un concepto de tubería para encadenar comandos repetitivos, sostiene una tubería en homenaje al concepto original de Unix.
McIlroy: Fue uno de los pocos lugares donde casi ejercí un control administrativo sobre Unix, estaba presionando para que se hicieran esas cosas, sí.
{{cite web}}
: CS1 maint: nombres numéricos: lista de autores ( enlace )