LINUX 文件IO

 1、 一切皆文件

1.1、文件的概念

       在 Linux 中,有一句经典的话叫做:一切皆文件。这句话是站在内核的角度说的,因为 在内

核中所有的设备 (除了网络接口) 都一律使用 Linux 独有的虚拟文件系统 (VFS) 来管 理。这样做的

最终目的,是将各种不同的设备用“文件”这个概念加以封装和屏蔽,简化应 用层编程的难度。文

件,是 Linux 系统最重要的两个抽象概念之一 (另一个是进程) 。另外,VFS 中有个非常重要的结

构体叫file{},这个结构体中包含一个非常重要的成员 叫做file_operation,他通过提供一个统一

的、包罗万象的操作函数集合,来统一规范将来 文件所有可能的操作。某一种文件或设备所支持

的操作都是这个结构体的子集。做 Linux 底 层开发的人对该结构体都应该非常熟悉。

图 1- 1  从应用层的 read( )到底层的 xxx_read( )

        图 1- 1 以 read( )为例子,说明了为什么在上层应用中可以对千差万别的设备进行读操作, 头

号功臣就是 file_operation 提供了统一的接口,实际上,VFS 不仅包括 file 结构体,还有 inode 结

构体和 super_block 结构体,正是他们的存在,应用层程序才得以摆脱底层设备的差异细节,独立

于设备之外。

       可以看到,内核做了掐头去尾的事情,提供了一个沟通上下的框架,如果你是软件工程师,就

站在用户空间使用下面内核提供的接口,来为你的应用程序服务。如果你是底层驱动 工程师,就

站在操作硬件设备的角度,结合具体设备的操作方式,实现上面内核规定好的各 个该设备可以支

持的接口函数。

       有了内核提供的中间层,我们在操作很多不同类型的文件的时候就方便多了,比方说读 取文

件 a.txt 的内容,跟读取触摸屏的坐标数据,读取鼠标的坐标信息等等,用的都是函数 read(),虽

然底层的实现代码也许不一样,但是用户空间的进程并不关心也无需操心,Linux的系统 IO 数屏

蔽了各类文件的差异,使得我们站在应用编程开发者的角度看下去,产生好像各类文件都一样的感

觉。这就是 Linux 应用编程中一切皆文件的说法的由来。

1.2、各类文件

在 Linux 中,文件总共被分成了 7 种,他们分别是:

        1,普通文件 (regular) :存在于外部存储器中,用于存储普通数据。

        2, 目录文件 (directory) :用于存放目录项,是文件系统管理的重要文件类型。

        3,管道文件 (pipe) :一种用于进程间通信的特殊文件,也称为命名管道 FIFO。

        4,套接字文件 (socket) :一种用于网络间通信的特殊文件。

        5,链接文件 (link) :用于间接访问另外一个目标文件,相当于 Windows 快捷方式。

        6,字符设备文件 (character) :字符设备在应用层的访问接口。

        7,块设备文件 (block) :块设备在应用层的访问接口。

2、文件操作

2.1、系统IO  (没有缓冲区,效率不高)   

         2.1.1、函数接口:
        2.1.1.1、open()

         int open(const char *pathname, int flags);

         int open(const char *pathname, int flags, mode_t mode);

功能

打开一个指定的文件并获得文件描述符,或者创建一个新文件

头文件

#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:即将要打开的文件

flags

O_RDONLY:只读方式打开文件

这三个参数互斥

O_WRONLY:只写方式打开文件

O_RDWR:读写方式打开文件

O_CREAT:如果文件不存在,则创建该文件。

O_EXCL:如果使用 O_CREAT 选项且文件存在,则返回错误消息。

O_NOCTTY:如果文件为终端,那么终端不可以作为调用 open()系统调 用的那个进程的控制终端。

O_TRUNC:如文件已经存在,则删除文件中原有数据。

O_APPEND:以追加方式打开文件。

mode

如果文件被新建,指定其权限为 mode  (八进制表示法)

返回值

成功

大于等于 0 的整数 (即文件描述符)

失败

- 1

备注

使用系统调用 open( )需要注意的问题有:

1,flags 的各种取值可以用位或的方式叠加起来,比如创建文件的时候需要满足这样的 选项:读写方式打开,不存在要新建,如果存在了则清空他。那么此时指定的 flags 的取值 应该是:O_RDWR | O_CREAT | O_TRUNC。

2,mode 是八进制权限,比如 0644,或者 0755 等。

3,它可以用来打开普通文件、块设备文件、字符设备文件、链接文件和管道文件,但 只能用来创建普通文件,每一种特殊文件的创建都有其特定的其他函数。

4,其返回值就是一个代表这个文件的描述符,是一个非负整数。这个整数将作为以后

何系统 IO 函数对其操作的句柄,或称入口。

        2.1.1.2、close()

功能

关闭文件并释放相应资源

头文件

#include <unistd.h>

原型

int close(int fd);

参数

fd:即将要关闭的文件的描述符

返回值

成功

0

失败

- 1

备注

重复关闭一个已经关闭了的文件或者尚未打开的文件是安全的。

        2.1.1.3、read()

功能

从指定文件中读取数据

头文件

#include <unistd.h>

原型

ssize_t read(int fd, void *buf, size_t count);

参数

fd:从文件fd 中读数据

