fcntl() - Unix,Linux系統呼叫 - 技術教學
Tutorials Point


  Unix初學者教程
  Unix Shell程式設計
  高階Unix
  Unix有用參考
  Unix有用資源
  精選閱讀

版權所有 © 2014 tutorialspoint



  首頁     參考資料     討論論壇     關於TP  

fcntl() - Unix,Linux系統呼叫


previous next AddThis Social Bookmark Button

廣告

名稱

fcntl - 操作檔案描述符

概要

#include <unistd.h> 
#include <fcntl.h> 

int fcntl(int fd, int cmd); int fcntl(int fd, int cmd, long arg); int fcntl(int fd, int cmd, struct flock *lock);

描述

fcntl() 對開啟的檔案描述符 fd 執行以下描述的操作之一。操作由 cmd 確定。

複製檔案描述符

標籤描述
F_DUPFD 找到大於或等於 arg 的最小可用檔案描述符,並使其成為 fd 的副本。這與dup2(2) 不同,後者使用指定的精確描述符。

成功時,返回新的描述符。

更多詳情請參見 dup(2)。

檔案描述符標誌

以下命令操作與檔案描述符關聯的標誌。目前,只定義了一個這樣的標誌:FD_CLOEXEC,即關閉執行標誌。如果FD_CLOEXEC 位為 0,則檔案描述符將在execve(2) 中保持開啟狀態,否則它將被關閉。

標籤描述
F_GETFD 讀取檔案描述符標誌。
F_SETFD 將檔案描述符標誌設定為 arg 指定的值。

檔案狀態標誌

每個開啟的檔案描述符都有一些關聯的狀態標誌,由open(2) 初始化,並可能由fcntl(2) 修改。複製的檔案描述符(由dup()、fcntl(F_DUPFD)、fork() 等建立)引用相同的檔案描述符,因此共享相同的檔案狀態標誌。

檔案狀態標誌及其語義在open(2) 中進行了描述。

標籤描述
F_GETFL 讀取檔案狀態標誌。
F_SETFL 將檔案狀態標誌設定為 arg 指定的值。arg 中的檔案訪問模式(O_RDONLYO_WRONLYO_RDWR)和檔案建立標誌(即O_CREATO_EXCLO_NOCTTYO_TRUNC)將被忽略。在 Linux 中,此命令只能更改O_APPENDO_ASYNCO_DIRECTO_NOATIMEO_NONBLOCK 標誌。

建議鎖

F_GETLKF_SETLKF_SETLKW 用於獲取、釋放和測試記錄鎖(也稱為檔案段鎖或檔案區域鎖)的存在。第三個引數 lock 是指向至少具有以下欄位(順序未指定)的結構體的指標。

struct flock { ... short l_type; /* Type of lock: F_RDLCK, F_WRLCK, F_UNLCK */ short l_whence; /* How to interpret l_start: SEEK_SET, SEEK_CUR, SEEK_END */ off_t l_start; /* Starting offset for lock */ off_t l_len; /* Number of bytes to lock */ pid_t l_pid; /* PID of process blocking our lock (F_GETLK only) */ ... };

此結構體的 l_whencel_startl_len 欄位指定我們希望鎖定的位元組範圍。l_start 是鎖定的起始偏移量,相對於以下任何一個進行解釋:檔案的開頭(如果 l_whenceSEEK_SET);當前檔案偏移量(如果 l_whenceSEEK_CUR);或檔案的結尾(如果 l_whenceSEEK_END)。在最後兩種情況下,只要偏移量不位於檔案的開頭之前,l_start 可以為負數。l_len 是一個非負整數(但請參見下面的註釋),指定要鎖定的位元組數。可以鎖定檔案末尾之後的位元組,但不能鎖定檔案開頭之前的位元組。將 l_len 指定為 0 具有特殊含義:鎖定從 l_whencel_start 指定的位置開始到檔案結尾的所有位元組,無論檔案增長多大。

l_type 欄位可以用於在檔案上放置讀鎖(F_RDLCK)或寫鎖(F_WRLCK)。任何數量的程序都可以在檔案區域上持有讀鎖(共享鎖),但只有一個程序可以持有寫鎖(獨佔鎖)。獨佔鎖排斥所有其他鎖,包括共享鎖和獨佔鎖。單個程序只能在一個檔案區域上持有鎖的一種型別;如果將新的鎖應用於已鎖定的區域,則現有鎖將轉換為新的鎖型別。(如果新鎖指定的位元組範圍與現有鎖的範圍不完全一致,則此類轉換可能涉及拆分、縮小或與現有鎖合併。)

