subprocess - Genera Processi Addizionali

Scopo: Fa partire e comunica con processi addizionali

Il modulo subprocess supporta tre API per lavorare con processi. La funzione run(), aggiunta in Python 3.5 è una API ad alto livello per eseguire un processo con opzione per catturare il suo output. Le funzioni call(), check_call(), e check_output() sono tre precedenti API di alto livello, portate dalla versione 2 di Python. Esse sono ancora supportate e largamente usate in programmi esistenti. La classe Popen è una API di basso livello usata per costruire le altre API e utile per interazioni di processo più complesse. Il costruttore per Popen riceve argomenti per impostare il nuovo processo in modo che il genitore possa comunicare con esso tramite pipe. Fornisce tutte le funzionalità degli altri moduli e funzioni che rimpiazza, e altro ancora. L'API è consistente per tutti gli utilizzi, e molti dei passi supplementari necessari (tipo la chiusura di descrittori di file extra e l'assicurare che le pipe siano chiuse) sono "incorporate" invece che essere gestite dal codice dell'applicazione separatamente.

Il modulo subprocess è concepito per sostituire funzioni tipo os.system(), os.spawn(), le varianti di popen() nei moduli os e popen2, così come il modulo commands. Per facilitare il confronto di subprocess con questi altri moduli, molti degli esempi di questa sezione ricreano quelli usati per os e popen2.

L'API per lavorare con Unix e Windows è grossomodo la stessa, ma l'implementazione sottostante è leggermente diversa a causa della differenza nei modelli di processo nei sistemi operativi. Tutti gli esempi qui mostrati sono testati su MAC Os X. L'esperienza personale in un sistema operativo diverso da Unix potrebbe essere diversa.

Eseguire un Comando Esterno

Per eseguire un comando esterno senza interagire con esso, proprio come si farebbe con os.system(), si usa la funzione run().

# subprocess_os_system.py

import subprocess

completed = subprocess.run(['ls', '-1'])
print('returncode:', completed.returncode)

Gli argomenti di riga di comando sono passati come lista di stringhe, che consente di evitare l'escape di apici o altri caratteri speciali che potrebbero essere interpretati dalla shell. run() ritorna una istanza di CompletedProcess con informazioni circa il processo tipo il codice di uscita e l'output.

$ python3 subprocess_os_system.py

index.rst
interaction.py
repeater.py
signal_child.py
signal_parent.py
subprocess_check_output_error_trap_output.py
subprocess_os_system.py
subprocess_pipes.py
subprocess_popen2.py
subprocess_popen3.py
subprocess_popen4.py
subprocess_popen_read.py
subprocess_popen_write.py
subprocess_run_check.py
subprocess_run_output.py
subprocess_run_output_error.py
subprocess_run_output_error_suppress.py
subprocess_run_output_error_trap.py
subprocess_shell_variables.py
subprocess_signal_parent_shell.py
subprocess_signal_setpgrp.py
returncode: 0

Impostando l'argomento shell a True, fa sì che subprocess generi un processo intermedio di shell dalla quale viene eseguito il comando. La modalità predefinita è di eseguire il comando direttamente.

# subprocess_shell_variables.py

import subprocess

completed = subprocess.run('echo $HOME', shell=True)
print('returncode:', completed.returncode)

Usando una shell intermedia implica che le variabili, i modelli glob e altre caratteristiche speciali della shell nella stringa di comando sono elaborate prima che il comando venga eseguito.

$ python3 subprocess_shell_variables.py

/home/robby
returncode: 0
Usando run() senza passare check=True equivale alla chiamata di call(), che restituisce solo il codice di uscita dal processo.
Gestione degli Errori

L'attributo returncode di CompletedProcess è il codice di uscita del programma. Il chiamante è responsabile dell'interpretazione per rilevare errori. Se l'argomento check di run() è True, il codice di uscita viene verificato e, qualora indichi che un errore si è verificato, viene sollevata l'eccezione CalledProcessError.

# subprocess_run_check.py

import subprocess

try:
    subprocess.run(['false'], check=True)
except subprocess.CalledProcessError as err:
    print('ERRORE:', err)

Il comando false esce sempre con un codice di stato diverso da zero, che viene interpretato da run() come un errore.

$ python3 subprocess_run_check.py

ERRORE: Command '['false']' returned non-zero exit status 1
Passando check=True a run() equivale alla chiamata di check_call().
Catturare Output

