linux的文件 I/O操作
Linux下的文件類型簡述
1、文件分類
1、linux 中對目錄和設(shè)備的操作都是文件操作, 文件分為普通文件, 目錄文件, 鏈接文件和設(shè)備文件。
2、普通文件: 也稱磁盤文件, 并且能夠進(jìn)行隨機(jī)的數(shù)據(jù)存儲(能夠自由 seek
定位到某一個位置);
3、管道: 是一個從一端發(fā)送數(shù)據(jù), 另一端接收數(shù)據(jù)的數(shù)據(jù)通道;
4、目錄: 也稱為目錄文件, 它包含了保存在目錄中文件列表的簡單文件。
5、設(shè)備: 該類型的文件提供了大多數(shù)物理設(shè)備的接口。 它又分為兩種類型:字符型設(shè)備和塊設(shè)備。 字符型設(shè)備一次只能讀出和寫入一個字節(jié)的數(shù)據(jù),包括調(diào)制解調(diào)器、 終端、 打印機(jī)、 聲卡以及鼠標(biāo); 塊設(shè)備必須以一定大小的塊來讀出或者寫入數(shù)據(jù), 塊設(shè)備包括 CD-ROM、 RAM 驅(qū)動器和磁盤驅(qū)動器等, 一般而言, 字符設(shè)備用于傳輸數(shù)據(jù), 塊設(shè)備用于存儲數(shù)據(jù)。
6、鏈接: 類似于 Windows 的快捷方式和 Linux 里的別名, 指包含到達(dá)另一個文件路徑的文件。
7、套接字: 在 Linux 中,套接字也可以當(dāng)作文件來進(jìn)行處理。
2、標(biāo)準(zhǔn) I/O 文件操作(基于文件指針帶有緩沖區(qū)的)
**1. 文件的創(chuàng)建, 打開與關(guān)閉**#include <stdio.h> //頭文件包含 FILE *fopen(const char *path,const char *mode); //文件名 模式 int fclose(FILE *stream);fopen 以 mode 的方式打開或創(chuàng)建文件, 如果成功, 將返回一個文件指針, 失敗則返回 NULL. fopen 創(chuàng)建的文件的訪問權(quán)限將以 0666 與當(dāng)前的 umask 結(jié)合來確定。
Linux 系統(tǒng)中,mode 里面的’b’(二進(jìn)制)可以去掉, 但是為了保持與其他系統(tǒng)的兼容性, 建議不要去掉。
ab 和 a+b 為追加模式,這兩種模式寫數(shù)據(jù)時都將是在文件末尾添加, 所以比較適合于多進(jìn)程寫同一個文件的情況下保證數(shù)據(jù)的完整性。
2. 讀寫文件
數(shù)據(jù)塊讀寫:
#include <stdio.h> size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); size_t fwrite(void *ptr, size_t size, size_t nmemb, FILE *stream);
fread 從文件流 stream 中讀取 nmemb 個元素, 寫到 ptr 指向的內(nèi)存中, 每個元素的大小為 size 個字節(jié)。
fwrite 從 ptr 指向的內(nèi)存中讀取 nmemb 個元素, 寫到文件流 stream 中, 每個元素 size 個字節(jié)。所有的文件讀寫函數(shù)都從文件的當(dāng)前讀寫點(diǎn)開始讀寫, 讀寫完以后, 當(dāng)前讀寫點(diǎn)自動往后移動size*nmemb 個字節(jié)。
整塊 copy, 速度較快, 但是是二進(jìn)制操作。
格式化讀寫
printf、scanf、 int fprintf(FILE *stream, const char *format, ...); 重點(diǎn) sprintf(buf,”the string is;%s”,str); 重點(diǎn) int sscanf(char *str, const char *format, …); 重點(diǎn)fprintf 將格式化后的字符串寫入到文件流 stream 中 sprintf 將格式化后的字符串寫入到字符串 str 中
單個字符讀寫
#include <stdio.h> int fgetc(FILE *stream); int fputc(int c, FILE *stream); int getc(FILE *stream); ? 等同于 fgetc(FILE* stream) int putc(int c, FILE *stream); ? 等同于 fputc(int c, FILE* stream) int getchar(void); ? 等同于 fgetc(stdin); int putchar(int c); ? 等同于 fputc(int c, stdout);字符串讀寫:
char *fgets(char *s, int size, FILE *stream); int fputs(const char *s, FILE *stream); int puts(const char *s); ? 等同于 fputs(const char *s,stdout); char *gets(char *s); ? 等同于 fgets(const char *s, int size, stdin);文件定位:
文件定位指讀取或設(shè)置文件當(dāng)前讀寫點(diǎn), 所有的通過文件指針讀寫數(shù)據(jù)的
函數(shù), 都是從文件的當(dāng)前讀寫點(diǎn)讀寫數(shù)據(jù)的。
3. 目錄相關(guān)操作
改變目錄或文件的訪問權(quán)限#include <sys/stat.h> int chmod(const char* path, mode_t mode);//mode 形如: 0777 path 參數(shù)指定的文件被修改為具有 mode 參數(shù)給出的訪問權(quán)限。獲取、 改變當(dāng)前目錄:#include <unistd.h> //頭文件 char *getcwd(char *buf, size_t size); //獲取當(dāng)前目錄, 相當(dāng)于 pwd 命令 int chdir(const char *path); //修改當(dāng)前目錄, 即切換目錄, 相當(dāng)于 cd 命令getcwd()函數(shù),倘若參數(shù) buf 為 NULL, getcwd()會依 參數(shù) size 的大小自動配置內(nèi)存(使用 malloc()), 如果參數(shù) size 也為 0, 則 getcwd()會依工作目錄絕對路徑的字符串程度來決定所配置的內(nèi)存大小 chdir()函數(shù): 用來將當(dāng)前的工作目錄改變成以參數(shù) path 所指的目錄創(chuàng)建和刪除目錄: #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> int mkdir(const char *pathname, mode_t mode); //創(chuàng)建目錄,mode 是目錄權(quán)限 int rmdir(const char *pathname); //刪除目錄 也可用#include <unistd.h> int unlink(const char *pathname); 來刪除硬連接數(shù)獲取目錄信息#include <sys/types.h> #include <dirent.h> DIR *opendir(const char *name); //打開一個目錄 struct dirent *readdir(DIR *dir); //讀取目錄的一項(xiàng)信息, 并返回該項(xiàng)信息的結(jié)構(gòu)體指針 void rewinddir(DIR *dir); //重新定位到目錄文件的頭部 void seekdir(DIR *dir,off_t offset);//用來設(shè)置目錄流目前的讀取位置 off_t telldir(DIR *dir); //返回目錄流當(dāng)前的讀取位置 int closedir(DIR *dir); //關(guān)閉目錄文件#include <sys/types.h> #include <sys/stat.h> #include <unistd.h> int stat(const char *pathname, struct stat *buf); 獲取文件狀態(tài)讀取目錄信息的步驟為:1. 用 opendir 函數(shù)打開目錄;2. 使用 readdir 函數(shù)迭代讀取目錄的內(nèi)容, 如果已經(jīng)讀取到目錄末尾, 又想重新開始讀, 則可以使用 rewinddir 函數(shù)將文件指針重新定位到目錄文件的起始位置;3. 用 closedir 函數(shù)關(guān)閉目錄1、opendir()用來打開參數(shù) name 指定的目錄, 并返回 DIR*形態(tài)的目錄流 2、readdir()函數(shù)用來讀取目錄的信息, 并返回一個結(jié)構(gòu)體指針, 該指針保存了目錄的相關(guān)信息。 有錯誤發(fā)生或者讀取到目錄文件尾則返回 NULL; 3、seekdir()函數(shù)用來設(shè)置目錄流目前的讀取位置, 再調(diào)用 readdir()函數(shù)時, 便可以從此新位置開始讀取。 參數(shù) offset 代表距離目錄文件開頭的偏移量。 4、telldir()函數(shù)用來返回目錄流當(dāng)前的讀取位置。接下來看一下相關(guān)結(jié)構(gòu)體的內(nèi)容: 1、目錄信息結(jié)構(gòu)體: struct dirent {ino_t d_ino; /* inode number( 此目錄進(jìn)入點(diǎn)的 inode) */off_t d_off; /* offset to the next dirent( 目錄開頭到進(jìn)入點(diǎn)的位移 */unsigned short d_reclen; /* length of this record( 目錄名的長度) */unsigned char d_type; /* type of file( 所指的文件類型) */char d_name[256]; /* filename( 文件名) */ }; 2、文件狀態(tài)及相關(guān)信息結(jié)構(gòu)體: struct stat {dev_t st_dev; /*如果是設(shè)備, 返回設(shè)備表述符, 否則為 0*/ino_t st_ino; /* i 節(jié)點(diǎn)號 */mode_t st_mode; /* 文件類型 */nlink_t st_nlink; /* 鏈接數(shù) */uid_t st_uid; /* 屬主 ID */gid_t st_gid; /* 組 ID */dev_t st_rdev; /* 設(shè)備類型*/off_t st_size; /* 文件大小, 字節(jié)表示 */blksize_t st_blksize; /* 塊大小*/blkcnt_t st_blocks; /* 塊數(shù) */time_t st_atime; /* 最后訪問時間*/time_t st_mtime; /* 最后修改時間*/time_t st_ctime; /* 最后權(quán)限修改時間 */ };接下來舉一個實(shí)例: 以樹形結(jié)構(gòu)的形式輸出指定目錄下面的所有文件 #include <unistd.h> #include <stdio.h> #include <dirent.h> #include <string.h> #include <sys/stat.h> #include <stdlib.h> void printdir(char *dir, int depth) {DIR *dp = opendir(dir);if(NULL == dp){fprintf(stderr,"cannot open directory: %s\n", dir);return;} chdir(dir);struct dirent *entry;struct stat statbuf;while((entry = readdir(dp)) != NULL){stat(entry->d_name,&statbuf);if(S_ISDIR(statbuf.st_mode)){if(strcmp(".",entry->d_name) == 0 || strcmp("..",entry->d_name) == 0)continue;printf("%*s%s/\n",depth,"",entry->d_name);printdir(entry->d_name,depth+4);}elseprintf("%*s%s\n",depth,"",entry->d_name);//printf(“%*s” ,4,” *” ); 該函數(shù)表示輸出“___*” ,前面輸出 3 個空格。//如果是 printf(“%*s” ,4,“**” );則表示輸出“__**” , 前面輸出 2 個空格。} chdir("..");closedir(dp); }int main(int argc, char* argv[]) {char *topdir, pwd[2]=".";if (argc < 2)topdir=pwd;elsetopdir=argv[1];printf("Directory scan of %s\n",topdir);printdir(topdir,0);printf("done.\n");exit(0); }
4、標(biāo)準(zhǔn)輸入/輸出流
在進(jìn)程一開始運(yùn)行, 就自動打開了三個對應(yīng)設(shè)備的文件, 它們是標(biāo)準(zhǔn)輸入、 輸出、 錯誤流, 分別用全局文件指針 stdin、 stdout、 stderr 表示, stdin 具有可讀屬性, 缺省情況下是指從鍵盤的讀取輸入, stdout 和 stderr 具有可寫屬性, 缺省情況下是指向屏幕輸出數(shù)據(jù)。
示例:
3、基于文件描述符的文件操作(非緩沖)
1、文件描述符簡述
內(nèi)核為每個進(jìn)程維護(hù)一個已打開文件的記錄表, 文件描述符是一個較小的正整數(shù)( 0—1023) , 它代表記錄表的一項(xiàng), 通過文件描述符和一組基于文件描述符的文件操作函數(shù), 就可以實(shí)現(xiàn)對文件的讀、 寫、 創(chuàng)建、 刪除等操作。 常用基于文件描述符的函數(shù)有 open( 打開) 、 creat( 創(chuàng)建) 、 close( 關(guān)閉) 、 read( 讀取) 、 write( 寫入) 、 ftruncate( 改變文件大小) 、 lseek( 定位) 、 fsync( 同步) 、 fstat( 獲取文件狀態(tài)) 、 fchmod( 權(quán)限) 、 flock( 加鎖) 、 fcntl( 控制文件屬性) 、
dup( 復(fù)制) 、 dup2、 select 和 ioctl。 基于文件描述符的文件操作并非 ANSI C 的函數(shù)。
注:如果不清楚某個函數(shù)的具體實(shí)現(xiàn)形式, 可以通過下面的方式查詢
man 函數(shù)名 查看該函數(shù)的幫助。
2、操作函數(shù)
**打開、 創(chuàng)建和關(guān)閉文件** open 和 creat 都能打開和創(chuàng)建函數(shù), 原型為#include <sys/types.h> //頭文件 #include <sys/stat.h> #include <fcntl.h>int open(const char *pathname, int flags); //文件名 打開方式 int open(const char *pathname, int flags, mode_t mode);//文件名 打開方式 權(quán)限 int creat(const char *pathname, mode_t mode); //文件名 權(quán)限 //現(xiàn)在已經(jīng)不常用了 creat 函數(shù)等價于?open(pathname,O_CREAT|O_TRUNC|O_WRONLY,mode);open()函數(shù)出錯時返回-1, 相關(guān)參數(shù)如下:flags 和 mode 都是一組掩碼的合成值, flags 表示打開或創(chuàng)建的方式, mode 表示文件的訪問權(quán)限。close 用于文件的關(guān)閉: int close(int fd);//fd 表示文件描述詞,是先前由 open 或 creat 創(chuàng)建文件時的返回值。文件使用完畢后, 應(yīng)該調(diào)用 close 關(guān)閉它, **一旦調(diào)用close, 則該進(jìn)程對文件所加的鎖全都被釋放,并且使文件的打開引用計數(shù)減 1, 只有文件的打開引用計數(shù)變?yōu)?0 以后, 文件才會被真正的關(guān)閉。**注:
LINUX 中基于文件描述符的 open 函數(shù), 對于一個不存在的文件, 不能通過 O_WRONLY 的方式打開, 必須加上 O_CREAT 選項(xiàng)。
3、標(biāo)準(zhǔn)輸入輸出文件描述符
與標(biāo)準(zhǔn)的輸入輸出流對應(yīng), 在更底層的實(shí)現(xiàn)是用標(biāo)準(zhǔn)輸入、 標(biāo)準(zhǔn)輸出、 標(biāo)準(zhǔn)錯誤文件描述符表示的。它們分別用 STDIN_FILENO、STDOUT_FILENO 和 STDERR_FILENO 三個宏表示, 值分別是 0、 1、 2
標(biāo)準(zhǔn)輸入文件描述符 ? STDIN_FILENO ? 0
標(biāo)準(zhǔn)輸出文件描述符 ? STDOUT_FILENO ? 1
標(biāo)準(zhǔn)錯誤輸出文件描述符 ? STDERR_FILENO ? 2
4、I/O 多路轉(zhuǎn)接模型
在這種模型下, 如果請求的 I/O 操作阻塞, 且它不是真正阻塞 I/O,而是讓其中的一個函數(shù)等待, 在這期間, I/O 還能進(jìn)行其他操作。
5、文件共享時,一個進(jìn)程內(nèi)核為其維護(hù)的數(shù)據(jù)結(jié)構(gòu)圖—3張表
linux并沒有采用v節(jié)點(diǎn)而是才用的是與文件相關(guān)的i節(jié)點(diǎn),與文件無關(guān)的i節(jié)點(diǎn)
dup之后的內(nèi)核數(shù)據(jù)結(jié)構(gòu)
總結(jié)
以上是生活随笔為你收集整理的linux的文件 I/O操作的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 工具的意义
- 下一篇: 使用 remix-ide(Browser