Рубрики
Без рубрики

os – переносимый доступ к специфическим функциям операционной системы

Автор оригинала: Doug Hellmann.

Цель:

Портативный доступ к специфическим функциям операционной системы.

Модуль os предоставляет оболочку для модулей конкретной платформы, таких как posix , nt и mac . API для функций, доступных на всех платформах, должен быть одинаковым, поэтому использование модуля os обеспечивает некоторую мобильность. Однако не все функции доступны на каждой платформе. Многие из функций управления процессами, описанных в этом обзоре, недоступны для Windows.

Документация Python для модуля os имеет подзаголовок «Разные интерфейсы операционной системы». Модуль состоит в основном из функций для создания и управления запущенными процессами или содержимым файловой системы (файлами и каталогами), а также из нескольких других функций.

Изучение содержимого файловой системы

Чтобы подготовить список содержимого каталога в файловой системе, используйте listdir () .

os_listdir.py

import os
import sys

print(sorted(os.listdir(sys.argv[1])))

Возвращаемое значение – это несортированный список всех названных членов данного каталога. Не делается различий между файлами, подкаталогами или символическими ссылками.

$ 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']

Функция walk () рекурсивно просматривает каталог и для каждого подкаталога генерирует кортеж , содержащий путь к каталогу, любые непосредственные подкаталоги этого пути и список имен любых файлов в этом каталоге.

os_walk.py

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()

В этом примере показан рекурсивный список каталогов.

$ 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

Если требуется больше информации, чем имена файлов, вероятно, будет более эффективно использовать scandir () , чем listdir () , поскольку больше информации собирается в одной системе. звоните, когда каталог сканируется.

os_scandir.py

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(
        nameentry.name,
        typtyp,
    ))

scandir () возвращает последовательность экземпляров DirEntry для элементов в каталоге. У объекта есть несколько атрибутов и методов для доступа к метаданным о файле.

$ 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

Управление разрешениями файловой системы

Подробную информацию о файле можно получить с помощью stat () или lstat () (для проверки статуса чего-то, что может быть символической ссылкой).

os_stat.py

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))

Результат будет зависеть от того, как был установлен пример кода. Попробуйте передать в 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

В Unix-подобных системах права доступа к файлам можно изменить с помощью chmod () , передав режим как целое число. Значения режима могут быть созданы с использованием констант, определенных в модуле stat . В этом примере переключается бит разрешения пользователя на выполнение:

os_stat_chmod.py

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)

Сценарий предполагает, что у него есть разрешения, необходимые для изменения режима файла при запуске.

$ python3 os_stat_chmod.py

Adding execute permission

Функцию access () можно использовать для проверки прав доступа процесса к файлу.

os_access.py

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))

Результаты будут отличаться в зависимости от того, как установлен пример кода, но результат будет примерно таким:

$ python3 os_access.py

Testing: os_access.py
Exists: True
Readable: True
Writable: True
Executable: False

В документации библиотеки для access () есть два специальных предупреждения. Во-первых, нет особого смысла вызывать access () для проверки возможности открытия файла перед фактическим вызовом для него open () . Между двумя вызовами есть небольшой, но реальный промежуток времени, в течение которого права доступа к файлу могут измениться. Другое предупреждение относится в основном к сетевым файловым системам, которые расширяют семантику разрешений POSIX. Некоторые типы файловых систем могут отвечать на вызов POSIX о том, что у процесса есть разрешение на доступ к файлу, а затем сообщать об ошибке, когда попытка выполняется с помощью open () по какой-то причине, не проверенной с помощью вызова POSIX. В общем, лучше вызвать open () с требуемым режимом и поймать IOError , возникшую в случае возникновения проблемы.

Создание и удаление каталогов

Есть несколько функций для работы с каталогами в файловой системе, включая создание, перечисление содержимого и его удаление.

os_directories.py

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)

Есть два набора функций для создания и удаления каталогов. При создании нового каталога с помощью mkdir () все родительские каталоги должны уже существовать. При удалении каталога с помощью rmdir () фактически удаляется только конечный каталог (последняя часть пути). Напротив, makedirs () и deletedirs () работают со всеми узлами в пути. makedirs () создаст любые несуществующие части пути, а deletedirs () удалит все родительские каталоги, пока они пусты.