標籤描述
F_SETLK 獲取鎖(當 l_typeF_RDLCKF_WRLCK 時)或釋放鎖(當 l_typeF_UNLCK 時),鎖定的位元組由 lockl_whencel_startl_len 欄位指定。如果另一個程序持有衝突的鎖,則此呼叫返回 -1 並將 errno 設定為EACCESEAGAIN
F_SETLKW F_SETLK 相同,但是如果在檔案上持有衝突的鎖,則等待該鎖釋放。如果在等待時捕獲到訊號,則呼叫被中斷,並(在訊號處理程式返回後)立即返回(返回值為 -1,errno 設定為EINTR)。
F_GETLK 對於此呼叫的輸入,lock 描述我們想在檔案上放置的鎖。如果可以放置鎖,fcntl() 不會實際放置它,而是在 lockl_type 欄位中返回F_UNLCK,並使結構體的其他欄位保持不變。如果一個或多個不相容的鎖會阻止放置此鎖,則fcntl() 會在 lockl_typel_whencel_startl_len 欄位中返回其中一個鎖的詳細資訊,並將 l_pid 設定為持有該鎖的程序的 PID。

為了放置讀鎖,fd 必須以讀取模式開啟。為了放置寫鎖,fd 必須以寫入模式開啟。要放置兩種型別的鎖,請以讀寫模式開啟檔案。

記錄鎖除了透過顯式的F_UNLCK刪除外,還會在程序終止或關閉任何引用持有鎖的檔案的檔案描述符時自動釋放。這很糟糕:這意味著程序可能會丟失對諸如 /etc/passwd/etc/mtab 等檔案上的鎖,因為某些庫函式決定開啟、讀取和關閉它。

記錄鎖不會被透過fork(2) 建立的子程序繼承,但會保留在execve(2) 中。

由於stdio(3) 庫執行了緩衝,因此應避免在該包中的例程中使用記錄鎖定;而應該使用read(2) 和write(2)。

強制鎖定

(非 POSIX。)上述記錄鎖可以是建議鎖或強制鎖,預設情況下為建議鎖。

建議鎖不會被強制執行,僅在合作程序之間有用。

強制鎖對所有程序強制執行。如果程序試圖對具有不相容強制鎖的檔案區域執行不相容的訪問(例如,read(2) 或write(2)),則結果取決於其開啟的檔案描述符是否啟用了O_NONBLOCK標誌。如果未啟用O_NONBLOCK標誌,則系統呼叫將被阻塞,直到鎖被移除或轉換為與訪問相容的模式。如果啟用了O_NONBLOCK標誌,則系統呼叫將失敗,並出現EAGAINEWOULDBLOCK錯誤。

要使用強制鎖,必須在包含要鎖定的檔案的系統和檔案本身上啟用強制鎖定。使用mount(8) 的“-o mand”選項或mount(2) 的MS_MANDLOCK標誌可以在檔案系統上啟用強制鎖定。透過停用檔案的組執行許可權並啟用設定組 ID 許可權位(請參見chmod(1) 和chmod(2))可以在檔案上啟用強制鎖定。

管理訊號

F_GETOWNF_SETOWNF_GETSIGF_SETSIG 用於管理 I/O 可用性訊號

標籤描述
F_GETOWN 獲取當前接收檔案描述符 fd 上事件的 SIGIO 和 SIGURG 訊號的程序 ID 或程序組 ID。程序 ID 以正值返回;程序組 ID 以負值返回(但請參見下面的錯誤)。
F_SETOWN 設定將接收檔案描述符 fd 上事件的 SIGIO 和 SIGURG 訊號的程序 ID 或程序組 ID。程序 ID 指定為正值;程序組 ID 指定為負值。最常見的情況是,呼叫程序將自身指定為所有者(即,arg 指定為getpid())。

如果在檔案描述符上設定了O_ASYNC狀態標誌(透過使用open(2)呼叫提供此標誌,或使用fcntl()的F_SETFL命令),則只要該檔案描述符上的輸入或輸出成為可能,就會發送 SIGIO 訊號。F_SETSIG 可用於獲取除 SIGIO 之外的訊號的傳遞。如果此許可權檢查失敗,則訊號將被靜默丟棄。

