Linux应用编程之——文件IO

Linux应用编程之——文件IO

Linux应用编程之——文件IO

在Linux系统中有一个重要的概念:一切皆文件,它把一切资源都看作是文件,包括硬件设备,通常称为设备文件。前面我们也尝试过通过脚本读写文件的方式,实现了对硬件的访问。所以如果不了解Linux的文件操作,那想要点亮开发板的LED灯都费劲!对Linux文件IO的处理,这意味着程序完全可以像使用文件那样使用磁盘文件,串口,打印机等等。

文件IO操作示意图

在Linux下,一个与文件操作相关的应用程序结构如下所示。
在这里插入图片描述

上图解构如下:
应用层指用户编写的程序,如我们的hello.c。
GNU C库(glibc)即C语言标准库,例如在编译器章节介绍的libc.so.6文件,它 包含了printf、malloc,以及本章使用的fopen、fread、fwrite等文件操作函数。
用户程序和glibc库都是属于用户空间的,本质都是用户程序。
应用层的程序和glibc可能会调用到”系统调用层(SCI)”的函数,这些函数 是Linux内核对外提供的函数接口,用户通过这些函数向系统申请操作。例如,C库 的printf函数使用了系统的vsprintf和write函数,C库的fopen、fread、fwrite分别 调用了系统的open、read、w rite函数,具体可以阅读glibc的源码了解。
由于文件系统种类非常多,跟文件操作相关的open、read、write等函数经过虚 拟文件系统层,再访问具体的文件系统。
总的来说,为了使不同的文件系统共存, Linux内核在用户层与具体文件 系统之前增加了虚拟文件系统中间层,它对复杂的系统进行抽象化,对用户提供了统 一的文件操作接口。无论是ext2/3/4、FAT32、NTFS存储的文件,还是/proc、/sys提供 的信息还是硬件设备,无论内容是在本地还是网络上,都使用 一样的open、read、write来访问,使得”一切皆文件”的理念被实现,这也正是软件中间层的魅力。

常用文件操作(C标准库)

通用的C标准库接口访问文件,标准库实际是对系统调用再次进行了封装。使用C标准库编写的代码,能方便地在不同的系统上移植。

操作系统的核心部分,即内核,是由一组设备驱动程序组成。他们是一组对系统硬件进行控制的底层接口,为了向用户提供一个一致的接口,其封装了所有与硬件相关的特性。

fopen详解:

在这里说一句,在开发的问题的时候,遇到不熟悉的函数,一定要使用man手册,而不是老是在网上找答案。

fopen库函数用于打开或创建文件,返回相应的文件流。
#include <stdio.h>
FILE *fopen(const char *pathname, const char *mode);

pathname参数用于指定要打开或创建的文件名。
mode参数用于指定文件的打开方式,注意该参数是一个字符串,输入时需要带双引号:
“r”:以只读方式打开,文件指针位于文件的开头。
“r+”:以读和写的方式打开,文件指针位于文件的开头。
“w”:以写的方式打开,不管原文件是否有内容都把原内容清空掉,文件指针位于文件的开头。
“w+”: 同上,不过当文件不存在时,前面的”w”模式会返回错误,而此处的”w+”则会创建新文件。
“a”:以追加内容的方式打开,若文件不存在会创建新文件,文件指针位于文件的末尾。与”w+”的区别是它不会清空原文件的内容而是追加。
“a+”:以读和追加的方式打开,其它同上。
fopen的返回值是FILE类型的文件文件流,当它的值不为NULL时表示正常,后续的fread、fwrite等函数可通过文件流访问对应的文件。

fread详解:

fread库函数用于从文件流中读取数据。
#include <stdio.h>
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

fread的返回值为成功读取的项数(项的单位为size)。

fwrite详解:

fwrite库函数用于把数据写入到文件流。
#include <stdio.h>
size_t fwrite(void *ptr, size_t size, size_t nmemb, FILE *stream);

它的操作与fread相反,把ptr数组中的内容写入到stream文件流,写入的项数为nmemb,每项 大小为size,返回值为成功写入的项数(项的单位为size)。

fclose详解:

fclose库函数用于关闭指定的文件流,关闭时它会把尚未写到文件的内容都写出。
因为标准 库会对数据进行缓冲,所以需要使用fclose来确保数据被写出。
#include <unistd.h>
int close(int fd);

它的操作与fread相反,把ptr数组中的内容写入到stream文件流,写入的项数为nmemb,每项 大小为size,返回值为成功写入的项数(项的单位为size)。

fflush详解:

fflush函数用于把尚未写到文件的内容立即写出。常用于确保前面操作的数据被写 入到磁盘上。
fclose函数本身也包含了fflush的操作
#include <unistd.h>
int fflush(FILE *stream);

