第一章 嵌入式应用编程:文件I/O基础

您所在的位置:网站首页 嵌入式的开发内容包括 第一章 嵌入式应用编程:文件I/O基础

第一章 嵌入式应用编程:文件I/O基础

2024-07-16 11:27:10| 来源: 网络整理| 查看: 265

文章目录 前言文件描述符open打开文件函数原型函数参数和返回值含义open函数使用示例 write写文件函数原型函数参数和返回值含义 read读文件函数原型函数参数和返回值含义 close关闭文件函数原型函数参数和返回值含义 lseek函数原型函数参数和返回值含义lseek函数使用示例

前言

本章主要介绍文件IO操作相关系统调用,一个通用的IO模型通常包括打开文件、读写文件、关闭文件这些基本操作,主要涉及到4个函数:open()、read()、write()以及close()。我们先来看一个简单地文件读写示例,应用程序代码如下所示:

#include #include #include #include int main(void) { char buff[1024]; int fd1, fd2; int ret; /*打开源文件src_file(只读方式) */ fd1 = open("./src_file", O_RDONLY); if (-1==fd1) return fd1; /*打开目标文件dest_file(只写方式) */ fd2 = open("./dest_file", O_WRONLY); if (-1 == fd2) { ret = fd2; goto out1; } /*读取源文件1KB数据到buff中*/ ret = read(fd1, buff, sizeof(buff)); if (-1==ret) goto out2; /*将buff中的数据写入目标文件*/ ret = write(fd2, buff, sizeof(buff)); if (-1==ret) goto out2; ret=0; out2: /*关闭目标文件*/ close(fd2); out1: /*关闭源文件*/ close(fd1); return ret; }

这段代码非常简单明了,代码所要实现的功能在注释当中已经描述得很清楚了,从源文件src_file中读取1KB数据,然后将其写入到目标文件dest_file中(这里假设当前目录下这两个文件都是存在的);在进行读写操作之前,首先调用open函数将源文件和目标文件打开,成功打开之后再调用read函数从源文件中读取1KB数据,然后再调用write函数将这1KB数据写入到目标文件中,至此,文件读写操作就完成了,读写操作完成之后,最后调用close函数关闭源文件和目标文件。

接下来我们给大家详细介绍这些函数以及相关的内容。

文件描述符

调用open函数会有一个返回值,譬如以下代码中的fd1和fd2,这是一个int类型的数据,在open函数执行成功的情况下,会返回一个非负整数,该返回值就是一个文件描述符(file descriptor),这说明文件描述符是一个非负整数;对于Linux内核而言,所有打开的文件都会通过文件描述符进行索引。

int fd1, fd2; fd1 = open("./src_file", O_RDONLY); fd2 = open("./dest_file", O_WRONLY);

对于一个进程来说,文件描述符是一种有限资源,文件描述符是从0开始分配的,譬如说进程中第一个被打开的文件对应的文件描述符是0、第二个文件是1、第三个文件是2、第4个文件是3……以此类推,所以由此可知,文件描述符数字最大值为1023(0~1023)。每一个被打开的文件在同一个进程中都有一个唯一的文件描述符,不会重复,如果文件被关闭后,它对应的文件描述符将会被释放,那么这个文件描述符将可以再次分配给其它打开的文件、与对应的文件绑定起来。

每次给打开的文件分配文件描述符都是从最小的没有被使用的文件描述符(0~1023)开始,当之前打开的文件被关闭之后,那么它对应的文件描述符会被释放,释放之后也就成为了一个没有被使用的文件描述符了。

不过,当我们在程序中,调用open函数打开文件的时候,分配的文件描述符一般都是从3开始。因为0、1、2这三个文件描述符已经默认被系统占用了,分别分配给了系统标注输入(0)、标注输出(1)以及标准错误(2)。

Tip: 一个进程可以打开多个文件,但是在Linux系统中,一个进程可以打开的文件数是有限制,并不是可以无限制打开很多的文件。在Linux系统下,我们可以通过ulimit命令来查看进程可打开的最大文件数。

ulimit -n

在这里插入图片描述

该最大值默认情况下是1024,也就意味着一个进程最多可以打开1024个文件,当然这个限制数其实是可以设置的。

open打开文件

