fcntl —— 系統(tǒng)調(diào)用 fcntlioctl?


本模塊基于文件描述符來進(jìn)行文件控制和 I/O 控制。它是 Unix 系統(tǒng)調(diào)用 fcntl()ioctl() 的接口。關(guān)于這些調(diào)用的完整描述,請(qǐng)參閱 Unix 手冊(cè)的 fcntl(2)ioctl(2) 頁面。

本模塊的所有函數(shù)都接受文件描述符 fd 作為第一個(gè)參數(shù)??梢允且粋€(gè)整數(shù)形式的文件描述符,比如 sys.stdin.fileno() 的返回結(jié)果,或?yàn)?io.IOBase 對(duì)象,比如 sys.stdin 提供一個(gè) fileno(),可返回一個(gè)真正的文件描述符。

在 3.3 版更改: 本模塊的操作以前觸發(fā)的是 IOError,現(xiàn)在則會(huì)觸發(fā) OSError。

在 3.8 版更改: fcntl 模塊現(xiàn)在有了 F_ADD_SEALSF_GET_SEALSF_SEAL_* 常量,用于文件描述符 os.memfd_create() 的封裝。

在 3.9 版更改: On macOS, the fcntl module exposes the F_GETPATH constant, which obtains the path of a file from a file descriptor. On Linux(>=3.15), the fcntl module exposes the F_OFD_GETLK, F_OFD_SETLK and F_OFD_SETLKW constants, which are used when working with open file description locks.

在 3.10 版更改: 在 Linux 2.6.11 以上版本中,fcntl 模塊提供了 F_GETPIPE_SZ 和``F_SETPIPE_SZ`` 常量,分別用于檢查和修改管道的大小。

在 3.11 版更改: On FreeBSD, the fcntl module exposes the F_DUP2FD and F_DUP2FD_CLOEXEC constants, which allow to duplicate a file descriptor, the latter setting FD_CLOEXEC flag in addition.

這個(gè)模塊定義了以下函數(shù):

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

對(duì)文件描述符 fd 執(zhí)行 cmd 操作(能夠提供 fileno() 方法的文件對(duì)象也可以接受)。 cmd 可用的值與操作系統(tǒng)有關(guān),在 fcntl 模塊中可作為常量使用,名稱與相關(guān) C 語言頭文件中的一樣。參數(shù) arg 可以是整數(shù)或 bytes 對(duì)象。若為整數(shù)值,則本函數(shù)的返回值是 C 語言 fcntl() 調(diào)用的整數(shù)返回值。若為字節(jié)串,則其代表一個(gè)二進(jìn)制結(jié)構(gòu),比如由 struct.pack() 創(chuàng)建的數(shù)據(jù)。該二進(jìn)制數(shù)據(jù)將被復(fù)制到一個(gè)緩沖區(qū),緩沖區(qū)地址傳給 C 調(diào)用 fcntl()。調(diào)用成功后的返回值位于緩沖區(qū)內(nèi),轉(zhuǎn)換為一個(gè) bytes 對(duì)象。返回的對(duì)象長(zhǎng)度將與 arg 參數(shù)的長(zhǎng)度相同。上限為 1024 字節(jié)。如果操作系統(tǒng)在緩沖區(qū)中返回的信息大于 1024 字節(jié),很可能導(dǎo)致內(nèi)存段沖突,或更為不易察覺的數(shù)據(jù)錯(cuò)誤。

如果 fcntl() 調(diào)用失敗,會(huì)觸發(fā) OSError

引發(fā)一條 auditing 事件 fcntl.fcntl,參數(shù)為 fdcmd、arg。

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

本函數(shù)與 fcntl() 函數(shù)相同,只是參數(shù)的處理更加復(fù)雜。

request 參數(shù)的上限是 32位。termios 模塊中包含了可用作 request 參數(shù)其他常量,名稱與相關(guān) C 頭文件中定義的相同。

參數(shù) arg 可為整數(shù)、支持只讀緩沖區(qū)接口的對(duì)象(如 bytes )或支持讀寫緩沖區(qū)接口的對(duì)象(如 bytearray )。

除了最后一種情況,其他情況下的行為都與 fcntl() 函數(shù)一樣。

