文件IO

標準輸入:0
標準輸出:1
錯誤輸出:2
符號常量爲:STDIN_FILENO,STDOUT_FILENO,STDERR_FILENO

open或者openat:
int open(const char* path,int oflag,....);
int openat(int fd,const char *path,int oflag,...);
path:要打開或者要創建的名字
oflag:說明此函數的多個選項
    O_RDONLY    只讀打開
    O_WRONLY    只寫打開
    O_RDWR      讀寫打開
大多數的實現將O_RDONLY定義爲0,O_WRONLY定義爲1,O_RDWR定義爲2
    O_EXEC      只執行打開
    O_SEARCH    只搜索打開

這5個常量中必須指定一個且只能指定一個。下列常量則是可選的:
    O_APPEND    每次都追加到文件的尾端
    O_CLOEXEC   把FD_CLOEXEC常量設置爲文件描述符標誌
    O_CREAT     若文件不存在則創建它。使用這個選項的時候,
            open參數需同時說明第三個參數mode(openat則是第四個)
            用mode指定該新文件的訪問權限位,可以使用進程umask
            值修改它
    O_DIRECTORY     如果path不是目錄,則出錯
    O_EXCL      如果同時指定了O_CREAT,而文件已經存在,則出錯
    O_NOCTTY    如果path引用的是終端設備,則不將該設備分配作爲此進程的
            終端控制
    O_NOFOLLOW  如果path引用的是一個符號連接,則出錯
    O_NONBLOCK  如果path引用的是一個FIFO、一個塊特殊文件或一個字符
            特殊文件,則此選項爲文件的本次打開操作和後續的I/O
            操作設置非組塞模式
    O_SYNC      每次write等待物理I/O操作完成,包括由該write操作引起
            的文件屬性跟新所需的I/O
    O_TRUNC     如果此文件存在,而且爲只寫或者是讀-寫成功打開,則將其
            長度截斷爲0
    O_TTY_INIT  如果打開一個還未打開的終端設備,設置非標準termios參數
            值,使其符合signal UNIX Specification
    O_DSYNC     使每次write要等待物理I/O操作完成,但是如果寫操作並不
            影響讀取剛寫入的數據,則不需等待文件屬性被更新
    O_RSYNC     使每一個文件描述符作爲參數進行的read操作等待,直至
            所有的對文件同一部分掛起的寫操作都完成了

creat函數:創建一個新的文件
    int creat(const char *path,mode_t mode);
    等效於open(path,O_WRONLY|O_CREAT|O_TRUNC,mode);
    不足是它以只寫的方式打開所創建的文件。
    使用open實現:
    open(path,O_RDWR | O_CREAT | O_TRUNC,mode);


close函數:打開一個文件
    #include <unistd.h>
    int close(int fd);
    關閉一個文件的時候還會釋放該進程加在該文件上的所有記錄鎖。
    當一個進程終止的時候,內核自動關閉它所有的打開文件。

lseek函數:當前文件偏移量
    調用lseek顯式爲一個打開文件設置偏移量
    #include <unistd.h>
    off_t lseek(int fd,off_t offset,int whence);
    其中offset於參數whence有關:
    SEEK_SET:設置偏移量爲離文件開始處offset個字節
    SEEK_CUR:設置偏移量爲離當前位置加offset,可正可負
    SEEK_END:設置偏移量爲離文件末尾加offset
        off_t currpos;
        currpos=lseek(fd,0,SEEK_CUR);
        如果文件描述符指向的是一個管道、FIFO、網絡套接字,則lseek
        返回-1,並將errno設置爲ESPIPE.
    whence:0(絕對偏移量)
    whence:1(相對當前位置的偏移量)
    whence:2(相對文件末尾的偏移量)

        #include <unistd.h>
        #include <stdio.h>
        #include <stdlib.h>

        int main(void){
            if(lseek(STDIN_FILENO,0,SEEK_CUR)==-1){
                printf("cannot seek\n");
            }
            else
                printf("seek ok\n");
            exit(0);
        }

read函數:從打開文件中讀取數據
    #include <unistd.h>
    ssize_t read(int fd,void *buf,size_t nbytes);
    read成功,則返回讀到的字節數。如已到達文件末尾,則返回0。


write函數:向打開文件寫數據
    #include <unistd.h>
    ssize_t write(int fd,const void *buf,size_t nbytes);
    若返回成功,表示的是以寫的字節數,若失敗,返回的是-1。


