Linux系統程式設計(3)——檔案與IO之fcntl函式

尹成發表於2014-07-24

linux檔案I/O用:open、read、write、lseek以及close函式實現了檔案的開啟、讀寫等基本操作。fcntl函式可以根據檔案描述詞來操作檔案。

 

用法:

int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd, struct flock*lock);

引數:

fd:檔案描述詞。

cmd:操作命令。

arg:供命令使用的引數。

lock:同上。

 

有以下操作命令可供使用

 

1. F_DUPFD :複製檔案描述詞 。

2. FD_CLOEXEC :設定close-on-exec標誌。如果FD_CLOEXEC位是0,執行execve的過程中,檔案保持開啟。反之則關閉。

3. F_GETFD :讀取檔案描述詞標誌。

4. F_SETFD :設定檔案描述詞標誌。

5. F_GETFL :讀取檔案狀態標誌。

6. 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。

7. 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:釋放檔案鎖。

為了設定讀鎖,檔案必須以讀的方式開啟。為了設定寫鎖,檔案必須以寫的方式開啟。為了設定讀寫鎖,檔案必須以讀寫的方式開啟。

8. 訊號管理

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。

9. 租約( Leases)

F_SETLEASE 和 F_GETLEASE 被用於當前程式在檔案上的租約。檔案租約提供當一個程式試圖開啟或折斷檔案內容時,擁有檔案租約的程式將會被通告的機制。

F_SETLEASE:根據以下符號值設定或者刪除檔案租約

  •  F_RDLCK設定讀租約,當檔案由另一個程式以寫的方式開啟或折斷內容時,擁有租約的當前程式會被通告。
  •   F_WRLCK設定寫租約,當檔案由另一個程式以讀或以寫的方式開啟或折斷內容時,擁有租約的當前程式會被通告。
  •   F_UNLCK刪除檔案租約。
  •   F_GETLEASE:獲取租約型別。

 

10.檔案或目錄改變通告

(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:權能不允許

  

在檔案已經共享的情況下如何操作,也就是當多個使用者共同使用、操作一個檔案時,Linux 通常採用的方法是給檔案上鎖,來避免共享的資源產生競爭的狀態。

檔案鎖包括建議性鎖和強制性鎖。

建議性鎖要求每個上鎖檔案的程式都要檢查是否有鎖存,並且尊重已有的鎖。在一般情況下,核心和系統都不使用建議性鎖。強制性鎖是由核心執行的鎖,當一個檔案被上鎖進行寫入操作的時候,核心將阻止其他任何檔案對其進行讀寫操作。採用強制性鎖對效能的影響很大,每次讀寫操作都必須檢查是否有鎖存在。

在 Linux 中,實現檔案上鎖的函式有lock和fcntl,其中flock用於對檔案施加建議性鎖,而fcntl不僅可以施加建議性鎖,還可以施加強制鎖。同時,fcntl還能對檔案的某一記錄進行上鎖,也就是記錄鎖。

記錄鎖又可分為讀取鎖和寫入鎖,其中讀取鎖又稱為共享鎖,它能夠使多個程式都能在檔案的同一部分建立讀取鎖。而寫入鎖又稱為排斥鎖,在任何時刻只能有一個程式在檔案的某個部分上建立寫入鎖。當然,在檔案的同一部分不能同時建立讀取鎖和寫入鎖。

注意:

fcntl是一個非常通用的函式,它還可以改變檔案程式各方面的屬性,在本節中,主要介紹它建立記錄鎖的方法,關於它其他使用者感興趣的讀者可以參看fcntl手冊。

 

下面首先給出了使用fcntl 函式的檔案記錄鎖函式。在該函式中,首先給flock 結構體的對應位賦予相應的值。接著使用兩次fcntl函式分別用於給相關檔案上鎖和判斷檔案是否可以上鎖,這裡用到的cmd值分別為F_SETLK 和F_GETLK。

這個函式的原始碼如下所示:

/*lock_set函式*/
void lock_set(int fd, int type)
{
struct flock lock;
lock.l_whence = SEEK_SET;//賦值lock結構體
lock.l_start = 0;
lock.l_len =0;
while(1){
lock.l_type = type;
/*根據不同的type值給檔案上鎖或解鎖*/
if((fcntl(fd, F_SETLK, &lock)) == 0){
if( lock.l_type == F_RDLCK )
printf("read lock set by%d\n",getpid());
else if( lock.l_type == F_WRLCK )
printf("write lock set by%d\n",getpid());
else if( lock.l_type == F_UNLCK )
printf("release lock by%d\n",getpid());
return;
}
/*判斷檔案是否可以上鎖*/
fcntl(fd, F_GETLK,&lock);
/*判斷檔案不能上鎖的原因*/
if(lock.l_type != F_UNLCK){
/*/該檔案已有寫入鎖*/
if( lock.l_type == F_RDLCK )
printf("read lock already set by%d\n",lock.l_pid);
/*該檔案已有讀取鎖*/
else if( lock.l_type == F_WRLCK )
printf("write lock already set by%d\n",lock.l_pid);
getchar();
}
}
}


下面的例項是測試檔案的寫入鎖,這裡首先建立了一個hello檔案,之後對其上寫入鎖,最後釋放寫入鎖。程式碼如下所示:

/*fcntl_write.c測試檔案寫入鎖主函式部分*/
#include <unistd.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
    int fd;
    /*首先開啟檔案*/
    fd=open("hello",O_RDWR | O_CREAT,0666);
    if(fd < 0){
        perror("open");
        exit(1);
    }
    /*給檔案上寫入鎖*/
    lock_set(fd, F_WRLCK);
    getchar();
    /*給檔案接鎖*/
    lock_set(fd, F_UNLCK);
    getchar();
    close(fd);
    exit(0);
}

接下來的程式是測試檔案的讀取鎖,原理同上面的程式一樣。

/*fcntl_read.c測試檔案讀取鎖主函式部分*/
#include <unistd.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
    int fd;
    fd=open("hello",O_RDWR | O_CREAT,0666);
    if(fd < 0){
        perror("open");
        exit(1);
    }
    /*給檔案上讀取鎖*/
    lock_set(fd, F_RDLCK);
    getchar();
    /*給檔案接鎖*/
    lock_set(fd, F_UNLCK);
    getchar();
    close(fd);
    exit(0);
}


相關文章