Unix编程:文件I/O操作及文件描述符
2014-08-22 21:59
239 查看
Unix系统中大多数文件I/O需要用到以下五个函数:open,read,write,lseek以及close。这些函数通常被称为不带缓冲的I/O(这些函数都是在内核中执行,它们直接对内核缓存区进行读写)。
文件描述符
对于内核而言,所有打开的文件都通过文件描述符引用,文件描述符是一个非负整数,当打开一个现有文件或创建一个新文件时,内核向进程返回一个文件描述符。
open 函数:调用open函数可以打开或创建一个文件
已打开的文件在内核中,用file结构体表示.
open函数具有处理符号链接(指向一个文件的间接指针,也就是文件的索引的索引)的功能,如果用open打开文件时,如果传递给open函数的路径名指定了一个符号链接,那么open函数跟随链接到达你所指定的文件,若此符号链接所指向的文件并不存在,则open返回出错。
creat 函数:创建一个新文件
close 函数:关闭一个已经打开的文件
每个打开的文件都有一个与其相关联的“当前文件偏移量”。用以度量从文件开始处计算的字节数。通常,读、写操作都是从当前文件偏移量处开始,并使偏移量增加所读写的字节数。系统默认打开一个文件,偏移量指定为零,除非指定O_APPEND选项
我们可以调用lseek 显式地为一个打开的文件设置其偏移量
read 函数:从打开文件中读数据
write 函数:向打开的文件写数据
上面教科书式的介绍了这几个常用的I/O函数。下面通过几个简单程序来理解这几个函数的应用。
![](https://img-blog.csdn.net/20140817220406795)
最终的examplefiles 文件存在空洞
揭秘文件描述符
内核支持的文件描述符数据结构如下图
![](https://img-blog.csdn.net/20140822215122830)
右侧的表称为 i 节点表,整个系统只有一张。该表可以视为结构体数组,该数组的一个元素对应于一个物理文件。
中间的表称为文件表,整个系统只有一张,该表可以视为结构体数组,一个结构体中有很多字段,其中有3个字段比较重要:
file status flags:用于记录文件被打开时采用的选项,其实记录的就是open 调用中用户指定的第 2个参数
current file offset:用于记录文件的当前读写位置(指针)正是由于此字段的存在,使得一个文件被打开并读取后,下一次读取将从上一次读取的字符后开始读取
v-node ptr:该字段是指针,指向右侧表的一个元素,从而关联了物理文件
右侧的表称为文件描述符表,每个进程有且只有一张,该表可以视为指针数组,数组的元素指向文件表的一个元素,最重要的是数组元素的下标就是文件描述符。
open函数的实际操作就是:新建一个 i 节点表元素,让其对应打开的物理文件,新建一个文件表的元素,根据open的第二个参数设置文件状态标志,将当前文件偏移量置0,将v节点指针指向刚建立的i节点表元素,在文件描述符表中寻找一个上未使用的元素,在该元素中填入一个指针值,让其指向刚建立的文件表元素,该元素下标作为open的返回值,也就是文件描述符。
这样一来,当调用read(write)函数时,根据传入的文件描述符,系统可以找到对应的文件描述符元素,进而找到文件表的元素,找到i节点表元素,从而完成对物理文件的读写。
参考资料:
《Unix 高级环境编程》
《Linux下C语言应用编程》
文件描述符
对于内核而言,所有打开的文件都通过文件描述符引用,文件描述符是一个非负整数,当打开一个现有文件或创建一个新文件时,内核向进程返回一个文件描述符。
open 函数:调用open函数可以打开或创建一个文件
#include <fcntl.h> int open(const char *pathname, int oflag, .../*mode_t mode*/); //返回值:若成功则返回文件描述符,若出错则返回-1 /* pathname:要打开或创建文件的名字 oflags:文件打开的方式 第三个参数仅当open创建新文件时才使用 */
已打开的文件在内核中,用file结构体表示.
open函数具有处理符号链接(指向一个文件的间接指针,也就是文件的索引的索引)的功能,如果用open打开文件时,如果传递给open函数的路径名指定了一个符号链接,那么open函数跟随链接到达你所指定的文件,若此符号链接所指向的文件并不存在,则open返回出错。
creat 函数:创建一个新文件
#include <fcntl.h> int creat(const char *pathname, mode_t mode); //返回值:若成功则返回为只写打开的文件描述符,若出错则返回-1 /*该函数等效于 open(pathname, O_RDWR|O_CREAT|O_TRUNC, mode); creat 函数存在不足,只能以只写方式打开所创建的文件,现在一般调用open函数来创建*/
close 函数:关闭一个已经打开的文件
#include <unistd.h> int close(int filedes); //返回值:若成功则返回0,若出错则返回-1lseek 函数:
每个打开的文件都有一个与其相关联的“当前文件偏移量”。用以度量从文件开始处计算的字节数。通常,读、写操作都是从当前文件偏移量处开始,并使偏移量增加所读写的字节数。系统默认打开一个文件,偏移量指定为零,除非指定O_APPEND选项
我们可以调用lseek 显式地为一个打开的文件设置其偏移量
#include <unistd.h> off_t lseek(int filedes, off_t offset, int whence); //返回值:若成功则返回新的文件偏移量,若出错则返回-1 /* 参数offset 的解释与参数whence的值有关,偏移量可以为负值,whence的值: SEEK_SET:距文件开始处offset 个字节 SEEK_CUR:当前值加offset SEEK_END:文件长度加offset */lseek 仅将当前的文件偏移量记录在内核中,它并不引起任何I/O操作,然后,该偏移量用于下一个读或写操作。
read 函数:从打开文件中读数据
#include <unistd.h> ssize_t read(int filedes, void *buf, size_t nbytes); //返回值:若成功则返回读到的字节数,若已到文件结尾则返回0,若出错则返回-1 /*从filedes关联的文件中读取nbytes的字节数到buf中,然后返回实际读的字节数*/读操作从文件的当前偏移量处开始,在成功返回之前,该偏移量将增加实际读到的字节数,read 有时实际读到的字节数会少于要求读的字节数。
write 函数:向打开的文件写数据
#include <unistd.h> ssize_t write(int filedes, const void *buf, size_t nbytes); //返回值:若成功则返回已写的字节数,若出错则返回-1 /*将buf中nbytes个字节写入到filedes关联的文件中,返回实际写入的字节数*/对于普通文件,写操作从文件的当前偏移量处开始,成功写之后,该文件偏移量增加实际写的字节数。
上面教科书式的介绍了这几个常用的I/O函数。下面通过几个简单程序来理解这几个函数的应用。
#include <stdio.h> #include <sys/types.h> #include <fcntl.h> #include <unistd.h> #define BUFFSIZE 4096 int main(void) { int fd; int n; char buf[BUFFSIZE]; char data[] = "sunburn"; off_t offset; if((fd = open("examplefiles", O_RDWR)) == -1) perror("open error\n"); printf("fd = %d\n", fd); offset = lseek(fd, 0, SEEK_CUR); printf("offset = %d\n", offset); if((n = read(fd, buf, BUFFSIZE)) == -1) perror("read error\n"); if(write(STDOUT_FILENO, buf, n) != n)//标准输出 perror("write error\n"); offset = lseek(fd, -5, SEEK_CUR);//当前偏移量前移 printf("offset = %d\n", offset); lseek(fd, 100, SEEK_SET);//设置新的偏移量 write(fd, data, sizeof(data)/sizeof(char));//基于新的偏移量写入数据 close(fd); return 0; }下面是程序验证结果
最终的examplefiles 文件存在空洞
揭秘文件描述符
内核支持的文件描述符数据结构如下图
右侧的表称为 i 节点表,整个系统只有一张。该表可以视为结构体数组,该数组的一个元素对应于一个物理文件。
中间的表称为文件表,整个系统只有一张,该表可以视为结构体数组,一个结构体中有很多字段,其中有3个字段比较重要:
file status flags:用于记录文件被打开时采用的选项,其实记录的就是open 调用中用户指定的第 2个参数
current file offset:用于记录文件的当前读写位置(指针)正是由于此字段的存在,使得一个文件被打开并读取后,下一次读取将从上一次读取的字符后开始读取
v-node ptr:该字段是指针,指向右侧表的一个元素,从而关联了物理文件
右侧的表称为文件描述符表,每个进程有且只有一张,该表可以视为指针数组,数组的元素指向文件表的一个元素,最重要的是数组元素的下标就是文件描述符。
open函数的实际操作就是:新建一个 i 节点表元素,让其对应打开的物理文件,新建一个文件表的元素,根据open的第二个参数设置文件状态标志,将当前文件偏移量置0,将v节点指针指向刚建立的i节点表元素,在文件描述符表中寻找一个上未使用的元素,在该元素中填入一个指针值,让其指向刚建立的文件表元素,该元素下标作为open的返回值,也就是文件描述符。
这样一来,当调用read(write)函数时,根据传入的文件描述符,系统可以找到对应的文件描述符元素,进而找到文件表的元素,找到i节点表元素,从而完成对物理文件的读写。
参考资料:
《Unix 高级环境编程》
《Linux下C语言应用编程》
相关文章推荐
- UNIX环境编程之<二>文件共享I/O操作dup,fcntl函数
- UNIX环境编程学习笔记(4)——文件I/O之dup复制文件描述符
- UNIX环境编程学习笔记(4)——文件I/O之dup复制文件描述符
- Unix高级编程:文件的基本操作、mmap将文件映射虚拟地址、文件描述符的复制
- unix c编程:不带缓冲的文件 I/O(文件描述符)
- UNIX环境高级编程学习之第六章系统数据文件和信息 用链表的形式读出一个服务器的远程用户登入登出信息
- linux内核情景分析 UNIX环境高级编程 {进程 文件 内存}
- UNIX环境高级编程学习之第四章文件和目录-用C实现Shell中的"ls -l"功能
- UNIX环境高级编程学习之第三章文件IO-文件写操作
- Unix环境高级程序设计入门--文件系统的相关编程(上)
- Unix编程-列出目录下文件列表
- UNIX环境高级编程学习之第六章系统数据文件和信息-修改第四章实现的Shell的“ls -l”功能
- Unix环境高级编程第三章文件I/O摘记
- [UNIX环境高级编程]apue.h头文件的配置
- Unix下C语言----目录文件编程
- 第四节 UNIX标准文件编程库
- UNIX环境编程—文件I/O
- Unix环境编程-系统文件和信息
- Unix环境高级程序设计入门----文件系统的相关编程(下)
- UNIX环境高级编程学习之第三章文件IO-文件读偏移操作