pread和pwrite:
    #include <unistd.h>
    ssize_t pread(int fd,void *buf,size_t nbytes,off_t offset);
    返回值:讀到的字節數,若已到文件尾,返回0;若出錯,返回-1
    ssize_t pwrite(int fd,const void *buf,size_t nbytes,off_t offset);
    返回值:若成功,返回已寫的字節數;若出錯,返回-1

    調用pread相當於調用lseek後調用read,但是pread有區別:
    1、調用pread時,無法中斷其定位和讀操作
    2、不更新當前文件偏移量
    調用pread相當於調用lseek後調用write,但是pwrite有類似的區別

dup和dup2函數:複製一個現有的文件描述符
    #include <unistd.h>
    int dup(int fd);
    int dup2(int fd,int fd2);
    有dup返回的新文件描述符一定是當前可用文件描述符中的最小的數值。
    對於dup2,可以用fd2參數指定新描述符的值。如果fd2已經打開,則先將其關閉。
    如若fd等於fd2,則dup2返回fd2,而不關閉它。否則,fd2的FD_CLOSEXEC文件
    描述符標誌就被清除,這樣fd2在進程調用exec時是打開狀態.

    調用dup(fd);
    等效於fcntl(fd,F_DUPFD,0);
    而調用dup2(fd,fd2);
    等效於:
        close(fd2);
        fcntl(fd,F_DUPFD,fd2);
    dup2並不完全等同於以上,區別如下:
        1、dup2是一個原子操作,而close和fcntl包括兩個函數調用。
        可能在close和fcntl之間調用了信號捕獲函數,它們可能修改文件
        描述符,如果不同線程改變了文件描述符也會出現問題
        2、dup2和fcntl有一些不同的errno

sync、fsync和fdatasync函數:
    傳統的UNIX系統實現在內核中設有緩衝區高速緩存或者頁高速緩存,大多數磁盤
    I/O都通過緩衝區進行。
    當我們向文件寫入數據時,內核通常先將數據複製到緩衝區中,然後排入隊列,晚些時候
    再寫入磁盤。
    通常,當內核需要重用緩衝區來存放其他磁盤塊數據時,它會把所以延遲寫數據塊寫入磁盤
    爲了保證磁盤上實際文件系統與緩衝區中內容的一致性,UNIX系統提供了sync、fsync、
    fdatasync函數。

    #include <unistd.h>
    int fsync(int fd);
    int fdatasync(int fd);
    void sync(void);

    sync只是將所以修改過的塊緩衝區排入寫隊列,然後就返回,它並不等待實際寫磁盤
    操作結束。
    通常,成爲update的系統守護進程週期性地調用(一般隔30秒)sync函數。這就
    保證了定期沖洗(flush)內核的塊緩衝區。命令sync(l)也調用sync函數
    fsync只對由文件描述符fd指定的一個文件起作用,並且等待  寫磁盤操作結束。
    fsync可用於數據庫這樣的應用程序。
    fdatasync函數類似於fsync函數,但它隻影響文件的數據部分。而除數據外fsync
    還會同步更新文件的屬性。

簡單說文件描述符:
    文件描述符在形式上是一個非負整數。實際上,它是一個索引值,指向內核爲每一個進程所維護
    的該進程打開文件的記錄表。當程序打開一個現有文件或者創建一個新文件時,內核向進程返回
    一個文件描述符。

