1、fcntl:manipulate file descriptor
1)簡介:fcntl(file control)函數可執行各種描述符控制操作。
#include <unistd.h> #include <fcntl.h> int fcntl(int fd, int cmd, ... /* arg */ );
2)正確的使用方法(以設置“非阻塞”標記為例):
int flags; // 先獲取當前的flags if ((flags = fcntl(sockfd, F_GETFL, 0)) < 0) // 錯誤處理... flags |= O_NONBLOCK; // 加上“非阻塞”flag if (fcntl(sockfd, F_SETFL, flags) < 0) // 設置新的flags // 錯誤處理...
3)常用用法:
(1)把一個套接字設置為非阻塞型:cmd為F_SETFL,flags“包含”O_NONBLOCK。
(2)把一個套接字設置成一旦其狀態發生變化,內核就產生一個SIGIO:cmd為F_SETFL,flags“包含”O_ASYNC。
(3)關於套接字的當前屬主。
cmd為F_SETOWN:指定用於接收SIGIO或SIGURG信號的套接字屬主(進程ID或進程組ID)。SIGIO是套接字被設置為信號驅動式I/O型后產生的,而SIGURG是在新的帶外數據到達套接字時產生的。
SIGIO與SIGURG與其他信號的不同之處在於,這兩個信號僅在已使用F_SETOWN命令給相關套接字指派了屬主后才會產生。F_SETOWN命令對應的arg參數是正整數時,指出接收信號的進程ID;為負整數時,其絕對值指出接收信號的進程組ID(此時整個進程組中的所有進程接收信號)。
cmd為F_GETOWN:返回套接字的當前屬主。
(4)記錄上鎖。記錄上鎖的Posix接口是fcntl函數。
這種情況下fcntl的arg參數為指向flock結構的指針:
struct flock { short l_type; // F_RDLCK, F_WRLCK, F_UNLCK short l_whence; // SEEK_SET, SEEK_CUR, SEEK_END off_t l_start; // 相對的起始位置 off_t l_len; // 長度(0表示直到文件末尾) pid_t l_pid; // F_GETLK返回的PID ... };
相關cmd:
F_SETLK:獲取(l_type為F_RDLCK或F_WRLCK)或釋放(l_type為F_UNLCK)由arg指向的flock結構所描述的鎖。如果鎖無法獲得,立即返回EACCES或EAGAIN。
F_SETLKW:與F_SETLK類似。但會阻塞到能夠獲得鎖為止。
F_GETLK:檢查由arg指向的鎖以確定是否有某個已存在的鎖會妨礙新鎖授予調用進程。如果當前沒有這樣的鎖存在,由arg指向的flock結構的l_type被置為F_UNLCK。否則,關於這個已存在鎖的信息將在由arg指向的flock結構中返回,其中包括持有該鎖的進程ID。
這里的記錄上鎖是關聯到某個進程的,因此:
I、鎖不能通過fork由子進程繼承。
II、如果進程關閉文件的任意一個描述符,則進程在該文件上的所有(記錄)鎖都被移除。
III、進程中的線程共享了鎖。因此,一個多線程程序不能使用記錄上鎖保證線程訪問同一文件相同區域的同步性。
其他特點:
I、后續成功執行的F_SETLK或F_SETLKW命令會覆蓋先前執行的針對同一字節范圍的同樣兩個命令。
II、記錄上鎖不應該同標准I/O函數庫一起使用,因為該函數庫會執行內部緩沖。當某個文件需要上鎖時,為避免問題,應對它使用read/write。
III、Posix記錄上鎖稱為勸告性上鎖。因此,一個進程可以無視一個勸告性鎖而寫一個讀鎖定文件,或讀一個寫鎖定文件。這種鎖只有在協作進程間才有用。
Posix.1只定義勸告性上鎖。有些系統(如System V)提供了另一種記錄上鎖,稱為強制性上鎖。使用強制性鎖后,內核檢查每個read/write,以驗證其操作不會干擾由某個進程持有的某個鎖,比如與某個強制性鎖沖突的(阻塞式)read/write將把調用進程投入睡眠,直到該鎖釋放為止。
為對某個特定文件施行強制性上鎖,應滿足:使用"-o mand"選項掛載文件系統;文件的組成員執行位必須關掉,且SGID位必須打開(打開文件的SUID位而不打開它的用戶執行位是沒有意義的,同樣,打開SGID位而不打開組成員執行位也沒有意義。因此,以這種方式加上的強制性上鎖不會影響任何現有的用戶軟件。強制性上鎖不需要新的系統調用)。
# 在支持強制性記錄上鎖的系統上 [root@localhost ~]# chmod +l test [root@localhost ~]# ls -l test -rw-r-lr--. 1 root root 0 Aug 29 03:09 test
4)示例:
int main(int argc, char *argv[]) { if (argc != 2) { printf("usage: a.out <fd>\n"); exit(1); } int val = 0; if ((val = fcntl(atoi(argv[1]), F_GETFL)) < 0) { printf("fcntl error for fd %d\n", atoi(argv[1])); exit(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: printf("invalid access mode\n"); exit(1); } if (val & O_APPEND) printf(", append"); if (val & O_NONBLOCK) printf(", nonblocking"); printf("\n"); return 0; }
編譯成a.out,運行:
[root@localhost ~]# ./a.out 0 < /dev/tty # 將標准輸入(fd為0)重定向到/dev/tty read only [root@localhost ~]# ./a.out 2 2>>temp.foo # 將標准出錯重定向到temp.foo,且使用追加寫模式 write only, append [root@localhost ~]# ./a.out 5 5<>temp.foo # 在文件描述符5上以可讀可寫模式打開temp.foo read write
參考資料:
《UNIX網絡編程 卷1:套接字聯網API》
《UNIX環境高級編程》
《UNIX網絡編程 卷2:進程間通信》
不斷學習中。。。
