標籤:linux c++ 檔案系統 非同步
fcntl
功能描述:根據檔案描述來操作檔案的特性。
#include <unistd.h>
#include <fctl.h>
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd, struct flock *lock);
http://blog.csdn.net/xiaoliangsky/article/details/40303245
描述
fctl針對(檔案)描述提供控制。fd是被參數cmd操作(如下面描述)的描述符,針對cmd的值,fctl能夠接受第三個參數arg。
返回值
fctl()的返回值與命令有關。如果出錯,所有命令都返回-1,如果成功則返回某個其他值。下面三個命令有特定的返回值:
F_DUPFD 返回新的檔案描述符
F_GETFD 返回相應標誌
F_GETFL 返迴文件狀態標記
F_GETOWN 返回一個正的進程ID或負的進程組ID
fcntl函數有5中功能:
1)複製一個現有的描述符(cm=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_SET)
1. cmd=F_DUPFD
F_DUPFD 返回一個如下描述的(檔案)描述符
·最小的大於或等於arg的一個可用的描述符
·與原始操作符一樣的某對象的引用
·如果對象是檔案(file)的話,則返回一個新的描述符,這個描述符與arg共用相同的位移量(offset)
·相同的訪問模式(讀,寫或讀/寫)
·相同的檔案狀態標誌(如:兩個檔案描述符共用相同的狀態標誌)
·與新的檔案描述符結合在一起的close-on-exec標誌被設定成交叉式訪問execve(2)的系統調用
實際上調用dup(oldfd);
等效於
fcntl(odlfd, F_DUPFD, 0);
而調用dup2(oldfd, newfd);
等效於
close(oldfd);
fcntl(oldfd, F_DUPFD, newfd);
2. cmd=F_GETFD或F_SETFD
F_GETFD 取得與檔案描述符fd聯合的close-on-exec標誌,類似FD_CLOEXEC。如果返回值和FD_CLOEXEC進行與運算結果是,檔案保持交叉式訪問exec(),否則如果通過exec啟動並執行話,檔案將被關閉(arg 被忽略)
F_SETFD 設定close-on-exec標誌,該標誌以參數arg的FD_CLOEXEC位決定,應當瞭解很多現存的涉及檔案描述符標誌的程式並不使用常數 FD_CLOEXEC,而是將此標誌設定為0(系統預設,在exec時不關閉)或1(在exec時關閉)
在修改檔案描述符標誌或檔案狀態標誌時必須謹慎,先要取得現在的標誌值,然後按照希望修改它,最後設定新標誌值。不能只是執行F_SETFD或F_SETFL命令,這樣會關閉以前設定的標誌位。
3. cmd值的F_GETFL和F_SETFL:
F_GETFL 取得fd的檔案狀態標誌,如同下面的描述一樣(arg被忽略),在說明open函數時,已說明了檔案狀態標誌。不幸的是,三個存取方式標誌 (O_RDONLY, O_WRONLY, 以及O_RDWR)並不各佔1位。(這三種標誌的值各是0 , 1和2,由於曆史原因,這三種值互斥 — 一個檔案只能有這三種值之一) 因此首先必須用屏蔽字O_ACCMODE相與取得存取方式位,然後將結果與這三種值相比較。
F_SETFL 設定給arg描述符狀態標誌,可以更改的幾個標誌是:O_APPEND,O_NONBLOCK,O_SYNC 和 O_ASYNC。而fcntl的檔案狀態標誌總共有7個:O_RDONLY, O_WRONLY, O_RDWR, O_APPEND, O_NONBLOCK, O_SYNC和O_ASYNC。
可更改的幾個標誌如下面的描述:
O_NONBLOCK 非阻塞I/O,如果read(2)調用沒有可讀取的資料,或者如果write(2)操作將阻塞,則read或write調用將返回-1和EAGAIN錯誤
O_APPEND 強制每次寫(write)操作都添加在檔案大的末尾,相當於open(2)的O_APPEND標誌
O_DIRECT 最小化或去掉reading和writing的緩衝影響。系統將企圖避免緩衝你的讀或寫的資料。如果不能夠避免緩衝,那麼它將最小化已經被緩衝了的資料造成的影響。如果這個標誌用的不夠好,將大大的降低效能
O_ASYNC 當I/O可用的時候,允許SIGIO訊號發送到進程組,例如:當有資料可以讀的時候
4. cmd=F_GETOWN和F_GETOWN
F_GETOWN 取得當前正在接受SIGIO或者SIURG訊號的進程id或進程組id,進程組id返回的是負值(arg被忽略)
F_SETOWN 設定將接受SIGIO或SIGURG訊號的進程id或進程組id,進程組id通過提供負值的arg來說明(arg絕對值的一個進程組ID),否則arg將被認為是進程id
5. cmd=F_GETLK,F_SETLK或F_SETLKW
獲得/設定記錄鎖的功能,成功則返回0,若有錯誤返回-1,錯誤原因存在於errno。
F_GETLK 通過第三個參數arg(一個指向flock的結構體)取得第一個阻塞lock description指向的鎖。取得的資訊將覆蓋傳到fctl()的flock結構的資訊。如果沒有發現能夠阻止本次鎖產生的鎖,這個結構將不被改變,除非鎖的類型被設定成F_UNLCK
F_SETLK 按照指向結構體flock的指標的第三個參數arg所描述的鎖的資訊設定或者清除一個檔案的segment鎖。F_SETLK被用來實現共用(或讀)鎖(F_RDLCK)或獨佔(寫)鎖(F_WRLCK),同樣可以去掉這兩種鎖(F_UNLCK)。如果共用鎖定或獨佔鎖不能被設定,fcntl()將立即返回EAGAIN
F_SETLKW 除了共用鎖定或獨佔鎖被其他的鎖阻塞這種情況外,這個命令和F_SETLK是一樣的。如果共用鎖定或獨佔鎖被其它的鎖阻塞,進程等待直到這個請求能夠完成。當fcntl()正在等待檔案的某個地區的時候捕捉到一個訊號,如果這個訊號沒有被指定SA_RESTART,fcntl將被中斷
當一個共用鎖定被set到一個檔案的某段的時候,其他的進程可以set共用鎖定到這個段或這個段的一部分。共用鎖定阻止任何其他進程set獨佔鎖到這段保護區的任何部分。如果檔案描述符沒有以讀的訪問方式開啟,共用鎖定的佈建要求會失敗。
獨佔鎖阻止任何其他的進程在這段保護地區任何位置設定共用鎖定或獨佔鎖。如果檔案描述符不是以寫的方式開啟,獨佔鎖的請求會失敗。
結構體flock的指標:
struct flock{ short int l_type;//鎖定的狀態 short int l_whence;//決定l_start位置 off_t l_start;//鎖定地區的開頭位置 off_t l_len;//鎖定地區的大小 pid_t l_pid;//鎖定動作的進程};
l_type有三種狀態:
F_RDLCK 建立一個供讀取的鎖定
F_WRLCK 建立一個供寫入的鎖定
F_UNLCK 刪除之前建立的鎖定
l_whence也有三種方式:
SEEK_SET 以檔案開頭為鎖定的起始位置
SEEK_CUR 以檔案結尾為鎖定的起始位置
SEEK_END 以檔案結尾為鎖定的起始位置
fcntl有兩種鎖:建議性鎖和強制性鎖
建議性鎖是這樣規定的:每個使用上鎖檔案的進程都要檢查是否有鎖存在,當然還得尊重已有的鎖。核心和系動總體上都堅持不使用建議性鎖,它們依靠程式員遵守這個規定。
強制性鎖是由核心使用的:當檔案被上鎖來進行寫入操作時,在鎖定該檔案的進程釋放該鎖之前,核心會阻止任何對該檔案的讀寫訪問,每次讀或寫訪問都得檢查鎖是否存在。
系統預設fcntl都是建議性鎖,強制性鎖是非POSIX標準的。如果要使用強制性鎖,要使整個系統可以使用強制性鎖,那麼得需要重新掛載檔案系統,mount使用參數 -0 mand 開啟強制性鎖,或者關閉已加鎖檔案的組執行許可權並且開啟該檔案的set-GID許可權位。
建議性鎖只在cooperating processes之間才有用。對cooperating process的理解是最重要的,它指的是會影響其它進程的進程或被別的進程所影響的進程,舉兩個例子:
1) 我們可以同時在兩個視窗中運行同一個命令,對同一個檔案進行操作,那麼這兩個進程就是cooperating processes
2) cat file | sort,那麼cat和sort產生的進程就是使用了pipe的cooperating processes
使用fcntl檔案鎖進行I/O操作必須小心:進程在開始任何I/O操作前如何去處理鎖,在對檔案解鎖前如何完成所有的操作,是必須考慮的。如果在設定鎖之前開啟檔案,或者讀取該鎖之後關閉檔案,另一個進程就可能在上鎖/解鎖操作和開啟/關閉操作之間的幾分之一秒內訪問該檔案。當一個進程對檔案加鎖後,無論它是否釋放所加的鎖,只要檔案關閉,核心都會自動釋放加在檔案上的建議性鎖(這也是建議性鎖和強制性鎖的最大區別),所以不要想設定建議性鎖來達到永久不讓別的進程訪問檔案的目的(強制性鎖才可以);強制性鎖則對所有進程起作用。
fcntl使用三個參數F_SETLK/F_SETLKW,F_UNLCK和F_GETLK來分別要求、釋放、測試record locks。record locks是對檔案一部分而不是整個檔案的鎖,這種細緻的控制使得進程更好的協作以共用檔案。fcntl能夠用於讀取鎖和寫入鎖,read lock也叫shared lock(共用鎖定),因為多個cooperating processes能夠在檔案的統一部分建立讀取鎖;write lock被稱為excluseive lock(排斥鎖),因為任何時刻只能有一個cooperating processes在檔案的某部分建立寫入鎖。如果cooperating processes對檔案進行操作,那麼它們可以同時對檔案加read lock;在一個cooperating processes加write lock之前,必須釋放別的cooperating processes載入該檔案的read lock和write lock,也就是說,對於檔案只能有一個write lock存在,read lock和write lock不能共存。
下面的例子使用F_GETFL得到檔案狀態資訊:
#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <errno.h>#include <fcntl.h>int main(){int fd;int var;if ((fd = open("test.txt", O_RDWR | O_CREAT | O_EXCL, 0664)) == -1){perror("open error");exit(1);}if ((var = fcntl(fd, F_GETFL, 0)) < 0){perror("fcntl error");close(fd);exit(1);}switch(var & O_ACCMODE){case O_RDONLY:printf("Read only..\n");break;case O_WRONLY:printf("Write only..\n");break;case O_RDWR:printf("Read And Write..\n");break;default:printf("Do't know..\n");break;}if (var & O_APPEND){printf("And Append..\n");}if (var & O_NONBLOCK){printf("And Nonblocking..\n");}if (close(fd) == -1){perror("close error");}return 0;}
測試鎖的例子
http://blog.csdn.net/xiaoliangsky/article/details/40303245
//讀加寫鎖在同一個進程測試
#include <stdio.h>#include <stdlib.h>#include <errno.h>#include <unistd.h>#include <fcntl.h>int main(){int fd;int fret;struct flock rdflk;struct flock wrflk;struct flock tmflk;///*關於加鎖:1)如果檔案是以唯讀開啟的,只能加讀鎖,加寫鎖時會失敗返回-12)如果檔案是以唯寫開啟的,只能加寫鎖,加讀鎖時會失敗返回-13)如果檔案是以讀寫開啟的,在同一個進程中,如果同時加了兩種鎖,後加的鎖會覆蓋前面加的鎖。 比如先加寫鎖,後加讀鎖,結果檔案加上的是讀鎖;如果先加讀鎖,後加寫鎖,檔案最終加上的是寫鎖。//*if ((fd = open("test.txt", O_RDWR)) == -1){perror("open error");exit(1);}if ((fret = fcntl(fd, F_GETFD)) == -1){perror("fcntl error");exit(1);}fcntl(fd, F_SETFD, 1);//在exec時關閉rdflk.l_type = F_RDLCK;rdflk.l_whence = SEEK_SET;rdflk.l_start = 0;rdflk.l_len = 100;wrflk = rdflk;wrflk.l_type = F_WRLCK;//檔案先加讀鎖if (fcntl(fd, F_SETLK, &rdflk) == -1){perror("fcntl set rdflk error");exit(1);}//後加寫鎖,最終寫鎖會覆蓋讀鎖,檔案加的是寫鎖if (fcntl(fd, F_SETLK, &wrflk) == -1){perror("fcntl set wrflk error");exit(1);} //在這個用fcntl(fd, F_GETLK, &tmflk); return 0;}
//唯讀開啟,加寫鎖報錯
#include <stdio.h>#include <stdlib.h>#include <errno.h>#include <unistd.h>#include <fcntl.h>int main(){int fd;int fret;struct flock rdflk;struct flock wrflk;//關於加鎖://1)如果檔案是以唯讀開啟的,只能加讀鎖,加寫鎖時會失敗返回-1if ((fd = open("test.txt", O_RDONLY)) == -1){perror("open error");exit(1);}rdflk.l_type = F_RDLCK;rdflk.l_whence = SEEK_SET;rdflk.l_start = 0;rdflk.l_len = 100;wrflk = rdflk;wrflk.l_type = F_WRLCK;//加寫鎖,會出現錯誤,因為檔案是以唯讀開啟的//fcntl set wrflk error: Bad file descriptorif (fcntl(fd, F_SETLK, &wrflk) == -1){perror("fcntl set wrflk error");exit(1);}return 0;}同樣以唯寫開啟加讀鎖也出錯:
//2)如果檔案是以唯寫開啟的,只能加寫鎖,加讀鎖時會失敗返回-1if ((fd = open("test.txt", O_WRONLY)) == -1){perror("open error");exit(1);}//加讀鎖,會出現錯誤,因為檔案是以唯寫開啟的//fcntl set wrflk error: Bad file descriptorif (fcntl(fd, F_SETLK, &rdflk) == -1){perror("fcntl set wrflk error");exit(1);}
//以讀寫方式開啟,在不同進程之間加鎖
/*
當一個共用鎖定被set到一個檔案的某段的時候,其他的進程可以set共用鎖定到這個段或這個段的一部分。共用鎖定阻止任何其他進程set獨佔鎖到這段保護區的任何部分。如果檔案描述符沒有以讀的訪問方式開啟,共用鎖定的佈建要求會失敗。
獨佔鎖阻止任何其他的進程在這段保護地區任何位置設定共用鎖定或獨佔鎖。如果檔案描述符不是以寫的方式開啟,獨佔鎖的請求會失敗。
總結:如果有一個進程加寫鎖,其他進程不能加任何鎖;如果是讀鎖,其他進程只能加讀鎖。
*/
代碼:
#include <stdio.h>#include <stdlib.h>#include <errno.h>#include <unistd.h>#include <fcntl.h>#include <sys/types.h>#define BUF_SIZE 512int main(){int fd;int bytes_read;int status;pid_t pid;const char *chbuf;char buf[BUF_SIZE];struct flock tmflk;struct flock rdflk;struct flock wrflk;chbuf = "xxxxx";rdflk.l_type = F_RDLCK;rdflk.l_whence = SEEK_SET;rdflk.l_start = 0;rdflk.l_len = 100;wrflk = rdflk;tmflk = rdflk;wrflk.l_type = F_WRLCK;//在進程之間以讀寫方式開啟,在不同進程之間加讀寫鎖if ((fd = open("test.txt", O_RDWR | O_APPEND)) == -1){perror("open error");exit(1);}if ((pid = fork()) < 0){perror("fork error");exit(1);}else if (pid > 0){//父進程加讀鎖if (fcntl(fd, F_SETLK, &wrflk) == -1) {perror("parent fcntl set rdflk error");exit(1); }//然子進程嘗試加鎖sleep(1);//寫入資料if (write(fd, chbuf, sizeof(chbuf)) == -1){perror("parent write error");}tmflk = rdflk;tmflk.l_type = F_UNLCK;//然子進程嘗試申請鎖,以便測試在有讀鎖的情況下,其他進程不能加鎖sleep(3);//釋放鎖if (fcntl(fd, F_SETLK, &tmflk) == -1){perror("fcntl unlck error");}waitpid(pid, &status, 0);//等待子進程返回}else{sleep(1);tmflk = rdflk;//嘗試枷鎖while (1){ //擷取檔案鎖的資訊if (fcntl(fd, F_GETLK, &tmflk) == -1){perror("child fcntl getlk error");}if (tmflk.l_type == F_UNLCK){break;}if (tmflk.l_type == F_RDLCK){printf("process Id:%d has locked with F_RDLCK\n", tmflk.l_pid);}else if (tmflk.l_type == F_WRLCK){printf("process Id:%d has locked with F_WRLCK\n", tmflk.l_pid);}sleep(1);}//加讀鎖if (fcntl(fd, F_SETLK, &rdflk) == -1){perror("fcntl setlk rdflk error");exit(1);}printf("child fcntl get the rdflk\n");lseek(fd, rdflk.l_whence, rdflk.l_start);//重定位if((bytes_read = read(fd, buf, BUF_SIZE)) < 0){perror("read error");exit(1);}buf[bytes_read-1] = '\0';write(STDOUT_FILENO, buf, bytes_read);printf("\n");tmflk = rdflk;tmflk.l_type = F_UNLCK;fcntl(fd, F_SETLK, &tmflk);}close(fd);return 0;}
下面是來自博文:http://blog.sina.com.cn/s/blog_4b226b9201011974.html寫的也不錯
參數:
fd:檔案描述詞。
cmd:操作命令。
arg:供命令使用的參數。
lock:同上。
有以下操作命令可供使用
一. F_DUPFD :複製檔案描述詞 。
二. FD_CLOEXEC :設定close-on-exec標誌。如果FD_CLOEXEC位是0,執行execve的過程中,檔案保持開啟。反之則關閉。
三. F_GETFD :讀取檔案描述詞標誌。
四. F_SETFD :設定檔案描述詞標誌。
五. F_GETFL :讀取檔案狀態標誌。
六. F_SETFL :設定檔案狀態標誌。
其中O_RDONLY, O_WRONLY, O_RDWR, O_CREAT, O_EXCL, O_NOCTTY 和 O_TRUNC不受影響,
可以更改的標誌有 O_APPEND,O_ASYNC, O_DIRECT, O_NOATIME 和 O_NONBLOCK。
七. F_GETLK, F_SETLK 和 F_SETLKW :擷取,釋放或測試記錄鎖,使用到的參數是以下結構體指標:
F_SETLK:在指定的位元組範圍擷取鎖(F_RDLCK, F_WRLCK)或者釋放鎖(F_UNLCK)。如果與另一個進程的鎖操作發生衝突,返回 -1並將errno設定為EACCES或EAGAIN。
F_SETLKW:行為如同F_SETLK,除了不能擷取鎖時會睡眠等待外。如果在等待的過程中接收到訊號,會立即返回並將errno置為EINTR。
F_GETLK:擷取檔案鎖資訊。
F_UNLCK:釋放檔案鎖。
為了設定讀鎖,檔案必須以讀的方式開啟。為了設定寫鎖,檔案必須以寫的方式開啟。為了設定讀寫鎖,檔案必須以讀寫的方式開啟。
八. 訊號管理
F_GETOWN, F_SETOWN, F_GETSIG 和 F_SETSIG 被用於IO可擷取的訊號。
F_GETOWN:擷取當前在檔案描述詞 fd上接收到SIGIO 或 SIGURG事件訊號的進程或進程組標識 。
F_SETOWN:設定將要在檔案描述詞fd上接收SIGIO 或 SIGURG事件訊號的進程或進程組標識 。
F_GETSIG:擷取標識輸入輸出可進行的訊號。
F_SETSIG:設定標識輸入輸出可進行的訊號。
使用以上命令,大部分時間程式無須使用select()或poll()即可實現完整的非同步I/O。
九. 租約( Leases)
F_SETLEASE 和 F_GETLEASE 被用於當前進程在檔案上的租約。檔案租約提供當一個進程試圖開啟或折斷檔案內容時,擁有檔案租約的進程將會被通告的機制。
F_SETLEASE:根據以下符號值設定或者刪除檔案租約
1.F_RDLCK設定讀租約,當檔案由另一個進程以寫的方式開啟或折斷內容時,擁有租約的當前進程會被通告。
2.F_WRLCK設定寫租約,當檔案由另一個進程以讀或以寫的方式開啟或折斷內容時,擁有租約的當前進程會被通告。
3.F_UNLCK刪除檔案租約。
F_GETLEASE:擷取租約類型。
十.檔案或目錄改變通告
(linux 2.4以上)當fd索引的目錄或目錄中所包含的某一檔案發生變化時,將會向進程發出通告。arg參數指定的通告事件有以下,兩個或多個值可以通過或運算組合。
1.DN_ACCESS 檔案被訪問 (read, pread, readv)
2.DN_MODIFY 檔案被修改(write, pwrite,writev, truncate, ftruncate)
3.DN_CREATE 檔案被建立(open, creat, mknod, mkdir, link, symlink, rename)
4.DN_DELETE 檔案被刪除(unlink, rmdir)
5.DN_RENAME 檔案被重新命名(rename)
6.DN_ATTRIB 檔案屬性被改變(chown, chmod, utime[s])
返回說明:
成功執行時,對於不同的操作,有不同的返回值
F_DUPFD: 新檔案描述詞
F_GETFD: 標誌值
F_GETFL: 標誌值
F_GETOWN: 檔案描述詞屬主
F_GETSIG: 讀寫變得可行時將要發送的通告訊號,或者0對於傳統的SIGIO行為
對於其它命令返回0。
失敗返回-1,errno被設為以下的某個值
EACCES/EAGAIN: 操作不被允許,尚未可行
EBADF: 檔案描述詞無效
EDEADLK: 探測到可能會發生死結
EFAULT: 鎖操作發生在可訪問的地址空間外
EINTR: 操作被訊號中斷
EINVAL: 參數無效
EMFILE: 進程已超出檔案的最大可使用範圍
ENOLCK: 鎖已被用盡
EPERM:權能不允許
參看資料:
http://www.cnblogs.com/andtt/articles/2178875.html
linux fcntl函數