zoukankan      html  css  js  c++  java
  • Python os.md

    os

    便携式访问操作系统的特定功能。os模块提供了对特定平台模块(如posix, nt, mac)的封装, 函数提供的api在很多平台上都可以相同使用, 所以使用os模块会变得很方便. 但不是所有函数在所有平台上都可用, 比如在本文中到的一些管理函数在windows上无法使用.
    在Python文档中os模块的副标题是”操作系统混合接口”, 模块包含的大部分函数用于创建和管理活动进程和文件系统(文件和目录), 当然除此之外还有其它一些函数. 本文中, 我们对如何获取和设置进程参数来进行讨论.

    Examining the File System Contents

    • os.listdir(path='.'):返回一个list,包含给定path 目录下所有条目的名字。该list是任意顺序,不包括特殊条目'.'以及'..',即使它们存在于目录中。path可以是str类型或bytes类型。如果path的类型为bytes,则返回的文件名也将为bytes类型;否则,它们的类型为str。
    • os.walk(top, topdown=True, onerror=None, followlinks=False):通过遍历目录树,自顶向下或自底向上生成目录树下的文件名。对于在根目录 top (包括根目录top 本身), 它都会yield一个3元tuple (dirpath, dirnames, filenames).
      dirpath是一个字符串,为目录路径。dirnames是dirpath中子目录的名称列表(不包括'.'和'..').filenames 为dirpath 下非目录文件的名称列表。注意,列表中的名称不包含路径部分。要获得dirpath 中的文件或目录的完整路径(以 top开头), 请使用os.path.join(dirpath, name).
      如果可选参数topdown为True或未指定,则在生成其任何子目录的三元组tuple之前生成其本身的三元组tuple。(简言之就是自上而下遍历)如果topdown是False,则在生成所有子目录的三元组之后生成其本身的三元组(即自下而上生成)。
      当topdown是True时,调用者可以修改dirnames列表(可能使用del )和walk()只会递归到名称保留在dirnames中的子目录;这可以用于修剪搜索,强加特定的访问顺序,或甚至通知walk()关于调用者在恢复walk()再次。在topdown为False时修改dir名称对步行的行为没有影响,因为在自下而上模式下,在生成dirpath之前生成。
      默认情况下,来自listdir()的错误将被忽略。如果指定了可选参数onerror,它应该是一个函数;it will be called with one argument, an OSError instance.它可以报告错误以继续遍历还是引发一个异常以停止遍历。请注意,文件名可用作异常对象的filename属性。
      默认情况下,walk()不会向下走到符号链接解析到目录。将followlinks设置为True以访问支持它们的系统上symlinks指向的目录。
    • is_dir(*, follow_symlinks=True):如果此条目是指向目录的目录或符号链接,则返回True;如果条目是或指向任何其他类型的文件,或者如果它不再存在,则返回False。如果follow_symlinks是False,则只有在此条目是目录(没有以下符号链接)时才返回True如果条目是任何其他类型的文件或者如果它不再存在,则返回False。
    • is_file(*, follow_symlinks=True):如果此条目是指向文件的文件或符号链接,则返回True;如果条目是或指向目录或其他非文件条目,或者如果它不再存在,则返回False。
      如果follow_symlinks是False,则只有在此条目是文件(没有以下符号链接)时才返回True如果条目是目录或其他非文件条目,或者如果它不再存在,则返回False。
    • is_symlink():如果此条目是符号链接(即使已损坏),则返回True;如果条目指向目录或任何类型的文件,或者如果它不再存在,则返回False。

    举例
    要列出文件系统上目录的内容列表,请使用listdir()。

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

    返回值是给定目录的所有命名成员的列表。文件、子目录或符号链接之间没有区别。

    # python os_study.py .
    ['shutil_copymode.py', 'os_study.py', 'testdata.csv', 'example', 'testout.csv', 'csv_study.py', 'unicode_chars', 'file_to_change.txt', 'shutil_copyfile.py', 'testdata.pipes', 'os_path_study.py', 'shutil_copy2.py', 'shutil_copyfile.py.copy', 'shutil_copystat.py', 'testdata_1.csv', 'shutil_study.py', 'shutil_copy.py']
    

    函数walk()递归地遍历一个目录,每个子目录生成一个包含目录路径的元组,该路径的任何直接子目录,以及该目录中任何文件的名称列表。

    import os
    import sys
    
    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)
        sub_dirs = [n + '/' for n in sub_dirs]
        contents = sub_dirs + files
        contents.sort()
        for c in contents:
            print('   {}'.format(c))
        print()
    

    这个例子显示了一个递归目录列表。

    # python os_study.py example/
    example/
       shutil_copy2.py
    
    

    如果需要的信息比文件的名称更多,那么使用scandir()比listdir()可能更高效,因为在扫描目录时,会在一个系统调用中收集更多的信息。

    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 = 'unknow'
        print(' {name}  {typ}'.format(name=entry.name, typ=typ))
    

    scandir()返回目录中条目的DirEntry实例的序列。这个对象有几个属性和方法来访问关于文件的元数据。

    # python os_study.py .
     shutil_copymode.py  file
     os_study.py  file
     testdata.csv  file
     example  dir
     testout.csv  file
     csv_study.py  file
     unicode_chars  file
     file_to_change.txt  file
     shutil_copyfile.py  file
     testdata.pipes  file
     os_path_study.py  file
     shutil_copy2.py  file
     shutil_copyfile.py.copy  file
     shutil_copystat.py  file
     testdata_1.csv  file
     shutil_study.py  file
     shutil_copy.py  file
    

    Managing File System Permissions

    os.stat(path, *, dir_fd=None, follow_symlinks=True):获取文件或文件的状态描述器。在给定路径上执行等效于stat()的系统调用。路径可以指定为字符串或打开的文件描述器。返回stat_result对象。
    class os.stat_result:对象的属性大致对应于stat结构的成员。它用于os.stat(),os.fstat()和os.lstat()的结果。

    • st_mode:文件模式:文件类型和文件模式位(权限)。
    • st_ino:信息节点号。
    • st_dev:此文件所在的设备的标识符。
    • st_nlink:硬链接数。
    • st_uid:文件所有者的用户标识符。
    • st_gid:文件所有者的组标识符。
    • st_size:文件大小(以字节为单位),如果它是常规文件或符号链接。符号链接的大小是它包含的路径名的长度,没有终止的空字节。
    • st_atime:最近访问时间(以秒为单位)。
    • st_mtime:最近内容修改的时间(以秒为单位)。
    • st_ctime:平台依赖:
      在Unix上最新的元数据更改的时间,
      在Windows上创建的时间,以秒为单位。
    • st_blocks:分配给文件的512字节块的数量。当文件有空洞时,这可能小于st_size / 512。
    • st_blksize:“首选”块大小,用于高效的文件系统I / O。以较小的块写入文件可能导致无效的读取 - 修改 - 重写。
    • st_rdev:如果是inode设备,则为设备类型。
    • st_flags:用户定义的文件标志。

    os.access(path, mode, *, dir_fd=None, effective_ids=False, follow_symlinks=True):用真实的 uid/gid (用户ID/组ID) 去测试 path (路径)自变量访问权限。

    举例
    可以使用stat()或lstat()来访问关于文件的详细信息(用于检查可能是符号链接的状态的状态)。

    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_study.py。

    # python os_study.py 
    os.stat(os_study.py):
       Size: 523
       Permissions: 0o100644
       Owner: 0
       Device: 2050
       Created      : Fri Jul 28 00:25:28 2017
       Last modified: Fri Jul 28 00:25:28 2017
       Last accessed: Fri Jul 28 00:25:37 2017
    # python os_study.py example/shutil_copy2.py 
    os.stat(example/shutil_copy2.py):
       Size: 16
       Permissions: 0o100644
       Owner: 0
       Device: 2050
       Created      : Mon Jul 10 00:03:26 2017
       Last modified: Mon Jul 10 00:03:16 2017
       Last accessed: Mon Jul 10 00:24:14 2017
    

    在类unix系统中,可以使用chmod()更改文件权限,将模式作为整数传递。模式值可以使用stat模块中定义的常量来构造。这个例子可以切换用户的执行权限位:

    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')
    
    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
    
    os.chmod(filename, new_permissions)
    

    该脚本假定它具有在运行时修改文件模式所必需的权限。

    # python os_study.py
    Adding execute permission
    # ll
    -rwxr--r-- 1 root root   8 81 23:33 os_stat_chmod_example.txt
    -rw-r--r-- 1 root root 431 81 23:33 os_study.py
    # cat os_stat_chmod_example.txt 
    contents
    

    函数access()可以用来测试一个进程对一个文件的访问权限。

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

    结果将根据示例代码的安装方式而有所不同,但输出结果与此类似:

    # python os_study.py
    Testing: os_study.py
    Exists: True
    Readable: True
    Writable: True
    Executable: False
    

    access()的库文档包括两个特别的警告。首先,在调用open()之前,调用access()来测试一个文件是否可以打开是没有多大意义的。在两个调用之间有一个很小但实际的时间窗口,在此期间,文件的权限可能会发生变化。另一个警告主要应用于扩展POSIX许可语义的网络文件系统。一些文件系统类型可能响应一个进程有权限访问一个文件的POSIX调用,然后在尝试使用open()进行尝试时报告失败,因为某些原因没有通过POSIX调用进行测试。总而言之,最好是使用所需模式调用open(),并在出现问题时捕获IOError。

    Creating and Deleting Directories

    os.mkdir(path, mode=0o777, *, dir_fd=None):使用数字模式模式创建一个名为path的目录。如果目录已存在,则会引发FileExistsError。在某些系统上,模式被忽略。在使用它的地方,当前的umask值首先被屏蔽。如果除了最后9个之外的位(即,设置模式的八进制表示的最后3位数字),它们的含义是平台相关的。在某些平台上,它们被忽略,您应该显式调用chmod()来设置它们。
    os.makedirs(name, mode=0o777, exist_ok=False):递归目录创建函数。像mkdir(),但使所有中间级目录需要包含叶子目录。如果exists_ok为False(默认值),则如果目标目录已存在,则会引发OSError。
    os.remove(path, *, dir_fd=None):Remove (delete) the file path. 如果路径是目录,则会引发OSError。使用rmdir()删除目录。在Windows上,尝试删除正在使用的文件会导致引发异常;在Unix上,目录条目将被删除,但是在原始文件不再使用之前,不会使分配给该文件的存储可用。
    os.unlink(path, *, dir_fd=None):Remove (delete) the file path. 此函数在语义上与remove()相同; unlink名称是其传统的Unix名称。
    os.rmdir(path, *, dir_fd=None):Remove (delete) the directory path. 仅当目录为空时才有效,否则引发OSError。为了删除整个目录树,可以使用shutil.rmtree()。
    os.removedirs(name):Remove directories recursively. 除了如果叶目录被成功删除,removedirs()尝试连续删除path中提到的每个父目录时,rmdir()直到出现错误(其被忽略,因为它通常意味着父目录不为空)。例如,os.removedirs('foo/bar/baz')将首先删除目录'foo/bar/baz',然后删除'foo/bar'和'foo'。如果无法成功删除叶子目录,则引发OSError。

    举例
    在文件系统上处理目录有几个函数,包括创建、列出内容和删除它们。

    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()和removedirs()对路径中的所有节点进行操作。makedirs()将创建不存在的路径的任何部分,并且removedirs()将删除所有的父目录,只要它们是空的。

    # python os_study.py
    Creating os_directories_example
    Creating os_directories_example/example.txt
    Cleaning up
    

    Working with Symbolic Links

    os.symlink(src, dst, target_is_directory=False, *, dir_fd=None):创建指向名为dst的src的符号链接。在Windows上,符号链接表示文件或目录,并且不会动态地变形到目标。如果目标存在,将创建符合连接的类型进行匹配。否则,如果target_is_directory为True,则符号链接将创建为目录,否则为符号链接(默认)。在非Windows平台上,将忽略target_is_directory。
    os.lstat(path, *, dir_fd=None):在给定路径上执行等效于lstat()的系统调用。类似于stat(),但不遵循符号链接。返回stat_result对象。在不支持符号链接的平台上,这是stat()的别名。
    os.readlink(path, *, dir_fd=None):返回一个表示符号链接指向的路径的字符串。结果可以是绝对路径名或相对路径名;如果是相对的,则可以使用os.path.join(os.path.dirname(path), result) t0>。

    如果路径是字符串对象,则结果也将是一个字符串对象,并且调用可能引发UnicodeDecodeError。如果路径是字节对象,则结果将是一个字节对象。

    举例
    对于支持它们的平台和文件系统,有使用符号链接的功能。

    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))
    
    os.unlink(link_name)
    

    使用symlink()来创建一个符号链接和readlink()来读取它,以确定链接指向的原始文件。lstat()函数就像stat(),是用来操作符号链接。

    # python os_study.py
    Creating link /tmp/os_study.py -> os_study.py
    Permissions: 0o120777
    Points to: os_study.py
    

    Safely Replacing an Existing File

    os.rename(src, dst, *, src_dir_fd=None, dst_dir_fd=None):重命名src 文件或目录为dst。如果dst是目录,则会引发OSError。在Unix 上,如果dst 存在而且是文件,如果用户有权限它将被默默地替换。如果src 和dst 位于不同的文件系统上,在某些Unix 上可能会失败。如果成功,该重命名将是一个原子操作(这时POSIX 的要求)。在Windows上,如果dst已经存在,则会引发OSError,即使它是一个文件。
    os.replace(src, dst, *, src_dir_fd=None, dst_dir_fd=None):重命名src 文件或目录为dst。如果dst是目录,则会引发OSError。如果dst存在并且是文件,如果用户具有权限,则将以静默替换。如果src和dst位于不同的文件系统上,则操作可能会失败。如果成功,该重命名将是一个原子操作(这时POSIX 的要求)。

    举例
    替换或重命名现有文件并不是一种强大的功能,可能会使应用程序暴露在竞争环境中。rename()和replace()函数实现这些操作的安全算法,在可能的情况下,使用基于posix的系统的原子操作。

    import os
    import glob
    
    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()函数在文件系统中工作,大多数情况下都是这样。如果将文件重命名为新文件系统,或者目标已经存在,那么重命名文件可能会失败。

    # python os_study.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'
    

    Detecting and Changing the Process Owner

    os.getuid():返回当前进程的真实用户ID。
    os.geteuid():回当前进程有效的用户ID。
    os.getgid():返回当前进程的实际组ID。
    os.getegid():返回当前进程有效的组ID。它对应于当前进程中正在执行的文件的“set id”。
    os.getgroups():返回与当前进程关联的附加组ID的列表。
    os.setegid(egid):设置当前进程的有效组ID。
    os.seteuid(euid):设置当前进程的有效用户ID。

    举例
    os 提供的下一组函数用于确定和更改进程所有者id。这些是守护进程或特殊系统程序的作者最常使用的,这些程序需要更改权限级别,而不是作为root运行。本节不试图解释Unix安全性、进程所有者等复杂的细节,请参阅本节末尾的参考列表以了解更多细节。
    下面的示例展示了一个流程的真实有效的用户和组信息,然后更改了有效值。这类似于一个守护进程在系统启动时作为根启动时需要做的事情,以降低特权级别并作为不同的用户运行。
    说明:在运行这个示例之前,要更改TEST_GID和TEST_UID值,以匹配系统上定义的实际用户。

    import os
    
    TEST_GID = 500
    TEST_UID = 500
    
    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 now change effective user.' 'Rerun as root.')
    else:
        print('CHANGE USER:')
        show_user_info()
        print()
    

    使用root用户运行,运行结果如下:

    BEFORE CHANGE:
    User (actual/effective)  : 0 / 0
    Group (actual/effective) : 0 / 0
    Actual Groups  : [0]
    
    CHANGE GROUP:
    User (actual/effective)  : 0 / 0
    Group (actual/effective) : 0 / 500
    Actual Groups  : [0]
    
    CHANGE USER:
    User (actual/effective)  : 0 / 500
    Group (actual/effective) : 0 / 500
    Actual Groups  : [0]
    
    

    Managing the Process Environment

    os.environ:表示字符串环境的mapping对象。例如,environ['HOME']是您的主目录的路径名(在某些平台上),与C中的getenv("HOME")
    此映射是在第一次导入os模块时捕获的,通常在Python启动期间作为处理site.py的一部分。此时间后对环境所做的更改不会反映在os.environ中,除了通过直接修改os.environ所做的更改。
    如果平台支持putenv()函数,则此映射可用于修改环境以及查询环境。putenv()将在映射修改时自动调用。
    os.system(command):在子shell中执行命令(字符串)。这通过调用标准C函数system()实现,并具有相同的限制。对sys.stdin等的更改不会反映在执行命令的环境中。如果命令生成任何输出,它将被发送到解释器标准输出流。
    在Unix上,返回值是以wait()指定的格式编码的进程的退出状态。注意,POSIX不指定C system()函数的返回值的含义,因此Python函数的返回值是系统相关的。

    举例
    此示例显示如何检索环境变量,并将值传递给子进程。

    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对象遵循标准的Python映射API,用于检索和设置值。 os.environ的更改导出为子进程。

    # python os_study.py
    Initial value: None
    Child process:
    
    
    Changed value: THIS VALUE WAS CHANGED
    Child process:
    THIS VALUE WAS CHANGED
    
    Removed value: None
    Child process:
    
    

    Managing the Process Working Directory

    os.getcwd():返回一条代表当前工作目录的字符串。
    os.pardir:操作系统使用的常量字符串来引用父目录。
    os.chdir(path):将当前的工作目录改为 path.

    举例
    具有层次文件系统的操作系统具有当前工作目录的概念——文件系统上的目录,当文件被相对路径访问时,进程使用该目录作为起始位置。可以使用getcwd()检索当前的工作目录,并使用chdir()进行更改。

    print('Starting:', os.getcwd())
    print('Moving up one:', os.pardir)
    os.chdir(os.pardir)
    print('After move:', os.getcwd())
    

    os.curdir和os.pardir用于以便携式方式引用当前和父目录。

    # python os_study.py
    Starting: /root/study/example
    Moving up one: ..
    After move: /root/study
    

    Creating Processes with os.fork()

    os.fork():Fork a child process. 在子项中返回0,在父项中返回子项的进程ID。如果发生错误OSError。
    os.getpid():返回当前进程id。
    os.getpgid(pid):返回进程ID为pid 的进程组ID。如果pid为0,返回当前进程的进程组ID。
    os.getpgrp():返回当前进程组的ID。
    os.getppid():返回父进程的进程ID。当父进程退出时,在Unix上返回的id是init进程(1)之一,在Windows上它仍然是同一个id,它可能已被另一个进程重用。
    os.kill(pid, sig):Send signal sig to the process pid. 在主机平台上可用的特定信号的常数在signal模块中定义。

    举例
    POSIX函数fork()和exec()(在Mac OS X、Linux和其他Unix变体下可用)通过OS模块公开。所有的书都是关于可靠地使用这些函数的,所以请检查图书馆或书店,了解更多的细节,而不是在这里介绍的。
    要创建一个新流程作为当前流程的克隆,使用fork():

    import os
    
    pid = os.fork()
    if pid:
        print('Chind process id:', pid)
    else:
        print('I am the child')
    

    每次运行示例时,输出都会根据系统的状态而变化,但它看起来是这样的:

    # python os_study.py
    Chind process id: 2722
    I am the child
    

    在fork之后,有两个进程运行相同的代码。对于一个程序来说,它需要检查fork()的返回值。如果值为0,则当前进程是子进程。如果它不是0,那么程序就在父进程中运行,而且返回值是子进程的进程id。

    import os
    import signal
    
    def signal_usr1(sigunm, frame):
        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(),在父进程中,在使用kill()发送USR1信号之前,暂停一小段时间。这个例子使用了一个短暂的停顿来给子进程设置信号处理程序的时间。一个真正的应用程序,不需要(或想要)调用sleep()。在子进程中,设置信号处理程序,然后休眠一段时间,给父母发送信号的时间。

    # python os_study.py
    Forking...
    PARENT: Pausing before sending signal...
    CHILD: Setting up signal handler
    CHILD: Pausing to wait for signal
    PARENT: Signaling 2870
    Received USR1 in process 2870
    

    在子进程中处理单独行为的一种简单方法是检查fork()和branch的返回值。与简单的分支相比,更复杂的行为可能需要更多的代码分离。在其他情况下,可能存在一个需要包装的现有程序。对于这两种情况,exec()系列函数都可以用来运行另一个程序。

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

    当一个程序由exec()运行时,该程序的代码将替换现有进程中的代码。

    # pwd
    /root/study/example
    # python os_study.py
    /root/study/example
    

    根据参数可用的形式,父进程的路径和环境是否应该复制到子目录中,exec()有很多变体。对于所有变体,第一个参数是一个路径或文件名 剩下的参数控制该程序的运行方式。 它们作为命令行参数传递或覆盖进程“environment”(请参阅os.environ和os.getenv)。

    Waiting for Child Processes

    os.wait():等待子进程的完成,并返回包含其pid和退出状态指示的元组:一个16位数字,其低字节是终止进程的信号编号,并且其高字节是退出状态(如果信号数字为零);如果产生了核心文件,则设置低字节的高位。
    os.waitpid(pid, options):这个功能的细节在Unix和Windows上有所不同。
    在Unix上:等待由进程id pid给出的子进程的完成,并返回包含其进程id和退出状态指示的元组(编码为wait() 。调用的语义受整数选项的值的影响,对于正常操作,该值应为0。
    如果pid大于0,waitpid()请求该特定进程的状态信息。如果pid是0,则请求用于当前进程的进程组中任何子进程的状态。如果pid是-1,则请求与当前进程的任何子进程相关。如果pid小于-1,则请求进程组-pid中任何进程的状态(pid )。

    举例
    许多计算密集型程序使用多个进程来处理Python和全局解释器锁的线程限制。当启动多个进程来运行单独的任务时,主程序将需要等待一个或多个进程在启动新任务之前完成,以避免服务器超载。使用wait()和相关函数有几种不同的方法。

    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()
    
    for i in range(2):
        print('PARENT: Waiting for {}'.format(i))
        done = os.wait()
        print('PARENT: Child done:', done)
    

    wait()的返回值是一个包含进程id和退出状态的元组,并将其组合为16位值。低字节是杀死进程的信号的数量,而高字节是进程在退出时返回的状态码。

    # python os_study.py
    PARENT 3162: Forking 0
    PARENT 3162: Forking 1
    PARENT: Waiting for 0
    WORKER 1: Starting
    WORKER 0: Starting
    WORKER 0: Finishing
    PARENT: Child done: (3198, 0)
    PARENT: Waiting for 1
    WORKER 1: Finishing
    PARENT: Child done: (3199, 0)
    

    要等待一个特定的进程,请使用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()
        workers.append(worker_pid)
    
    for pid in workers:
        print('PARENT: Waiting for {}'.format(pid))
        done = os.waitpid(pid, 0)
        print('PARENT: Child done:', done)
    

    传递目标进程的进程id和waitpid()块,直到该进程退出。

    # python os_study.py
    PARENT 3290: Forking 0
    PARENT 3290: Forking 1
    PARENT: Waiting for 3326
    WORKER 1: Starting
    WORKER 0: Starting
    WORKER 0: Finishing
    PARENT: Child done: (3326, 0)
    PARENT: Waiting for 3327
    WORKER 1: Finishing
    PARENT: Child done: (3327, 0)
    

    wait3()和wait4()以类似的方式工作,但是返回更详细的关于子进程的信息,使用pid、退出状态和资源使用。

    Spawning New Processes

    os.spawnlp(mode, file, ...):如果模式为P_NOWAIT,则此函数返回新进程的进程ID;如果模式为P_WAIT,则返回进程的退出代码(如果正常退出)或-signal,其中信号杀死了进程的信号。在Windows上,进程id实际上是进程句柄,因此可以与waitpid()函数一起使用。

    http://python.usyiyi.cn/translate/python_352/library/os.html#os.spawnlp
    举例
    为了方便起见,spawn()函数家族在一个语句中处理fork()和exec()

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

    第一个参数是一个模式,指示是否等待进程在返回之前完成。这个例子中等待。使用pnowait让其他进程启动,然后在当前进程中恢复。

    # python os_study.py
    /root/study/example
    

    Operating System Error Codes

    os.strerror(code):设置code 中的错误码对应的错误信息。在strerror()返回NULL的平台上,如果给出未知错误编号,则会引发ValueError。

    举例
    操作系统定义的错误代码和在errno模块中管理的错误代码可以使用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)))
    

    这个例子显示了与经常出现的错误代码相关联的消息。

    # python os_study.py
    [ 2] ENOENT: No such file or directory
    [ 4] EINTR : Interrupted system call
    [16] EBUSY : Device or resource busy
    
  • 相关阅读:
    1. 初探--prometheus调研
    Spring boot采坑记--- 在启动时RequstMappingHandlerMapping无法找到部分contorller类文件的解决方案
    Servlet——映射细节、3.0注解配置、线程不安全问题
    Servlet——概述、实现方式、生命周期、ServletConfig类
    HTTP——概述、请求和响应、GET和POST请求
    Tomcat——简介、目录结构等
    XML解析——Dom4j解析器
    XML解析——Jsoup解析器
    XML解析——Jaxp解析器
    XML——简介、语法、约束、解析
  • 原文地址:https://www.cnblogs.com/cuchadanfan/p/7302533.html
Copyright © 2011-2022 走看看