fcntl —— 系统调用 fcntlioctl


本模块基于文件描述符来执行文件和 I/O 控制。 它是 fcntl()ioctl() Unix 例程的接口。 请参阅 fcntl(2)ioctl(2) Unix 手册页了解详情。

可用性: Unix, 非 Emscripten, 非 WASI。

本模块的所有函数都接受文件描述符 fd 作为第一个参数。可以是一个整数形式的文件描述符,比如 sys.stdin.fileno() 的返回结果,或为 io.IOBase 对象,比如 sys.stdin 提供一个 fileno(),可返回一个真正的文件描述符。

在 3.3 版本发生变更: 本模块的操作以前触发的是 IOError,现在则会触发 OSError

在 3.8 版本发生变更: fcntl 模块现在有了 F_ADD_SEALSF_GET_SEALSF_SEAL_* 常量,用于文件描述符 os.memfd_create() 的封装。

在 3.9 版本发生变更: 在 macOS 上,fcntl 模块提供了 F_GETPATH 常量,从文件描述符获取文件的路径。 在 Linux(>=3.15) 上,fcntl 模块提供了 F_OFD_GETLK, F_OFD_SETLKF_OFD_SETLKW 常量,它们将在处理打开文件描述锁时被使用。

在 3.10 版本发生变更: 在 Linux 2.6.11 以上版本中,fcntl 模块提供了 F_GETPIPE_SZF_SETPIPE_SZ 常量,分别用于检查和修改管道的大小。

在 3.11 版本发生变更: 在 FreeBSD 上,fcntl 模块会暴露 F_DUP2FDF_DUP2FD_CLOEXEC 常量,它们允许复制文件描述符,后者还额外设置了 FD_CLOEXEC 旗标。

在 3.12 版本发生变更: 在 Linux >= 4.5 上,fcntl 模块将公开 FICLONEFICLONERANGE 常量,这允许在某些系统上(例如 btrfs, OCFS2, 和 XFS)通过将一个文件引用链接到另一个文件来共享某些数据。 此行为通常被称为“写入时拷贝”。

这个模块定义了以下函数:

fcntl.fcntl(fd, cmd, arg=0)

对文件描述符 fd 执行 cmd 操作(能够提供 fileno() 方法的文件对象也可以接受)。 cmd 可用的值与操作系统有关,在 fcntl 模块中可作为常量使用,名称与相关 C 语言头文件中的一样。参数 arg 可以是整数或 bytes 对象。若为整数值,则本函数的返回值是 C 语言 fcntl() 调用的整数返回值。若为字节串,则其代表一个二进制结构,比如由 struct.pack() 创建的数据。该二进制数据将被复制到一个缓冲区,缓冲区地址传给 C 调用 fcntl()。调用成功后的返回值位于缓冲区内,转换为一个 bytes 对象。返回的对象长度将与 arg 参数的长度相同。上限为 1024 字节。如果操作系统在缓冲区中返回的信息大于 1024 字节,很可能导致内存段冲突,或更为不易察觉的数据错误。

如果 fcntl() 调用失败,将引发 OSError

引发一条 auditing 事件 fcntl.fcntl,参数为 fdcmdarg

fcntl.ioctl(fd, request, arg=0, mutate_flag=True)

本函数与 fcntl() 函数相同,只是参数的处理更加复杂。

request 参数的上限是 32位。termios 模块中包含了可用作 request 参数其他常量,名称与相关 C 头文件中定义的相同。

参数 arg 可为整数、支持只读缓冲区接口的对象(如 bytes )或支持读写缓冲区接口的对象(如 bytearray )。

除了最后一种情况,其他情况下的行为都与 fcntl() 函数一样。

如果传入的是个可变缓冲区,那么行为就由 mutate_flag 参数决定。

如果 mutate_flag 为 False,缓冲区的可变性将被忽略,行为与只读缓冲区一样,只是没有了上述 1024 字节的上限——只要传入的缓冲区能容纳操作系统放入的数据即可。

如果 mutate_flag 为 True(默认值),那么缓冲区(实际上)会传给底层的 系统调用 ioctl() ,其返回代码则会回传给调用它的 Python,而缓冲区的新数据则反映了 ioctl() 的运行结果。这里做了一点简化,因为若是给出的缓冲区少于 1024 字节,首先会被复制到一个 1024 字节长的静态缓冲区再传给 ioctl() ,然后把结果复制回给出的缓冲区去。

如果 ioctl() 调用失败,将引发 OSError 异常。

举个例子:

>>> import array, fcntl, struct, termios, os
>>> os.getpgrp()
13341
>>> struct.unpack('h', fcntl.ioctl(0, termios.TIOCGPGRP, "  "))[0]
13341
>>> buf = array.array('h', [0])
>>> fcntl.ioctl(0, termios.TIOCGPGRP, buf, 1)
0
>>> buf
array('h', [13341])

触发一条 auditing 事件 fcntl.ioctl,参数为 fdrequestarg

fcntl.flock(fd, operation)

在文件描述符 fd 上执行加锁操作 operation (也接受能提供 fileno() 方法的文件对象)。 详见 Unix 手册 flock(2)。 (在某些系统中,此函数是用 fcntl() 模拟出来的。)

如果 flock() 调用失败,将引发 OSError 异常。

触发一条 审计事件 fcntl.flock,参数为 fdoperation

fcntl.lockf(fd, cmd, len=0, start=0, whence=0)

本质上是对 fcntl() 加锁调用的封装。fd 是要加解锁的文件描述符(也接受能提供 fileno() 方法的文件对象),cmd 是以下值之一:

fcntl.LOCK_UN

释放一个已存在的锁 。

fcntl.LOCK_SH

获取一个共享的锁。

fcntl.LOCK_EX

获得一个独占的锁。

fcntl.LOCK_NB

与其他三个 LOCK_* 常量中的任何一个进行位或操作,使请求不阻塞。

如果使用了 LOCK_NB ,但无法获取锁 ,则 OSError 将被引发 ,异常将被 errno 属性 设置为 EACCESEAGAIN (取决于操作系统;为便于移植,请检查这两个值)。 至少在某些系统中,只有当文件描述符指向一个已打开供写入的文件时,才能使用:const:!LOCK_EX

len 是要锁定的字节数,start 是自 whence 开始锁定的字节偏移量,whenceio.IOBase.seek() 的定义一样。

start 的默认值为 0,表示从文件起始位置开始。len 的默认值是 0,表示加锁至文件末尾。 whence 的默认值也是 0。

触发一条 审计事件 fcntl.lockf,参数为 fdcmdlenstartwhence

示例(都是运行于符合 SVR4 的系统):

import struct, fcntl, os

f = open(...)
rv = fcntl.fcntl(f, fcntl.F_SETFL, os.O_NDELAY)

lockdata = struct.pack('hhllhh', fcntl.F_WRLCK, 0, 0, 0, 0, 0)
rv = fcntl.fcntl(f, fcntl.F_SETLKW, lockdata)

注意,在第一个例子中,返回值变量 rv 将存有整数;在第二个例子中,该变量中将存有一个 bytes 对象。lockdata 变量的结构布局视系统而定——因此采用 flock() 调用可能会更好。

参见

模块 os

如果加锁旗标 O_SHLOCKO_EXLOCK 存在于 os 模块中(仅 BSD 专属),则 os.open() 函数提供了对 lockf()flock() 函数的替代。