fcntl函數可以改變已打開的文件的性質。
#include <fcntl.h>int fcntl( int filedes, int cmd, ... /* int arg */ );返回值:若成功則依賴于cmd,若出錯則返回-1
在本節的各實例中,第三個參數總是一個整數,與上面所示函數原型中的注釋部分相對應。但是在說明記錄鎖時,第三個參數則是指向一個結構的指針。
fcntl函數有5種功能:
(1)復制一個現有的描述符(cmd = F_DUPFD )。
(2)獲得/設置文件描述符標記(cmd = F_GETFD或F_SETFD)。
(3)獲得/設置文件狀態標志(cmd = F_GETFL或F_SETFL)。
(4)獲得/設置異步I/O所有權(cmd = F_GETOWN或F_SETOWN)。
(5)獲得/設置記錄鎖(cmd = F_GETLK、F_SETLK或F_SETLKW)。
我們先說明這10種cmd值得前7種(后3種,在談記錄鎖時說明)。我們將涉及與進程表項中各文件描述符相關聯的文件描述符標志,以及每個文件表項中的文件狀態標志。
F_DUPFD 復制文件描述符filedes。新文件描述符作為函數值返回。它是尚未打開的各描述符中大于或等于第三個參數值(取為整型值)中各值的最小值。新描述符與filedes共享同一文件表項。但是,新描述符有它自己的一套文件描述符標志,其FD_CLOEXEC文件描述符標志被清除(這表示該描述符在通過一個exec時仍保持有效)。
F_GETFD 對應于filedes的文件描述符標志作為函數值返回。當前只定義了一個文件描述符標志FD_CLOEXEC。
F_SETFD 對于filedes設置文件描述符標志。新標志值按第三個參數(取為整型值)設置。
注:應當了解很多現有的涉及文件描述符標志的程序并不使用常量FD_CLOEXEC,而是將此標志設置為0(系統默認,在exec時不關閉)或1(在exec時關閉)。
F_GETFL 對應于filedes的文件狀態標志作為函數值返回。文件狀態標志如下表3-3:(表3-3中的各個標志,除了三個訪問方式標志(O_RDONLY、O_WRONLY以及O_RDWR)不各占一位,其他標志分別占文件狀態標志的一個bit位)
不幸的是,三個訪問方式標志(O_RDONLY、O_WRONLY以及O_RDWR)并不各占1位(由于歷史原因,這三種標志的值分別是0、1和2。這三個訪問方式標志用文件狀態標志的后兩位表示,00:O_RDONLY,01:O_WRONLY,10:O_RDWR。這三種值互斥——一個文件只能有這三種值之一)。因此首先必須用屏蔽字O_ACCMODE(0x0003)取得訪問模式位(文件狀態標志的后兩位),然后將結果與這三種值的任一種做比較。
F_SETFL 將文件狀態標志設置為第三個參數的值(取為整型值)。可以更改的幾個標志是:O_APPEND、O_NONBLOCK、O_SYNC、O_DSYNC、O_RSYNC、O_FSYNC和O_ASYNC。
F_GETOWN 取當前接受SIGIO和SIGURG信號的進程ID或進程組ID。
F_SETOWN 設置接收SIGIO和SIGURG信號的進程ID或進程組ID。正當arg指定一個進程ID,負的arg表示等于arg絕對值的一個進程組ID。
fcntl的返回值與命令有關。如果出錯,所有命令都返回-1,如果成功則返回某個其他值。下列四個命令有特定的返回值:F_DUPFD、F_GETFD、F_GETFL以及F_GETOWN。第一個返回新的文件描述符,接下來兩個返回相應標志,最后一個返回一個正當進程ID或負的進程組ID。
程序清單3-4 對于指定的描述符打印文件標志
[root@localhost apue]# cat PRog3-4.c#include "apue.h"#include <fcntl.h>int main(int argc, char *argv[]){ int val; if(argc != 2) err_quit("usage: prog3-4 <descriptor#>"); if((val = fcntl(atoi(argv[1]), F_GETFL, 0)) < 0) err_sys("fcntl error for fd %d", atoi(argv[1])); switch(val & O_ACCMODE) { case O_RDONLY: printf("read only"); break; case O_WRONLY: printf("write only"); break; case O_RDWR: printf("read write"); break; default: err_dump("unknown access mode"); } if(val & O_APPEND) printf(", append"); if(val & O_NONBLOCK) printf(", nonblocking");#if defined(O_SYNC) if(val & O_SYNC) printf(", synchronous writes");#endif#if !defined(_POSIX_C_SOURCE) && defined(O_FSYNC) if(val & O_FSYNC) printf(", synchronous writes");#endif putchar('/n'); exit(0);}
注意,我們使用了功能測試宏_POSIX_C_SOURCE,并且條件編譯了POSIX.1中沒有定義的文件訪問標志。
在修改文件描述符標志或文件狀態標志時必須謹慎,先要取得現有的標志值,然后根據需要修改它,最后設置新標志值。不能只是執行F_SETFD或F_SETFL命令,這樣會關閉以前設置的標志位。
程序清單3-5 對一個文件描述符打開一個或多個文件狀態標志
[root@localhost apue]# cat prog3-5.c#include "apue.h"#include <fcntl.h>void set_fl(int fd, int flags) /* flags are file status flags to turn to */{ int val; if((val = fcntl(fd, F_GETFL, 0)) < 0) err_sys("fcntl F_GETFL error"); val |= flags; /* turn on flags */ if(fcntl(fd, F_SETFL, val) < 0) err_sys("fcntl F_SETFL error");}
如果將中間的一條語句改為:
val &= ~flags; /* turn flags off */
就構成另一個函數,我們稱其為clr_fl。
比較fsync和fdatasync與O_SYNC標志,fsync和fdatasync在我們需要時更新文件內容,O_SYNC標志則在我們每次寫至文件時更新文件內容。
fcntl的必要性:我們的程序在一個描述符(標準輸出)上進行操作,但是根本不知道由shell打開的相應的文件名。因為這是shell打開的,于是不能在打開時,按我們的要求設置O_SYNC標志。fcntl則允許僅知道打開文件描述符時可以修改其性質。
本篇博文內容摘自《UNIX環境高級編程》(第二版),僅作個人學習記錄所用。關于本書可參考:http://www.apuebook.com/。
新聞熱點
疑難解答