在Linux系统中要操作一个文件,需要先打开该文件,得到文件描述符,然后再对文件进行相应的读写操作(或其他操作),最后在关闭该文件;open函数用于打开文件,当然除了打开已经存在的文件之外,还可以创建一个新的文件,函数原型如下所示:

函数原型 #include #include #include int open(const char *pathname, int flags); int open(const char *pathname, int flags, mode_t mode); 函数参数和返回值含义

pathname:字符串类型,用于标识需要打开或创建的文件,可以包含路径(绝对路径或相对路径)信息,譬如:"./src_file"(当前目录下的src_file文件)、"/home/dengtao/hello.c"等;如果pathname是一个符号链接,会对其进行解引用。

flags:调用open函数时需要提供的标志,包括文件访问模式标志以及其它文件相关标志,这些标志使用宏定义进行描述,都是常量,open函数提供了非常多的标志,我们传入flags参数时既可以单独使用某一个标志,也可以通过位或运算(|)将多个标志进行组合。

mode:此参数用于指定新建文件的访问权限,只有当flags参数中包含O_CREAT或O_TMPFILE标志时才有效(O_TMPFILE标志用于创建一个临时文件)。

返回值:成功将返回文件描述符,文件描述符是一个非负整数;失败将返回-1。

open函数flags参数值介绍

标志用途说明O_RDONLY以只读方式打开文件文件访问权限标志,传入的flags参数中必须要包含其中一种标志,而且只能包含一种。O_WRONLY以只写方式打开文件文件访问权限标志,传入的flags参数中必须要包含其中一种标志,而且只能包含一种。O_RDWR以可读可写方式打开文件文件访问权限标志,传入的flags参数中必须要包含其中一种标志,而且只能包含一种。O_CREAT如果pathname参数指向的文件不存在则创建此文件使用此标志时,调用open函数需要传入第3个参数mode,参数mode用于指定新建文件的访问权限,稍后将对此进行说明。open函数的第3个参数只有在使用了O_CREAT或O_TMPFILE标志时才有效。O_DIRECTORY如果pathname参数指向的不是一个目录,则调用open失败O_EXCL此标志一般结合O_CREAT标志一起使用,用于专门创建文件。在flags参数同时使用到了O_CREAT和O_EXCL标志的情况下,如果pathname参数指向的文件已经存在,则open函数返回错误。可以用于测试一个文件是否存在,如果不存在则创建此文件,如果存在则返回错误,这使得测试和创建两者成为一个原子操作。O_NOFOLLOW如果pathname参数指向的是一个符号链接,将不对其进行解引用,直接返回错误。不加此标志情况下,如果pathname参数是一个符号链接,会对其进行解引用。

以上给大家介绍了一些比较常用的标志,open函数的flags标志并不止这些,还有很多标志这里并没有给大家进行介绍,譬如O_APPEND、O_ASYNC、O_DSYNC、O_NOATIME、O_NONBLOCK、O_SYNC以及O_TRUNC等。

Tips:不同内核版本所支持的flags标志是存在差别的,譬如说新版本内核所支持的标志可能在老版本是不支持的,亦或者老版本支持的标志在新版本已经被取消、替代,man手册中对一些标志是从哪个版本开始支持的有简单地说明,读者可以自行阅读!

前面我们说过,flags参数时既可以单独使用某一个标志,也可以通过位或运算(|)将多个标志进行组合,譬如:

open("./src_file", O_RDONLY) //单独使用某一个标志 open("./src_file", O_RDONLY |O_NOFOLLOW) //多个标志组合

当我们调用open函数去新建一个文件时,也需要指定该文件的权限,而mode参数便用于指定此文件的权限,接下来看看我们该如何通过mode参数来表示文件的权限,首先mode参数的类型是mode_t,这是一个u32无符号整形数据,权限表示方法如下所示: 在这里插入图片描述

我们从低位从上看,每3个bit位分为一组,分别表示: O—这3个bit位用于表示其他用户的权限; G—这3个bit位用于表示同组用户(group)的权限,即与文件所有者有相同组ID的所有用户; U—这3个bit位用于表示文件所属用户的权限,即文件或目录的所属者; S—这3个bit位用于表示文件的特殊权限