傳送訊號到F_SETOWN指定的擁有者程序(組)受與kill(2)中描述的相同的許可權檢查約束,其中傳送程序是使用F_SETOWN的程序(但請參見下面的錯誤)。

如果檔案描述符 fd 指向套接字,F_SETOWN還會選擇在套接字上到達帶外資料時傳遞 SIGURG 訊號的接收者。(在select(2) 將套接字報告為具有“異常情況”的任何情況下都會發送 SIGURG。)

如果在支援執行緒組的多執行緒程序(例如,NPTL)中將非零值賦給F_SETSIG,則賦給F_SETOWN的正值具有不同的含義:它不再是標識整個程序的程序 ID,而是標識程序內特定執行緒的執行緒 ID。因此,當使用F_SETSIG時,可能需要將gettid()的結果而不是getpid()傳遞給F_SETOWN才能獲得合理的結果。(在當前的 Linux 執行緒實現中,主執行緒的執行緒 ID 與其程序 ID 相同。這意味著單執行緒程式在這種情況下可以使用gettid()或getpid()。)但是,請注意,本段中的語句不適用於為套接字上的帶外資料生成的 SIGURG 訊號:此訊號始終傳送到程序或程序組,具體取決於賦給F_SETOWN的值。另請注意,Linux 對可能排隊到程序的即時訊號數量有限制(參見getrlimit(2) 和signal(7)),如果達到此限制,則核心會恢復為傳遞 SIGIO,並且此訊號將傳遞到整個程序而不是特定執行緒。

F_GETSIG 獲取輸入或輸出變為可能時傳送的訊號。值為零表示傳送 SIGIO。任何其他值(包括 SIGIO)都是要傳送的訊號,在這種情況下,如果使用 SA_SIGINFO 安裝,則訊號處理程式可以獲得其他資訊。
F_SETSIG 設定輸入或輸出變為可能時傳送的訊號。值為零表示傳送預設的 SIGIO 訊號。任何其他值(包括 SIGIO)都是要傳送的訊號,在這種情況下,如果使用 SA_SIGINFO 安裝,則訊號處理程式可以獲得其他資訊。

此外,將非零值傳遞給F_SETSIG會將訊號接收者從整個程序更改為程序內的特定執行緒。有關更多詳細資訊,請參見F_SETOWN的說明。

透過使用非零值的F_SETSIG,併為訊號處理程式設定 SA_SIGINFO(參見sigaction(2)),有關 I/O 事件的額外資訊將透過siginfo_t 結構傳遞給處理程式。如果si_code欄位指示源是 SI_SIGIO,則si_fd欄位給出與事件關聯的檔案描述符。否則,不會指示哪些檔案描述符處於掛起狀態,您應該使用通常的機制(select(2)、poll(2)、設定了O_NONBLOCKread(2) 等)來確定哪些檔案描述符可用於 I/O。

透過選擇即時訊號(值 >= SIGRTMIN),可以使用相同的訊號編號排隊多個 I/O 事件。(排隊取決於可用記憶體)。如果為訊號處理程式設定了 SA_SIGINFO,則可以獲得額外資訊,如上所述。

使用這些機制,程式可以實現完全非同步的 I/O,而無需大多數時候使用select(2) 或poll(2)。

O_ASYNCF_GETOWNF_SETOWN 的使用特定於 BSD 和 Linux。F_GETSIGF_SETSIG 是 Linux 特定的。POSIX 具有非同步 I/O 和aio_sigevent 結構來實現類似的功能;這些功能也作為 GNU C 庫 (Glibc) 的一部分在 Linux 中可用。

租約

F_SETLEASEF_GETLEASE(Linux 2.4 及更高版本)分別用於建立和檢索呼叫程序對fd引用的檔案的租約的當前設定。檔案租約提供了一種機制,透過該機制,持有租約的程序(“租約持有者”)會在程序(“租約破壞者”)嘗試open(2) 或truncate(2) 該檔案時收到通知(透過傳遞訊號)。

標籤描述
F_SETLEASE 根據整數arg中指定的以下值之一設定或刪除檔案租約

