學習內容:
1)open函數的flag
2)linux系統如何管理文件
3)lseek詳解
4)dup和dup2函數介紹
5)標准IO庫介紹
如何查man手冊:man 1 xx查linux shell命令,man 2 xxx查API, man 3 xxx查庫函數
1、open函數的flag
大家有沒有發現open函數有兩個如下,
int open(const char *pathname, int flags); int open(const char *pathname, int flags, mode_t mode);
多了一個mode,查看man手冊可以發現作用為O_CREAT創建了新文件時對文件的權限設置,如下圖:
常用的flag:
1)讀寫權限:O_RDONLY(只讀), O_WRONLY(只寫), O_RDWR(可讀可寫).
2)打開存在並有內容的文件時:O_APPEND(追加)、O_TRUNC(覆蓋)
3)打開不存在的文件時:O_CREAT(覆蓋創建)、O_EXCL(確保文件創建) O_EXCL標志和O_CREAT標志來結合使用,沒有文件時創建文件,有這個文件時會報錯提醒我們。
4)阻塞與非阻塞:O_NONBLOCK(非阻塞)我們打開一個文件默認就是阻塞式的,如果你希望以非阻塞的方式打開文件,則flag中要加O_NONBLOCK標志,只用於設備文件,而不用於普通文件。
5)write非阻塞等待:O_SYNC 無O_SYNC時write只是將內容寫入底層緩沖區即可返回,在合適的時候會將buf中的內容一次性的同步到硬盤中。這種設計是為了提升硬件操作的性能和銷量,提升硬件壽命;但是有時候我們希望硬件不好等待,直接將我們的內容寫入硬盤中,這時候就可以用O_SYNC標志。
2、linux系統如何管理文件
1)硬盤中的靜態文件和inode(i節點)
硬盤中可以分為兩大區域:一個是硬盤內容管理表項,另一個是真正存儲內容的區域。操作系統訪問硬盤時是先去讀取硬盤內容管理表,從中找到我們要訪問的那個文件的扇區級別的信息,然后再用這個信息去查詢真正存儲內容的區域,最后得到我們要的文件。而這個管理表中以文件為單位記錄了各個文件的各種信息,每一個文件有一個信息列表(我們叫inode,i節點,其實質是一個結構體,這個結構體有很多元素,每個元素記錄了這個文件的一些信息,其中就包括文件名、文件在硬盤上對應的扇區號、塊號那些東西·····)
實用:大家格式化硬盤(U盤)時發現有:快速格式化和底層格式化。快速格式化非常快,格式化一個32GB的U盤只要1秒鍾,普通格式化格式化速度慢。這兩個的差異?其實快速格式化就是只刪除了U盤中的硬盤內容管理表(其實就是inode),真正存儲的內容沒有動。這種格式化的內容是有可能被找回的。
2)內存中被打開的文件和vnode(v節點)
一個程序的運行就是一個進程,我們在程序中打開的文件就屬於某個進程。每個進程都有一個數據結構用來記錄這個進程的所有信息(叫進程信息表),表中有一個指針會指向一個文件管理表,文件管理表中記錄了當前進程打開的所有文件及其相關信息。文件管理表中用來索引各個打開的文件的index就是文件描述符fd,我們最終找到的就是一個已經被打開的文件的管理結構體vnode。
3、lseek詳解
off_t lseek(int fd, off_t offset, int whence);
whence就是代表一個參考位置(用來表示文件開始處到文件當前位置的字節數)offset代表一個偏移量
1)文件指針:當我們要對一個文件進行讀寫時,一定需要先打開這個文件,所以我們讀寫的所有文件都是動態文件。動態文件在內存中的形態就是文件流的形式。
2)在動態文件中,我們會通過文件指針來表征這個正在操作的位置。所謂文件指針,就是我們文件管理表這個結構體里面的一個指針如上圖。所以文件指針其實是vnode中的一個元素。這個指針表示當前我們正在操作文件流的哪個位置。這個指針不能被直接訪問,linux系統用lseek函數來訪問這個文件指針。
3)read和write函數都是從當前文件指針處開始操作的,所以當我們用lseek顯式的將文件指針移動后,那么再去read/write時就是從移動過后的位置開始的。
4)用lseek計算文件長度
eg:return=lseek(fd,0,SEEK_END);//表示從文件末尾
5)用lseek構建空洞文件,這個文件中有一段是空的
6)使用實列:
首先我們看到a.txt里面全都是f,其實我們用O_APPEND就可以在后面追加內容,而我們用lseek來隨意位置追加內容
使用SEEK_END
lseek(fd,0,SEEK_END);//在write1前使用一次和O_APPEND同等效果,在write之后再使用一次即可的讀取出當前文件的長度
4、dup和dup2函數介紹
int dup(int oldfd); int dup2(int oldfd, int newfd);//返回值即為newfd
1) 什么是文件共享:同一個文件(同一個文件指的是同一個inode,同一個pathname)被多個獨立的讀寫體(幾乎可以理解為多個文件描述符)去同時(一個打開尚未關閉的同時另一個去操作)操作。
2) 進行文件描述符復制:dup並不能指定分配的新的文件描述符的數字,dup2系統調用修復了這個缺陷。dup返回的fd和原來的oldfd都指向oldfd打開的那個【動態文件】,操作這兩個fd實際操作的都是oldfd打開的那個文件。實際上構成了文件共享
3) fd小於3的分別對應於,stdin(0)、stdout(1)、stderr(3),也就是標准輸入、標准輸出、標准錯誤。
4)實現標准輸出的重定位實例
#include<stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include<unistd.h> #define FILEPATH "a.txt" int main(int argc,char *argv[]) { int fd=-1; int i=-1; //實際向文件中寫入的字節數 int j=-1;//實際向文件中寫入的字節數 int k;//用dup復制后的文件描述符 char b[100]= "bbbbbadfghgdfh"; //打開一個文件操作 fd=open(FILEPATH, O_RDWR ); if(-1==fd) { printf("文件打開失敗!\n"); perror("open:"); return 0; } else { printf("文件打開成功!fd=%d\n",fd); } close(1); //關閉標准輸出文件描述符1 k=dup(fd); //實現重定位 j=write(k,&b,20); if(-1==j) { printf("寫入文件失敗\n"); perror("寫入"); return 0; } printf("write succeed!\n"); close(fd); return 0; }
5、標准IO庫介紹
1)標准IO和文件IO有什么區別:標准IO是C庫函數(也是通過調用API來完成操作的),而文件IO是linux系統的API(API類似於一種接口,是由操作系統提供的)
2)常用標准IO函數介紹:fopen、fclose、fwrite、fread、ffulsh(刷新標准庫函數的緩存,直接寫進操作系統的緩沖區中)、fseek
3)一個簡單的標准IO讀寫文件實例
#include<stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include<unistd.h> #define FILEPATH "a.txt" int main(int argc,char *argv[]) { FILE *fd = NULL; //定義一個文件指針 int ret = 0; char Wrtebuff[10]={"sdfgdjdytr"}; fd=fopen(FILEPATH, "r+"); if(NULL == fd) { printf("文件打開失敗!\n"); perror("open"); return 0; } ret = fwrite(Wrtebuff,1,sizeof(Wrtebuff),fd); if(ret<0) { perror("write"); return 0; } printf("文件寫入成功!\n"); return 0; }