1. 程式人生 > >【Linux】基礎IO

【Linux】基礎IO

FILE *fp = fopen("file", "rwa+");    //開啟一個檔案 linux中的'\n'就是'\n' 而windows中的'\n'會替換'\r\n' C中fopen建立一個檔案abc,假設abc中有內容,再次建立abc檔案時,內容會被覆蓋掉。

C庫函式

    fgetc(fp); fputc(fp);   //讀/寫一個字元     fgets(buf, len, fp); fputs(buf, len, fp); //讀/寫一行     fscanf(fp, "%s", buf); fprintf(fp, "%s", buf); //按照給定的格式讀/寫     fread(buf, size, num, fp); fwrite(buf, size, num, fp); //讀寫   size*num才是最終要讀寫檔案的大小                                                                 //size表示一次讀寫檔案的大小,num表示次數     feof(fp); //判斷是否讀取到檔案結束,如果讀取結束,則返回真,反之則返回假。     fclose(fp);  //關閉一個檔案

    C中關於檔案的操作的庫函式的返回值實際上是一個結構體指標。      // 從字串中讀進與指定格式相符的資料 int sscanf(const char *str, const char *format, ...);     其中的format可以是一個或多個 {%[*] [width] [{h | l | I64 | L}]type | ' ' | '/t' | '/n' | 非%符號} sscanf 與 scanf 的區別     前者以固定的字串作為輸入源,後者以stdin作為輸入源。 * 亦可用於格式中, (即 %*d 和 %*s) 加了星號 (*) 表示跳過此資料不讀入. (也就是不把此資料讀入引數中) {a|b|c}表示a,b,c中選一,[d],表示可以有d也可以沒有d。 特別的:%*[width] [{h | l | I64 | L}]type 表示滿足該條件的被過濾掉,不會向目標引數中寫入值     %[a-z] 表示匹配a到z中任意字元,貪婪性(儘可能多的匹配)     %[aB'] 匹配a、B、'中一員,貪婪性     %[^a] 匹配非a的任意字元,貪婪性      檔案描述符:作業系統通過一個整數來 代表開啟的檔案,將這個數稱為檔案描述符。 OS在開啟檔案表中維護的開啟檔案狀態和資訊。 windows中稱為檔案控制代碼(handler)

                                         open files           程序能夠開啟的檔案描述符的個數是 [0, ulimit -n] //開啟檔案 int open(const char *pathname, int flags)     引數         pathname 要開啟的檔案         flags 開啟方式   可以理解為點陣圖(某一位置為1)             O_RDONLY : 只讀方式開啟  #define O_RDONLY 00 必須選擇一個O_WRONLY : 只寫方式開啟  #define O_WRONLY 01    第一位置為1 且只能選一個O_RDWR : 讀寫方式開啟     #define O_RDWR 02      第二位置為1                                       O_TRUNC : 清空檔案       #define O_TRUNC 01000    原來的檔案中有內容,加上這個選項可以在建立檔案的時候清除檔案內容。             O_APPEND : 追加檔案      #define O_APPEND 02000             O_CREAT :  建立檔案         #define O_CREAT 0100    0表示八進位制,這裡表示的是第7位置為1(3個二進位制位表示一個八進位制位)     返回值         失敗返回-1,成功返回檔案描述符 stdin 0 stdout 1 stderr 2

//指定建立檔案的許可權 int open(const char *path, int flags, mode_t mode);     path 要建立的檔名     flags   O_CREAT | O_RDWR           O_CREAT | O_EXCL  O_EXCL與O_CREAT放在一起才會生效    #define O_EXCL 0200     一定要加上O_CREAT                                    如果檔案存在就報錯,否則就建立。     mode  許可權0644  0666     建立檔案的許可權還會受到umask影響     O_CREAT | O_RDWR   表示新建一個檔案可讀寫,此時再次執行該程式碼時,新建的檔案並不會覆蓋掉之前的檔案。           從0開始第一個沒有被檔案描述符使用的數字     因此開啟檔案返回的檔案描述符是3

