天天看點

Linux的IO系統調用函數open、read、write、stat一、open 二、read、write三、lseek四、stat五、共享檔案 

今天我們來看一下幾個系統調用函數,以及他們的基本用法。

一、open

                                              int open ( char  *filename ,  int  flags , mode_t  mode );

            open 函數将filename轉換為一個檔案描述符,并且傳回描述符數字。傳回的描述符總是在程序中目前沒有打開的最小描述符。

①filename    

            檔案名

②flags

O_RDONLY 隻讀
O_WRONLY 隻寫
O_RDWR 可讀可寫
O_CREAT 如果檔案不存在,就建立它的一個空檔案
O_TRUNC 如果檔案已經存在,就清空它
O_APPEND 在每次寫操作前,設定檔案位置到檔案的結尾處,也就是追加操作

       flags參數也可以是一個或者更多位掩碼的或,為寫提供給一些額外的提示

③mode

        mode參數制定了新檔案的通路權限位

掩碼 描述

S_IRUSR

S_IWUSR

S_IXUSR

使用者(擁有者)能夠讀這個檔案

使用者(擁有者)能夠寫這個檔案

使用者(擁有者)能夠執行這個檔案

S_IRGRP

S_IWGRP

S_IXGRP

擁有者所在組的成員能夠讀這個檔案

擁有者所在組的成員能夠寫這個檔案

擁有者所在組的成員能夠執行這個檔案

S_IROTH

S_IWOTH

S_IXOTH

其他人(任何人)能夠讀這個檔案

其他人(任何人)能夠讀寫這個檔案

其他人(任何人)能夠執行這個檔案

下面的函數說明如何以讀的方式打開一個已存在的檔案

                 int fd=open("foo.txt",O_RDONLY,0);

下面的函數說明如何打開一個已存在的檔案,并在後面添加一些資料

                int fd=open("foo.txt",O_WRONLY|O_APPEND,0);

下面的函數經常用作建立一個新檔案

                int fd=open("foo.txt",O_CREAT|O_TRUNC|O_WRONLY,0);

二、read、write

                                 ssize_t  read ( int  fd ,  void  *buf , size_t n );

     read函數從描述符為fd的目前檔案位置複制最多n個位元組到記憶體位置buf。傳回值-1表示一個錯誤,而傳回值0表示EOF。否則,傳回值表示的實際傳送的位元組數量。

                                 ssize_t  write ( int fd, const void *buf , size_t  n );

     write函數從記憶體位置buf複制最多n個位元組到描述符fd的目前檔案位置。

下面的程式使用read和write調用一次一個位元組的從标準輸入(鍵盤)複制到标準輸出(螢幕)。

#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
    char c;
    
    while(read(STDIN_FILENO,&c,1)!=0)
        write(STDOUT_FILENO,&c,1);
    exit(0);
}
           

 敲一個字元,按下回車就顯示

Linux的IO系統調用函數open、read、write、stat一、open 二、read、write三、lseek四、stat五、共享檔案 

三、lseek

                                  off_t   lseek ( int fd , off_t  offset , int whence );

   lseek 定位一個檔案,傳回目前光标距檔案開頭的位元組數

offset 偏移量
SEEK_SET 參數offset 即為新的讀寫位置.
SEEK_CUR 以目前的讀寫位置往後增加offset 個位移量.
SEEK_END 将讀寫位置指向檔案尾後再增加offset 個位移量. 當whence 值為SEEK_CUR 或SEEK_END 時, 參數offet 允許負值的出現.
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
    /**
        abc.text檔案裡存放一個字元串“abcdefghijklmn”
    */
    int fd=open("abc.text",O_RDONLY,0);

    int buf[20];
    int num[10];
    int buf_size=read(fd,buf,20);
    printf("buf_size=%d",buf_size);

    //把光标設定到偏移量為2的地方
    int current_position=lseek(fd,2,SEEK_SET);
    printf("current_position=%d",current_position);

    //将光标後移3位
    int new_position_1=lseek(fd,3,SEEK_CUR);
    printf("new_position_1=%d",new_position_1);
    
    //讀檔案3個位元組,輸出到螢幕上
    read(fd,num,new_position_1-current_position);
    write(STDOUT_FILENO,num,new_position-current_position);
    printf("\n");

    //以檔案末尾為基準,向前移1位
    int new_position_2 =lseek(fd,-1,SEEK_END);
    pritnf("new_position_2=%d",new_position_2);

    //把光标置于檔案末尾,用這種辦辦法也可以算作求出檔案大小
    int end_position=lseek(fd,0,SEEK_END);
    printf("end_position=%d",end_position);

}
           