$ python3 os_directories.py

Creating os_directories_example
Creating os_directories_example/example.txt
Cleaning up

Работа с символическими ссылками

Для платформ и файловых систем, которые их поддерживают, есть функции для работы с символическими ссылками.

os_symlinks.py

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)

Используйте symlink () для создания символической ссылки и readlink () для ее чтения, чтобы определить исходный файл, на который указывает ссылка. Функция lstat () похожа на stat () , но работает с символическими ссылками.

$ python3 os_symlinks.py

Creating link /tmp/os_symlinks.py -> os_symlinks.py
Permissions: 0o120755
Points to: os_symlinks.py

Безопасная замена существующего файла

Замена или переименование существующего файла не является идемпотентным и может подвергнуть приложения условиям гонки. Функции rename () и replace () реализуют безопасные алгоритмы для этих действий, используя атомарные операции в POSIX-совместимых системах, когда это возможно.

os_rename_replace.py

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)

Функции rename () и replace () большую часть времени работают в файловых системах. Переименование файла может завершиться неудачно, если он перемещается в новую файловую систему или место назначения уже существует.

$ 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'

Обнаружение и изменение владельца процесса

Следующий набор функций, предоставляемых os , используется для определения и изменения идентификаторов владельцев процессов. Чаще всего они используются авторами демонов или специальных системных программ, которым необходимо изменить уровень разрешений, а не запускаться от имени root . Этот раздел не пытается объяснить все сложные детали безопасности Unix, владельцев процессов и т. Д. См. Список ссылок в конце этого раздела для получения более подробной информации.

В следующем примере показаны реальные и эффективные сведения о пользователях и группах для процесса, а затем изменяются действующие значения. Это аналогично тому, что демон должен был бы сделать, когда он запускается от имени пользователя root во время загрузки системы, чтобы снизить уровень привилегий и запустить его от имени другого пользователя.

Примечание

Перед запуском примера измените значения TEST_GID и TEST_UID , чтобы они соответствовали реальному пользователю, определенному в системе.

os_process_user_example.py

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()

При запуске от имени пользователя с идентификатором 502 и группой 502 в OS X создается следующий вывод:

$ 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.

Значения не меняются, потому что, когда он не запущен как root, процесс не может изменить значение своего эффективного владельца. Любая попытка установить для эффективного идентификатора пользователя или группы значение, отличное от идентификатора текущего пользователя, вызывает OSError . Другой сценарий – запустить тот же сценарий с использованием sudo , чтобы он запускался с привилегиями root.

$ 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]

В этом случае, поскольку он запускается от имени пользователя root, сценарий может изменить действующего пользователя и группу для процесса. После изменения действующего UID процесс ограничивается разрешениями этого пользователя. Поскольку пользователи без полномочий root не могут изменить свою действующую группу, программе необходимо изменить группу перед изменением пользователя.

Управление технологической средой

Другой особенностью операционной системы, доступной программе через модуль os , является среда. Переменные, установленные в среде, отображаются в виде строк, которые можно прочитать с помощью os.environ или getenv () . Переменные среды обычно используются для значений конфигурации, таких как пути поиска, расположение файлов и флаги отладки. В этом примере показано, как получить переменную среды и передать значение дочернему процессу.

os_environ_example.py

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')

Объект os.environ следует стандартному API сопоставления Python для получения и установки значений. Изменения в os.environ экспортируются для дочерних процессов.

$ 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:

Управление рабочим каталогом процесса

Операционные системы с иерархическими файловыми системами имеют понятие текущий рабочий каталог – каталог в файловой системе, который процесс использует в качестве начального местоположения при доступе к файлам по относительным путям. Текущий рабочий каталог можно получить с помощью getcwd () и изменить с помощью chdir () .

os_cwd_example.py

import os

print('Starting:', os.getcwd())

print('Moving up one:', os.pardir)
os.chdir(os.pardir)

print('After move:', os.getcwd())

os.curdir и os.pardir используются для ссылки на текущий и родительский каталоги переносимым способом.

$ python3 os_cwd_example.py

Starting: .../pymotw-3/source/os
Moving up one: ..
After move: .../pymotw-3/source