//功能:從fd檔案中讀取資料到buf所指向的空間,該空間的大小為len。 //讀取檔案 //返回實際讀取的位元組數 int read(int fd, char *buf, size_t len);     fd 要讀取的檔名     buf 緩衝區     len 最多讀取字元  期望讀取多少位元組的資料

//往fd所指向的檔案中寫入資料,資料的其實地址為buf,大小為len int write(int fd, char *buf, size_t len);

EEXIST == errno   EEXIST表示檔案存在報錯的返回值

//關閉檔案 int close(int fd);

vimdiff a b  比較a檔案與b檔案的差異

^M  實際上就是ctrl+v ctrl+m   注意要在編輯模式下才會出現,藍色的。     解決windows端拷貝資料到linux vim環境中出現^M  換行     :%s/^M//g  即可替換為空

//定位 //返回值:新的位置相對於檔案開頭的偏移量     int lseek(int fd, off_t offset, int whence);                         偏移量      偏移量的基準位置                                     SEEK_SET  開始                                     SEEK_CUR  當前                                     SEEK_END  結尾     //獲取檔案大小     lseek(fd, 0, SEEK_END);          求檔案大小struct stat sbuf;               sbuf.st_size                    struct stat {                dev_t     st_dev;         /* ID of device containing file */                ino_t     st_ino;         /* inode number */                mode_t    st_mode;        /* protection */                nlink_t   st_nlink;       /* number of hard links */                uid_t     st_uid;         /* user ID of owner */                gid_t     st_gid;         /* group ID of owner */                dev_t     st_rdev;        /* device ID (if special file) */                off_t     st_size;        /* total size, in bytes */                blksize_t st_blksize;     /* blocksize for filesystem I/O */                blkcnt_t  st_blocks;      /* number of 512B blocks allocated */

               /* Since Linux 2.6, the kernel supports nanosecond                   precision for the following timestamp fields.                   For the details before Linux 2.6, see NOTES. */

               struct timespec st_atim;  /* time of last access */                struct timespec st_mtim;  /* time of last modification */                struct timespec st_ctim;  /* time of last status change */

           #define st_atime st_atim.tv_sec      /* Backward compatibility */            #define st_mtime st_mtim.tv_sec            #define st_ctime st_ctim.tv_sec     };

     C庫函式之查詢     int fseek(FILE *stream, long offset, int whence);     long ftell(FILE * stream);  //告訴stream在哪找      od -c test.c  檢視每個字元的位置 也就是所有字元都可顯(tab以及空格等等)          ACE網路庫     char *想象成快取也就是char []          lseek(fd, 1024*1024*1024, SEEK_CUR);  表示在當前位置處偏移1G          實際上SEEK_CUR後面的1G都是空洞檔案hole(灰色地帶,不存資料)。     ls -l檢視檔案大小,此時顯示1個多G,實際上這些是虛擬記憶體,是假的,但是OS會提前把這麼大的記憶體佔用(有可能以後會用到),     du -h可以檢視其真正儲存在磁碟上的大小。           df -h檢視磁碟佔用 du -h檢視檔案真正佔用磁碟的大小

    > abc  表示把空檔案輸出重定向到abc檔案中         du -h檢視檔案大小發現是0          開啟的檔案會在PCB中建立一張表(或者說一個結構體)fds(ps:這張表的大小在32位機上是32),如果開啟檔案個數大於32,那麼會動態 開闢一塊記憶體,在這張fds表中,每建立一個檔案,就會在表中建立一個編號(是個指標),實際上也就是檔案描述符,該編號指向一張 檔案表(ps:一個結構體),檔案表中存有開啟標誌(方式:讀/寫)、當前讀寫位置(應用:lseek偏移2位,位置就加2)、refcnt(引用計數) (ps:表示有多少個檔案描述符指向檔案表)(fork建立一個子程序後,子程序會複製父程序的資料,同時指向這張檔案表,那麼refcnt就變成了2) 檔案表中的一個指標指向vnode表,vnode表包括很多資訊(ls -l 獲取的檔案許可權,建立時間,檔案大小等等資訊(stat結構),除了檔名), vnode的一個指標指向了inode表(index 索引表,可以知道資料在磁碟上哪塊存著),C庫函式中的檔案操作函式,其結構體一定會包含檔案描述符, 因為read時,就通過檔案描述符訪問到檔案表、vnode表以及inode表的資訊。     父程序徹底結束的話,會釋放掉PCB,但是不會關閉檔案表,每開啟一個檔案都對應一個檔案表。     close一個檔案,refcnt就會減去1,直到refcnt減到0,才會釋放掉檔案表。     fork建立一個子程序後,子程序會複製父程序的資料,同時指向這張檔案表,那麼refcnt就變成了2,當關閉父程序後,     refcnt減去1,子程序不會受到影響,仍然指向檔案表,假如關掉了檔案表,那麼子程序寫資料時就會報錯(因為檔案表中的當前讀寫位置不存在)。          檔案描述符就是從0開始的整數。當我們開啟檔案時,作業系統在記憶體中要建立相應的資料結構來描述目標檔案。於是就有了 file結構體。它表示一個已經開啟的檔案物件,而程序執行open系統呼叫,所以必須讓程序和檔案關聯起來。每個程序都有一個指標 *files, 指向一張檔案表files_struct,該表最重要的部分就是包含一個指標陣列,每個元素都是一個指向開啟檔案的指標! 所以,本質上,檔案描述符就是該陣列的下標。只要拿著檔案描述符,就可以找到對應的檔案。