I canali standard di input e output per il processo fatto partire da run() sono legati all'input e output del genitore. Il che significa che il programma chiamante non può catturare l'output del comando. Si passi PIPE per gli argomenti di stdout e stderror per catturare l'output per una successiva elaborazione.

# subprocess_run_output.py

import subprocess

completed = subprocess.run(
    ['ls', '-1', '/home/robby/test'],
    stdout=subprocess.PIPE,
)
print('returncode:', completed.returncode)
print('Ci sono {} byte in stdout:\n{}'.format(
    len(completed.stdout),
    completed.stdout.decode('utf-8'))
)

Il comando ls -1 /home/robby/test viene eseguito con successo, quindi il testo che stampa verso l'output standard viene catturate e restituito.

$ python3 subprocess_run_output.py

returncode: 0
Ci sono 299 byte in stdout:
subprocess_os_system.py
subprocess_pipes.py
subprocess_popen2.py
subprocess_popen3.py
subprocess_popen4.py
subprocess_popen_read.py
subprocess_popen_write.py
subprocess_run_check.py
subprocess_run_output.py
subprocess_shell_variables.py
subprocess_signal_parent_shell.py
subprocess_signal_setsid.py
Passare check=True e impostando stdout a PIPE equivale a usare check_output.

L'esempio successivo esegue una serie di comandi in una sub-shell. I messaggi sono inviati allo standard output e allo standard error prima che i comandi escano con un codice di errore.

# subprocess_run_output_error.py

import subprocess

try:
    completed = subprocess.run(
        'echo to stdout; echo to stderr 1>&2; exit 1',
        check=True,
        shell=True,
        stdout=subprocess.PIPE,
    )
except subprocess.CalledProcessError as err:
    print('ERRORE:', err)
else:
    print('returncode:', completed.returncode)
    print('Ci sono {} byte in stdout: {!r}'.format(
        len(completed.stdout),
        completed.stdout.decode('utf-8'))
    )

Il messaggio allo standard error viene stampato sulla console, ma il messaggio verso lo standard output viene nascosto.

$ python3 subprocess_run_output_error.py
to stderr
ERRORE: Command 'echo to stdout; echo to stderr 1>&2; exit 1' returned non-zero exit status 1

Per evitare la scrittura sulla console di messaggi di errore dai comandi eseguiti tramite run(), si imposti il parametro stderr alla costante PIPE.

# subprocess_run_output_error_trap.py

import subprocess

try:
    completed = subprocess.run(
        'echo to stdout; echo to stderr 1>&2; exit 1',
        shell=True,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
    )
except subprocess.CalledProcessError as err:
    print('ERRORE:', err)
else:
    print('returncode:', completed.returncode)
    print('Ci sono {} byte in stdout: {!r}'.format(
        len(completed.stdout),
        completed.stdout.decode('utf-8'))
    )
    print('Ci sono {} byte in stderr: {!r}'.format(
        len(completed.stderr),
        completed.stderr.decode('utf-8'))
    )

Questo esempio non imposta check=True in modo che l'output del comando venga catturato e stampato.

$ python3 subprocess_run_output_error_trap.py

returncode: 1
Ci sono 10 byte in stdout: 'to stdout\n'
Ci sono 10 byte in stderr: 'to stderr\n'

Per catturare i messaggi di errore quanto si usa check_output(), si imposta stderr a STDOUT, e i messaggi saranno combinati con il resto dell'output dal comando.

# subprocess_check_output_error_trap_output.py

import subprocess

try:
    output = subprocess.check_output(
        'echo to stdout; echo to stderr 1>&2',
        shell=True,
        stderr=subprocess.STDOUT,
    )
except subprocess.CalledProcessError as err:
    print('ERRORE:', err)
else:
    print('Ci sono {} byte in output: {!r}'.format(
        len(output),
        output.decode('utf-8'))
    )

L'ordine nell'output può variare a seconda di come venga applicato il buffering al canale standard output e di quanti dati siano stampati.

$ python3 subprocess_check_output_error_trap_output.py

Ci sono 20 byte in output: 'to stdout\nto stderr\n'
Sopprimere l'Output

Nei casi dove l'output non dovrebbe essere mostrato o catturato, si usi DEVNULL per sopprimere il canale di output. Questo esempio sopprime i canali di standard error e output.