Запуск внешних команд

Предупреждение

Многие из этих функций для работы с процессами имеют ограниченную переносимость. Для более последовательного способа работы с процессами независимо от платформы см. Вместо этого модуль подпроцесса.

Самый простой способ запустить отдельную команду, не взаимодействуя с ней вообще, – это system () . Он принимает единственный строковый аргумент, который представляет собой командную строку, выполняемую подпроцессом, выполняющим оболочку.

os_system_example.py

import os

# Simple command
os.system('pwd')

Возвращаемое значение system () – это значение выхода оболочки, запускающей программу, упакованную в 16-битное число, причем старший байт – статус выхода, а младший байт – номер сигнала, который вызвал выполнение процесса. умереть или ноль.

$ python3 -u os_system_example.py

.../pymotw-3/source/os

Поскольку команда передается непосредственно в оболочку для обработки, она может включать синтаксис оболочки, такой как глобализация или переменные среды.

os_system_shell.py

import os

# Command with shell expansion
os.system('echo $TMPDIR')

Переменная среды $ TMPDIR в этой строке раскрывается, когда оболочка запускает командную строку.

$ python3 -u os_system_shell.py

/var/folders/5q/8gk0wq888xlggz008k8dr7180000hg/T/

Если команда явно не запускается в фоновом режиме, вызов system () блокируется до завершения. Стандартный ввод, вывод и ошибка дочернего процесса привязаны к соответствующим потокам, принадлежащим вызывающему по умолчанию, но могут быть перенаправлены с помощью синтаксиса оболочки.

os_system_background.py

import os
import time

print('Calling...')
os.system('date; (sleep 3; date) &')

print('Sleeping...')
time.sleep(5)

Однако это приводит к обману в оболочке, и есть более эффективные способы сделать то же самое.

$ python3 -u os_system_background.py

Calling...
Sat Dec 31 12:33:20 EST 2016
Sleeping...
Sat Dec 31 12:33:23 EST 2016

Создание процессов с помощью os.fork ()

Функции POSIX fork () и exec () (доступные в Mac OS X, Linux и других вариантах Unix) доступны через os модуль. О надежном использовании этих функций написаны целые книги, поэтому проверьте библиотеку или книжный магазин для получения более подробной информации, чем представлено здесь в этом введении.

Чтобы создать новый процесс как клон текущего процесса, используйте fork () :

os_fork_example.py

import os

pid  os.fork()

if pid:
    print('Child process id:', pid)
else:
    print('I am the child')

Результат будет зависеть от состояния системы каждый раз при запуске примера, но он будет выглядеть примерно так:

$ python3 -u os_fork_example.py

Child process id: 29190
I am the child

После вилки два процесса выполняют один и тот же код. Чтобы программа могла определить, в какой из них она находится, ей необходимо проверить возвращаемое значение fork () . Если значение равно 0 , текущий процесс является дочерним. Если это не 0 , программа выполняется в родительском процессе, и возвращаемое значение – идентификатор процесса дочернего процесса.

os_kill_example.py

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)

Родитель может отправлять сигналы дочернему процессу, используя kill () и модуль signal. Во-первых, определите обработчик сигнала, который будет вызываться при получении сигнала. Затем fork () и в родительской паузе на короткое время перед отправкой сигнала USR1 с помощью kill () . В этом примере используется короткая пауза, чтобы дать дочернему процессу время для настройки обработчика сигнала. Настоящему приложению не требуется (или не нужно) вызывать sleep () . В дочернем элементе настройте обработчик сигнала и отправляйтесь в режим сна на некоторое время, чтобы дать родителю время отправить сигнал.

$ 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

Простой способ справиться с раздельным поведением дочернего процесса – проверить возвращаемое значение fork () и ветвь. Более сложное поведение может потребовать большего разделения кода, чем простая ветвь. В других случаях может быть существующая программа, которую нужно обернуть. Для обеих этих ситуаций серию функций exec * () можно использовать для запуска другой программы.

os_exec_example.py

import os

child_pid  os.fork()
if child_pid:
    os.waitpid(child_pid, 0)
else:
    os.execlp('pwd', 'pwd', '-P')

