嵌入式Linux应用程序开发详解-第6 章 文件IO编程.pdf
《嵌入式Linux应用程序开发详解-第6 章 文件IO编程.pdf》由会员分享,可在线阅读,更多相关《嵌入式Linux应用程序开发详解-第6 章 文件IO编程.pdf(44页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、华清远见嵌入式培训专家 http:/ 华清远见培训教材 “黑色经典”系列之嵌入式“黑色经典”系列之嵌入式 Linux 应用程序开发详解应用程序开发详解 第 6 章 文件 I/O 编程 本章目标 在搭建起嵌入式开发环境之后,从本章开始,读者将真正开始学习嵌入式 Linux 的应用开发。由于嵌入式 Linux 是经 Linux 裁减而来的,它的系统调用及用户编程接口 API 与 Linux基本是一致的,因此,在以后的章节中,笔者将首先介绍 Linux 中相关内容的基本编程开发,主要讲解与嵌入式 Linux 中一致的部分,然后再将程序移植到嵌入式的开发板上运行。因此,没有开发板的读者也可以先在 Li
2、nux 上开发相关应用程序,这对以后进入嵌入式 Linux 的实际开发是十分有帮助的。本章主要讲解文件 I/O 相关开发,经过本章的学习,读者将会掌握以下内容。掌握 Linux 中系统调用的基本概念 掌握 Linux 中用户编程接口(API)及系统命令的相互关系 掌握文件描述符的概念 掌握 Linux 下文件相关的不带缓存 I/O 函数的使用 掌握 Linux 下设备文件读写方法 掌握 Linux 中对串口的操作 熟悉 Linux 中标准文件 I/O 函数的使用 QQ:313638714http:/华清远见嵌入式培训专家 http:/ 华清远见培训教材 6.1 Linux 系统调用及用户编程接
3、口(API)由于本章是讲解Linux 编程开发的第1 章,因此希望读者更加明确Linux 系统调用和用户编程接口(API)的概念。在了解了这些之后,会对Linux 以及Linux 的应用编程有更深入地理解。6.1.1 系统调用 所谓系统调用是指操作系统提供给用户程序调用的一组“特殊”接口,用户程序可以通过这组“特殊”接口来获得操作系统内核提供的服务。例如用户可以通过进程控制相关的系统调用来创建进程、实现进程调度、进程管理等。在这里,为什么用户程序不能直接访问系统内核提供的服务呢?这是由于在 Linux 中,为了更好地保护内核空间,将程序的运行空间分为内核空间和用户空间(也就是常称的内核态和用户
4、态),它们分别运行在不同的级别上,在逻辑上是相互隔离的。因此,用户进程在通常情况下不允许访问内核数据,也无法使用内核函数,它们只能在用户空间操作用户数据,调用用户空间的函数。但是,在有些情况下,用户空间的进程需要获得一定的系统服务(调用内核空间程序),这时操作系统就必须利用系统提供给用户的“特殊接口”系统调用规定用户进程进入内核空间的具体位置。进行系统调用时,程序运行空间需要从用户空间进入内核空间,处理完后再返回到用户空间。Linux 系统调用部分是非常精简的系统调用(只有 250 个左右),它继承了 UNIX 系统调用中最基本和最有用的部分。这些系统调用按照功能逻辑大致可分为进程控制、进程间
5、通信、文件系统控制、系统控制、存储管理、网络管理、socket 控制、用户管理等几类。6.1.2 用户编程接口(API)前面讲到的系统调用并不是直接与程序员进行交互的,它仅仅是一个通过软中断机制向内核提交请求,以获取内核服务的接口。在实际使用中程序员调用的通常是用户编程接口API,也就是本书后面要讲到的 API 函数。但并不是所有的函数都一一对应一个系统调用,有时,一个 API 函数会需要几个系统调用来共同完成函数的功能,甚至还有一些 API 函数不需要调用相应的系统调用(因此它所完成的不是内核提供的服务)。在Linux 中,用户编程接口(API)遵循了在UNIX中最流行的应用编程界面标准PO
6、SIX标准。POSIX 标准是由IEEE和ISO/IEC共同开发的标准系统。该标准基于当时现有的UNIX 实践和经验,描述了操作系统的系统调用编程接口(实际上就是 API),用于保证应用程序可以在源代码一级上在多种操作系统上移植运行。这些系统调用编程接口主要是通过C库(libc)实现的。6.1.3 系统命令 以上讲解了系统调用、用户编程接口(API)的概念,分析了它们之间的相互关系,那么,读者在第 2 章中学到的那么多的 Shell 系统命令与它们之间又是怎样的关系呢?系统命令相对 API 更高了一层,它实际上一个可执行程序,它的内部引用了用户编程接口(API)来实现相应的功能。它们之间的关系
7、如下图 6.1 所示。QQ:313638714http:/ 嵌入式 Linux 应用程序开发详解第 6 章、文件 IO 编程 华清远见培训教材 用户编程接口 API 系统命令 系统调用 内核空间 用户空间 图 6.1 系统调用、API及系统命令之间的关系 6.2 Linux 中文件及文件描述符概述 正如第 1 章中所述,在 Linux 中对目录和设备的操作都等同于文件的操作,因此,大大简化了系统对不同设备的处理,提高了效率。Linux 中的文件主要分为 4 种:普通文件、目录文件、链接文件和设备文件。那么,内核如何区分和引用特定的文件呢?这里用到的就是一个重要的概念文件描述符。对于 Linux
8、 而言,所有对设备和文件的操作都使用文件描述符来进行的。文件描述符是一个非负的整数,它是一个索引值,并指向内核中每个进程打开文件的记录表。当打开一个现存文件或创建一个新文件时,内核就向进程返回一个文件描述符;当需要读写文件时,也需要把文件描述符作为参数传递给相应的函数。通常,一个进程启动时,都会打开 3 个文件:标准输入、标准输出和标准出错处理。这3 个文件分别对应文件描述符为 0、1 和 2(也就是宏替换 STDIN_FILENO、STDOUT_FILENO和 STDERR_FILENO,鼓励读者使用这些宏替换)。基于文件描述符的 I/O 操作虽然不能移植到类 Linux 以外的系统上去(如
9、 Windows),但它往往是实现某些 I/O 操作的惟一途径,如 Linux 中低级文件操作函数、多路 I/O、TCP/IP 套接字编程接口等。同时,它们也很好地兼容 POSIX 标准,因此,可以很方便地移植到任何 POSIX 平台上。基于文件描述符的 I/O 操作是 Linux 中最常用的操作之一,希望读者能够很好地掌握。6.3 不带缓存的文件 I/O 操作 本节主要介绍不带缓存的文件 I/O 操作,主要用到 5 个函数:open、read、write、lseek和 close。这里的不带缓存是指每一个函数都只调用系统中的一个函数。这些函数虽然不是QQ:313638714http:/华清远
10、见嵌入式培训专家 http:/ 华清远见培训教材 ANSI C 的组成部分,但是是 POSIX 的组成部分。6.3.1 open 和 close(1)open 和 close 函数说明 open 函数是用于打开或创建文件,在打开或创建文件时可以指定文件的属性及用户的权限等各种参数。close 函数是用于关闭一个打开文件。当一个进程终止时,它所有已打开的文件都由内核自动关闭,很多程序都使用这一功能而不显示地关闭一个文件。(2)open 和 close 函数格式 open 函数的语法格式如表 6.1 所示。表 6.1 open 函数语法要点 所需头文件#include /提供类型 pid_t 的定
11、义#include#include 续表 函数原型 int open(const char*pathname,flags,int perms)pathname 被打开的文件名(可包括路径名)O_RDONLY:只读方式打开文件 O_WRONLY:可写方式打开文件 O_RDWR:读写方式打开文件 O_CREAT:如果该文件不存在,就创建一个新的文件,并用第三个参数为其设置权限 O_EXCL:如果使用 O_CREAT 时文件存在,则可返回错误消息。这一参数可测试文件是否存在 O_NOCTTY:使用本参数时,如文件为终端,那么终端不可以作为调用 open()系统调用的那个进程的控制终端 O_TRUNC
12、:如文件已经存在,并且以只读或只写成功打开,那么会先全部删除文件中原有数据 flag:文件打 开 的 方式 O+APPEND:以添加方式打开文件,在打开文件的同时,文件指针指向文件的末尾 函数传入值 perms 被打开文件的存取权限,为 8 进制表示法 函数返回值 成功:返回文件描述符 失败:1 在 open 函数中,flag 参数可通过“|”组合构成,但前 3 个函数不能相互组合。perms 是文件的存取权限,采用 8 进制表示法,相关内容读者可参见第 2 章。close 函数的语法格式如下表 6.2 所示。表 6.2 close 函数语法要点 QQ:313638714http:/ 嵌入式
13、Linux 应用程序开发详解第 6 章、文件 IO 编程 华清远见培训教材 所需头文件#include 函数原型 int close(int fd)函数输入值 fd:文件描述符 函数返回值 0:成功 1:出错 (3)open 和 close 函数使用实例 下面实例中的 open 函数带有 3 个 flag 参数:O_CREAT、O_TRUNC 和 O_WRONLY,这样就可以对不同的情况指定相应的处理方法。另外,这里对该文件的权限设置为 0600。其源码如下所示:/*open.c*/#include#include#include#include#include#include int mai
14、n(void)int fd;/*调用 open 函数,以可读写的方式打开,注意选项可以用“|”符号连接*/if(fd=open(/tmp/hello.c,O_CREAT|O_TRUNC|O_WRONLY,0600)0)perror(open:);exit(1);else printf(Open file:hello.c%dn,fd);if(close(fd)0)perror(close:);exit(1);else printf(Close );exit(0);root(none)1#./open QQ:313638714http:/华清远见嵌入式培训专家 http:/ 华清远见培训教材 Op
15、en file:hello.c 3 Close hello.c root(none)tmp#ls-l|grep hello.c-rw-1 root root 0 Dec 4 00:59 hello.c 经过交叉编译后,将文件下载到目标板,则该可执行文件运行后就能在目录/tmp 下新建一个 hello.c 的文件,其权限为 0600。注意 open 函数返回的文件描述符一定是最小的未用文件描述符。由于一个进程在启动时自动打开了0、1、2 三个文件描述符,因此,该文件运行结果中返回的文件描述符为 3。读者可以尝试在调用 open 函数之前,加依据 close(0),则此后在 open 函数时返回的
16、文件描述符为 0(若关闭文件描述符 1,则在执行时会由于没有标准输出文件而无法输出)。6.3.2 read、write 和 lseek(1)read、write 和 lseek 函数作用 read 函数是用于将指定的文件描述符中读出数据。当从终端设备文件中读出数据时,通常一次最多读一行。write 函数是用于向打开的文件写数据,写操作从文件的当前位移量处开始。若磁盘已满或超出该文件的长度,则 write 函数返回失败。lseek 函数是用于在指定的文件描述符中将文件指针定位到相应的位置。(2)read 和 write 函数格式 read 函数的语法格式如下表 6.3 所示。表 6.3 read
17、 函数语法要点 所需头文件#include 函数原型 ssize_t read(int fd,void*buf,size_t count)fd:文件描述符 buf:指定存储器读出数据的缓冲区 函数传入值 count:指定读出的字节数 函数返回值 成功:读到的字节数 0:已到达文件尾 1:出错 在读普通文件时,若读到要求的字节数之前已到达文件的尾部,则返回的字节数会小于希望读出的字节数。write 函数的语法格式如下表 6.4 所示。表 6.4 write 函数语法要点 所需头文件#include 函数原型 ssize_t write(int fd,void*buf,size_t count)Q
18、Q:313638714http:/ 嵌入式 Linux 应用程序开发详解第 6 章、文件 IO 编程 华清远见培训教材 fd:文件描述符 buf:指定存储器写入数据的缓冲区 函数传入值 count:指定读出的字节数 函数返回值 成功:已写的字节数 1:出错 在写普通文件时,写操作从文件的当前位移处开始。lseek 函数的语法格式如下表 6.5 所示。表 6.5 lseek 函数语法要点 所需头文件#include#include 函数原型 off_t lseek(int fd,off_t offset,int whence)fd:文件描述符 函数传入值 offset:偏移量,每一读写操作所需要
19、移动的距离,单位是字节的数量,可正可负(向前移,向后移)续表 SEEK_SET:当前位置为文件的开头,新位置为偏移量的大小 SEEK_CUR:当前位置为文件指针的位置,新位置为当前位置加上偏移量 whence:当前位置的基点 SEEK_END:当前位置为文件的结尾,新位置为文件的大小加上偏移量的大小 函数返回值 成功:文件的当前位移 1:出错 (3)函数使用实例 该示例程序首先打开上一节中创建的文件,然后对此文件进行读写操作(记得要将文件打开属性改为可读写,将文件权限也做相应更改)。接着,写入“Hello!Im writing to this file!”,此时文件指针位于文件尾部。接着在使用
20、 lseek 函数将文件指针移到文件开始处,并读出 10个字节并将其打印出来。程序源代码如下所示:/*write.c*/#include#include#include#include#include#include#include#define MAXSIZE QQ:313638714http:/华清远见嵌入式培训专家 http:/ 华清远见培训教材 int main(void)int i,fd,size,len;char*buf=Hello!Im writing to this file!;char buf_r10;len=strlen(buf);/*首先调用 open 函数,并指定相应的
21、权限*/if(fd=open(/tmp/hello.c,O_CREAT|O_TRUNC|O_RDWR,0666)0)perror(open:);exit(1);else printf(open file:hello.c%dn,fd);/*调用 write 函数,将 buf 中的内容写入到打开的文件中*/if(size=write(fd,buf,len)0)perror(write:);exit(1);else printf(Write:%sn,buf);/*调用 lsseek 函数将文件指针移到文件起始,并读出文件中的 10 个字节*/lseek(fd,0,SEEK_SET);if(size=
22、read(fd,buf_r,10)0)perror(read:);exit(1);else printf(read form file:%sn,buf_r);if(close(fd)0)perror(close:);exit(1);else printf(Close );exit(0);root(none)1#./write open file:hello.c 3 QQ:313638714http:/ 嵌入式 Linux 应用程序开发详解第 6 章、文件 IO 编程 华清远见培训教材 Write:Hello!Im writing to this file!read form file:Hell
23、o!Im Close hello.c root(none)1#cat/tmp/hello.c Hello!Im writing to this file!6.3.3 fcntl(1)fcntl 函数说明 前面的这 5 个基本函数实现了文件的打开、读写等基本操作,这一节将讨论的是,在文件已经共享的情况下如何操作,也就是当多个用户共同使用、操作一个文件的情况,这时,Linux 通常采用的方法是给文件上锁,来避免共享的资源产生竞争的状态。文件锁包括建议性锁和强制性锁。建议性锁要求每个上锁文件的进程都要检查是否有锁存在,并且尊重已有的锁。在一般情况下,内核和系统都不使用建议性锁。强制性锁是由内核执行的
24、锁,当一个文件被上锁进行写入操作的时候,内核将阻止其他任何文件对其进行读写操作。采用强制性锁对性能的影响很大,每次读写操作都必须检查是否有锁存在。在 Linux 中,实现文件上锁的函数有 lock 和 fcntl,其中 flock 用于对文件施加建议性锁,而 fcntl 不仅可以施加建议性锁,还可以施加强制锁。同时,fcntl 还能对文件的某一记录进行上锁,也就是记录锁。记录锁又可分为读取锁和写入锁,其中读取锁又称为共享锁,它能够使多个进程都能在文件的同一部分建立读取锁。而写入锁又称为排斥锁,在任何时刻只能有一个进程在文件的某个部分上建立写入锁。当然,在文件的同一部分不能同时建立读取锁和写入锁
25、。注意 fcntl 是一个非常通用的函数,它还可以改变文件进程各方面的属性,在本节中,主要介绍它建立记录锁的方法,关于它其他用户感兴趣的读者可以参看 fcntl 手册。(2)fcntl 函数格式 用于建立记录锁的 fcntl 函数格式如表 6.6 所示。表 6.6 fcntl 函数语法要点 所需头文件#include#include#include 函数原型 int fcnt1(int fd,int cmd,struct flock*lock)fd:文件描述符 F_DUPFD:复制文件描述符 F_GETFD:获得 fd 的 close-on-exec 标志,若标志未设置,则文件经过 exec函
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 嵌入式Linux应用程序开发详解-第6 文件IO编程 嵌入式 Linux 应用程序 开发 详解 文件 IO 编程
限制150内