fcntl --- fcntlioctl 系统调用


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

Availability: Unix, not WASI.

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

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

在 3.8 版本发生变更: fcntl 模块现在包含 F_ADD_SEALS, F_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)通过将一个文件引用链接到另一个文件来共享某些数据。 此行为通常被称为“写入时拷贝”。

在 3.13 版本发生变更: 在 Linux >= 2.6.32 上,fcntl 模块会暴露 F_GETOWN_EX, F_SETOWN_EX, F_OWNER_TID, F_OWNER_PID, F_OWNER_PGRP 常量,它们允许针对特定线程、进程或进程组的直接 I/O 可用性信号。 在 Linux >= 4.13 上,fcntl 模块会暴露 F_GET_RW_HINT, F_SET_RW_HINT, F_GET_FILE_RW_HINT, F_SET_FILE_RW_HINTRWH_WRITE_LIFE_* 常量,它们允许向内核通知有关在给定 inode 上或通过特定的打开文件描述符写入的相对预计生命期。 在 Linux >= 5.1 和 NetBSD 上,fcntl 模块会暴露 F_SEAL_FUTURE_WRITE 常量供 F_ADD_SEALSF_GET_SEALS 操作使用。 在 FreeBSD 上,fcntl 模块会暴露 F_READAHEAD, F_ISUNIONSTACKF_KINFO 常量。 在 macOS 和 FreeBSD 上,fcntl 模块会暴露 F_RDAHEAD 常量。 在 NetBSD 和 AIX 上,fcntl 模块会暴露 F_CLOSEM 常量。 在 NetBSD 上,fcntl 模块会暴露 F_MAXFD 常量。 在 macOS 和 NetBSD 上,fcntl 模块会暴露 F_GETNOSIGPIPEF_SETNOSIGPIPE 常量。

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

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

在文件描述符 fd (也可以是提供 fileno() 方法的文件对象) 上执行操作 cmd。 用作 cmd 的值依赖于具体操作系统,并且会是 fcntl 模块中的常量,使用与相应 C 头文件中所使用的相同名称。 参数 arg 可以是一个整数值, bytes 对象或字符串。 arg 的类型和大小必须与相应 C 文档所规定操作的参数的类型和大小相匹配。

arg 是一个整数时,该函数将返回 C fcntl() 调用的整数返回值。

当参数是 bytes 对象时,它代表一个二进制数组结构,例如由 struct.pack() 所创建的数据。 字符串值将使用 UTF-8 编码格式编码为二进制数据。 该二进制数据会被拷贝到一个缓冲区,其地址将被传给 C fcntl() 调用。 成功调用后的返回值将是该缓冲区的内容所转换成的 bytes 对象。 被返回对象的长度将与 arg 参数的长度相同。 对象长度上限为 1024 字节。

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

备注

如果 arg 的类型和大小与相应操作的类型和大小不匹配(例如,如果预期传入一个指针却传入了一个整数,或者操作系统返回的缓冲区中的信息大于 1024 字节),这就很可能导致段错误或更微妙的数据损坏。

引发一个 审计事件 fcntl.fcntl 并附带参数 fd, cmd, arg

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

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

request 形参被限制为能被放入 32 或 64 个比特位的值,具体取决于所在的平台。 在 termios 模块中还包含一些可被用作 request 参数的额外常量,其名称与相关 C 语言头文件中所使用的相同。

形参 arg 可以是一个整数, bytes-like object 或者字符串。 arg 的类型和大小必须与对应 C 文档中规定的参数的类型和大小相匹配。

如果 arg 不支持读写缓冲区接口或者 mutate_flag 为假值,则其行为与 fcntl() 函数一样。

如果 arg 支持读写缓冲区接口 (就像 bytearray) 并且 mutate_flag 为(默认的)真值,那么缓冲区(实际上)会被传给下层的 ioctl() 系统调用,后者的返回代码则会回传给调用方 Python 对象,而缓冲区的新内容将反映 ioctl() 的动作。 这里做了一点简化,因为如果给出的缓冲区长度小于 1024 字节则它会先被拷贝到一个长度为 1024 字节的静态缓冲区然后再传给 ioctl() 并把结果拷贝回给出的缓冲区。

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

备注

如果 arg 的类型和大小与对应操作的参数的类型和大小不相匹配(例如,如果预期传入一个指针却传入了一个整数,或者由操作系统返回的缓冲区中的信息大于 1024 字节),这就很有可能导致段错误或更微妙的数据损坏。

举个例子:

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

引发一个 审计事件 fcntl.ioctl 并附带参数 fd, request, arg

fcntl.flock(fd, operation, /)

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

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

引发一个 审计事件 fcntl.flock 并附带参数 fd, operation

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 并附带参数 fd, cmd, len, start, whence

示例(都是运行于符合 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() 函数的替代。