buf:指向存放读到的数据的缓冲区

count:想要从文件 fd 中读取的字节数

返回值

成功

实际读到的字节数

失败

- 1

备注

实际读到的字节数小于等于 count

        2.1.1.4、write()

功能

将数据写入指定的文件

头文件

#include <unistd h>

原型

ssize_t write(int fd, const void *buf, size_t count);

参数

fd:将数据写入到文件 fd 中

buf:指向即将要写入的数据

count:要写入的字节数

返回值

成功

实际写入的字节数

失败

- 1

备注

实际写入的字节数小于等于 count

        2.1.1.5、lseek()

功能

调整文件位置偏移量

头文件

#include <sys/types.h>

#include <unistd.h>

原型

off_t lseek(int fd, off_t offset, int whence);

参数

fd:要调整位置偏移量的文件的描述符

offset:新位置偏移量相对基准点的偏移

whence:基准点

SEEK_SET:文件开头处

SEEK_CUR:当前位置

SEEK_END:文件末尾处

返回值

成功

新文件位置偏移量

失败

- 1

备注

 2.2、标准IO

        2.2.1、fopen()

功能

获取指定文件的文件指针

头文件

#include <stdio.h>

原型

FILE *fopen(const char *path, const char *mode);

参数

path:即将要打开的文件

mode

“r” :  以只读方式打开文件,要求文件必须存在。

“r+” :  以读写方式打开文件,要求文件必须存在。

“w” :  以只写方式打开文件,文件如果不存在将会创建新文件,如果存 在将会将其内容清空。

“w+” :  以读写方式打开文件,文件如果不存在将会创建新文件,如果存 在将会将其内容清空。

“a” :  以只写方式打开文件,文件如果不存在将会创建新文件,且文件位 置偏移量被自动定位到文件末尾 (即以追加方式写数据) 。

“a+” :  以读写方式打开文件,文件如果不存在将会创建新文件,且文件 位置偏移量被自动定位到文件末尾 (即以追加方式写数据) 。

返回值

成功

文件指针

失败

NULL

备注

        2.2.2、fclose()

功能

关闭指定的文件并释放其资源

头文件

#include <stdio.h>

原型

int fclose(FILE *fp);

参数

fp:即将要关闭的文件

返回值

成功

0

失败

EOF

备注

        2.2.3、fread()

功能

从指定文件读取若干个数据块

头文件

#include <sys/ioctl.h>

原型

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

参数

ptr: 自定义缓冲区指针

size:数据块大小

nmemb:数据块个数

stream:即将被读取数据的文件指针

返回值

成功

读取的数据块个数,等于 nmemb

失败

读取的数据块个数,小于 nmemb 或等于 0

备注

当返回小与nmemb 时,文件 stream 可能已达末尾,或者遇到错误

        2.2.4、fwrite()

功能

将若干块数据写入指定的文件

头文件

#include <sys/ioctl.h>

原型

size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);

参数

ptr: 自定义缓冲区指针

size:数据块大小

nmemb:数据块个数

stream:即将被写入数据的文件指针

返回值

成功

写入的数据块个数,等于 sinmembze

失败

写入的数据块个数,小于 nmemb 或等于 0

备注

        2.2.5、fseek()

功能

设置指定文件的当前位置偏移量

头文件

#include <sys/ioctl.h>

原型

int fseek(FILE *stream, long offset, int whence);

参数

stream:需要设置位置偏移量的文件指针

offset:新位置偏移量相对基准点的偏移

whence:基准点

SEEK_SET:文件开头处

SEEK_CUR:当前位置

SEEK_END:文件末尾处

返回值

成功

0

失败

- 1

备注

        2.2.6、ftell()

功能

获取指定文件的当前位置偏移量

头文件

#include <sys/ioctl.h>

原型

long ftell(FILE *stream);

参数

stream:需要返回当前文件位置偏移量的文件指针

返回值

成功

当前文件位置偏移量

失败

- 1

备注

         2.2.7、rewind()

功能

将指定文件的当前位置偏移量设置到文件开头处

头文件

#include <sys/ioctl.h>

原型

void rewind(FILE *stream);

参数

stream:需要设置位置偏移量的文件指针

返回值

备注

该函数的功能是将文件 strean 的位置偏移量置位到文件开头处。

3、目录检索

3.1、opendir()和readdir()

        现在来看看,对于一个目录而言,我们是怎么处理的。其实操作目录跟标准 IO 函数操 作文件类似,也是先获得“ 目录指针”,然后读取一个个的“目录项”。用到的接口函数是:

功能

打开目录以获得目录指针

头文件

#include <sys/types.h>

#include <dirent.h>

原型

DIR *opendir(const char *name);

参数

name: 目录名

返回值

成功

目录指针

失败

NULL

备注

功能

读取目录项

头文件

#include <dirent.h>

原型

struct dirent *readdir(DIR *dirp);

参数

dirp:读出目录项的目录指针

返回值

成功

目录项指针

失败

NULL

备注

从目录中读到的所谓目录项,是一个这样的结构体:

struct dirent

{

ino_t d_ino; // 文件索引号

off_t d_off; //  目录项偏移量

unsigned short d_reclen; // 该目录项大小

unsigned char d_type; // 文件类型

char d_name[256]; // 文件名

};

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值