?


版權聲明:本文為博主原創文章,未經博主允許不得轉載。
一、fcntl函數
功能:操縱文件描述符,改變已打開的文件的屬性
int fcntl(int fd, int cmd, ... /* arg */ );
?
cmd的取值可以如下:
復制文件描述符
F_DUPFD (long)
設置/獲取文件描述符標志
F_GETFD (void)
F_SETFD (long)
設置/獲取文件狀態標志
F_GETFL (void)
F_SETFL (long)
獲取/設置文件鎖
F_GETLK
F_SETLK,F_SETLKW
?
其中復制文件描述符可參見《linux系統編程之文件與I/O(五):打開文件的內核結構file和重定向》,文件描述符的標志只有一個即FD_CLOEXEC,設置/獲取文件描述符標志看這里。下面先來看設置/獲取文件狀態標志。
F_SETFL:
On Linux ?this ?command?can change only the O_APPEND, O_ASYNC, O_DIRECT, O_NOATIME, and O_NONBLOCK flags.
示例程序如下:
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | ? | /************************************************************************* ????>?File?Name:?file_fcntl.c ????>?Author:?Simba ????>?Mail:?dameng34@163.com ????>?Created?Time:?Sat?23?Feb?2013?02:34:02?PM?CST ?************************************************************************/ #include<sys/types.h> #include<sys/stat.h> #include<unistd.h> #include<fcntl.h> #include<stdio.h> #include<stdlib.h> #include<errno.h> #include<string.h> #define?ERR_EXIT(m)?\ ????do?{?\ ????????perror(m);?\ ????????exit(EXIT_FAILURE);?\ ????}?while(0) void?set_flag(int,?int); void?clr_flag(int,?int); int?main(int?argc,?char?*argv[]) { ????char?buf[1024]?=?{0}; ????int?ret; ????/* ????????int?flags; ????????flags?=?fcntl(0,?F_GETFL,?0); ????????if?(flags?==?-1) ????????????ERR_EXIT("fcntl?get?flag?error"); ????????ret?=?fcntl(0,?SETFL,?flags?|?O_NONBLOCK);?//設置為非阻塞,但不更改其他狀態 ????????if?(ret?==?-1) ????????????ERR_EXIT("fcntl?set?flag?error"); ????*/ ????set_flag(0,?O_NONBLOCK); ????ret?=?read(0,?buf,?1024); ????if?(ret?==?-1) ????????ERR_EXIT("read?error"); ????printf("buf=%s\n",?buf); ????return?0; } void?set_flag(int?fd,?int?flags) { ????int?val; ????val?=?fcntl(fd,?F_GETFL,?0); ????if?(val?==?-1) ????????ERR_EXIT("fcntl?get?flag?error"); ????val?|=?flags; ????if?(fcntl(fd,?F_SETFL,?val)?<?0) ????????ERR_EXIT("fcntl?set?flag?error"); } void?clr_flag(int?fd,?int?flags) { ????int?val; ????val?=?fcntl(fd,?F_GETFL,?0); ????if?(val?==?-1) ????????ERR_EXIT("fcntl?get?flag?error"); ????val?&=?~flags; ????if?(fcntl(fd,?F_SETFL,?val)?<?0) ????????ERR_EXIT("fcntl?set?flag?error"); } |
?
simba@ubuntu:~/Documents/code/linux_programming/APUE/File_IO$ ./file_fcntl?
read error: Resource temporarily unavailable
因為將標準輸入的狀態更改為非阻塞,則read不會阻塞等待輸入而立即返回錯誤,errno將被置為EAGAIN,即可以重新嘗試。
?
二、文件鎖結構體
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_whence, ?l_start, l_len 決定了被鎖定的文件范圍。當fcntl 函數的cmd為F_GETLK時,flock 結構體的 l_pid 參數會返回持有寫鎖的進程id。進程退出或者文件描述符被關閉時,會釋放所有的鎖。
?
示例程序如下:
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | ? | /************************************************************************* ????>?File?Name:?file_flock.c ????>?Author:?Simba ????>?Mail:?dameng34@163.com ????>?Created?Time:?Sat?23?Feb?2013?02:34:02?PM?CST ?************************************************************************/ #include<sys/types.h> #include<sys/stat.h> #include<unistd.h> #include<fcntl.h> #include<stdio.h> #include<stdlib.h> #include<errno.h> #include<string.h> #define?ERR_EXIT(m)?\ ????do?{?\ ????????perror(m);?\ ????????exit(EXIT_FAILURE);?\ ????}?while(0) int?main(int?argc,?char?*argv[]) { ????int?fd; ????fd?=?open("test2.txt",?O_CREAT?|?O_RDWR?|?O_TRUNC,?0664); ????if?(fd?==?-1) ????????ERR_EXIT("open?error"); ????/*?只有對文件有相應的讀寫權限才能施加對應的文件鎖?*/ ????struct?flock?lock; ????memset(&lock,?0,?sizeof(lock)); ????lock.l_type?=?F_WRLCK;?//?排他鎖,即不允許其他進程再對其加任何類型的鎖,但讀鎖(共享鎖)允許 ????lock.l_whence?=?SEEK_SET; ????lock.l_start?=?0;?//從文件開頭開始鎖定 ????lock.l_len?=?0;?//?文件全部內容鎖住 ????if?(fcntl(fd,?F_SETLK,?&lock)?==?0) ????{ ????????/*?若為F_SETLKW,這時如果鎖已經被其他進程占用,則此進程會阻塞直到其他進程釋放鎖*/ ????????printf("lock?success\n"); ????????printf("press?any?key?to?unlock\n"); ????????getchar(); ????????lock.l_type?=?F_UNLCK; ????????if?(fcntl(fd,?F_SETLK,?&lock)?==?0) ????????????printf("unlock?success\n"); ????????else ????????????ERR_EXIT("unlock?fail"); ????} ????else ????????ERR_EXIT("lock?fail"); ????return?0;?//進程退出會對所有文件解鎖 } |
?
測試如下:
我們先在一個 終端執行程序:
simba@ubuntu:~/Documents/code/linux_programming/APUE/File_IO$ ./file_flock?
lock success
press any key to unlock
?
現在文件已經被鎖住了,而且沒有按下任何按鍵,所以卡在這里,也還沒解鎖,接著在另一個終端再次執行同個程序:
simba@ubuntu:~/Documents/code/linux_programming/APUE/File_IO$ ./file_flock?
lock fail: Resource temporarily unavailable
會立即返回錯誤,因為我們希望施加的是排他鎖,而現在前面一個進程正在占用寫鎖還沒釋放,所以嘗試施加鎖失敗,而如果fcntl 函數的cmd 設置為 F_SETLKW,即帶w的版本,則此進程會一直阻塞直到前面一個進程釋放了鎖。
?
參考:《APUE》