標籤描述
F_RDLCK 獲得讀取租約。這將導致呼叫程序在檔案以寫入方式開啟或被截斷時收到通知。讀取租約只能放在以只讀方式開啟的檔案描述符上。
F_WRLCK 獲得寫入租約。這將導致呼叫者在檔案以讀取或寫入方式開啟或被截斷時收到通知。只有在當前沒有其他程序開啟檔案的情況下,才能在檔案上放置寫入租約。
F_UNLCK 從檔案中刪除我們的租約。
程序在一個檔案上只能持有(擁有)一種型別的租約。
租約只能在常規檔案上獲得。非特權程序只能在 UID 與程序的檔案系統 UID 匹配的檔案上獲得租約。具有CAP_LEASE 功能的程序可以在任意檔案上獲得租約。
F_GETLEASE 透過返回F_RDLCKF_WRLCKF_UNLCK 來指示我們在fd引用的檔案上持有什麼型別的租約,分別表示呼叫程序在檔案上持有讀取租約、寫入租約或沒有租約。(省略fcntl() 的第三個引數。)

當程序(“租約破壞者”)執行與透過F_SETLEASE 建立的租約衝突的open() 或truncate() 時,系統呼叫將被核心阻塞,並且核心透過向其傳送訊號(預設情況下為 SIGIO)來通知租約持有者。租約持有者應該響應收到此訊號,執行準備讓另一個程序訪問檔案所需的任何清理工作(例如,重新整理快取緩衝區),然後刪除或降低其租約。透過執行指定argF_UNLCKF_SETLEASE 命令來刪除租約。如果我們當前在檔案上持有寫入租約,並且租約破壞者正在開啟檔案以進行讀取,那麼將租約降低到讀取租約就足夠了。這是透過執行指定argF_RDLCKF_SETLEASE 命令來完成的。

如果租約持有者未能在/proc/sys/fs/lease-break-time中指定的時間(秒)內降低或刪除租約,則核心將強制刪除或降低租約持有者的租約。

一旦租約被自願或強制刪除或降低,並且假設租約破壞者尚未取消其系統呼叫的阻塞,核心就會允許租約破壞者的系統呼叫繼續進行。

如果租約破壞者的阻塞open() 或truncate() 被訊號處理程式中斷,則系統呼叫將失敗並出現EINTR錯誤,但其他步驟仍按上述說明進行。如果租約破壞者在open() 或truncate() 中被阻塞時被訊號殺死,則其他步驟仍按上述說明進行。如果租約破壞者在呼叫open() 時指定了O_NONBLOCK標誌,則呼叫將立即失敗並出現EWOULDBLOCK錯誤,但其他步驟仍按上述說明進行。

用於通知租約持有者的預設訊號是 SIGIO,但這可以使用fcntl() 的F_SETSIG 命令更改。如果執行了F_SETSIG 命令(甚至指定 SIGIO 的命令),並且使用 SA_SIGINFO 建立了訊號處理程式,則處理程式將接收siginfo_t 結構作為其第二個引數,並且此引數的si_fd 欄位將儲存已被另一個程序訪問的租約檔案的描述符。(如果呼叫者對多個檔案持有租約,這將很有用。)

檔案和目錄更改通知 (dnotify)

標籤描述
F_NOTIFY (Linux 2.4 及更高版本) 當fd引用的目錄或其包含的任何檔案發生更改時提供通知。要通知的事件在arg中指定,arg 是透過將以下一個或多個位進行 OR 運算而指定的位掩碼

描述(目錄中的事件)
DN_MODIFY檔案被修改(寫入、pwrite、
 writev、truncate、ftruncate)
DN_CREATE檔案被建立(open、creat、mknod、
 mkdir、link、symlink、rename)
DN_DELETE檔案被取消連結(unlink、將 rename 到
 另一個目錄、rmdir)
DN_RENAME檔案在這個目錄中被重新命名(rename)
 目錄 (rename)
DN_ATTRIB檔案的屬性被更改
 (chown、chmod、utime[s])

(為了獲得這些定義,必須定義 _GNU_SOURCE 功能測試宏。)

目錄通知通常是“一次性”的,應用程式必須重新註冊才能接收進一步的通知。或者,如果arg中包含DN_MULTISHOT,則通知將一直有效,直到顯式刪除。

一系列F_NOTIFY請求是累積的,arg中的事件將新增到已監視的集合中。要停用所有事件的通知,請進行F_NOTIFY呼叫,並將arg指定為 0。

