文件控制 fcntl函數詳解

摘要:本文主要討論文件控制fcntl函數的基本應用.dup函數可以複製文件描述符,而fcntl函數與dup函數有着異曲同工之妙.並且還有更加強大的功能,可以獲取或設置已打開文件的性質,操作文件鎖.

1.fcntl函數

    在《重定向編程 dup和dup2》一文中,介紹了dup和dup2兩個函數,函數是提供了複製一個現存的文件描述符的功能,而本文介紹的fcntl函數提供了進一步管理文件描述符的各種手段,用它可以對以打開的描述符執行各種控制操作,比如,可以像dup和dup2一樣複製一個文件描述符,還有獲取或設置文件描述符標誌(只讀、只寫等等),操縱文件鎖.

頭文件:
#include <unistd.h>
#include <fcntl.h>
定義函數:
int fcntl(int filedes, int cmd);
int fcntl(int filedes, int cmd, longarg);
int fcntl(int filedes, int cmd,structflock *lock);
返回值:若成功返回值依賴與cmd,若出錯則返回-1.如果設置屬性,正確返回0,錯誤返回-1;如果是讀取屬性,正確返回該屬性值,錯誤返回-1.如,下例三個命令有特定的返回值:F_DUPFD、F_GETFD、F_GETFL以及F_GETOWN.
F_DUPFD:返回新的文件描述符.
F_GETFD:返回相應的標誌.
F_GETFL以及F_GETOWN:返回一個進程ID或負的進程組ID.
函數說明:
   函數第一個參數爲欲修改屬性的文件描述符,第三個整數總是整數或記錄鎖(記錄鎖這裏不討論),如果是記錄鎖,第三個參數則是指向一個結構體指針.
   fcntl()對打開的文件描述符filedes執行各種控制操作,具體是那一操作則由第二個參數cmd決定,表1列出了該參數的所有允許值.根據參數cmd的值,有一些參數還要提供第三個參數,就是文件鎖.

表1 fcntl函數cmd值以及描述
 
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_SETLF).

2.F_DUPFD

    複製文件描述符filefes.新文件描述符最爲函數返回值.新文件描述符具有以下特性:
(1)新描述符是尚未打開的各文件描述符中大於或等於第三個參數值(取正整數)中各值的最小值.
(2)新描述符與filedes共享同一個文件表項(類似於dup函數);即,新描述符與filedes原始文件相同的文件指針,相同的打開文件(或管道),相同的文件狀態標誌,相同的文件模式.如圖2所示,fd=3與fd=1兩個文件描述符是指向同一個文件表的.但是,新文件描述符有它自己的一套文件描述符標誌,其FD_CLOEXEC文件描述符標誌被清除.
(3)將與新文件描述符關聯的close-on-exe標誌設置爲在各exec(2)系統調用之間保持打開狀態.
    當進程打開一個文件時,內核中的數據結構如圖1所示,也就是在程序複製文件描述符之前,內核狀態是這樣子的。每個進程默認只能打開1024個文件描述符,當一個進程打開一個文件時,默認會從0開始查找未被使用的描述符,由於0,1,2默認被佔用,所有一般從3開始使用。

 
圖1 進程打開一個文件時的內核數據結構狀態
文件表項:每一個打開的文件對應着一張文件表,文件表可以共享,當多個文件描述符指向同一個文件表時,文件表中的refcnt字段會相應變化.文件表中包含着文件狀態標識:文件的打開模式(R,W,RW,APPEND,NOBLOCK等)、當前文件偏移量、refcnt:被引用數量、v節點指針:指向一個v節點表。
v結點表:每個文件對應一個v結點表,無論被多少個進程打開都只有一個,它包括v節點信息(主要是stat結構體中的信息),i節點信息等。
    簡單地說,複製文件描述符僅僅在當前進程打開的文件表項新增一項(圖2:新增了fd=3這一項),兩者(fd=1或fd=3)同時指向內核中的文件表信息,操作文件描述符任意一個會影響到另外一個.

 
圖2 進程複製文件描述符後的內核數據結構狀態
例子1:複製文件描述符,並使用複製前後的文件描述符往打開的文件寫入信息.
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

void main()
{
        int fd,newfd;
        char *bufFD="Advanced Programming! write by fd\n";
        char *bufNewFD="Advanced Programming! write by NewFD\n";
        fd = open("test.txt",O_RDWR|O_CREAT,0644);
        if(fd==-1)
        {
                printf("open file error%m\n");
                exit(-1);
        }

        //開始複製了
        newfd = fcntl(fd,F_DUPFD);
        //使用fd寫
        write(fd,bufFD,strlen(bufFD));
        close(fd);

        //使用newfd寫
        write(newfd,bufNewFD,strlen(bufNewFD));

        if(close(newfd)==-1)
        {
                printf("close error\n");
        }
        printf("now the content of file :\n");
        system("cat test.txt");
        exit(0);
}
輸出:
:Advanced Programming! write by fd
:Advanced Programming! write by NewFD
    可以看出,對fd或newfd進行讀寫操作時對同一個文件操作,而且還可以看到fd關閉後,對newfd沒有影響,使用newfd還可以操作打開的文件.
    對於複製一個文件描述符這個問題,fcntl和dup函數是有異曲同工之妙.
調用:

dup(filedes);
等價於:
fcntl(filedes, F_DUPFD, 0);
而調用:
dup2(filedes,filedes2);
等價於:
close(filedes2);
fcntl(filesdes,F_DUPFD,filedes);
注意:對於後一種情況,dup2並不完全等價於close加上fcntl.區別:
(1)dup2是原子操作,而close及fcntl則包括兩個函數調用.有可能在close和fcntl之間插入執行信號捕獲函數,中間這個過程修改了文件描述符.
(2)dup2和fcntl有某些不同的errno.

3.F_GETFL

    F_GETFL用於獲取文件狀態標誌和其訪問模式,對應於filedes的文件狀態標誌作爲返回值.F_GETFL支持的標誌.如表2.
表2 文件狀態標誌說明

例子2:監測文件當前的讀取權限.如果文件具有讀取權限,則返回可讀信息,如果具有寫權限,則返回可寫信息.否則返回出錯信息.
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
        int accmode,val;
        if(argc!=2)
        {
                printf("error:\n");
        }
        if((val=fcntl(atoi(argv[1]),F_GETFL,0))<0)
        {
                printf("error:%m\n");
                exit(-1);
        }
        accmode = val & O_ACCMODE;
        switch(accmode)
        {
                case O_RDONLY:
                        printf("read only\n");
                        break;
                case O_WRONLY:
                        printf("write only\n");
                        break;
                case O_RDWR:
                        printf("read write\n");
                        break;
                default:
                        printf("unknow access mode\n");
        }
        exit(0);
}
輸出:
:./a.out test.txt
:read write

4.綜述

    複製文件描述符的三種方法已學習的差不多,牢記dup、dup2和fcntl函數,因爲,從此一生受用.關於fcntl的使用還有很多未挖掘,到這裏先簡單入門,日後再進一步加深學習.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------勿在浮沙築高臺,靜下心來,慢慢地沉澱---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
發佈了33 篇原創文章 · 獲贊 44 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章