如果傳入的是個(gè)可變緩沖區(qū),那么行為就由 mutate_flag 參數(shù)決定。

如果 mutate_flag 為 False,緩沖區(qū)的可變性將被忽略,行為與只讀緩沖區(qū)一樣,只是沒有了上述 1024 字節(jié)的上限——只要傳入的緩沖區(qū)能容納操作系統(tǒng)放入的數(shù)據(jù)即可。

如果 mutate_flag 為 True(默認(rèn)值),那么緩沖區(qū)(實(shí)際上)會(huì)傳給底層的 系統(tǒng)調(diào)用 ioctl() ,其返回代碼則會(huì)回傳給調(diào)用它的 Python,而緩沖區(qū)的新數(shù)據(jù)則反映了 ioctl() 的運(yùn)行結(jié)果。這里做了一點(diǎn)簡(jiǎn)化,因?yàn)槿羰墙o出的緩沖區(qū)少于 1024 字節(jié),首先會(huì)被復(fù)制到一個(gè) 1024 字節(jié)長(zhǎng)的靜態(tài)緩沖區(qū)再傳給 ioctl() ,然后把結(jié)果復(fù)制回給出的緩沖區(qū)去。

如果 ioctl() 調(diào)用失敗,則會(huì)觸發(fā) OSError 異常。

舉個(gè)例子:

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

觸發(fā)一條 auditing 事件 fcntl.ioctl,參數(shù)為 fd 、request 、arg。

fcntl.flock(fd, operation)?

在文件描述符 fd 上執(zhí)行加鎖操作 operation (也接受能提供 fileno() 方法的文件對(duì)象)。 詳見 Unix 手冊(cè) flock(2)。 (在某些系統(tǒng)中,此函數(shù)是用 fcntl() 模擬出來的。)

如果 flock() 調(diào)用失敗,就會(huì)觸發(fā) OSError 異常。

觸發(fā)一條 審計(jì)事件 fcntl.flock,參數(shù)為 fdoperation。

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

本質(zhì)上是對(duì) fcntl() 加鎖調(diào)用的封裝。fd 是要加解鎖的文件描述符(也接受能提供 fileno() 方法的文件對(duì)象),cmd 是以下值之一:

  • LOCK_UN ——解鎖

  • LOCK_SH —— 獲取一個(gè)共享鎖

  • LOCK_EX —— 獲取一個(gè)獨(dú)占鎖

如果 cmdLOCK_SHLOCK_EX,則還可以與 LOCK_NB 進(jìn)行按位或運(yùn)算,以避免在獲取鎖時(shí)出現(xiàn)阻塞。 如果用了 LOCK_NB,無法獲取鎖時(shí)將觸發(fā) OSError,此異常的 errno 屬性將被設(shè)為 EACCESEAGAIN (視操作系統(tǒng)而定;為了保證可移植性,請(qǐng)檢查這兩個(gè)值)。 至少在某些系統(tǒng)上,只有當(dāng)文件描述符指向需要寫入而打開的文件時(shí),才可以使用 LOCK_EX。

len 是要鎖定的字節(jié)數(shù),start 是自 whence 開始鎖定的字節(jié)偏移量,whenceio.IOBase.seek() 的定義一樣。

start 的默認(rèn)值為 0,表示從文件起始位置開始。len 的默認(rèn)值是 0,表示加鎖至文件末尾。 whence 的默認(rèn)值也是 0。

觸發(fā)一條 審計(jì)事件 fcntl.lockf,參數(shù)為 fd 、 cmdlen、 startwhence。

示例(都是運(yùn)行于符合 SVR4 的系統(tǒng)):

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)

注意,在第一個(gè)例子中,返回值變量 rv 將存有整數(shù);在第二個(gè)例子中,該變量中將存有一個(gè) bytes 對(duì)象。lockdata 變量的結(jié)構(gòu)布局視系統(tǒng)而定——因此采用 flock() 調(diào)用可能會(huì)更好。

參見

模塊 os

如果 os 模塊中存在加鎖標(biāo)志 O_SHLOCKO_EXLOCK (僅在BSD上),那么 os.open() 函數(shù)提供了 lockf()flock() 函數(shù)的替代方案。