# subprocess_run_output_error_suppress.py

import subprocess

try:
    completed = subprocess.run(
        'echo to stdout; echo to stderr 1>&2; exit 1',
        shell=True,
        stdout=subprocess.DEVNULL,
        stderr=subprocess.DEVNULL,
    )
except subprocess.CalledProcessError as err:
    print('ERRORE:', err)
else:
    print('returncode:', completed.returncode)
    print('stdout è {!r}'.format(completed.stdout))
    print('stderr è {!r}'.format(completed.stderr))

Il nome DEVNULL proviene dal file speciale di dispositivo di Unix /dev/null, che risponde con un carattere di fine file quando aperto in lettura e riceve ma ignora qualsiasi dato in input quando è aperto in scrittura.

$ python3 subprocess_run_output_error_suppress.py

returncode: 1
stdout è None
stderr è None

Lavorare Direttamente con le Pipe

Le funzioni run(), check_call() e check_output sono wrapper attorno alla classe Popen. L'uso diretto di Popen fornisce maggior controllo sul modo nel quale il comando viene eseguito, e come i suoi canali di input e output sono elaborati. Ad esempio, passando diversi argomenti per stdin, stdout, e stderr è possibile imitare le varianti di os.popen().

Comunicazione Unidirezionale con un Processo

Per eseguire un processo e leggere tutto il suo output, si imposti il valore di stdout a PIPE e si invochi communicate().

# subprocess_popen_read.py

import subprocess

print('lettura:')
proc = subprocess.Popen(
    ['echo', '"to stdout"'],
    stdout=subprocess.PIPE,
)
stdout_value = proc.communicate()[0].decode('utf-8')
print('stdout:', repr(stdout_value))

Questo è simile al modo in cui popen() funziona, a eccezione del fatto che la lettura è gestita internamente dall'istanza di Popen.

$ python3 subprocess_popen_read.py

lettura:
stdout: '"to stdout"\n'

Per impostare una pipe per consentire al programma chiamante di scrivere dati, si imposta stdin a PIPE.

# subprocess_popen_write.py

import subprocess

print('scrittura:')
proc = subprocess.Popen(
    ['cat', '-'],
    stdin=subprocess.PIPE,
)
proc.communicate('stdin: to stdin\n'.encode('utf-8'))

Per inviare una volta dati al canale standard input del processo, si passano i dati a communicate(). E' come usare popen() con modalità 'w'.

$ python3 subprocess_popen_write.py

scrittura:
stdin: to stdin
Comunicazione Bidirezionale con un Processo

Per impostare una istanza di Popen per leggere e scrivere allo stesso tempo, si usa una combinazione delle tecniche precedenti.

# subprocess_popen2.py

import subprocess

print('popen2:')

proc = subprocess.Popen(
    ['cat', '-'],
    stdin=subprocess.PIPE,
    stdout=subprocess.PIPE,
)
msg = 'attraverso stdin a stdout'.encode('utf-8')
stdout_value = proc.communicate(msg)[0].decode('utf-8')
print('passa attraverso:', repr(stdout_value))

Questo imposta la pipe per imitare popen2().

$ python3 -u subprocess_popen2.py

popen2:
passa attraverso: 'attraverso stdin a stdout'
Catturare l'Error Output

E' anche possibile seguire sia il canale per stdout che per stderr, così come farebbe popen3().

# subprocess_popen3.py

import subprocess

print('popen3:')
proc = subprocess.Popen(
    'cat -; echo "to stderr" 1>&2',
    shell=True,
    stdin=subprocess.PIPE,
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE,
)
msg = 'attraverso stdin a stdout'.encode('utf-8')
stdout_value, stderr_value = proc.communicate(msg)
print('passa attraverso:', repr(stdout_value.decode('utf-8')))

Leggere da stderr funziona allo stesso modo di stdout. Passare PIPE indica a Popen di attaccarsi al canale, mentre communicate() legge tutti i dati da esso prima di ritornare.

$ python3 -u subprocess_popen3.py

popen3:
passa attraverso: 'attraverso stdin a stdout'
Combinare Output Error ed Output Standard

Per dirigere l'output error dal processo verso il suo canale di output standard, si usa STDOUT per stderr invece che PIPE.

# subprocess_popen4.py

import subprocess