Tips:open函数O_RDONLY、O_WRONLY以及O_RDWR这三个标志表示以什么方式去打开文件,譬如以只写方式打开(open函数得到的文件描述符只能对文件进行写操作,不能读)、以只读方式打开(open函数得到的文件描述符只能对文件进行读操作,不能写)、以可读可写方式打开(open函数得到的文件描述符可对文件进行读和写操作);与文件权限之间的联系,只有用户对该文件具有相应权限时,才可以使用对应的标志去打开文件,否则会打开失败!譬如,我们的程序对该文件只有只读权限,那么执行open函数使用O_RDWR或O_WRONLY标志将会失败。

关于文件权限表示方法的问题,以上就给大家介绍这么多,在实际编程中,我们可以直接使用Linux中已经定义好的宏,不同的宏定义表示不同的权限,如下所示:

宏定义说明S_IRUSR允许文件所属者读文件S_IWUSR允许文件所属者写文件S_IXUSR允许文件所属者执行文件S_IRWXU允许文件所属者读、写、执行文件S_IRGRP允许同组用户读文件S_IWGRP允许同组用户写文件S_IXGRP允许同组用户执行文件S_IRWXG允许同组用户读、写、执行文件S_IROTH允许其他用户读文件S_IWOTH允许其他用户写文件S_IXOTH允许其他用户执行文件S_IRWXO允许其他用户读、写、执行文件S_ISUIDset-user-ID(特殊权限)S_ISGIDset-group-ID(特殊权限)S_ISVTXsticky(特殊权限)

这些宏既可以单独使用,也可以通过位或运算将多个宏组合在一起,譬如:

S_IRUSR | S_IWUSR|S_IROTH open函数使用示例

(1)使用open函数打开一个已经存在的文件(例如当前目录下的app.c文件),使用只读方式打开:

int fd = open("./app.c", O_RDONLY) if (-1 == fd) return fd;

(2)使用open函数打开一个已经存在的文件(例如当前目录下的app.c文件),使用可读可写方式打开:

int fd = open("./app.c", O_RDWR) if (-1 == fd) return fd;

(3)使用open函数打开一个指定的文件(譬如/home/dengtao/hello),使用可读可写方式,如果该文件是一个符号链接文件,则不对其进行解引用,直接返回错误:

int fd = open("/home/dengtao/hello", O_RDWR | O_NOFOLLOW); if (-1 == fd) return fd;

(4)使用open函数打开一个指定的文件(譬如/home/dengtao/hello),如果该文件不存在则创建该文件,创建该文件时,将文件权限设置如下:

文件所属者拥有读、写、执行权限同组用户与其他用户只有读权限使用可读可写方式打开 int fd =open("/home/dengtao/hello", O_RDWR | O_CREAT,S_IRWXU | S_IRGRP | S_IROTH); if (-1 == fd) return fd; write写文件

调用write函数可向打开的文件写入数据,其函数原型如下所示:

函数原型 #include ssize_t write(int fd, const void *buf, size_t count);

首先使用write函数需要先包含unistd.h头文件。

函数参数和返回值含义

fd:文件描述符。关于文件描述符,前面已经给大家进行了简单地讲解,这里不再重述!我们需要将进行写操作的文件所对应的文件描述符传递给write函数。

buf:指定写入数据对应的缓冲区。

count:指定写入的字节数。

返回值:如果成功将返回写入的字节数(0表示未写入任何字节),如果此数字小于count参数,这不是错误,譬如磁盘空间已满,可能会发生这种情况;如果写入出错,则返回-1。

Tip:对于普通文件(我们一般操作的大部分文件都是普通文件,譬如常见的文本文件、二进制文件等),不管是读操作还是写操作,一个很重要的问题是:从文件的哪个位置开始进行读写操作?也就是IO操作所对应的位置偏移量,读写操作都是从文件的当前位置偏移量处开始,当然当前位置偏移量可以通过lseek系统调用进行设置,关于此函数后面再讲;默认情况下当前位置偏移量一般是0,也就是指向了文件起始位置,当调用read、write函数读写操作完成之后,当前位置偏移量也会向后移动对应字节数,譬如当前位置偏移量为1000个字节处,调用write()写入或read()读取500个字节之后,当前位置偏移量将会移动到1500个字节处。