fseek详解:

fseek函数用于设置下一次读写函数操作的位置。
#include <unistd.h>
int fseek(FILE *stream, long offset, int whence);

其中的offset参数用于指定位置,whence参数则定义了offset的意义,whence的可取值如下:

SEEK_SET:offset是一个绝对位置。
SEEK_END:offset是以文件尾为参考点的相对位置。
SEEK_CUR:offset是以当前位置为参考点的相对位置。

文件操作(系统调用)

Linux提供的文件操作系统调用常用的有open、write、read、lseek、close等。
别人或许会问,为什么上面是fopen,这里确实open呢?
open 是系统调用 返回的是文件句柄,文件的句柄是文件在文件描述副表里的索引,fopen是C的库函数,返回的是一个指向文件结构的指针。
open是linux下的底层系统调用函数,fopen与freopen c/c++下的标准I/O库函数,带输入/输出缓冲。
一般用fopen打开普通文件,用open打开设备文件,fopen是标准c里的,而open是linux的系统调用.他们的层次不同.fopen可移植,open不能。

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);

Linux使用open函数来打开文件,并返回该文件对应的文件描述符。函数参数的具体说明如下:
pathname:要打开或创建的文件名;
flag:指定文件的打开方式,具体有以下参数,见下表 flag参数值。

表 flag参数值
标志位 含义
O_RDONLY 以只读的方式打开文件,该参数与O_WRONLY和O_RDWR只能三选一
O_WRONLY 以只写的方式打开文件
O_RDWR 以读写的方式打开文件
O_CREAT 创建一个新文件
O_APPEND 将数据写入到当前文件的结尾处
O_TRUNC 如果pathname文件存在,则清除文件内容
C库函数fopen的mode参数与系统调用open的flags参数有如下表中的等价关系。

表 fopen的mode与open的flags参数关系
fopen的mode参数 open的flags参数
r O_RDONLY
w O_WRONLY | O_CREAT | O_TRUNC
a O_WRONLY | O_CREAT | O_APPEND
r+ O_RDWR
w+ O_RDWR | O_CREAT | O_TRUNC
a+ O_RDWR | O_CREAT | O_APPEND

mode:当open函数的flag值设置为O_CREAT时,必须使用mode参数来设置文件 与用户相关的权限。mode可用的权限如下表所示,表中各个参数可使用”| “来组 合。
表 文件权限
\ 标志位 含义
当前用户 S_IRUSR 用户拥有读权限
\ S_IWUSR 用户拥有写权限
\ S_IXUSR 用户拥有执行权限
\ S_IRWXU 用户拥有读、写、执行权限
当前用户组 S_IRGRP 当前用户组的其他用户拥有读权限
\ S_IWGRP 当前用户组的其他用户拥有写权限
\ S_IXGRP 当前用户组的其他用户拥有执行权限
\ S_IRWXG 当前用户组的其他用户拥有读、写、执行权限
其他用户 S_IROTH 其他用户拥有读权限
\ S_IWOTH 其他用户拥有写权限
\ S_IXOTH 其他用户拥有执行权限
\ S_IROTH 其他用户拥有读、写、执行权限

read详解:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
ssize_t read(int fd, void *buf, size_t count);

read函数用于从文件中读取若干个字节的数据,保存到数据缓冲区buf中,并返 回实际读取的字节数,具体函数参数如下:
fd:文件对应的文件描述符,可以通过fopen函数获得。另外,当一个程序 运行时,Linux默认有0、1、2这三个已经打开的文件描述符,分别对应了标准输入、标准输出、标准错误输出,即可以直接访问这三种文件描述符;
buf:指向数据缓冲区的指针;
count:读取多少个字节的数据。

write详解:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
ssize_t write(int fd, const void *buf, size_t count);

write函数用于往文件写入内容,并返回实际写入的字节长度,具体函数参数如下:
fd:文件对应的文件描述符,可以通过fopen函数获得。
buf:指向数据缓冲区的指针;
count:往文件中写入多少个字节

close详解:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int close(int fd);

当我们完成对文件的操作之后,想要关闭该文件,可以调用close函数,来关闭该fd文件描述符对应的文件。

lseek详解:

lseek函数可以用与设置文件指针的位置,并返回文件指针相对于文件头 的位置。

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
off_t lseek(int fd, off_t offset, int whence);

它的用法与flseek一样,其中的offset参数用于指定位置,whence参数则定义了offset的意义,whence的可取值如下:
SEEK_SET:offset是一个绝对位置。
SEEK_END:offset是以文件尾为参考点的相对位置。
SEEK_CUR:offset是以当前位置为参考点的相对位置。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章