os — Acceso portable a funciones específicas del sistema operativo¶
Propósito: | Acceso portable a funciones específicas del sistema operativo. |
---|
El módulo os
proporciona un contenedor para módulos específicos de la
plataforma, tales como posix
, nt
y mac
. La interfaz de
programación para las funciones disponibles en todas las plataformas debe ser
la misma, por lo que usar el módulo os
ofrece cierta medida de
portabilidad. Sin embargo, no todas las funciones están disponibles en todas
las plataformas. Muchas de las funciones de gestión de procesos descritas en
este resumen no están disponibles para Windows.
La documentación de Python para el módulo os
se subtitula «Interfaces
diversas del sistema operativo». El módulo consta principalmente de funciones
para crear y administrar procesos en ejecución o contenido del sistema de
archivos (archivos y directorios), además de algunos otras funcionalidades.
Examinar el contenido del sistema de archivos¶
Para preparar una lista de los contenidos de un directorio en el sistema de
archivos, usa listdir()
.
import os
import sys
print(sorted(os.listdir(sys.argv[1])))
El valor de retorno es una lista sin clasificar de todos los miembros nombrados del directorio dado. No se hace distinción entre archivos, subdirectorios o enlaces simbólicos.
$ python3 os_listdir.py .
['index.rst', 'os_access.py', 'os_cwd_example.py',
'os_directories.py', 'os_environ_example.py',
'os_exec_example.py', 'os_fork_example.py',
'os_kill_example.py', 'os_listdir.py', 'os_listdir.py~',
'os_process_id_example.py', 'os_process_user_example.py',
'os_rename_replace.py', 'os_rename_replace.py~',
'os_scandir.py', 'os_scandir.py~', 'os_spawn_example.py',
'os_stat.py', 'os_stat_chmod.py', 'os_stat_chmod_example.txt',
'os_strerror.py', 'os_strerror.py~', 'os_symlinks.py',
'os_system_background.py', 'os_system_example.py',
'os_system_shell.py', 'os_wait_example.py',
'os_waitpid_example.py', 'os_walk.py']
La función walk()
atraviesa un directorio de forma recursiva y para cada
subdirectorio genera un tuple
que contiene la ruta del directorio,
cualquier subdirectorio inmediato de esa ruta y una lista de los nombres de los
archivos en ese directorio.
import os
import sys
# If we are not given a path to list, use /tmp
if len(sys.argv) == 1:
root = '/tmp'
else:
root = sys.argv[1]
for dir_name, sub_dirs, files in os.walk(root):
print(dir_name)
# Make the subdirectory names stand out with /
sub_dirs = [n + '/' for n in sub_dirs]
# Mix the directory contents together
contents = sub_dirs + files
contents.sort()
# Show the contents
for c in contents:
print(' {}'.format(c))
print()
Este ejemplo muestra un listado de directorio recursivo.
$ python3 os_walk.py ../zipimport
../zipimport
__init__.py
example_package/
index.rst
zipimport_example.zip
zipimport_find_module.py
zipimport_get_code.py
zipimport_get_data.py
zipimport_get_data_nozip.py
zipimport_get_data_zip.py
zipimport_get_source.py
zipimport_is_package.py
zipimport_load_module.py
zipimport_make_example.py
../zipimport/example_package
README.txt
__init__.py
Si se necesita más información que los nombres de los archivos, es probable que
sea más eficiente usar scandir()
que listdir()
porque se recopila más
información en una llamada al sistema cuando se escanea el directorio .
import os
import sys
for entry in os.scandir(sys.argv[1]):
if entry.is_dir():
typ = 'dir'
elif entry.is_file():
typ = 'file'
elif entry.is_symlink():
typ = 'link'
else:
typ = 'unknown'
print('{name} {typ}'.format(
name=entry.name,
typ=typ,
))
scandir()
devuelve una secuencia de instancias DirEntry
para los
elementos en el directorio. El objeto tiene varios atributos y métodos para
acceder a metadatos sobre el archivo.
$ python3 os_scandir.py .
index.rst file
os_access.py file
os_cwd_example.py file
os_directories.py file
os_environ_example.py file
os_exec_example.py file
os_fork_example.py file
os_kill_example.py file
os_listdir.py file
os_listdir.py~ file
os_process_id_example.py file
os_process_user_example.py file
os_rename_replace.py file
os_rename_replace.py~ file
os_scandir.py file
os_scandir.py~ file
os_spawn_example.py file
os_stat.py file
os_stat_chmod.py file
os_stat_chmod_example.txt file
os_strerror.py file
os_strerror.py~ file
os_symlinks.py file
os_system_background.py file
os_system_example.py file
os_system_shell.py file
os_wait_example.py file
os_waitpid_example.py file
os_walk.py file
Administrar permisos del sistema de archivos¶
Se puede acceder a información detallada sobre un archivo usando stat()
o
lstat()
(para verificar el estado de algo que podría ser un enlace
simbólico).
import os
import sys
import time
if len(sys.argv) == 1:
filename = __file__
else:
filename = sys.argv[1]
stat_info = os.stat(filename)
print('os.stat({}):'.format(filename))
print(' Size:', stat_info.st_size)
print(' Permissions:', oct(stat_info.st_mode))
print(' Owner:', stat_info.st_uid)
print(' Device:', stat_info.st_dev)
print(' Created :', time.ctime(stat_info.st_ctime))
print(' Last modified:', time.ctime(stat_info.st_mtime))
print(' Last accessed:', time.ctime(stat_info.st_atime))
El resultado variará dependiendo de cómo se instaló el código de ejemplo.
Intenta pasar diferentes nombres de archivo en la línea de comando a
os_stat.py
.
$ python3 os_stat.py
os.stat(os_stat.py):
Size: 593
Permissions: 0o100644
Owner: 527
Device: 16777218
Created : Sat Dec 17 12:09:51 2016
Last modified: Sat Dec 17 12:09:51 2016
Last accessed: Sat Dec 31 12:33:19 2016
$ python3 os_stat.py index.rst
os.stat(index.rst):
Size: 26878
Permissions: 0o100644
Owner: 527
Device: 16777218
Created : Sat Dec 31 12:33:10 2016
Last modified: Sat Dec 31 12:33:10 2016
Last accessed: Sat Dec 31 12:33:19 2016
En sistemas tipo Unix, los permisos de archivo se pueden cambiar usando
chmod()
, pasando el modo como un entero. Los valores de modo pueden
construirse usando constantes definidas en el módulo stat
. Este ejemplo
alterna el bit de permiso de ejecución del usuario:
import os
import stat
filename = 'os_stat_chmod_example.txt'
if os.path.exists(filename):
os.unlink(filename)
with open(filename, 'wt') as f:
f.write('contents')
# Determine what permissions are already set using stat
existing_permissions = stat.S_IMODE(os.stat(filename).st_mode)
if not os.access(filename, os.X_OK):
print('Adding execute permission')
new_permissions = existing_permissions | stat.S_IXUSR
else:
print('Removing execute permission')
# use xor to remove the user execute permission
new_permissions = existing_permissions ^ stat.S_IXUSR
os.chmod(filename, new_permissions)
La secuencia de comandos asume que tiene los permisos necesarios para modificar el modo del archivo cuando se ejecuta.
$ python3 os_stat_chmod.py
Adding execute permission
La función access()
se puede usar para probar los derechos de acceso que un
proceso tiene para un archivo.
import os
print('Testing:', __file__)
print('Exists:', os.access(__file__, os.F_OK))
print('Readable:', os.access(__file__, os.R_OK))
print('Writable:', os.access(__file__, os.W_OK))
print('Executable:', os.access(__file__, os.X_OK))
Los resultados variarán según cómo esté instalado el código de ejemplo, pero el resultado será similar a esto:
$ python3 os_access.py
Testing: os_access.py
Exists: True
Readable: True
Writable: True
Executable: False
La documentación de la biblioteca para access()
incluye dos advertencias
especiales. Primero, no tiene mucho sentido llamar a access()
para probar
si se puede abrir un archivo antes de llamar a open()
en él. Hay una
pequeña, pero real, ventana de tiempo entre las dos llamadas durante la cual
los permisos en el archivo podrían cambiar. La otra advertencia se aplica
principalmente a los sistemas de archivos en red que extienden la semántica de
permisos POSIX. Algunos tipos de sistemas de archivos pueden responder a la
llamada POSIX de que un proceso tiene permiso para acceder a un archivo, luego
informan una falla cuando el intento se realiza usando open()
por alguna
razón no probada a través de la llamada POSIX. Con todo, es mejor llamar a
open()
con el modo requerido y detectar el IOError
generado si hay un
problema.
Crear y eliminar directorios¶
Existen varias funciones para trabajar con directorios en el sistema de archivos, incluida la creación, el listado de contenidos y su eliminación.
import os
dir_name = 'os_directories_example'
print('Creating', dir_name)
os.makedirs(dir_name)
file_name = os.path.join(dir_name, 'example.txt')
print('Creating', file_name)
with open(file_name, 'wt') as f:
f.write('example file')
print('Cleaning up')
os.unlink(file_name)
os.rmdir(dir_name)
Hay dos conjuntos de funciones para crear y eliminar directorios. Al crear un
nuevo directorio con mkdir()
, todos los directorios principales ya deben
existir. Al eliminar un directorio con rmdir()
, solo se elimina realmente
el directorio hoja (la última parte de la ruta). En contraste, makedirs()
y removeirs()
operan en todos los nodos en la ruta. makedirs()
creará
cualquier parte de la ruta que no exista, y removedirs()
eliminará todos
los directorios principales, siempre que estén vacíos.
$ python3 os_directories.py
Creating os_directories_example
Creating os_directories_example/example.txt
Cleaning up
Trabajar con enlaces simbólicos¶
Para las plataformas y los sistemas de archivos que los admiten, existen funciones para trabajar con enlaces simbólicos.
import os
link_name = '/tmp/' + os.path.basename(__file__)
print('Creating link {} -> {}'.format(link_name, __file__))
os.symlink(__file__, link_name)
stat_info = os.lstat(link_name)
print('Permissions:', oct(stat_info.st_mode))
print('Points to:', os.readlink(link_name))
# Cleanup
os.unlink(link_name)
Use symlink()
para crear un enlace simbólico y readlink()
para leerlo y
determinar el archivo original al que apunta el enlace. La función lstat()
es como stat()
, pero opera en enlaces simbólicos.
$ python3 os_symlinks.py
Creating link /tmp/os_symlinks.py -> os_symlinks.py
Permissions: 0o120755
Points to: os_symlinks.py
Sustitución segura de un archivo existente¶
Reemplazar o renombrar un archivo existente no es idempotente y puede exponer
las aplicaciones a condiciones de carrera. Las funciones rename()
y
replace()
implementan algoritmos seguros para estas acciones, utilizando
operaciones atómicas en sistemas compatibles con POSIX cuando sea posible.
import glob
import os
with open('rename_start.txt', 'w') as f:
f.write('starting as rename_start.txt')
print('Starting:', glob.glob('rename*.txt'))
os.rename('rename_start.txt', 'rename_finish.txt')
print('After rename:', glob.glob('rename*.txt'))
with open('rename_finish.txt', 'r') as f:
print('Contents:', repr(f.read()))
with open('rename_new_contents.txt', 'w') as f:
f.write('ending with contents of rename_new_contents.txt')
os.replace('rename_new_contents.txt', 'rename_finish.txt')
with open('rename_finish.txt', 'r') as f:
print('After replace:', repr(f.read()))
for name in glob.glob('rename*.txt'):
os.unlink(name)
Las funciones rename()
y replace()
funcionan en sistemas de archivos,
la mayoría de las veces. El cambio de nombre de un archivo puede fallar si se
está moviendo a un nuevo sistema de archivos o si el destino ya existe.
$ python3 os_rename_replace.py
Starting: ['rename_start.txt']
After rename: ['rename_finish.txt']
Contents: 'starting as rename_start.txt'
After replace: 'ending with contents of rename_new_contents.txt'
Determinar y cambiar el propietario del proceso¶
El siguiente conjunto de funciones proporcionadas por os
se utiliza para
determinar y cambiar los identificadores de propietario del proceso. Estos son
utilizados con mayor frecuencia por los autores de demonios o programas
especiales del sistema que necesitan cambiar el nivel de permiso en lugar de
ejecutarse como root
. Esta sección no trata de explicar todos los detalles
intrincados de la seguridad de Unix, los propietarios de procesos, etc.
Consulta la lista de referencias al final de esta sección para obtener más
detalles.
El siguiente ejemplo muestra la información real y efectiva del usuario y del grupo para un proceso, y luego cambia los valores efectivos. Esto es similar a lo que un demonio necesitaría hacer cuando se inicia como root durante un arranque del sistema, para reducir el nivel de privilegio y ejecutarse como un usuario diferente.
Nota
Antes de ejecutar el ejemplo, cambia los valores TEST_GID
y TEST_UID
para que coincidan con un usuario real definido en el sistema.
import os
TEST_GID = 502
TEST_UID = 502
def show_user_info():
print('User (actual/effective) : {} / {}'.format(
os.getuid(), os.geteuid()))
print('Group (actual/effective) : {} / {}'.format(
os.getgid(), os.getegid()))
print('Actual Groups :', os.getgroups())
print('BEFORE CHANGE:')
show_user_info()
print()
try:
os.setegid(TEST_GID)
except OSError:
print('ERROR: Could not change effective group. '
'Rerun as root.')
else:
print('CHANGE GROUP:')
show_user_info()
print()
try:
os.seteuid(TEST_UID)
except OSError:
print('ERROR: Could not change effective user. '
'Rerun as root.')
else:
print('CHANGE USER:')
show_user_info()
print()
Cuando se ejecuta como usuario con el ID 502 y el grupo 502 en OS X, se produce esta salida:
$ python3 os_process_user_example.py
BEFORE CHANGE:
User (actual/effective) : 527 / 527
Group (actual/effective) : 501 / 501
Actual Groups : [501, 701, 402, 702, 500, 12, 61, 80, 98, 398,
399, 33, 100, 204, 395]
ERROR: Could not change effective group. Rerun as root.
ERROR: Could not change effective user. Rerun as root.
Los valores no cambian porque cuando no se ejecuta como raíz, un proceso no
puede cambiar su valor de propietario efectivo. Cualquier intento de
establecer la identificación de usuario efectiva o la identificación de grupo
en otra cosa que no sea la del usuario actual provoca un OSError
. Ejecutar
la misma secuencia de comandos usando sudo
para que comience con
privilegios de root es una historia diferente.
$ sudo python3 os_process_user_example.py
BEFORE CHANGE:
User (actual/effective) : 0 / 0
Group (actual/effective) : 0 / 0
Actual Groups : [0, 1, 2, 3, 4, 5, 8, 9, 12, 20, 29, 61, 80,
702, 33, 98, 100, 204, 395, 398, 399, 701]
CHANGE GROUP:
User (actual/effective) : 0 / 0
Group (actual/effective) : 0 / 502
Actual Groups : [0, 1, 2, 3, 4, 5, 8, 9, 12, 20, 29, 61, 80,
702, 33, 98, 100, 204, 395, 398, 399, 701]
CHANGE USER:
User (actual/effective) : 0 / 502
Group (actual/effective) : 0 / 502
Actual Groups : [0, 1, 2, 3, 4, 5, 8, 9, 12, 20, 29, 61, 80,
702, 33, 98, 100, 204, 395, 398, 399, 701]
En este caso, dado que comienza como root, la secuencia de comandos puede cambiar el usuario efectivo y el grupo para el proceso. Una vez que se cambia el UID efectivo, el proceso se limita a los permisos de ese usuario. Debido a que los usuarios no root no pueden cambiar su grupo efectivo, el programa necesita cambiar el grupo antes de cambiar el usuario.
Gestionar el entorno del proceso¶
Otra característica del sistema operativo expuesta a un programa a través del
módulo os
es el entorno. Las variables establecidas en el entorno son
visibles como cadenas que se pueden leer a través de os.environ
o
getenv()
. Las variables de entorno se usan comúnmente para valores de
configuración, como rutas de búsqueda, ubicaciones de archivos e indicadores de
depuración. Este ejemplo muestra cómo recuperar una variable de entorno y
pasar un valor a un proceso secundario.
import os
print('Initial value:', os.environ.get('TESTVAR', None))
print('Child process:')
os.system('echo $TESTVAR')
os.environ['TESTVAR'] = 'THIS VALUE WAS CHANGED'
print()
print('Changed value:', os.environ['TESTVAR'])
print('Child process:')
os.system('echo $TESTVAR')
del os.environ['TESTVAR']
print()
print('Removed value:', os.environ.get('TESTVAR', None))
print('Child process:')
os.system('echo $TESTVAR')
El objeto os.environ
sigue la interfaz de programación estándar de mapeo de
Python para recuperar y establecer valores. Los cambios en os.environ
se
exportan para procesos secundarios.
$ python3 -u os_environ_example.py
Initial value: None
Child process:
Changed value: THIS VALUE WAS CHANGED
Child process:
THIS VALUE WAS CHANGED
Removed value: None
Child process:
Administrar el directorio de trabajo del proceso¶
Los sistemas operativos con sistemas de archivos jerárquicos tienen un concepto
del directorio de trabajo actual: el directorio en el sistema de archivos que
el proceso usa como ubicación de inicio cuando se accede a los archivos con
rutas relativas. El directorio de trabajo actual se puede recuperar con
getcwd()
y cambiar con chdir()
.
import os
print('Starting:', os.getcwd())
print('Moving up one:', os.pardir)
os.chdir(os.pardir)
print('After move:', os.getcwd())
os.curdir
y os.pardir
se utilizan para referirse a los directorios
actuales y principales de manera portable.
$ python3 os_cwd_example.py
Starting: .../pymotw-3/source/os
Moving up one: ..
After move: .../pymotw-3/source
Ejecutar comandos externos¶
Advertencia
Muchas de estas funciones para trabajar con procesos tienen una portabilidad limitada. Para una forma más consistente de trabajar con procesos de manera independiente de la plataforma, ve el módulo :mod: subprocess en su lugar.
La forma más básica de ejecutar un comando por separado, sin interactuar con
él, es system()
. Toma un solo argumento de cadena, que es la línea de
comando que debe ejecutar un subproceso que ejecuta un shell.
import os
# Simple command
os.system('pwd')
El valor de retorno de system()
es el valor de salida del shell que ejecuta
el programa empaquetado en un número de 16 bits, con el byte alto el estado de
salida y el byte bajo el número de señal que causó la muerte del proceso, o
cero.
$ python3 -u os_system_example.py
.../pymotw-3/source/os
Dado que el comando se pasa directamente al shell para su procesamiento, puede incluir la sintaxis del shell, como el globbing o las variables de entorno.
import os
# Command with shell expansion
os.system('echo $TMPDIR')
La variable de entorno $TMPDIR
en esta cadena se expande cuando el shell
ejecuta la línea de comando.
$ python3 -u os_system_shell.py
/var/folders/5q/8gk0wq888xlggz008k8dr7180000hg/T/
A menos que el comando se ejecute explícitamente en segundo plano, la llamada a
system()
se bloquea hasta que se complete. La entrada, salida y error
estándar del proceso secundario están vinculados a las secuencias apropiadas
que posee la persona que llama de forma predeterminada, pero se pueden
redirigir utilizando la sintaxis de shell.
import os
import time
print('Calling...')
os.system('date; (sleep 3; date) &')
print('Sleeping...')
time.sleep(5)
Sin embargo, esto está entrando en trucos del shell, y hay mejores formas de lograr lo mismo.
$ python3 -u os_system_background.py
Calling...
Sat Dec 31 12:33:20 EST 2016
Sleeping...
Sat Dec 31 12:33:23 EST 2016
Crear procesos con os.fork()¶
Las funciones POSIX fork()
y exec()
(disponibles en Mac OS X, Linux y
otras variantes de Unix) se exponen a través del módulo os
. Se han escrito
libros completos sobre el uso confiable de estas funciones, por lo tanto,
consulta la biblioteca o la librería para obtener más detalles de los que se
presentan aquí en esta introducción.
Para crear un nuevo proceso como un clon del proceso actual, usa fork()
:
import os
pid = os.fork()
if pid:
print('Child process id:', pid)
else:
print('I am the child')
El resultado variará según el estado del sistema cada vez que se ejecute el ejemplo, pero se verá más o menos así:
$ python3 -u os_fork_example.py
Child process id: 29190
I am the child
Después del fork, hay dos procesos que ejecutan el mismo código. Para que un
programa indique en cuál está, debe verificar el valor de retorno de
fork()
.
Si el valor es 0
, el proceso actual es el hijo. Si no es 0
, el
programa se está ejecutando en el proceso primario y el valor de retorno es la
identificación del proceso secundario.
import os
import signal
import time
def signal_usr1(signum, frame):
"Callback invoked when a signal is received"
pid = os.getpid()
print('Received USR1 in process {}'.format(pid))
print('Forking...')
child_pid = os.fork()
if child_pid:
print('PARENT: Pausing before sending signal...')
time.sleep(1)
print('PARENT: Signaling {}'.format(child_pid))
os.kill(child_pid, signal.SIGUSR1)
else:
print('CHILD: Setting up signal handler')
signal.signal(signal.SIGUSR1, signal_usr1)
print('CHILD: Pausing to wait for signal')
time.sleep(5)
El padre puede enviar señales al proceso hijo usando kill()
y el módulo
signal
. Primero, define un manejador de señal que se invocará cuando se
reciba la señal. Luego fork()
, y en el padre pausa un corto período de
tiempo antes de enviar una señal USR1
usando kill()
. Este ejemplo
utiliza una breve pausa para darle tiempo al proceso secundario para configurar
el controlador de señal. Una aplicación real, no necesitaría (o querría)
llamar a sleep()
. En el proceso hijo, configura el controlador de señal y
vaya a dormir por un tiempo para darle tiempo a los padres para enviar la
señal.
$ python3 -u os_kill_example.py
Forking...
PARENT: Pausing before sending signal...
CHILD: Setting up signal handler
CHILD: Pausing to wait for signal
PARENT: Signaling 29193
Received USR1 in process 29193
Una forma simple de manejar un comportamiento separado en el proceso secundario
es verificar el valor de retorno de fork()
y salir. Un comportamiento más
complejo puede requerir más separación de código que una salida simple. En
otros casos, puede haber un programa existente que necesita ser ajustado. Para
ambas situaciones, la serie de funciones exec*()
se puede usar para
ejecutar otro programa.
import os
child_pid = os.fork()
if child_pid:
os.waitpid(child_pid, 0)
else:
os.execlp('pwd', 'pwd', '-P')
Cuando un programa es ejecutado por exec()
, el código de ese programa
reemplaza el código del proceso existente.
$ python3 os_exec_example.py
.../pymotw-3/source/os
Existen muchas variaciones de exec()
, dependiendo de la forma en que estén
disponibles los argumentos, si la ruta y el entorno del proceso padre deben
copiarse al hijo, etc. Para todas las variaciones, el primer argumento es una
ruta o nombre de archivo y los argumentos restantes controlan cómo se ejecuta
ese programa. Se pasan como argumentos de línea de comandos o anulan el
«entorno» del proceso (ve os.environ
y os.getenv
). Consulta la
documentación de la biblioteca para obtener detalles completos.
Esperar a procesos secundarios¶
Muchos programas computacionalmente intensivos utilizan múltiples procesos para
evitar las limitaciones de subprocesos de Python y el bloqueo global del
intérprete. Al iniciar varios procesos para ejecutar tareas separadas, el
proceso maestro tendrá que esperar a que uno o más de ellos finalicen antes de
iniciar otros nuevos, para evitar sobrecargar el servidor. Hay algunas formas
diferentes de hacerlo usando wait()
y funciones relacionadas.
Cuando no importa qué proceso secundario podría finalizar primero, usa
wait()
. Regresa tan pronto como se cierra cualquier proceso hijo.
import os
import sys
import time
for i in range(2):
print('PARENT {}: Forking {}'.format(os.getpid(), i))
worker_pid = os.fork()
if not worker_pid:
print('WORKER {}: Starting'.format(i))
time.sleep(2 + i)
print('WORKER {}: Finishing'.format(i))
sys.exit(i)
for i in range(2):
print('PARENT: Waiting for {}'.format(i))
done = os.wait()
print('PARENT: Child done:', done)
El valor de retorno de wait()
es una tupla que contiene la identificación
del proceso y el estado de salida combinados en un valor de 16 bits. El byte
bajo es el número de la señal que mató el proceso, y el byte alto es el código
de estado devuelto por el proceso cuando salió.
$ python3 -u os_wait_example.py
PARENT 29202: Forking 0
PARENT 29202: Forking 1
PARENT: Waiting for 0
WORKER 0: Starting
WORKER 1: Starting
WORKER 0: Finishing
PARENT: Child done: (29203, 0)
PARENT: Waiting for 1
WORKER 1: Finishing
PARENT: Child done: (29204, 256)
Para esperar un proceso específico, usa waitpid()
.
import os
import sys
import time
workers = []
for i in range(2):
print('PARENT {}: Forking {}'.format(os.getpid(), i))
worker_pid = os.fork()
if not worker_pid:
print('WORKER {}: Starting'.format(i))
time.sleep(2 + i)
print('WORKER {}: Finishing'.format(i))
sys.exit(i)
workers.append(worker_pid)
for pid in workers:
print('PARENT: Waiting for {}'.format(pid))
done = os.waitpid(pid, 0)
print('PARENT: Child done:', done)
Pasa la identificación del proceso del proceso de destino y waitpid()
bloquea hasta que ese proceso finalice.
$ python3 -u os_waitpid_example.py
PARENT 29211: Forking 0
PARENT 29211: Forking 1
PARENT: Waiting for 29212
WORKER 0: Starting
WORKER 1: Starting
WORKER 0: Finishing
PARENT: Child done: (29212, 0)
PARENT: Waiting for 29213
WORKER 1: Finishing
PARENT: Child done: (29213, 256)
wait3()
y wait4()
funcionan de manera similar, pero devuelven
información más detallada sobre el proceso secundario con el pid, el estado de
salida y el uso de recursos.
Generar nuevos procesos¶
Como conveniencia, la familia de funciones spawn()
maneja el fork()
y
exec()
en una declaración:
import os
os.spawnlp(os.P_WAIT, 'pwd', 'pwd', '-P')
El primer argumento es un modo que indica si esperar o no a que finalice el
proceso antes de regresar. Este ejemplo espera. Usa P_NOWAIT
para
permitir que comience el otro proceso, pero luego continúe en el proceso
actual.
$ python3 os_spawn_example.py
.../pymotw-3/source/os
Códigos de error del sistema operativo¶
Los códigos de error definidos por el sistema operativo y administrados en el
módulo errno
se pueden traducir a cadenas de mensajes usando
strerror()
.
import errno
import os
for num in [errno.ENOENT, errno.EINTR, errno.EBUSY]:
name = errno.errorcode[num]
print('[{num:>2}] {name:<6}: {msg}'.format(
name=name, num=num, msg=os.strerror(num)))
Este ejemplo muestra los mensajes asociados con algunos códigos de error que aparecen con frecuencia.
$ python3 os_strerror.py
[ 2] ENOENT: No such file or directory
[ 4] EINTR : Interrupted system call
[16] EBUSY : Resource busy
Ver también
- Documentación de la biblioteca estándar para os
- Notas para portar Python 2 a 3 para os
signal
– La sección del módulo designal
repasa las técnicas de manejo de señal con más detalle.subprocess
– El módulosubprocess
reemplaza aos.popen()
.multiprocessing
– El módulomultiprocessing
facilita el trabajo con procesos adicionales.tempfile
– El módulotempfile
para trabajar con archivos temporales.- Trabajar con árboles de directorio – El módulo
shutil
incluye también funciones para trabajar con árboles de directorios. - Speaking UNIX, Part 8. – Aprende cómo UNIX maneja tareas múltiples.
- Standard streams – Para más discusión sobre stdin, stdout y stderr.
- Delve into Unix Process Creation – Explica el ciclo de vida de un proceso de Unix.
- Advanced Programming in the UNIX(R) Environment De W. Richard Stevens y Stephen A. Rago. Publicado por Addison-Wesley Professional, 2005. ISBN-10: 0201433079 – Este libro cubre el trabajo con múltiples procesos, como el manejo de señales, el cierre de descriptores de archivos duplicados, etc.