linux 文件I/O教程
一,文件描述符
对内核而言,所以打开的文件都通过文件描述符引用。每个进程都有一些与之关联的文件描述符。文件描述符是一个非负整数。当打开一个现有文件或创建一个新文件时,内核向进程返回一个文件描述符。当读或写一个文件时,使用open或creat返回的文件描述符标识该文件,将其作为参数传送给read和write。
一般有三个以及打开的文件描述符,他们是:
兄弟连Linux培训
复制代码代码如下:
0:标准输入 STDIN_FILENO
1:标准输出 STDOUT_FILENO
2标准错误输出 STDERR_FILENO
每行后面的符号常量是依从POSIX而定的。
open函数
复制代码代码如下:
#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是要打开或创建文件的名字。
flag用来定义打开文件所采取的的动作,必须调用以下模式之一
O_RDONLY, O_WRONLY, O_RDWR分别代表只读,只写,读写方式打开。
open还可以包括以下可选模式的组合
O_APPEND:把写入数据追加到文件的尾端
O_CREAT:若文件不存在,则创建它。使用此选项时,需要第三个参数mode,用其指定该新文件的访问权限。
O_EXCL:如果同时指定了O_CREAT,而文件存在,则会出错。用此可以测试一个文件是否存在,如果存在,则创建文件,这使测试和创建两者成为一个原子操作。
O_TRUNC: 如果此文件存在,而且为只写或读写成功打开,则将其长度截为0。
open返回的文件描述符一定是最小的未用描述符数值。这一点被某些应用程序用在标准输入,标准输出或标准错误输出上。如,一个程序关闭了自己的标准输出,然后再次调用open,文件描述符1就会被调用,并且标准输出将被有效的重定向到另一个文件或设备。
POSIX规范还标准化了一个creat调用,此函数等效于
open(pathname,O_WONLY |O_CREAT | O_TRUNC, mode);
close函数
#include <unistd.h>
int close(int fd);
close调用终止一个文件描述符fd与对应文件之间的关联。文件描述符被释放后并能重新使用。close调用成功返回0,出错返回-1.
关闭一个文件时会释放该进程加在文件上的所有记录锁。当一个进程终止时,内核自动关闭它所有打开的文件。
lseek函数
每个打开的文件都有一个与其相关联的”当前文件偏移量”。按系统默认情况,当打开一个文件时,除非指定O_APPEND选项,否则该偏移量被设置为0。lseek可以为一个打开的文件设置偏移量。
复制代码代码如下:
#include<sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_toffset, intwhence);
offset用来指定位置,whence参数定义该偏移值的用法。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.
成功调用返回从文件头到文件指针被设置处的字节偏移值,失败返回-1。参数offset定义在<sys/types.h>中。
当偏移量大于文件长度时,出现空洞,空洞不占用存储区。
read函数
复制代码代码如下:
#include <unistd.h>
ssize_t read(int fd, void*buf, size_tcount);
将与文件描述符fd关联的文件中读入count个字符放到buf中。返回读入的字节数,它可能小于请求的字节数。如果read调用返回0,就表示未读入任何数据,已到达了文件尾。返回-1,就表示出错。
write函数
复制代码代码如下:
#include <unistd.h>
ssize_t write(int fd, constvoid *buf,size_t count);
把缓冲区buf的前count个字节写入与文件描述符fd相关联的文件中。返回实际写入的字节数,通常与count值相同;否则表示出错。出错的一个常见原因是:磁盘已写满,或者超出了一个给定进程的文件长度限制。
实例:创建一个文件,写入数据,移动当前偏移量,在读数据。
复制代码代码如下:
#include<unistd.h>//<unistd.h>必须最早出现,因为它可能会影响到其他头文件。#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 towrite\n");
if((fd =open(filename,O_RDWR|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH ))==-1)
{
perror("cannot openfile\n");
return 1;
}
printf("open filesuccessfully!\n");
printf("input astring:");
gets(buf);
//write intofile
if(write(fd,buf,strlen(buf))!=strlen(buf))
{
perror("cannot writeintofile\n");
return 1;
}
close(fd);
printf("open file toread.\n");
if((fd=open(filename,O_RDONLY))== -1)
{
perror("cannot open thefile.\n");
return 1;
}
if(lseek(fd,3,SEEK_SET) ==-1)
{
perror("lseekerroe\n");
return 1;
}
//read from the file
if(read(fd,buf1,4)==-1)
{
perror("readerror.\n");
return 1;
}
printf("read from fileis%s\n",buf1);
close(fd);
return 0;
}
执行与输出结果:
复制代码代码如下:
root@jb51:~$gcc -o io io.c
root@jb51:~$./io
open a file towrite
open filesuccessfully!
input astring:akxivbaslzkncxcasbxbwwvaidxbd
open file toread.
read from fileis ivba
linux 文件I/O教程(2)
下面介绍了linux中有关文件I/O的相关内容,内核使用三种数据结构表示打开的文件,他们之间的关系决定了在文件共享方面一个进程对另一个进程可能产生的影响。
一,文件共享
内核使用三种数据结构表示打开的文件,他们之间的关系决定了在文件共享方面一个进程对另一个进程可能产生的影响。
1) 每个进程在进程表中都有一个记录项,记录项中包含一张打开文件描述表,可将其视为一个矢量,每个描述符占用一项。与每个文件描述符相关联的是:
a) 文件描述符标志
b) 指向一个文件表项的指针
2) 内核为所有打开文件维持一张文件表。每个文件表项包含:
a) 文件状态标志(读、写、读写、添些、同步和阻塞等)
b) 当前文件偏移量
c) 指向文件v节点表项的指针
3) 每个打开文件(或设备)都有一个v节点(v-node)结构。v节点包含了文件类型和对比文件进行各种操作的函数的指针。对于大多数文件,v节点还包含了该文件的i节点。i节点包含文件所有者、文件长度、文件所在的设备、指向文件实际数据块在磁盘上所在位置的指针等。
打开文件的内核数据结构
如果两个进程各自打开了同一个文件,则如图2所示。假定第一个进程在文件描述符3打开上该文件,而另一个进程在文件描述符4上打开该文件。每个进程都得得到一个文件表项,但对一个给定的文件只有一个v节点表项。每个进程都有自己的文件表项的一个理由是:使每个进程都有自己对该问价的当前偏移量。
现在对前一节文件I/O(1)的几个操作进一步说明:
1. 完成write之后,文件中当前偏移量即所增加的字节数。如果当前偏移量大于文件长度,则将i节点中当前文件长度设为当前文件偏移量。
2. 用O_APPEND打开一个文件,相应标志会被设置到文件状态标识中。每次写时,当前偏移量会被设置为i节点中的文件长度
3. lseek定位到文件尾端时,则文件当前偏移量会被设置为当前文件长度。
可能有多个文件描述符指向同一文件表项。调用dup和fork时都能看到这一点。
多个进程读同一文件能正确工作。但多个进程写同一文件时,可能产生预期不到的后果。可以利用原子操纵避免这种情况。
原子操作
一般而言,原子操作指的是由多部组成的操作。如果该院自地执行,要么执行完所以步骤,要么一步也不执行。
1. 添加至一个文件
考虑一个进程,它要讲数据添加到一个文件尾端。早期UNIX不支持open,所以可以如下实现:
复制代码代码如下:
if(lseek(fd, 0L, 2)<0)
err_sys(“lseekerror”);
if(write(fd, buf, 100) !=100)
err_sys(“writeerror”);
对于单个进程,这段程序能正常工作。但多个进程就不一定。结社进程A和B都对同一文件进行添加操作。每个进程都打开该文件,此时数据结构之间关系如图2中所示。假定A调用lseek,将A的当前偏移量设置为1500。进程B执行lseek也将其当前偏移量设为1500。然后B调用write,将当前偏移量增至1600。然后内核又进行进程切换使进程A恢复运行,当A调用write时,从其当前偏移量1500处将数据写入,将替换B刚写入到该文件中的数据。
问题出在逻辑操作“定位到文件尾端处,然后写“使用了两个分开的函数调用。解决办法是使这两个操作成为一个原子操作。O_APPEND标识,使内核每次对文件进行写之前,都将进程当前偏移量设置到该文件的尾端处。
2.pread和pwrite函数
原子性地定位搜索和执行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函数
复制代码代码如下:
#include <unistd.h>
int dup(int oldfd);
int dup2(int oldfd, intnewfd);
上面两个函数都可用来复制一个现存的文件描述符。
由dup返回的新文件描述符一定是当前可用文件描述符中的最小数值。用dup2则可以用newfd参数指定新描述符的数值。如果newfd已经打开,则先将其关闭。如果newfd等于oldfd,则dup2返回newfd而不关闭它。
图3.3显示了这种情况。
假定我们的进程执行了:
newfd = dup(1);
当此函数执行时,假设下一个可用的描述符是3。因为这两个描述符指向同一个文件表项,所以他们共享文件标志以及同一文件偏移量。
sync、fsync和fdatasync
复制代码代码如下:
#include <unistd.h>
void sync(void);
int fsync(int fd);
int fdatasync(int fd);
当将数据写入文件时,内核通常将数据复制到一个缓冲区,直到缓冲区写满,再将缓冲区排路输出队列,然后等待其到达队首,才进行实际的I/O操作。这种输出防暑被称为延迟写。延迟写减少了磁盘的读写次数,但却降低了文件内容的跟新速度。当系统发生故障时,延迟写可能造成文件跟新内容的丢失。为了保证磁盘上实际文件系统与缓冲区高速缓存中内容一致性,UNIX系统提供了sync、fsync和fdatasync 三个函数。
fcntl函数
复制代码代码如下:
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd,... /* arg */ );
可以改变已经打开文件的性质。
复制一个现有的描述符(cmd=F_DUPFD)
获得或设置文件描述符(cmd=F_GETFD|F_SETFD)
获得或设置文件状态标志(cmd=F_GETFL|F_SETFL)
获得或设置异步I/O所有权(cmd=F_GETOWN|F_SETOWN)
获得或设置记录锁(cmd=F_GETLK|F_SETLK、F_SETLKW)
可以用fcntl函数设置文件状态,常用设置套接字描述符为非阻塞O_NONBLOCK
ioctl函数
#include<sys/ioctl.h>
int ioctl(int d, intrequest, ...);
提供了一个用于控制设备及其描述符行为和配置底层服务的接口。
/dev/fd
打开文件/dev/fd/n等效于复制描述符n。
分享到:
相关推荐
linux入门教程 linux入门教程 linux入门教程
Linux入门培训教程旨在帮助初学者快速熟悉和掌握Linux操作系统的基础知识和常用命令。Linux是一种开源、免费的操作系统,被广泛应用于服务器、嵌入式设备以及个人计算机上。本教程将带你走进Linux的世界,理解它的...
通过阅读《Linux基础命令教程豪华版》、《Linux常用命令全集》、《Linux实用培训教程第一部分》和《Linux实用培训教程第二部分》等资料,你可以更深入地学习Linux的使用,逐步成为Linux操作的高手。记住,实践是检验...
Linux操作系统(内含各种Linux操作入门相关教程、Linux学习总结、常用命令汇总等),详细如下: linux操作系统,[Linux电子书合集],Linux 系统命令及其使用详解(大全),Linux_C_函数库,...计算机公司Linux培训教程等
详细的Linux入门讲解。让你明白如何操作。
在Linux环境中,经常需要对多个文件进行处理,特别是在数据处理和文本分析领域。Awk作为一种强大的文本处理工具,在处理这类任务时非常高效。本文将详细介绍两种常用的awk多文件操作方法,并结合具体实例来帮助理解...
关于linux入门必看的书,您若是高手了,就不用浪费时间了
本"Linux实用培训学习教程"旨在帮助初学者快速入门,同时也为有一定经验的用户提供了深入的技巧和知识。 教程内容可能包括以下几个方面: 1. **Linux基础知识**:介绍Linux的发展历史、发行版分类,如Ubuntu、...
黑马程序员 Linux 从入门到精通配套笔记 本资源汇总了 Linux 操作系统的发展史、安装方法、特点和分支等内容。下面是从文件中提取的知识点: 一、Linux 发展史 1. Linux 的前身是 Unix,于 1968 年由 Multics ...
### Linux C培训教程知识点概述 #### 一、课程概述与目标 - **课程性质**:本教程属于高级类,而非入门级,适用于已有一定Linux和C语言基础的学习者。 - **核心目标**: - 深入理解计算机系统的工作原理。 - 掌握...
第2章 【基础篇】Linux基础入门 第3章 【基础篇】Linux系统的硬件基石 第4章 【基础篇】以数据见状态 - Linux指标系统 第5章 【基础篇】硬件评估 - 不服跑个分 第6章 【进阶篇】文本处理三剑客 第7章 【进阶篇】文件...
"陈杰博士Linux入门培训教材"是一套专为初学者设计的教程,旨在帮助新接触Linux的人快速熟悉这个系统,并掌握基本的操作和编程技能。 1. **LINUX入门之概述.ppt**: 这个部分通常会介绍Linux的历史,它是如何从...
这个“Linux机密培训教程”作为经典资料,不仅适合初学者入门,也对有一定经验的Linux用户有很高的参考价值。通过深入学习和实践,可以提升个人在IT领域中的竞争力,特别是在系统管理、云计算和开发运维一体化...
Diff 命令是 Linux 系统中非常重要的工具,用于比较文件的内容,特别是比较两个版本不同的文件以找到改动的地方。 Diff 命令在命令行中打印每一个行的改动。最新版本的 diff 还支持二进制文件。Diff 程序的输出被...
这部分内容包括Linux的哲学、文件系统结构、命令行界面和基本命令的使用。 2. **Linux安装与维护**:涵盖如何规划和执行Linux系统的安装,包括硬件兼容性、分区策略、启动装载器GRUB的配置以及系统升级和更新方法。...
接着系统地讲解了嵌入式Linux的环境搭建,以及嵌入式Linux的I/O与文件系统的开发、进程控制开发、进程间通信开发、网络应用开发、基于中断的开发、设备驱动程序的开发以及嵌入式图形界面的开发等,并且还安排了丰富...
【千锋Linux】Shell脚本入门全套教程(103集),课程齐全,适合初学者,不是很详细,不是很完善,难度系数3颗星,就是常用的一些脚本编写的教程。包含pdf课件。
这是对LINUX自学者来说是一个很好的视频教程,它不但教会你linux ,还教会你如何自学计算机的其他知识。
通过以上知识点的总结,我们可以看出《Linux实用培训教程第一部分》旨在为Linux初学者提供一个全面的入门指南,不仅涵盖了Linux的基础知识,还深入到了系统管理和维护等高级主题,非常适合希望从零开始学习Linux的...