通知透過傳遞訊號來發生。預設訊號是 SIGIO,但這可以使用fcntl() 的F_SETSIG 命令更改。在後一種情況下,訊號處理程式將接收siginfo_t 結構作為其第二個引數(如果處理程式是使用 SA_SIGINFO 建立的),並且此結構的si_fd 欄位包含生成通知的檔案描述符(在對多個目錄建立通知時很有用)。

尤其是在使用DN_MULTISHOT時,應該使用即時訊號進行通知,以便可以排隊多個通知。

注意:新的應用程式應該考慮使用inotify介面(從核心 2.6.13 開始可用),它提供了一個更優越的介面來獲取檔案系統事件的通知。參見inotify(7)。

返回值

對於成功的呼叫,返回值取決於操作

標籤描述
F_DUPFD 新的描述符。
F_GETFD 標誌的值。
F_GETFL 標誌的值。
F_GETOWN 描述符所有者的值。
F_GETSIG 讀取或寫入變為可能時傳送的訊號的值,或者對於傳統的 SIGIO 行為為零。
所有其他命令
 零。

發生錯誤時,返回 -1,並相應地設定errno

錯誤

標籤描述
EACCESEAGAIN操作被其他程序持有的鎖禁止。
EAGAIN 操作被禁止,因為檔案已被另一個程序記憶體對映。
EBADF fd不是開啟的檔案描述符,或者命令是F_SETLKF_SETLKW,並且檔案描述符開啟模式與請求的鎖型別不匹配。
EDEADLK 檢測到指定的F_SETLKW命令將導致死鎖。
EFAULT lock位於您可訪問的地址空間之外。
EINTR 對於F_SETLKW,命令被訊號中斷。對於F_GETLKF_SETLK,在檢查或獲取鎖之前,命令被訊號中斷。這最可能發生在鎖定遠端檔案(例如,透過NFS鎖定)時,但有時也可能在本地發生。
EINVAL 對於F_DUPFDarg為負數或大於最大允許值。對於F_SETSIGarg不是允許的訊號編號。
EMFILE 對於F_DUPFD,程序已開啟最大數量的檔案描述符。
ENOLCK 開啟的段鎖過多,鎖表已滿,或遠端鎖定協議失敗(例如,透過NFS鎖定)。
EPERM 試圖清除已設定追加屬性的檔案的O_APPEND標誌。

註釋

dup2()返回的錯誤與F_DUPFD返回的錯誤不同。

自核心2.0以來,flock(2)和fcntl(2)放置的鎖的型別之間沒有互動。

POSIX.1-2001允許l_len為負數。(如果是負數,則鎖描述的區間覆蓋從l_start+l_lenl_start-1的位元組。)Linux從Linux 2.4.21和2.5.49開始支援此功能。

一些系統在struct flock中具有更多欄位,例如l_sysid。顯然,如果持有鎖的程序可能位於不同的機器上,則僅l_pid將不太有用。

錯誤

某些架構(特別是x86)上的Linux系統呼叫約定存在一個限制,這意味著如果F_GETOWN返回的(負)程序組ID落在-1到-4095的範圍內,則glibc會錯誤地將其解釋為系統呼叫中的錯誤;也就是說,fcntl()的返回值將為-1,而errno將包含(正)程序組ID。

在Linux 2.4及更早版本中,當非特權程序使用F_SETOWN將套接字檔案描述符的所有者指定為呼叫者以外的程序(組)時,可能會出現錯誤。在這種情況下,fcntl()可能會返回-1,並將errno設定為EPERM,即使所有者程序(組)是呼叫者有權向其傳送訊號的程序(組)。儘管返回此錯誤,但檔案描述符所有者已設定,並且訊號將傳送到所有者。

符合標準

SVr4, 4.3BSD, POSIX.1-2001。只有F_DUPFD、F_GETFD、F_SETFD、F_GETFL、F_SETFL、F_GETLK、F_SETLK、F_SETLKW、F_GETOWN和F_SETOWN操作在POSIX.1-2001中指定。

F_GETSIG、F_SETSIG、F_NOTIFY、F_GETLEASE和F_SETLEASE是Linux特有的。(定義_GNU_SOURCE宏以獲得這些定義。)

另請參見



previous next Printer Friendly

廣告


  

廣告



廣告