print('popen4:')
proc = subprocess.Popen(
    'cat -; echo "to stderr" 1>&2',
    shell=True,
    stdin=subprocess.PIPE,
    stdout=subprocess.PIPE,
    stderr=subprocess.STDOUT,
)
msg = 'attraverso stdin a stdout\n'.encode('utf-8')
stdout_value, stderr_value = proc.communicate(msg)
print('output combinato:', repr(stdout_value.decode('utf-8')))
print('valore di stderr:', repr(stderr_value))

Combinare l'output in questo modo è simile a quello in cui popen4() lavora.

$ python3 -u subprocess_popen4.py

popen4:
output combinato: 'attraverso stdin a stdout\nto stderr\n'
valore di stderr: None

Connettere Segmenti di una Pipe

Comandi multipli possono essere connessi tra loro in una conduttura (pipeline) in modo simile a quello con il quale lavora una shell Unix, creando istanze separate di Popen e concatenando i loro input e output. L'attributo stdout di una istanza di Popen viene usata come argomento per stdin per il successivo comando nella conduttura, al posto della costante PIPE. L'output viene letto dal gestore di stdout per il comando finale nella conduttura.

# subprocess_pipes.py

import subprocess

cat = subprocess.Popen(
    ['cat', 'index.rst'],
    stdout=subprocess.PIPE,
)

grep = subprocess.Popen(
    ['grep', '.. literalinclude::'],
    stdin=cat.stdout,
    stdout=subprocess.PIPE,
)

cut = subprocess.Popen(
    ['cut', '-f', '3', '-d:'],
    stdin=grep.stdout,
    stdout=subprocess.PIPE,
)

end_of_pipe = cut.stdout

print('File inclusi:')
for line in end_of_pipe:
    print(line.decode('utf-8').strip())

L'esempio riproduce la riga di comando:

$ cat index.rst | grep ".. literalinclude" | cut -f 3 -d:

La pipeline legge il file sorgente in formato reStructuredText per questa sezione e trova tutte le righe che includono altri file, poi stampa i nomi dei file che sono inclusi.

$ python3 -u subprocess_pipes.py

File inclusi:
        subprocess_os_system.py
        subprocess_shell_variables.py
        subprocess_popen_read.py
        subprocess_popen_write.py
        subprocess_popen2.py
        subprocess_popen3.py
        subprocess_popen4.py
        subprocess_pipes.py
        repeater.py
        interaction.py
        signal_child.py
        signal_parent.py
        subprocess_signal_parent_shell.py
        subprocess_signal_setsid.py

Interagire Con Un Altro Comando

Tutti gli esempi sopra riportati presuppongono una limitata interazione. Il metodo communicate() legge tutto l'output e attende che il processo figlio esca prima di ritornare. E' anche possibile scrivere verso e leggere da singoli gestori di pipe usati dalla istanza di Popen, mentre il programma è in esecuzione. Un semplice programma che legge dallo standard input e scrive verso lo standard output illustra questa tecnica.

script_repeater.py viene usato come processo figlio nell'esempio di seguito. Legge da stdin e scrive i valori verso stdout, una riga alla volta fino a che l'input si esaurisce. Scrive anche un messaggio verso stderr quando inizia e finisce, mostrando il tempo di vita del processo figlio.

# repeater.py

import sys

sys.stderr.write('repeater.py: inizio\n')
sys.stderr.flush()

while True:
    next_line = sys.stdin.readline()
    sys.stderr.flush()
    if not next_line:
        break
    sys.stdout.write(next_line)
    sys.stdout.flush()

sys.stderr.write('repeater.py: in uscita\n')
sys.stderr.flush()

Il successivo esempio di interazione usa i gestori di file stdin ed stdout che appartengono all'istanza di Popen in modi diversi. Nel primo esempio, una sequenza di 5 numeri viene scritta allo stdin del processo, e dopo ogni scrittura la successiva riga di output viene riletta. Nel secondo esempio, gli stessi 5 numeri sono scritti ma l'output viene letto in una sola volta usando communicate().

# interaction.py

import io
import subprocess

print('Una riga alla volta:')
proc = subprocess.Popen(
    'python3 repeater.py',
    shell=True,
    stdin=subprocess.PIPE,
    stdout=subprocess.PIPE,
)
stdin = io.TextIOWrapper(
    proc.stdin,
    encoding='utf-8',
    line_buffering=True,  # send data on newline
)
stdout = io.TextIOWrapper(
    proc.stdout,
    encoding='utf-8',
)
for i in range(5):
    line = '{}\n'.format(i)
    stdin.write(line)
    output = stdout.readline()
    print(output.rstrip())
