1. 程式人生 > >Linux系統呼叫--fcntl函式詳解

Linux系統呼叫--fcntl函式詳解

可以用fcntl 函式改變一個已開啟的檔案的屬性,可以重新設定讀、寫、追加、非阻塞等標誌(這些標誌稱為File StatusFlag),而不必重新open 檔案。
#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);


這個函式和open 一樣,也是用可變引數實現的,可變引數的型別和個數取決於前面的cmd 引數。

針對第2個引數,int cmd fcntl函式有五種功能:
• 複製一個現存的描述符(cmd=F_DUPFD)    。
• 獲得/設定檔案描述符標記(cmd=F_GETFD或F_SETFD)   。
• 獲得/設定檔案狀態標誌(cmd=F_GETFL或F_SETFL) 。
• 獲得/設定非同步I/O有權(cmd=F_GETOWN或F_SETOWN) 。
• 獲得/設定記錄鎖(cmd=F_GETLK,F_SETLK或F_SETLKW)。

我們將涉及與程序表項中各檔案描述符相關聯的檔案描述符標誌, 以及每個檔案表項中的檔案狀態標誌,

一~複製檔案描述符

• F_DUPFD 複製檔案描述符filedes,新檔案描述符作為函式值返回。它是尚未開啟的各
描述符中大於或等於第三個引數值(取為整型值)中各值的最小值。新描述符與 filedes 共享同
一檔案表項。但是,新描述符有它自己的一套檔案描述符標誌,其 F D _ C L O E X E C
檔案描述符標誌則被清除。
• F_GETFD 對應於filedes 的檔案描述符標誌作為函式值返回。當前只定義了一個檔案描
述符標誌FD_CLOEXEC。
• F_SETFD 對於filedes 設定檔案描述符標誌。新標誌值按第三個引數 (取為整型值)設定。
應當瞭解很多現存的涉及檔案描述符標誌的程式並不使用常數 F D _ C L O E X E C,而是將此
標誌設定為0(系統預設,在exec時不關閉)或1(在exec時關閉)。

二~檔案描述符號,套介面 屬性相關

• F_GETFL 對應於filedes 的檔案狀態標誌作為函式值返回。在說明 open函式時,已說明
了檔案狀態標誌   不幸的是,三個存取方式標誌 (O_RDONLY,O_WRONLY,以及O_RDWR)並不各佔1位。(正
如前述,這三種標誌的值各是 0、1和2,由於歷史原因。這三種值互斥 — 一個檔案只能有這
三種值之一。 )因此首先必須用遮蔽字 O_ACCMODE相與 取得存取方式位,然後將結果與這三種值
相比較。
• F_SETFL 將檔案狀態標誌設定為第三個引數的值 (取為整型值)。 可以更改的幾個標誌是:
O_APPEND,O_NONBLOCK,O_SYNC和O_ASYNC。

fcntl的檔案狀態標誌共有7個,O_RDONLY,O_WRONLY,O_RDWR,O_APPEND,O_NONBLOCK,O_SYNC和O_ASYNC



三~訊號驅動I/O , 帶外資料,設定套介面接受訊號的屬主
SIGIO,跟訊號驅動I/O有關
SIGURG, 和接受帶外資料有關
• F_GETOWN 取當前接收SIGIO和SIGURG訊號的程序ID或程序組ID。12.6.2節將論述這
兩種4.3+BSD非同步I/O訊號。
• F_SETOWN 設定接收SIGIO和SIGURG訊號的程序ID或程序組ID。正的arg指定一個進
程ID,負的arg表示等於arg絕對值的一個程序組ID。



下面的例子使用F_GETFL和F_SETFL這兩種fcntl 命令改變STDIN_FILENO的屬性,上O_NONBLOCK 選項,實現非阻塞讀終端的功能。

用fcntl改變File Status Flag
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#define MSG_TRY "try again\n"
int main(void)
{
char buf[10];
int n;
int flags;
flags = fcntl(STDIN_FILENO, F_GETFL);
flags |= O_NONBLOCK;
if (fcntl(STDIN_FILENO, F_SETFL, flags) == -1)
{
perror("fcntl");
exit(1);
}
tryagain:
n = read(STDIN_FILENO, buf, 10);
if (n < 0) 
{
if (errno == EAGAIN)
{
sleep(1);
write(STDOUT_FILENO, MSG_TRY,strlen(MSG_TRY));
goto tryagain;
}
perror("read stdin");
exit(1);
}
write(STDOUT_FILENO, buf, n);
return 0;
}