您好,欢迎访问三七文档
当前位置:首页 > 商业/管理/HR > 企业文化 > 嵌入式Linux应用程序开发详解-第6 章 文件IO编程
华清远见——嵌入式培训专家应用开发班培训教材“黑色经典”系列之《嵌入式Linux应用程序开发详解》第6章文件I/O编程本章目标在搭建起嵌入式开发环境之后,从本章开始,读者将真正开始学习嵌入式Linux的应用开发。由于嵌入式Linux是经Linux裁减而来的,它的系统调用及用户编程接口API与Linux基本是一致的,因此,在以后的章节中,笔者将首先介绍Linux中相关内容的基本编程开发,主要讲解与嵌入式Linux中一致的部分,然后再将程序移植到嵌入式的开发板上运行。因此,没有开发板的读者也可以先在Linux上开发相关应用程序,这对以后进入嵌入式Linux的实际开发是十分有帮助的。本章主要讲解文件I/O相关开发,经过本章的学习,读者将会掌握以下内容。掌握Linux中系统调用的基本概念掌握Linux中用户编程接口(API)及系统命令的相互关系掌握文件描述符的概念掌握Linux下文件相关的不带缓存I/O函数的使用掌握Linux下设备文件读写方法掌握Linux中对串口的操作熟悉Linux中标准文件I/O函数的使用QQ:313638714华清远见——嵌入式培训专家系统调用及用户编程接口(API)由于本章是讲解Linux编程开发的第1章,因此希望读者更加明确Linux系统调用和用户编程接口(API)的概念。在了解了这些之后,会对Linux以及Linux的应用编程有更深入地理解。6.1.1系统调用所谓系统调用是指操作系统提供给用户程序调用的一组“特殊”接口,用户程序可以通过这组“特殊”接口来获得操作系统内核提供的服务。例如用户可以通过进程控制相关的系统调用来创建进程、实现进程调度、进程管理等。在这里,为什么用户程序不能直接访问系统内核提供的服务呢?这是由于在Linux中,为了更好地保护内核空间,将程序的运行空间分为内核空间和用户空间(也就是常称的内核态和用户态),它们分别运行在不同的级别上,在逻辑上是相互隔离的。因此,用户进程在通常情况下不允许访问内核数据,也无法使用内核函数,它们只能在用户空间操作用户数据,调用用户空间的函数。但是,在有些情况下,用户空间的进程需要获得一定的系统服务(调用内核空间程序),这时操作系统就必须利用系统提供给用户的“特殊接口”——系统调用规定用户进程进入内核空间的具体位置。进行系统调用时,程序运行空间需要从用户空间进入内核空间,处理完后再返回到用户空间。Linux系统调用部分是非常精简的系统调用(只有250个左右),它继承了UNIX系统调用中最基本和最有用的部分。这些系统调用按照功能逻辑大致可分为进程控制、进程间通信、文件系统控制、系统控制、存储管理、网络管理、socket控制、用户管理等几类。6.1.2用户编程接口(API)前面讲到的系统调用并不是直接与程序员进行交互的,它仅仅是一个通过软中断机制向内核提交请求,以获取内核服务的接口。在实际使用中程序员调用的通常是用户编程接口——API,也就是本书后面要讲到的API函数。但并不是所有的函数都一一对应一个系统调用,有时,一个API函数会需要几个系统调用来共同完成函数的功能,甚至还有一些API函数不需要调用相应的系统调用(因此它所完成的不是内核提供的服务)。在Linux中,用户编程接口(API)遵循了在UNIX中最流行的应用编程界面标准——POSIX标准。POSIX标准是由IEEE和ISO/IEC共同开发的标准系统。该标准基于当时现有的UNIX实践和经验,描述了操作系统的系统调用编程接口(实际上就是API),用于保证应用程序可以在源代码一级上在多种操作系统上移植运行。这些系统调用编程接口主要是通过C库(libc)实现的。6.1.3系统命令以上讲解了系统调用、用户编程接口(API)的概念,分析了它们之间的相互关系,那么,读者在第2章中学到的那么多的Shell系统命令与它们之间又是怎样的关系呢?系统命令相对API更高了一层,它实际上一个可执行程序,它的内部引用了用户编程接口(API)来实现相应的功能。它们之间的关系如下图6.1所示。QQ:313638714《嵌入式Linux应用程序开发详解》——第6章、文件IO编程华清远见嵌入式Linux应用开发班培训教材用户编程接口API系统命令系统调用内核空间用户空间图6.1系统调用、API及系统命令之间的关系6.2Linux中文件及文件描述符概述正如第1章中所述,在Linux中对目录和设备的操作都等同于文件的操作,因此,大大简化了系统对不同设备的处理,提高了效率。Linux中的文件主要分为4种:普通文件、目录文件、链接文件和设备文件。那么,内核如何区分和引用特定的文件呢?这里用到的就是一个重要的概念——文件描述符。对于Linux而言,所有对设备和文件的操作都使用文件描述符来进行的。文件描述符是一个非负的整数,它是一个索引值,并指向内核中每个进程打开文件的记录表。当打开一个现存文件或创建一个新文件时,内核就向进程返回一个文件描述符;当需要读写文件时,也需要把文件描述符作为参数传递给相应的函数。通常,一个进程启动时,都会打开3个文件:标准输入、标准输出和标准出错处理。这3个文件分别对应文件描述符为0、1和2(也就是宏替换STDIN_FILENO、STDOUT_FILENO和STDERR_FILENO,鼓励读者使用这些宏替换)。基于文件描述符的I/O操作虽然不能移植到类Linux以外的系统上去(如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:313638714华清远见——嵌入式培训专家的组成部分,但是是POSIX的组成部分。6.3.1open和close(1)open和close函数说明open函数是用于打开或创建文件,在打开或创建文件时可以指定文件的属性及用户的权限等各种参数。close函数是用于关闭一个打开文件。当一个进程终止时,它所有已打开的文件都由内核自动关闭,很多程序都使用这一功能而不显示地关闭一个文件。(2)open和close函数格式open函数的语法格式如表6.1所示。表6.1open函数语法要点所需头文件#includesys/types.h//提供类型pid_t的定义#includesys/stat.h#includefcntl.h续表函数原型intopen(constchar*pathname,flags,intperms)pathname被打开的文件名(可包括路径名)O_RDONLY:只读方式打开文件O_WRONLY:可写方式打开文件O_RDWR:读写方式打开文件O_CREAT:如果该文件不存在,就创建一个新的文件,并用第三个参数为其设置权限O_EXCL:如果使用O_CREAT时文件存在,则可返回错误消息。这一参数可测试文件是否存在O_NOCTTY:使用本参数时,如文件为终端,那么终端不可以作为调用open()系统调用的那个进程的控制终端O_TRUNC:如文件已经存在,并且以只读或只写成功打开,那么会先全部删除文件中原有数据flag:文件打开的方式O+APPEND:以添加方式打开文件,在打开文件的同时,文件指针指向文件的末尾函数传入值perms被打开文件的存取权限,为8进制表示法函数返回值成功:返回文件描述符失败:-1在open函数中,flag参数可通过“|”组合构成,但前3个函数不能相互组合。perms是文件的存取权限,采用8进制表示法,相关内容读者可参见第2章。close函数的语法格式如下表6.2所示。表6.2close函数语法要点QQ:313638714《嵌入式Linux应用程序开发详解》——第6章、文件IO编程华清远见嵌入式Linux应用开发班培训教材所需头文件#includeunistd.h函数原型intclose(intfd)函数输入值fd:文件描述符函数返回值0:成功-1:出错(3)open和close函数使用实例下面实例中的open函数带有3个flag参数:O_CREAT、O_TRUNC和O_WRONLY,这样就可以对不同的情况指定相应的处理方法。另外,这里对该文件的权限设置为0600。其源码如下所示:/*open.c*/#includeunistd.h#includesys/types.h#includesys/stat.h#includefcntl.h#includestdlib.h#includestdio.hintmain(void){intfd;/*调用open函数,以可读写的方式打开,注意选项可以用“|”符号连接*/if((fd=open(/tmp/hello.c,O_CREAT|O_TRUNC|O_WRONLY,0600))0){perror(open:);exit(1);}else{printf(Openfile:hello.c%d\n,fd);}if(close(fd)0){perror(close:);exit(1);}elseprintf(Closehello.c\n);exit(0);}[root@(none)1]#./openQQ:313638714华清远见——嵌入式培训专家[root@(none)tmp]#ls-l|grephello.c-rw-------1rootroot0Dec400:59hello.c经过交叉编译后,将文件下载到目标板,则该可执行文件运行后就能在目录/tmp下新建一个hello.c的文件,其权限为0600。注意open函数返回的文件描述符一定是最小的未用文件描述符。由于一个进程在启动时自动打开了0、1、2三个文件描述符,因此,该文件运行结果中返回的文件描述符为3。读者可以尝试在调用open函数之前,加依据close(0),则此后在open函数时返回的文件描述符为0(若关闭文件描述符1,则在执行时会由于没有标准输出文件而无法输出)。6.3.2read、write和lseek(1)read、write和lseek函数作用read函数是用于将指定的文件描述符中读出数据。当从终端设备文件中读出数据时,通常一次最多读一行。write函数是用于向打开的文件写数据,写操作从文件的当前位移量处开始。若磁盘已满或超出该文件的长度,则write函数返回失败。lseek函数是用于在指定的文件描述符中将文件指针定位到相应的位置。(2)read和write函数格式read函数的语法格式如下表6.3所示。表6.3read函数语法要点所需头文件#includeunistd.h函数原型ssize_tread(intfd,void*buf,size_tcount)fd:文件描述符buf:指定存储器读出数据的缓冲区函数传入值count:指定读出的字节数函数返回值成功:读到的字节数0:已到达文件尾
本文标题:嵌入式Linux应用程序开发详解-第6 章 文件IO编程
链接地址:https://www.777doc.com/doc-4805480 .html