remainder = proc.communicate()[0].decode('utf-8')
print(remainder)

print()
print('Tutto l\'output in una volta:')
proc = subprocess.Popen(
    'python3 repeater.py',
    shell=True,
    stdin=subprocess.PIPE,
    stdout=subprocess.PIPE,
)
stdin = io.TextIOWrapper(
    proc.stdin,
    encoding='utf-8',
)
for i in range(5):
    line = '{}\n'.format(i)
    stdin.write(line)
stdin.flush()

output = proc.communicate()[0].decode('utf-8')
print(output)

Le righe "repeater.py: in uscita" si trovano in punti diversi nell'output per ogni tipo di ciclo:

$ python3 interaction.py

Una riga alla volta:
repeater.py: inizio
0
1
2
3
4
repeater.py: in uscita


Tutto l'output in una volta:
repeater.py: inizio
repeater.py: in uscita
0
1
2
3
4

Segnalazioni Tra Processi

Gli esempi di gestione del processo per il modulo os comprendono una dimostrazione della segnalazione tra processi usando os.fork() ed os.kill(). Visto che ogni istanza di Popen fornisce un attributo pid con l'identificativo del processo figlio, è possibile fare qualcosa di simile con subprocess. Il prossimo esempio combina due script. Questo processo figlio imposta un gestore di segnale per il segnale USR.

# signal_child.py

import os
import signal
import time
import sys

pid = os.getpid()
received = False


def signal_usr1(signum, frame):
    "Callback chiamato quando viene ricevuto un segnale"
    global received
    received = True
    print('FIGLIO {:>6}: Ricevuto USR1'.format(pid))
    sys.stdout.flush()


print('FIGLIO {:>6}: Impostazione del gestore di segnale'.format(pid))
sys.stdout.flush()
signal.signal(signal.SIGUSR1, signal_usr1)
print('FIGLIO {:>6}: In pausa in attesa del segnale'.format(pid))
sys.stdout.flush()
time.sleep(3)

if not received:
    print('FIGLIO {:>6}: Segnale mai ricevuto'.format(pid))

Questo script viene eseguito come processo genitore. Fa partire signal_child.py, poi invia il segnale USR1.

# signal_parent.py

import os
import signal
import subprocess
import time
import sys

proc = subprocess.Popen(['python3', 'signal_child.py'])
print('GENITORE: In pausa prima di inviare il segnale...')
sys.stdout.flush()
time.sleep(1)
print('GENITORE: Segnalazione al figlio')
sys.stdout.flush()
os.kill(proc.pid, signal.SIGUSR1)

Il risultato è:

$ python3 signal_parent.py

GENITORE: In pausa prima di inviare il segnale...
FIGLIO   6981: Impostazione del gestore di segnale
FIGLIO   6981: In pausa in attesa del segnale
GENITORE: Segnalazione al figlio
FIGLIO   6981: Ricevuto USR1

Gruppi di Processo / Sessioni

Se il processo creato da Popen genera dei sotto processi, questi suoi figli non riceveranno alcuno dei segnali inviati al genitore, Questo significa che, quando si usa l'argomento shell per Popen, sarà difficile far terminare il comando fatto partire dalla shell inviandogli un SIGINT o un SIGTERM.

# subprocess_signal_parent_shell.py

import os
import signal
import subprocess
import tempfile
import time
import sys

script = '''#!/bin/sh
echo "Shell script in esecuzione $$"
set -x
python3 signal_child.py
'''
script_file = tempfile.NamedTemporaryFile('wt')
script_file.write(script)
script_file.flush()

proc = subprocess.Popen(['sh', script_file.name])
print('GENITORE      : In pausa prima di segnalare {}...'.format(
    proc.pid))
sys.stdout.flush()
time.sleep(1)
print('GENITORE      : Segnalazione al figlio {}'.format(proc.pid))
sys.stdout.flush()
os.kill(proc.pid, signal.SIGUSR1)
time.sleep(3)

Il pid usato per inviare il segnale è diverso dal pid del figlio dello script della shell che è in attesa del segnale perchè in questo esempio, ci sono tre processi separati che interagiscono.:

  1. Il programma subprocess_signal_parent_shell.py
  2. Il processo della shell Unix che sta eseguendo lo script creato dal programma python principale
  3. Il programma signal_child.py