來看一下運作結果,讀位元組的時候,可以發現檔案的光标真的有在移動。

Linux的IO系統調用函數open、read、write、stat一、open 二、read、write三、lseek四、stat五、共享檔案 

四、stat

        應用程式能夠通過調用stat函數,檢索到關于檔案的資訊,有時也稱為檔案的中繼資料

                     int  stat (const  char  *filename , struct  *buf );

結構體buf有13個成員,我們簡單說兩個常用的即可。st_size,表示檔案位元組數大小。st_mode成員則編碼了檔案通路許可位和通路類型。Linux再sys/stat.h中定義了宏謂詞來确定st_mode成員的檔案類型,

S_ISREG(m) 這是一個普通檔案麼?
S_ISDIR(m) 這是一個目錄檔案麼
S_ISSOCK(m) 這是一個網絡套接字麼?

接下來看一個小代碼,如何利用st_mode檢視一個檔案權限和類型 

#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
    struct stat s;
    char *readok,*writeok,*exeok;

    stat(argv[1],&s);
    if(S_ISREG(s.st_mode))//普通檔案
        type="regular";
    else if(S_ISDIR(s.st_mode))//目錄
        type="directionary";
    else type="others";

    //檢視讀權限
    if((s.st_mode&S_IRUSR))
         readok="yes";
    else readok="no";
    
    //檢視寫權限
    if((s.st_mode&S_IWUSR))
         writeok="yes";
        else writeok="no";

    //檢視執行權限
    if((s.st_mode&S_IXUSR))
         exeok="yes";
        else exeok="no";
    printf("type\treadok\twriteok\texeok\n");
    printf("%s\t%s\t\%s\t%s\n",type,readok,writeok,exeok);

}
           
Linux的IO系統調用函數open、read、write、stat一、open 二、read、write三、lseek四、stat五、共享檔案 

五、共享檔案 

        可以用許多不同的方式來共享Linux。核心用三個相關的樹結構來表示打開的檔案。

描述符表 每個程序都有它獨立的描述符表,它的表項是由結成打開的檔案描述符來索引的。每個程序都有它獨立的描述符表項指向檔案表中的一個表項
檔案表 打開檔案的集合是由一張檔案表來表示的,所有程序共享這張表
v-node表 同檔案表一樣,所有的程序共享這張v-node表。每個表項包含stat結構中的大多數資訊,包括st_mode和st_size成員。

          ①下圖展示了一個示例,其中描述符1和3通過不同給的打開檔案表表項來引用兩個不同的檔案。這是一個典型的情況,沒有共享檔案,并且每個描述符對應一個不同的檔案。

大緻的代碼
int fd1=open("abc.text",O_RDONLY,0);
int fd3=open("123.text",O_RDONLY,0);
           
Linux的IO系統調用函數open、read、write、stat一、open 二、read、write三、lseek四、stat五、共享檔案 

           ② 多個描述符也可以通過不同的檔案表表項來引用同一個檔案。例如,如果同一個filename調用open函數兩次,就會發生這種情況。關鍵思想是每個描述符都有它自己的檔案位置,是以對不同描述符的讀操作可以從檔案的不同位置擷取資料。

大緻的代碼
int fd1=open("abc.text",O_RDONLY,0);
int fd3=open("abc.text",O_RDONLY,0);
           
Linux的IO系統調用函數open、read、write、stat一、open 二、read、write三、lseek四、stat五、共享檔案 

           ③父子程序共享相同的檔案表集合,是以共享相同的檔案位置。一個最重要的結果就是,在核心删除相應檔案表表項之前,父子程序必須都關閉了他們的描述符。

Linux的IO系統調用函數open、read、write、stat一、open 二、read、write三、lseek四、stat五、共享檔案 

          一些小練習

① 

//假設123.text檔案中存放的内容是123456790
int fd1=open("abc.text",O_RDONLY,0);
int fd2=open("123.text",O_RDONLY,0);
int buf1[20],buf2[20];

