【Linux系统编程】文件IO操作
文件描述符
在 Linux 的世界里,一切設備皆文件。我們可以系統調用中 I/O 的函數(I:input,輸入;O:output,輸出),對文件進行相應的操作(?open()、close()、write() 、read() 等)。
打開現存文件或新建文件時,系統(內核)會返回一個文件描述符,文件描述符用來指定已打開的文件。這個文件描述符相當于這個已打開文件的標號,文件描述符是非負整數,是文件的標識,操作這個文件描述符相當于操作這個描述符所指定的文件。
程序運行起來后(每個進程)都有一張文件描述符的表,標準輸入、標準輸出、標準錯誤輸出設備文件被打開,對應的文件描述符 0、1、2 記錄在表中。程序運行起來后這三個文件描述符是默認打開的。
#define STDIN_FILENO ?0?//標準輸入的文件描述符
#define STDOUT_FILENO 1?//標準輸出的文件描述符
#define STDERR_FILENO 2?//標準錯誤的文件描述符
在程序運行起來后打開其他文件時,系統會返回文件描述符表中最小可用的文件描述符,并將此文件描述符記錄在表中。Linux 中一個進程最多只能打開 NR_OPEN_DEFAULT?(即1024)個文件,故當文件不再使用時應及時調用 close() 函數關閉文件。
常用 I/0 函數
需要的頭文件:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int open(const char *pathname, int flags, mode_t mode);
功能:
打開文件,如果文件不存在則創建。
參數:
pathname:?文件的路徑及文件名。
flags:?打開文件的行為標志,如,以只讀方式(O_RDONLY,第一個為字母不是數據)打開,以讀寫或新建新文件的方式(O_RDWR|O_CREAT)打開。
mode: 這個參數,只有在文件不存在時有效,指新建文件時指定文件的權限(文件權限詳情,請點此鏈接)。
返回值:
成功:成功返回打開的文件描述符
失敗:-1
int close(int fd);
功能:
關閉已打開的文件
參數:
fd: 文件描述符,open()的返回值
返回值:
成功:0
失敗:-1
ssize_t write(int fd, const void *addr,?size_t count);
功能:
把指定數目的數據寫到文件(fd)
參數:
fd: 文件描述符
addr:?數據首地址
count:?寫入數據的長度(字節),一般情況下,數據有多少,就往文件里寫多少,不能多也不能少
返回值:
成功:實際寫入數據的字節個數
失敗:-1
ssize_t read(int fd, void *addr, size_t count);
功能:
把指定數目的數據讀到內存(緩沖區)
參數:
fd:?文件描述符
addr: 內存首地址
count:?讀取的字節個數
返回值:
成功:實際讀取到的字節個數
失敗:-1
實戰示例
接下來,我們使用以上 4 個系統調用 I/O 函數寫一個程序,能實現像系統命令 cp 的功能:
使用 open() 打開源文件,使用 read() 從文件讀數據,使用 write() 向目的文件寫數據,示例代碼如下:
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>int main(int argc, char *argv[]) {if((argc == 3) && (strcmp(argv[1], argv[2]) != 0)){// 保證有 3 個參數,而且源文件和目的文件名字不能一樣int fd_src, fd_dest, ret;//只讀方式打開源文件fd_src = open(argv[1], O_RDONLY); if(fd_src < 0){perror("open argv[1]");return -1;}// 新建目的文件fd_dest = open(argv[2], O_WRONLY|O_CREAT, 0755);if(fd_dest < 0){close(fd_src);perror("open argv[2]");return -1;}do{char buf[1024] = {0};// 從源文件讀取數據ret = read(fd_src, buf, sizeof(buf));// 把數據寫到目的文件,注意最后一個參數,有多少寫多少write(fd_dest, buf, ret);}while(ret > 0);// 關閉已打開的文件close(fd_src);close(fd_dest);}return 0; }運行結果如下:
文件IO
1. 引言
大多數LInux文件IO只需用到5個函數:open read write lseek close.
2. 文件描述符
對于內核而言,所有打開文件都由文件描述符引用。文件描述符是一個非負整數。當打開一個現存文件或創建一個新文件時,內核向進程返回一個文件描述符。當讀、寫一個文件時,
用open或create返回的文件描述符標識該文件,將其作為參數傳送給read或write。
在POSIX.1應用程序中,幻數0、1、2應被代換成符號常數STDIN_FILENO、STDOUT_FILENO和STDERR_FILENO。這些常數都定義在頭文件<unistd.h>中。
每個進程運行時,系統已分配3個文件描述符(0,1,2),分別對應標準輸入, 標準輸出, 標準錯誤輸出
3. 文件IO中常用的函數
3.1 open:?
打開一個文件或者設備文件, 返回一個文件描述符。當操作此文件描述符時,就是操作相應的文件或設備
int open(char *pathname,int falgs,.../*mode*/)
flags 必須指其中O_RDONLY ?O_WRONLY ?O_RDWR唯一項
可選項:
O_APPEND ? 每次寫操寫都將文件指針定位文件尾處
O_CREAT ? ?如果文件不存在創建新文件
O_EXCL ? ? 如指定O_CREAT時文件存在 出錯返回
O_TRUNC ? ?必須以O_WRONL或O_RDWR進行操作,把文件清空
O_NONBLOCK 以非阻塞的方式打開?
O_NOCTTY ? 如果打開文件為終端設備,不將該設備分配為此進程的控制終端
O_SYNC ? ? 每次write都等到I/O操作完成,并等文件的屬性更新
O_RSYNC ? ?作為read操作等侍,直到任何對同文件部分未決的寫入完成
O_DSYNC ? ?每次write都等到I/O操作完成,不等文件的屬性更
mode指定創建文件的權限
//創建一個文件,假設這個文件存在時要清空
open("文件", O_RDWR|O_CREAT|O_TRUNC, 0777);
fd = open("txt", O_RDONLY | O_CREAT | O_EXCL, 0644);
3.2 creat
? ?int creat(char *pathname,mode_t mode)
? ?等價于:open(char *,O_WRONLY | O_CREAT | O_TRUNC, mode)
3.3 read
讀取已經打開的文件中的數據。讀了文件以后,文件描述符對應文件的offset會自動偏移。
ssize_t read(int fd, void *buf, size_t count);
從文件描述符fd指向文件里讀取最大為count字節的數據放到buf指定的地址上去.
返回值: 為實際上讀取的數值, 為0時讀到文件尾, 為-1時錯誤
3.4 write
向指定的文件中寫數據。成功寫以后, 文件描述符的offset自動偏移
size_t ?write(int fd, const void *buf, size_t count);
把在buf地址指定的數據寫到fd指向的文件里,最大寫count字節.
成功返回寫了多少字節, -1失敗。write出錯的一個常見原因是:磁盤已寫滿,或者超過了對一個給定進程的文件長度限制。
3.5 lseek
每個打開文件都有一個與其相關聯的“當前文件位移量”。它是一個非負整數,用以度量
從文件開始處計算的字節數。就是改變文件描述符的偏移位置。
off_t lseek(int fildes, off_t offset, int whence);
whence:
SEEK_SET : 把偏移量設為offset, 從文件頭開始.
SEEK_CUR : 把當前偏移量加上offset的值
SEEK_END : 先從文件尾開始偏移offset的值
返回值:成功返回定位之后的文件指針偏移 失敗返回 -1
返回當前文件的偏移量
off_t currpos = lseek(fd, 0, SEEK_CUR);
3.7 close
把沒用的文件描述符關掉,把此文件描述符重新分配.
int close(int fd);
3.8 dup
可用來復制一個現存的文件描述符.
int dup(int oldfd);
int dup2(int oldfd, int newfd);
dup2(oldfd, newfd) //讓newfd成為oldfd的一個副本
dup2(fd, 1); //讓fd替代標準輸出
int ret=dup2(old,new)
如果new 打開的,則關閉new 返回新的文件描述符 失敗返回-1. du2是一個原子操作。
dup2可以用newfd參數指定新描述符的數值。如果newfd當前已經打開,則先將其關閉再做dup2操作,如果oldfd等于newfd,則dup2直接返回newfd而不用先關閉newfd再復制。
3.9 fcntl?
用于改變已打開的文件的性質 只能改變O_APPEND, O_NONBLOCK,O_ASYNC, ?O_DIRECT
int fcntl(int fileds,int cmd,.../*int arg */)
第三個參數除了cmd用于記錄鎖時為一個結構指針之外,其余均為整數
fcntl五種功能如下:
.復制一個現有的描述符 cmd=F_DUPFD
復制的新文件描述符清掉文件描述符標志 并且共享同一個文件表項
.獲得/設置文件描述符標記 cmd=F_GETFD/F_SETFD
flags = fcntl(fd, F_GETFL); //獲取
fcntl(fd, F_SETFL, flags); ?//設置
.獲得/設置文件狀態標志 cmd=F_GETFL/F_SETFL
?忽略 O_CREAT, O_EXCL, O_NOCTTY, O_TRUNC標志
?由于歷史原因O_RDONLY O_WRONLY O_RDWR并不各占一位,
?它們之間互斥。因此首先必須用屏蔽字O_ACCMODE?
?取得訪問模式然后與這三種flags比較
?value=fcntl(fd,F_GETFL,0);
?switch(value & O_ACCMODE){
case O_RDONLY :
?}
.獲得/設置導步I/O所有權cmd=F_GETOWN/F_SETOWN
.獲得/設置記錄鎖 cmd=F_GETLK F_SETLK/F_SETLKW
? ?
3.10 pread ?pwrite ?
定位文件進行讀寫 不影響文件指針 偏移和讀寫操作為原子操作
3.11 sync
? ?void sync(void) ? ??
? ? :函數只是將所有修改過的塊緩沖區排入寫隊列,然后就返回,并不等實際寫磁盤完成
? ?int fsync(int fd)?
? ? :等待實際磁盤寫操作完成,并且同步更新文件的屬性,可用于數據庫類型的應用程序
? ?int fdatasync(int fd)
? ? :類似于fsync,但只影響文件的數據部分不影響文件的屬性
? ?
3.12 ioctl?
int ioctl(int fd, int request, ...);
? ? 稱之為I/O操作的垃圾箱 只要其字操函數不能或難于實現在 它都可能很容易做到
==========================================================
4. 某些系統下提供名為/dev/fd/N 等文件。打開文件/dev/fd/N 等效于復制N文件描述符(假定N描述符是打開的)
與其N共享文件表項
也有某些系統為/dev/fd/stdin ? /dev/fd/stdout 等,均為同等操作
homework:
1. ?實現mycopy拷貝一個文件到另外一個文件(功能相當于 cp a.txt b.txt)
2. ?實現mytouch 創建一個文件(功能相當于touch a.txt)。
3. ?編寫一個同dup2功能相同的函數,要求不調用fcntl函數并且要有正確的出錯處理。
4. ?在如啟用添加標志打開一文件以便讀、寫,能否用lseek在任一位置開始讀?能否用lseek更新文件中任一部分的數據?請寫一段程序以驗證之。
總結
以上是生活随笔為你收集整理的【Linux系统编程】文件IO操作的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Linux系统编程】 Linux系统调
- 下一篇: 【Ubuntu】Linux系统( ubu