fcntl函數:改變已經打開文件的屬性
    #include <fcntl.h>
    int fcntl(int fd,int cmd,.../*int arg*/);
    返回值:成功,則依賴於cmd,出錯,返回-1。

    fcntl函數的5種功能:
    1、複製一個已有的描述符(cmd=F_DUPFD或者F_DUPFD_CLOEXEC)
    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_SETLK或者F_SETLKW);

    F_DUPFD     複製文件描述符fd。新文件描述符作爲函數只返回。它是尚未打開的
            各描述符大於或者等於第3個參數值(取爲整形值)中各值的最小值。
            新描述符與fd共享同一文件表項。但是,新描述符有它自己的一套文件
            描述符標誌,其FD_CLOEXEC文件描述符標誌被清除(這表示該描述符在
            exec時仍保持有效)

    F_DUPFD_CLOEXEC 複製文件描述符,設置與新描述符關聯的FD_CLOEXEC文件描述符標
            志的值,返回新文件描述符。

    F_GETFD     對應與fd的文件描述符標誌作爲函數值返回。當前只定義了一個文件
            描述符標誌FD_CLOEXEC。

    F_SETFD     用於設置文件描述符標誌。新標誌值按第3個參數(取爲整數值)設置
            現在很多程序不使用FD_CLOEXEC,而是將此標誌設置爲0,(系統默認)
            在exec時不關閉,1在exec關閉

    F_GETFL     對應與fd的文件狀態標誌作爲函數值返回

    F_SETFL     將文件狀態標誌設置爲第3個參數的值,(取整形值)。
            可以更改的標誌爲:O_APPEND  O_NONBLOCK  O_SYNC  O_DSYNC
            O_RSYNC  O_FSYNC  O_ASYNC

    F_GETOWN    獲取當前接受SIGIO和SIGURG信號的進程ID或進程組ID。

    F_SETOWN    設置接受SIGIO和SIGURG信號的進程ID或進程組ID。
            正的arg指定一個進程ID,負的arg表示等於arg絕對值的一個進程組ID

fcntl的返回值與命令有關。如果出錯,所以命令都返回-1,如果成功返回其他值
F_DUPFD:返回的是新的文件描述符
F_GETFD:返回的時相應的標誌
F_GETFL:返回的時相應的標誌
F_GETOWN:返回一個正的進程ID或者一個負的進程組ID

例子:
O_ACCMODE<0003>:讀寫文件操作時,用於取出flag的低2位
O_RDONLY<00>:只讀打開
O_WRONLY<01>:只寫打開
O_RDWR<02>:讀寫打開 
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <fcntl.h>

    int main(int argc,char *argv[]){
        int val;

        if(argc!=2){
                printf("usage:a.out <descriptor#>");
        }

        if((val=fcntl(atoi(argv[1]),F_GETFL,0))<0)
                printf("fcntl error for fd %d",atoi(argv[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("unknown access mode");
        }

        if(val & O_APPEND)
                printf(",append");
        if(val & O_NONBLOCK)
                printf(",nonblocking");
        if(val & O_SYNC)
                printf(",synchronous writes");

        #if !defined(_POSIX_C_SOURCE) && defined(O_FSYNC) && (O_FSYNC != O_SYNC)
                if(val & O_FSYNC)
                        printf(",synchronous writes");
        #endif

        putchar('\n');
        exit(0);
    }
運行:
    ./a.out 0 < /dev/tty
    ./a.out 1 > temp.foo
    ./a.out 2 2>>temp.foo
    ./a.out 5 5<>temp.foo
    字句5<>temp.foo表示的時文件描述符5上打開文件temp.foo以供讀、寫。
修改文件描述符標誌或者是文件狀態標誌(示例函數):
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <fcntl.h>

    void set_fl(int fd,int flags)
    {
        int val;

        if((val=fcntl(fd,F_GETFL,0))<0)
        {
            printf("fcntl F_GETFL error");
        }

        val |= flags;

        if(fcntl(fd,F_SETFL,val)<0)
            printf("fcntl F_SETFL error");
    }
    其中將一條語句改爲:
    val &=~flags;//表示的時關閉某個文件標誌
    如果加上set_fl(STDOUT_FILENO,O_SYNC);開啓了同步寫操作
    這樣使得每次write都要等待,直到數據已寫到磁盤上再返回。

ioctl函數:
    ioctl函數一直是I/O操作的雜物箱。
    #include <unistd.h>
    #include <sys/ioctl.h>
    int ioctl(int fd,int request,...)

/dev/fd:
    較新的系統都提供名爲/dev/fd的目錄,其目錄項是名爲0,1,2等文件
    函數的調用:
    fd=open("/dev/fd/0",mode);
    大多數系統忽略它所指定的mode,而另外一些系統則要求mode必須是所引用的文件
    在這裏是標準輸入,初始打開是所使用的打開模式的一個子集。
    上面等效於:fd=dup(0);
    若描述符0先前打開爲只讀,那麼我們也只能對fd進行讀操作。即使系統忽略打開模式,
    而且下列調用是成功的:
    fd=open("/dev/fd/0",O_RDWR);
    我們仍然不能對fd進行寫操作。
發佈了50 篇原創文章 · 獲贊 21 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章