read(fd1,buf1,20);
write(STDOUT_FILENO,buf1,20);

read(fd2,buf2,20);
write(STDOUT_FILENO,buf2,20);
           

他的結果是簡單的。對應了第一種情況,打開了兩個不同的檔案,都是從頭輸出.。

Linux的IO系統調用函數open、read、write、stat一、open 二、read、write三、lseek四、stat五、共享檔案 

② 

//假設我們讀的檔案中的内容是 abcdefgh

int main(int argc, char *argv[])
{
    int fd1, fd2, fd3;
    char c1, c2, c3;
    char *fname = argv[1];
    fd1 = Open(fname, O_RDONLY, 0);
    fd2 = Open(fname, O_RDONLY, 0);
    fd3 = Open(fname, O_RDONLY, 0);
    //此處解釋以下,dup2,簡單來說這個函數的效果就是凡是對fd3的操作都可以看做是對fd2的操作
    dup2(fd2, fd3);

    Read(fd1, &c1, 1);
    Read(fd2, &c2, 1);
    Read(fd3, &c3, 1);
    printf("c1 = %c, c2 = %c, c3 = %c\n", c1, c2, c3);

    Close(fd1);
    Close(fd2);
    Close(fd3);
    return 0;
}
           
Linux的IO系統調用函數open、read、write、stat一、open 二、read、write三、lseek四、stat五、共享檔案 

由于dup2,對fd3的操作都相當于對f2的操作。c1讀到fd1中的a,c2讀到了fd2中的a,此時fd2中的光标在‘a’的後面,是以c3讀到了fd2中的b。

int main(int argc, char *argv[])
{
    int fd1, fd2, fd3;
    char *fname = argv[1];

    //建立一個名為fname的新檔案,可讀可寫,在裡面寫入pqrs
    fd1 = Open(fname, O_CREAT|O_TRUNC|O_RDWR, S_IRUSR|S_IWUSR);
    Write(fd1, "pqrs", 4);	

    //以追加寫的方式打開剛剛建立号的fname的檔案,在裡面寫入jklmn
    fd3 = Open(fname, O_APPEND|O_WRONLY, 0);
    Write(fd3, "jklmn", 5);

    //dup,使之後對fd2的操作都變為對fd1的操作
    fd2 = dup(fd1);  /* Allocates new descriptor */
    Write(fd2, "wxyz", 4);
    Write(fd3, "ef", 2);

    Close(fd1);
    Close(fd2);
    Close(fd3);
    return 0;
}
           

最終它的運作結果是 pqrswxyznef ,我們來看一下他的實作過程。

Linux的IO系統調用函數open、read、write、stat一、open 二、read、write三、lseek四、stat五、共享檔案 
Linux的IO系統調用函數open、read、write、stat一、open 二、read、write三、lseek四、stat五、共享檔案 
Linux的IO系統調用函數open、read、write、stat一、open 二、read、write三、lseek四、stat五、共享檔案 
Linux的IO系統調用函數open、read、write、stat一、open 二、read、write三、lseek四、stat五、共享檔案 

//假設讀取的檔案中存放的是abcdefgh

int main(int argc, char *argv[])
{
    int fd1;
    int s = getpid() & 0x1;
    printf("s=%d\n",s);

    char c1, c2;
    char *fname = argv[1];

    fd1 = open(fname, O_RDONLY, 0);
    read(fd1, &c1, 1);

    if (fork()) {
	    /* Parent */
	    sleep(s);
	    read(fd1, &c2, 1);
	    printf("Parent: c1 = %c, c2 = %c\n", c1, c2);
    } else {
	    /* Child */
	    sleep(1-s);
	    read(fd1, &c2, 1);
	    printf("Child: c1 = %c, c2 = %c\n", c1, c2);
    }
    return 0;
}
           
Linux的IO系統調用函數open、read、write、stat一、open 二、read、write三、lseek四、stat五、共享檔案 

可以看到,子程序列印結束後,大概等待一秒,父程序才列印出來。,read在fork之前進行了第一次讀,c1沒有争議的讀到了a,此時光标在‘a’後。子程序因為沒有sleep,進而先比父程序先讀,讀到了b,此時光标在‘b’後。父程序sleep結束後,讀取c2,也就讀到了c。

繼續閱讀