linux系統(tǒng)中文件I/O教程

linux 文件I/O教程(1)
一,文件描述符
對內(nèi)核而言,所以打開的文件都通過文件描述符引用。每個進程都有一些與之關(guān)聯(lián)的文件描述符。文件描述符是一個非負整數(shù)。當打開一個現(xiàn)有文件或創(chuàng)建一個新文件時,內(nèi)核向進程返回一個文件描述符。當讀或?qū)懸粋€文件時,使用open或creat返回的文件描述符標識該文件,將其作為參數(shù)傳送給read和write。
一般有三個以及打開的文件描述符,他們是:
0:標準輸入 STDIN_FILENO
1:標準輸出 STDOUT_FILENO
2標準錯誤輸出 STDERR_FILENO
每行后面的符號常量是依從POSIX而定的。
open函數(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);
pathname是要打開或創(chuàng)建文件的名字。
flag用來定義打開文件所采取的的動作,必須調(diào)用以下模式之一
O_RDONLY, O_WRONLY, O_RDWR分別代表只讀,只寫,讀寫方式打開。
open還可以包括以下可選模式的組合
O_APPEND:把寫入數(shù)據(jù)追加到文件的尾端
O_CREAT:若文件不存在,則創(chuàng)建它。使用此選項時,需要第三個參數(shù)mode,用其指定該新文件的訪問權(quán)限。
O_EXCL:如果同時指定了O_CREAT,而文件存在,則會出錯。用此可以測試一個文件是否存在,如果存在,則創(chuàng)建文件,這使測試和創(chuàng)建兩者成為一個原子操作。
O_TRUNC: 如果此文件存在,而且為只寫或讀寫成功打開,則將其長度截為0。
open返回的文件描述符一定是最小的未用描述符數(shù)值。這一點被某些應(yīng)用程序用在標準輸入,標準輸出或標準錯誤輸出上。如,一個程序關(guān)閉了自己的標準輸出,然后再次調(diào)用open,文件描述符1就會被調(diào)用,并且標準輸出將被有效的重定向到另一個文件或設(shè)備。
POSIX規(guī)范還標準化了一個creat調(diào)用,此函數(shù)等效于
open(pathname,O_WONLY |O_CREAT | O_TRUNC, mode);
close函數(shù)
#include <unistd.h>
int close(int fd);
close調(diào)用終止一個文件描述符fd與對應(yīng)文件之間的關(guān)聯(lián)。文件描述符被釋放后并能重新使用。close調(diào)用成功返回0,出錯返回-1.
關(guān)閉一個文件時會釋放該進程加在文件上的所有記錄鎖。當一個進程終止時,內(nèi)核自動關(guān)閉它所有打開的文件。
lseek函數(shù)
每個打開的文件都有一個與其相關(guān)聯(lián)的”當前文件偏移量”。按系統(tǒng)默認情況,當打開一個文件時,除非指定O_APPEND選項,否則該偏移量被設(shè)置為0。lseek可以為一個打開的文件設(shè)置偏移量。
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, intwhence);
offset用來指定位置,whence參數(shù)定義該偏移值的用法。whence可取以下值:
SEEK_SET: The offset is set to offset bytes.
SEEK_CUR: The offset is set to its current locationplus offset bytes.
SEEK_END: The offset is set to the size of the fileplus offset bytes.
成功調(diào)用返回從文件頭到文件指針被設(shè)置處的字節(jié)偏移值,失敗返回-1。參數(shù)offset定義在<sys/types.h>中。
當偏移量大于文件長度時,出現(xiàn)空洞,空洞不占用存儲區(qū)。
read函數(shù)
#include <unistd.h>
ssize_t read(int fd, void *buf, size_tcount);
將與文件描述符fd關(guān)聯(lián)的文件中讀入count個字符放到buf中。返回讀入的字節(jié)數(shù),它可能小于請求的字節(jié)數(shù)。如果read調(diào)用返回0,就表示未讀入任何數(shù)據(jù),已到達了文件尾。返回-1,就表示出錯。
write函數(shù)
#include <unistd.h>
ssize_t write(int fd, const void *buf,size_t count);
把緩沖區(qū)buf的前count個字節(jié)寫入與文件描述符fd相關(guān)聯(lián)的文件中。返回實際寫入的字節(jié)數(shù),通常與count值相同;否則表示出錯。出錯的一個常見原因是:磁盤已寫滿,或者超出了一個給定進程的文件長度限制。
實例:創(chuàng)建一個文件,寫入數(shù)據(jù),移動當前偏移量,在讀數(shù)據(jù)。
#include<unistd.h> //<unistd.h>必須最早出現(xiàn),因為它可能會影響到其他頭文件。#include<stdio.h>
#include<fcntl.h>
#include<string.h>
#include<errno.h>
int main()
{
char* filename = ".//file";
char buf[100];
char buf1[5];
int fd;
printf("open a file to write\n");
if((fd = open(filename,O_RDWR|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH ))==-1)
{
perror("cannot open file\n");
return 1;
}
printf("open file successfully!\n");
printf("input a string:");
gets(buf);
//write intofile
if(write(fd,buf,strlen(buf)) !=strlen(buf))
{
perror("cannot write intofile\n");
return 1;
}
close(fd);
printf("open file to read.\n");
if((fd=open(filename,O_RDONLY)) == -1)
{
perror("cannot open thefile.\n");
return 1;
}
if(lseek(fd,3,SEEK_SET) == -1)
{
perror("lseek erroe\n");
return 1;
}
//read from the file
if(read(fd,buf1,4)==-1)
{
perror("read error.\n");
return 1;
}
printf("read from file is%s\n",buf1);
close(fd);
return 0;
}
執(zhí)行與輸出結(jié)果:
root@jb51:~$gcc -o io io.c
root@jb51:~$./io
open a file towrite
open filesuccessfully!
input a string:akxivbaslzkncxcasbxbwwvaidxbd
open file toread.
read from fileis ivba
linux 文件I/O教程(2)
下面介紹了linux中有關(guān)文件I/O的相關(guān)內(nèi)容,內(nèi)核使用三種數(shù)據(jù)結(jié)構(gòu)表示打開的文件,他們之間的關(guān)系決定了在文件共享方面一個進程對另一個進程可能產(chǎn)生的影響。
一,文件共享
內(nèi)核使用三種數(shù)據(jù)結(jié)構(gòu)表示打開的文件,他們之間的關(guān)系決定了在文件共享方面一個進程對另一個進程可能產(chǎn)生的影響。
1) 每個進程在進程表中都有一個記錄項,記錄項中包含一張打開文件描述表,可將其視為一個矢量,每個描述符占用一項。與每個文件描述符相關(guān)聯(lián)的是:
a) 文件描述符標志
b) 指向一個文件表項的指針
2) 內(nèi)核為所有打開文件維持一張文件表。每個文件表項包含:
a) 文件狀態(tài)標志(讀、寫、讀寫、添些、同步和阻塞等)
b) 當前文件偏移量
c) 指向文件v節(jié)點表項的指針
3) 每個打開文件(或設(shè)備)都有一個v節(jié)點(v-node)結(jié)構(gòu)。v節(jié)點包含了文件類型和對比文件進行各種操作的函數(shù)的指針。對于大多數(shù)文件,v節(jié)點還包含了該文件的i節(jié)點。i節(jié)點包含文件所有者、文件長度、文件所在的設(shè)備、指向文件實際數(shù)據(jù)塊在磁盤上所在位置的指針等。
打開文件的內(nèi)核數(shù)據(jù)結(jié)構(gòu)
如果兩個進程各自打開了同一個文件,則如圖2所示。假定第一個進程在文件描述符3打開上該文件,而另一個進程在文件描述符4上打開該文件。每個進程都得得到一個文件表項,但對一個給定的文件只有一個v節(jié)點表項。每個進程都有自己的文件表項的一個理由是:使每個進程都有自己對該問價的當前偏移量。
現(xiàn)在對前一節(jié)文件I/O(1)的幾個操作進一步說明:
1. 完成write之后,文件中當前偏移量即所增加的字節(jié)數(shù)。如果當前偏移量大于文件長度,則將i節(jié)點中當前文件長度設(shè)為當前文件偏移量。
2. 用O_APPEND打開一個文件,相應(yīng)標志會被設(shè)置到文件狀態(tài)標識中。每次寫時,當前偏移量會被設(shè)置為i節(jié)點中的文件長度
3. lseek定位到文件尾端時,則文件當前偏移量會被設(shè)置為當前文件長度。
可能有多個文件描述符指向同一文件表項。調(diào)用dup和fork時都能看到這一點。
多個進程讀同一文件能正確工作。但多個進程寫同一文件時,可能產(chǎn)生預(yù)期不到的后果??梢岳迷硬倏v避免這種情況。
原子操作
一般而言,原子操作指的是由多部組成的操作。如果該院自地執(zhí)行,要么執(zhí)行完所以步驟,要么一步也不執(zhí)行。
1. 添加至一個文件
考慮一個進程,它要講數(shù)據(jù)添加到一個文件尾端。早期UNIX不支持open,所以可以如下實現(xiàn):
if(lseek(fd, 0L, 2)<0)
err_sys(“lseekerror”);
if(write(fd, buf, 100) != 100)
err_sys(“writeerror”);
對于單個進程,這段程序能正常工作。但多個進程就不一定。結(jié)社進程A和B都對同一文件進行添加操作。每個進程都打開該文件,此時數(shù)據(jù)結(jié)構(gòu)之間關(guān)系如圖2中所示。假定A調(diào)用lseek,將A的當前偏移量設(shè)置為1500。進程B執(zhí)行l(wèi)seek也將其當前偏移量設(shè)為1500。然后B調(diào)用write,將當前偏移量增至1600。然后內(nèi)核又進行進程切換使進程A恢復(fù)運行,當A調(diào)用write時,從其當前偏移量1500處將數(shù)據(jù)寫入,將替換B剛寫入到該文件中的數(shù)據(jù)。
問題出在邏輯操作“定位到文件尾端處,然后寫“使用了兩個分開的函數(shù)調(diào)用。解決辦法是使這兩個操作成為一個原子操作。O_APPEND標識,使內(nèi)核每次對文件進行寫之前,都將進程當前偏移量設(shè)置到該文件的尾端處。
2.pread和pwrite函數(shù)
原子性地定位搜索和執(zhí)行I/0。
#include <unistd.h>
ssize_t pread(int fd, void *buf, size_tcount, off_t offset);
ssize_t pwrite(int fd, const void *buf,size_t count, off_t offset);
ssize_t pread(int fd, void *buf, size_tcount, off_t offset);
ssize_t pwrite(int fd, const void *buf,size_t count, off_t offset);
dup和dup2函數(shù)
#include <unistd.h>
int dup(int oldfd);
int dup2(int oldfd, int newfd);
上面兩個函數(shù)都可用來復(fù)制一個現(xiàn)存的文件描述符。
由dup返回的新文件描述符一定是當前可用文件描述符中的最小數(shù)值。用dup2則可以用newfd參數(shù)指定新描述符的數(shù)值。如果newfd已經(jīng)打開,則先將其關(guān)閉。如果newfd等于oldfd,則dup2返回newfd而不關(guān)閉它。
圖3.3顯示了這種情況。
假定我們的進程執(zhí)行了:
newfd = dup(1);
當此函數(shù)執(zhí)行時,假設(shè)下一個可用的描述符是3。因為這兩個描述符指向同一個文件表項,所以他們共享文件標志以及同一文件偏移量。
sync、fsync和fdatasync
#include <unistd.h>
void sync(void);
int fsync(int fd);
int fdatasync(int fd);
當將數(shù)據(jù)寫入文件時,內(nèi)核通常將數(shù)據(jù)復(fù)制到一個緩沖區(qū),直到緩沖區(qū)寫滿,再將緩沖區(qū)排路輸出隊列,然后等待其到達隊首,才進行實際的I/O操作。這種輸出防暑被稱為延遲寫。延遲寫減少了磁盤的讀寫次數(shù),但卻降低了文件內(nèi)容的跟新速度。當系統(tǒng)發(fā)生故障時,延遲寫可能造成文件跟新內(nèi)容的丟失。為了保證磁盤上實際文件系統(tǒng)與緩沖區(qū)高速緩存中內(nèi)容一致性,UNIX系統(tǒng)提供了sync、fsync和fdatasync 三個函數(shù)。
fcntl函數(shù)
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* arg */ );
可以改變已經(jīng)打開文件的性質(zhì)。
復(fù)制一個現(xiàn)有的描述符(cmd=F_DUPFD)
獲得或設(shè)置文件描述符(cmd=F_GETFD|F_SETFD)
獲得或設(shè)置文件狀態(tài)標志(cmd=F_GETFL|F_SETFL)
獲得或設(shè)置異步I/O所有權(quán)(cmd=F_GETOWN|F_SETOWN)
獲得或設(shè)置記錄鎖(cmd=F_GETLK|F_SETLK、F_SETLKW)
可以用fcntl函數(shù)設(shè)置文件狀態(tài),常用設(shè)置套接字描述符為非阻塞O_NONBLOCK
ioctl函數(shù)
#include <sys/ioctl.h>
int ioctl(int d, int request, ...);
提供了一個用于控制設(shè)備及其描述符行為和配置底層服務(wù)的接口。
/dev/fd
打開文件/dev/fd/n等效于復(fù)制描述符n。
相關(guān)文章
Fedora Linux 42 穩(wěn)定版發(fā)布: 帶來大量新功能和軟件更新
Fedora 42昨日發(fā)布,這是 Red Hat 贊助開發(fā)的杰出前沿 Linux 發(fā)行版的最新版,包含大量新功能和軟件更新,使其成為 2025 年上半年發(fā)布的一款出色的 Linux 操作系統(tǒng)之一,內(nèi)2025-04-16如何在Linux查看硬盤信息? 查看Linux硬盤大小類型和硬件信息的5種方法
使用Linux系統(tǒng)的過程中,查看和了解硬盤信息是非常重要的工作,尤其是對于系統(tǒng)管理員而言,那么在Linux系統(tǒng)中如何查看硬盤信息?以下是具體內(nèi)容介紹2025-03-12如何在 Linux 中查看 CPU 詳細信息? 3招輕松查看CPU型號、核心數(shù)和溫度
在日常運維工作中,獲取 CPU 信息是系統(tǒng)運維管理員常見的工作內(nèi)容,無論是為了性能調(diào)優(yōu)、硬件升級還是僅僅滿足好奇心2025-03-11什么是 Arch Linux? 獨樹一幟的Arch Linux發(fā)行版分析
Arch Linux是為簡化,優(yōu)化,現(xiàn)代化,實用主義,用戶中心和多功能性而創(chuàng)建Linux發(fā)行版,究竟是什么讓 Arch 與眾不同?下面我們就來簡要解讀2025-02-19如何在Linux環(huán)境下制作 Win11裝機U盤?
一直用的linux辦公,想要將筆記本電腦從 Linux 系統(tǒng)切換回 Windows 11,我們可以制作一個win11裝機u盤,詳細如下2025-02-17Rsnapshot怎么用? 基于Rsync的強大Linux備份工具使用指南
Rsnapshot 不僅可以備份本地文件,還能通過 SSH 備份遠程文件,接下來詳細介紹如何安裝、配置和使用 Rsnapshot,包括創(chuàng)建每小時、每天、每周和每月的本地備份,以及如何進2025-02-06Linux Kernel 6.13發(fā)布:附更新內(nèi)容及新特性解讀
Linux 內(nèi)核 6.13 正式發(fā)布,新版本引入了惰性搶占支持,簡化內(nèi)核搶占邏輯,通過減少與調(diào)度器相關(guān)的調(diào)用次數(shù),讓內(nèi)核在運行時表現(xiàn)更優(yōu),從而提高效率2025-01-23五大特性引領(lǐng)創(chuàng)新! 深度操作系統(tǒng) deepin 25 Preview預(yù)覽版發(fā)布
今日,深度操作系統(tǒng)正式推出deepin 25 Preview版本,該版本集成了五大核心特性:磐石系統(tǒng)、全新DDE、Treeland窗口合成器、AI For OS以及Distrobox子系統(tǒng)2025-01-18Linux Mint Xia 22.1重磅發(fā)布: 重要更新一覽
Beta 版 Linux Mint“Xia” 22.1 發(fā)布,新版本基于 Ubuntu 24.04,內(nèi)核版本為 Linux 6.8,這次更新帶來了諸多優(yōu)化和改進,進一步鞏固了 Mint 在 Linux 桌面操作系統(tǒng)領(lǐng)域的2025-01-16LinuxMint怎么安裝? Linux Mint22下載安裝圖文教程
Linux Mint22發(fā)布以后,有很多新功能,很多朋友想要下載并安裝,該怎么操作呢?下面我們就來看看詳細安裝指南2025-01-16