$ python3 subprocess_signal_parent_shell.py

GENITORE      : In pausa prima di segnalare 7374...
Shell script in esecuzione 7374
+ python3 signal_child.py
FIGLIO   7375: Impostazione del gestore di segnale
FIGLIO   7375: In pausa in attesa del segnale
GENITORE      : Segnalazione al figlio 7374
FIGLIO   7375: Segnale mai ricevuto

Per inviare segnali ai discendenti senza conoscere il loro id di processo si usa un gruppo di processi da associare ai figli in modo che possa essere inviata una segnalazione a tutti insieme. Il gruppo di processi viene creato con os.setpgrp(), che imposta l'identificativo del gruppo di processo pari all'id del processo corrente. Tutti i processi figli ereditano il gruppo di processo dal proprio genitore, e visto che si dovrebbe impostare solo nella shell creata da Popen e i suoi discendenti, non occorre chiamare os.setpgrp() nello stesso processo dove è stato creato Popen. Al contrario la funzione viene passata a Popen come argomento di preexec_fn in modo che possa essere eseguito dopo il fork() all'interno del nuovo processo, prima che usi exec() per eseguire la shell. Per inviare segnali all'intero gruppo di processo, usare os.killpg() con il valore pid dell'istanza di Popen.

# subprocess_signal_setpgrp.py

import os
import signal
import subprocess
import tempfile
import time
import sys


def show_setting_prgrp():
    print('Chiamata di os.setpgrp() da {}'.format(os.getpid()))
    os.setpgrp()
    print('Il gruppo di procsso è ora {}'.format(
        os.getpid(), os.getpgrp()))
    sys.stdout.flush()


script = '''#!/bin/sh
echo "Shell script in esecuzione $$"
set -x
python3 signal_child.py
'''
script_file = tempfile.NamedTemporaryFile('wt')
script_file.write(script)
script_file.flush()

proc = subprocess.Popen(
    ['sh', script_file.name],
    preexec_fn=show_setting_prgrp,
)
print('GENITORE      : In pausa prima di segnalare {}...'.format(
    proc.pid))
sys.stdout.flush()
time.sleep(1)
print('GENITORE      : Segnalazione del gruppo di processo {}'.format(
    proc.pid))
sys.stdout.flush()
os.killpg(proc.pid, signal.SIGUSR1)
time.sleep(3)

La sequenza di eventi è.

  1. Il programma genitore istanzia Popen.
  2. L'istanza di Popen esegue un fork di un nuovo processo
  3. Il nuovo processo esegue os.setpgrp().
  4. IL nuovo processo esegue exec() per far partire la shell-
  5. La shell esegue lo script
  6. La shell esegue un nuovo fork e quel processo esegue Python
  7. Python esegue signal_child.py.
  8. Il programma genitore segnala il processo di gruppo usando il pid della shell.
  9. I processi della shell e Python ricevono il segnale.
  10. La shell ignora il segnale.
  11. Il processo Python che sta eseguendo signal_child.py chiama il gestore di segnale.
$ python3 subprocess_signal_setpgrp.py

Chiamata di os.setpgrp() da 8117
Il gruppo di procsso è ora 8117
GENITORE      : In pausa prima di segnalare 8117...
Shell script in esecuzione 8117
+ python3 signal_child.py
FIGLIO   8118: Impostazione del gestore di segnale
FIGLIO   8118: In pausa in attesa del segnale
GENITORE      : Segnalazione del gruppo di processo 8117
FIGLIO   8118: Ricevuto USR1

Conclusioni

Vedere anche:

subprocess
La documentazione della libreria standard per questo modulo.
os
Sebbene subprocess sostituisca molte di esse, le funzioni per lavorare con i processi contenute nel modulo os sono ancora largamente usate nel codice esistente.
Unix Signals and Process Groups
Una buona descrizione delle segnalazioni in Unix e come i gruppi di processi funzionano.
signal
Ulteriori dettagli circa l'uso del modulo signal.
Advanced Programming in the UNIX(R) Environment
Tratta il lavorare con processi multipli, tipo la gestione di segnali, la chiusura di descrittori di file duplicati, ecc.
pipes
Modelli di pipeline di comandi della shell Unix nella libreria standard