read读文件

调用read函数可从打开的文件中读取数据,其函数原型如下所示:

函数原型 #include ssize_t read(int fd, void *buf, size_t count);

首先使用read函数需要先包含unistd.h头文件。

函数参数和返回值含义

fd:文件描述符。与write函数的fd参数意义相同。

buf:指定用于存储读取数据的缓冲区。

count:指定需要读取的字节数。

返回值:如果读取成功将返回读取到的字节数,实际读取到的字节数可能会小于count参数指定的字节数,也有可能会为0,譬如进行读操作时,当前文件位置偏移量已经到了文件末尾。实际读取到的字节数少于要求读取的字节数,譬如在到达文件末尾之前有30个字节数据,而要求读取100个字节,则read读取成功只能返回30;而下一次再调用read读,它将返回0(文件末尾)。

close关闭文件

可调用close函数关闭一个已经打开的文件,其函数原型如下所示:

函数原型 #include int close(int fd);

首先使用close函数需要先包含unistd.h头文件,当我们对文件进行IO操作完成之后,后续不再对文件 进行操作时,需要将文件关闭。

函数参数和返回值含义

fd:文件描述符,需要关闭的文件所对应的文件描述符。

返回值:如果成功返回0,如果失败则返回-1。

Tip:除了使用close函数显式关闭文件之外,在Linux系统中,当一个进程终止时,内核会自动关闭它打开的所有文件,也就是说在我们的程序中打开了文件,如果程序终止退出时没有关闭打开的文件,那么内核会自动将程序中打开的文件关闭。很多程序都利用了这一功能而不显式地用close关闭打开的文件。

显式关闭不再需要的文件描述符往往是良好的编程习惯,会使代码在后续修改时更具有可读性,也更可靠,进而言之,文件描述符是有限资源,当不再需要时必须将其释放、归还于系统。

lseek

对于每个打开的文件,系统都会记录它的读写位置偏移量,我们也把这个读写位置偏移量称为读写偏移量,记录了文件当前的读写位置,当调用read()或write()函数对文件进行读写操作时,就会从当前读写位置偏移量开始进行数据读写。

读写偏移量用于指示read()或write()函数操作时文件的起始位置,会以相对于文件头部的位置偏移量来表示,文件第一个字节数据的位置偏移量为0。

打开文件时,会将读写偏移量设置为指向文件开始位置处,以后每次调用read()、write()将自动对其进行调整,以指向已读或已写数据后的下一字节,因此,连续的调用read()和write()函数将使得读写按顺序递增,对文件进行操作。我们先来看看lseek函数的原型,如下所示:

函数原型 #include #include off_t lseek(int fd, off_t offset, int whence);

首先调用lseek函数需要包含和两个头文件。

函数参数和返回值含义

fd:文件描述符。

offset:偏移量,以字节为单位。

whence:用于定义参数offset偏移量对应的参考值,该参数为下列其中一种(宏定义):

SEEK_SET:读写偏移量将指向offset字节位置处(从文件头部开始算);SEEK_CUR:读写偏移量将指向当前位置偏移量+offset字节位置处,offset可以为正、也可以为负,如果是正数表示往后偏移,如果是负数则表示往前偏移;SEEK_END:读写偏移量将指向文件末尾+offset字节位置处,同样offset可以为正、也可以为负, 如果是正数表示往后偏移、如果是负数则表示往前偏移。

返回值:成功将返回从文件头部开始算起的位置偏移量(字节为单位),也就是当前的读写位置;发生 错误将返回-1。

lseek函数使用示例

(1)将读写位置移动到文件开头处:

off_t off = lseek(fd, 0, SEEK_SET); if (-1 == off) return -1;

(2)将读写位置移动到文件末尾:

off_t off = lseek(fd, 0, SEEK_END); if (-1 == off) return -1;

(3)将读写位置移动到偏移文件开头100个字节处:

off_t off = lseek(fd, 100, SEEK_SET); if (-1 == off) return -1;

(4)获取当前读写位置偏移量:

/* 函数执行成功将返回文件当前读写位置。 */ off_t off = lseek(fd, 0, SEEK_CUR); if (-1 == off) return -1;


【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