Когда программа запускается с помощью exec () , код этой программы заменяет код существующего процесса.

$ python3 os_exec_example.py

.../pymotw-3/source/os

Существует множество вариантов exec () , в зависимости от формы, в которой доступны аргументы, от того, следует ли копировать путь и среду родительского процесса в дочерний процесс и т. Д. Для всех вариантов первый аргумент – это путь или имя файла, а остальные аргументы управляют работой этой программы. Они либо передаются в качестве аргументов командной строки, либо переопределяют «среду» процесса (см. os.environ и os.getenv ). Обратитесь к документации библиотеки для получения полной информации.

Ожидание дочерних процессов

Многие программы с интенсивными вычислениями используют несколько процессов для обхода ограничений потоковой передачи Python и глобальной блокировки интерпретатора. При запуске нескольких процессов для выполнения отдельных задач мастеру нужно будет дождаться завершения одного или нескольких из них, прежде чем запускать новые, чтобы избежать перегрузки сервера. Есть несколько способов сделать это с помощью wait () и связанных функций.

Если не имеет значения, какой дочерний процесс может завершиться первым, используйте wait () . Он возвращается, как только завершается любой дочерний процесс.

os_wait_example.py

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)

Возвращаемое значение из wait () – это кортеж, содержащий идентификатор процесса и статус выхода, объединенные в 16-битное значение. Младший байт – это номер сигнала, завершившего процесс, а старший байт – это код состояния, возвращаемый процессом при выходе.

$ 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)

Чтобы дождаться определенного процесса, используйте waitpid () .

os_waitpid_example.py

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)

Передайте идентификатор целевого процесса, и waitpid () блокируется, пока этот процесс не завершится.

$ 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 () и wait4 () работают аналогичным образом, но возвращают более подробную информацию о дочернем процессе с идентификатором pid, статусом выхода и использованием ресурсов.

Создание новых процессов

Для удобства семейство функций spawn () обрабатывает fork () и exec () в одном операторе:

os_spawn_example.py

import os

os.spawnlp(os.P_WAIT, 'pwd', 'pwd', '-P')

Первый аргумент – это режим, указывающий, следует ли ждать завершения процесса перед возвратом. Этот пример ждет. Используйте P_NOWAIT , чтобы разрешить запуск другого процесса, но затем возобновить текущий процесс.

$ python3 os_spawn_example.py

.../pymotw-3/source/os

Коды ошибок операционной системы

Коды ошибок, определенные операционной системой и управляемые в модуле errno , могут быть преобразованы в строки сообщения с помощью strerror () .

os_strerror.py

import errno
import os

for num in [errno.ENOENT, errno.EINTR, errno.EBUSY]:
    name  errno.errorcode[num]
    print('[{num:>2}] {name:<6}: {msg}'.format(
        namename, numnum, msgos.strerror(num)))

В этом примере показаны сообщения, связанные с некоторыми кодами ошибок, которые часто возникают.

$ python3 os_strerror.py

[ 2] ENOENT: No such file or directory
[ 4] EINTR : Interrupted system call
[16] EBUSY : Resource busy

Смотрите также

  • стандартная библиотека документации для ОС
  • Заметки о переносе Python 2 на 3 для os
  • signal – в разделе модуля signal более подробно рассматриваются методы обработки сигналов.
  • subprocess – модуль subprocess заменяет os.popen () .
  • многопроцессорность – модуль multiprocessing упрощает работу с дополнительными процессами.
  • tempfile – модуль tempfile для работы с временными файлами.
  • Работа с деревьями каталогов – модуль shutil также включает функции для работы с деревьями каталогов.
  • Использование UNIX, часть 8. – Узнайте, как в UNIX решается многозадачность.
  • Стандартные потоки – для более подробного обсуждения stdin, stdout и stderr.
  • Подробнее о создании процесса Unix – объясняет жизненный цикл процесса Unix.
  • Расширенное программирование в среде UNIX (R) У. Ричард Стивенс и Стивен А. Раго. Опубликовано Addison-Wesley Professional, 2005. ISBN-10: 0201433079 – В этой книге рассматривается работа с несколькими процессами, например обработка сигналов, закрытие дублированных файловых дескрипторов и т. Д.