在/usr/include/libio.h

struct _IO_FILE {   int _flags;       /* High-order word is _IO_MAGIC; rest is flags. */ #define _IO_file_flags _flags   // 緩衝區相關   /* The following pointers correspond to the C++ streambuf protocol. */   /* Note:  Tk uses the _IO_read_ptr and _IO_read_end fields directly. */   char* _IO_read_ptr;   /* Current read pointer */   char* _IO_read_end;   /* End of get area. */   char* _IO_read_base;  /* Start of putback+get area. */   char* _IO_write_base; /* Start of put area. */   char* _IO_write_ptr;  /* Current put pointer. */   char* _IO_write_end;  /* End of put area. */   char* _IO_buf_base;   /* Start of reserve area. */   char* _IO_buf_end;    /* End of reserve area. */   /* The following fields are used to support backing up and undo. */   char *_IO_save_base; /* Pointer to start of non-current get area. */   char *_IO_backup_base;  /* Pointer to first valid character of backup area */   char *_IO_save_end; /* Pointer to end of non-current get area. */   struct _IO_marker *_markers;   struct _IO_FILE *_chain;

fopen() 與 open()

    typedef struct _IO_FILE     {         int cnt; // 當前位置到末尾還剩下多少字元還沒有讀取         char *base; // 基準位置,使用者空間內開闢的一片快取,base指向起始地址         char *ptr; // 當前位置,指向當前正在讀寫位置         int fd; // 系統呼叫的返回值     }FILE;              fopen()         {             FILE *fp = malloc();             fp->fd = open();             return fp;         }              需要注意的是:呼叫C庫函式時,由於不停地切換使用者空間到核心空間,會帶來很大的系統開銷。     鑑於不停地切換空間,C庫函式於是就先開闢出來一片快取供使用,這樣每次讀取就可以了,不需要切換空間。     僅讀取檔案的話,使用C庫函式讀取效率更高一點。 C庫提供的IO是帶快取的,而OS提供的沒有。     fread()更有效率一點,這是因為一開始獲取到檔案描述符3對應的檔案表後,會先把base指向的快取區填滿,不需要每次切換核心態和使用者態。     read(fd, buf, 1024) read一次讀取磁碟最多為4K時效率最高,也就是記憶體頁的大小。          int ftruncate(int fd, off_t length);     ftruncate  截斷檔案大小      (下載電影,比如說大小4G,開始下載時,就會先佔用4G的記憶體,產生一個hole檔案,然後再慢慢下載)     truncate命令用來改變檔案大小      檔案定位函式     rewind 反繞     fseek 隨機定位