嵌入式Linux应用程序开发详解 第六章 文件IO编程.pdf

上传人:asd****56 文档编号:70322314 上传时间:2023-01-19 格式:PDF 页数:44 大小:668.10KB
返回 下载 相关 举报
嵌入式Linux应用程序开发详解 第六章 文件IO编程.pdf_第1页
第1页 / 共44页
嵌入式Linux应用程序开发详解 第六章 文件IO编程.pdf_第2页
第2页 / 共44页
点击查看更多>>
资源描述

《嵌入式Linux应用程序开发详解 第六章 文件IO编程.pdf》由会员分享,可在线阅读,更多相关《嵌入式Linux应用程序开发详解 第六章 文件IO编程.pdf(44页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。

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 函数的使用 更多电子书教程下载请登陆h t t p:/d o w n.z z b a i k e.c o m/e b o o k 本站提供的电子

3、书教程均为网上搜集,如果该教程涉及或侵害到您的版权请联系我们。华清远见嵌入式培训专家 http:/ 华清远见培训教材 6.1 Linux 系统调用及用户编程接口(API)由于本章是讲解Linux 编程开发的第1 章,因此希望读者更加明确Linux 系统调用和用户编程接口(API)的概念。在了解了这些之后,会对Linux 以及Linux 的应用编程有更深入地理解。6.1.1 系统调用 所谓系统调用是指操作系统提供给用户程序调用的一组“特殊”接口,用户程序可以通过这组“特殊”接口来获得操作系统内核提供的服务。例如用户可以通过进程控制相关的系统调用来创建进程、实现进程调度、进程管理等。在这里,为什么

4、用户程序不能直接访问系统内核提供的服务呢?这是由于在 Linux 中,为了更好地保护内核空间,将程序的运行空间分为内核空间和用户空间(也就是常称的内核态和用户态),它们分别运行在不同的级别上,在逻辑上是相互隔离的。因此,用户进程在通常情况下不允许访问内核数据,也无法使用内核函数,它们只能在用户空间操作用户数据,调用用户空间的函数。但是,在有些情况下,用户空间的进程需要获得一定的系统服务(调用内核空间程序),这时操作系统就必须利用系统提供给用户的“特殊接口”系统调用规定用户进程进入内核空间的具体位置。进行系统调用时,程序运行空间需要从用户空间进入内核空间,处理完后再返回到用户空间。Linux 系

5、统调用部分是非常精简的系统调用(只有 250 个左右),它继承了 UNIX 系统调用中最基本和最有用的部分。这些系统调用按照功能逻辑大致可分为进程控制、进程间通信、文件系统控制、系统控制、存储管理、网络管理、socket 控制、用户管理等几类。6.1.2 用户编程接口(API)前面讲到的系统调用并不是直接与程序员进行交互的,它仅仅是一个通过软中断机制向内核提交请求,以获取内核服务的接口。在实际使用中程序员调用的通常是用户编程接口API,也就是本书后面要讲到的 API 函数。但并不是所有的函数都一一对应一个系统调用,有时,一个 API 函数会需要几个系统调用来共同完成函数的功能,甚至还有一些 A

6、PI 函数不需要调用相应的系统调用(因此它所完成的不是内核提供的服务)。在Linux 中,用户编程接口(API)遵循了在UNIX中最流行的应用编程界面标准POSIX标准。POSIX 标准是由IEEE和ISO/IEC共同开发的标准系统。该标准基于当时现有的UNIX 实践和经验,描述了操作系统的系统调用编程接口(实际上就是 API),用于保证应用程序可以在源代码一级上在多种操作系统上移植运行。这些系统调用编程接口主要是通过C库(libc)实现的。6.1.3 系统命令 以上讲解了系统调用、用户编程接口(API)的概念,分析了它们之间的相互关系,那么,读者在第 2 章中学到的那么多的 Shell 系统

7、命令与它们之间又是怎样的关系呢?系统命令相对 API 更高了一层,它实际上一个可执行程序,它的内部引用了用户编程接口(API)来实现相应的功能。它们之间的关系如下图 6.1 所示。更多电子书教程下载请登陆h t t p:/d o w n.z z b a i k e.c o m/e b o o k 本站提供的电子书教程均为网上搜集,如果该教程涉及或侵害到您的版权请联系我们。嵌入式 Linux 应用程序开发详解第 6 章、文件 IO 编程 华清远见培训教材 用户编程接口 API 系统命令 系统调用 内核空间 用户空间 图 6.1 系统调用、API及系统命令之间的关系 6.2 Linux 中文件及文

8、件描述符概述 正如第 1 章中所述,在 Linux 中对目录和设备的操作都等同于文件的操作,因此,大大简化了系统对不同设备的处理,提高了效率。Linux 中的文件主要分为 4 种:普通文件、目录文件、链接文件和设备文件。那么,内核如何区分和引用特定的文件呢?这里用到的就是一个重要的概念文件描述符。对于 Linux 而言,所有对设备和文件的操作都使用文件描述符来进行的。文件描述符是一个非负的整数,它是一个索引值,并指向内核中每个进程打开文件的记录表。当打开一个现存文件或创建一个新文件时,内核就向进程返回一个文件描述符;当需要读写文件时,也需要把文件描述符作为参数传递给相应的函数。通常,一个进程启

9、动时,都会打开 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 中

10、最常用的操作之一,希望读者能够很好地掌握。6.3 不带缓存的文件 I/O 操作 本节主要介绍不带缓存的文件 I/O 操作,主要用到 5 个函数:open、read、write、lseek和 close。这里的不带缓存是指每一个函数都只调用系统中的一个函数。这些函数虽然不是 更多电子书教程下载请登陆h t t p:/d o w n.z z b a i k e.c o m/e b o o k 本站提供的电子书教程均为网上搜集,如果该教程涉及或侵害到您的版权请联系我们。华清远见嵌入式培训专家 http:/ 华清远见培训教材 ANSI C 的组成部分,但是是 POSIX 的组成部分。6.3.1 ope

11、n 和 close(1)open 和 close 函数说明 open 函数是用于打开或创建文件,在打开或创建文件时可以指定文件的属性及用户的权限等各种参数。close 函数是用于关闭一个打开文件。当一个进程终止时,它所有已打开的文件都由内核自动关闭,很多程序都使用这一功能而不显示地关闭一个文件。(2)open 和 close 函数格式 open 函数的语法格式如表 6.1 所示。表 6.1 open 函数语法要点 所需头文件#include /提供类型 pid_t 的定义#include#include 续表 函数原型 int open(const char*pathname,flags,in

12、t perms)pathname 被打开的文件名(可包括路径名)O_RDONLY:只读方式打开文件 O_WRONLY:可写方式打开文件 O_RDWR:读写方式打开文件 O_CREAT:如果该文件不存在,就创建一个新的文件,并用第三个参数为其设置权限 O_EXCL:如果使用 O_CREAT 时文件存在,则可返回错误消息。这一参数可测试文件是否存在 O_NOCTTY:使用本参数时,如文件为终端,那么终端不可以作为调用 open()系统调用的那个进程的控制终端 O_TRUNC:如文件已经存在,并且以只读或只写成功打开,那么会先全部删除文件中原有数据 flag:文件打 开 的 方式 O+APPEND:

13、以添加方式打开文件,在打开文件的同时,文件指针指向文件的末尾 函数传入值 perms 被打开文件的存取权限,为 8 进制表示法 函数返回值 成功:返回文件描述符 失败:1 在 open 函数中,flag 参数可通过“|”组合构成,但前 3 个函数不能相互组合。perms 是文件的存取权限,采用 8 进制表示法,相关内容读者可参见第 2 章。close 函数的语法格式如下表 6.2 所示。表 6.2 close 函数语法要点 更多电子书教程下载请登陆h t t p:/d o w n.z z b a i k e.c o m/e b o o k 本站提供的电子书教程均为网上搜集,如果该教程涉及或侵害

14、到您的版权请联系我们。嵌入式 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#

15、include int main(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 更多电子书教程下载请登陆h t t p:/d o w n.z z

16、b a i k e.c o m/e b o o k 本站提供的电子书教程均为网上搜集,如果该教程涉及或侵害到您的版权请联系我们。华清远见嵌入式培训专家 http:/ 华清远见培训教材 Open 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 函数返回的文件描述符一定是最小的未用文件描述符。由于一个进程在

17、启动时自动打开了0、1、2 三个文件描述符,因此,该文件运行结果中返回的文件描述符为 3。读者可以尝试在调用 open 函数之前,加依据 close(0),则此后在 open 函数时返回的文件描述符为 0(若关闭文件描述符 1,则在执行时会由于没有标准输出文件而无法输出)。6.3.2 read、write 和 lseek(1)read、write 和 lseek 函数作用 read 函数是用于将指定的文件描述符中读出数据。当从终端设备文件中读出数据时,通常一次最多读一行。write 函数是用于向打开的文件写数据,写操作从文件的当前位移量处开始。若磁盘已满或超出该文件的长度,则 write 函数

18、返回失败。lseek 函数是用于在指定的文件描述符中将文件指针定位到相应的位置。(2)read 和 write 函数格式 read 函数的语法格式如下表 6.3 所示。表 6.3 read 函数语法要点 所需头文件#include 函数原型 ssize_t read(int fd,void*buf,size_t count)fd:文件描述符 buf:指定存储器读出数据的缓冲区 函数传入值 count:指定读出的字节数 函数返回值 成功:读到的字节数 0:已到达文件尾 1:出错 在读普通文件时,若读到要求的字节数之前已到达文件的尾部,则返回的字节数会小于希望读出的字节数。write 函数的语法格

19、式如下表 6.4 所示。表 6.4 write 函数语法要点 所需头文件#include 函数原型 ssize_t write(int fd,void*buf,size_t count)更多电子书教程下载请登陆h t t p:/d o w n.z z b a i k e.c o m/e b o o k 本站提供的电子书教程均为网上搜集,如果该教程涉及或侵害到您的版权请联系我们。嵌入式 Linux 应用程序开发详解第 6 章、文件 IO 编程 华清远见培训教材 fd:文件描述符 buf:指定存储器写入数据的缓冲区 函数传入值 count:指定读出的字节数 函数返回值 成功:已写的字节数 1:出错

20、 在写普通文件时,写操作从文件的当前位移处开始。lseek 函数的语法格式如下表 6.5 所示。表 6.5 lseek 函数语法要点 所需头文件#include#include 函数原型 off_t lseek(int fd,off_t offset,int whence)fd:文件描述符 函数传入值 offset:偏移量,每一读写操作所需要移动的距离,单位是字节的数量,可正可负(向前移,向后移)续表 SEEK_SET:当前位置为文件的开头,新位置为偏移量的大小 SEEK_CUR:当前位置为文件指针的位置,新位置为当前位置加上偏移量 whence:当前位置的基点 SEEK_END:当前位置为文

21、件的结尾,新位置为文件的大小加上偏移量的大小 函数返回值 成功:文件的当前位移 1:出错 (3)函数使用实例 该示例程序首先打开上一节中创建的文件,然后对此文件进行读写操作(记得要将文件打开属性改为可读写,将文件权限也做相应更改)。接着,写入“Hello!Im writing to this file!”,此时文件指针位于文件尾部。接着在使用 lseek 函数将文件指针移到文件开始处,并读出 10个字节并将其打印出来。程序源代码如下所示:/*write.c*/#include#include#include#include#include#include#include#define MAXS

22、IZE 更多电子书教程下载请登陆h t t p:/d o w n.z z b a i k e.c o m/e b o o k 本站提供的电子书教程均为网上搜集,如果该教程涉及或侵害到您的版权请联系我们。华清远见嵌入式培训专家 http:/ 华清远见培训教材 int main(void)int i,fd,size,len;char*buf=Hello!Im writing to this file!;char buf_r10;len=strlen(buf);/*首先调用 open 函数,并指定相应的权限*/if(fd=open(/tmp/hello.c,O_CREAT|O_TRUNC|O_RDW

23、R,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=read(fd,buf_r,10)0)perror(read:);exit(1);else pri

24、ntf(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 更多电子书教程下载请登陆h t t p:/d o w n.z z b a i k e.c o m/e b o o k 本站提供的电子书教程均为网上搜集,如果该教程涉及或侵害到您的版权请联系我们。嵌入式 Linux 应用程序开发详解第 6 章、文件 IO 编程 华清远见培训教材 Write:Hello!Im writing to

25、this file!read form file:Hello!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 通常采用的方法是给文件上锁,来避免共享的资源产生竞争的状态。文件锁包括建议性锁和强制性锁。建议性锁要求每个上锁文件的进程都要检查是否有锁存在,并且尊重已有的锁。在一般

26、情况下,内核和系统都不使用建议性锁。强制性锁是由内核执行的锁,当一个文件被上锁进行写入操作的时候,内核将阻止其他任何文件对其进行读写操作。采用强制性锁对性能的影响很大,每次读写操作都必须检查是否有锁存在。在 Linux 中,实现文件上锁的函数有 lock 和 fcntl,其中 flock 用于对文件施加建议性锁,而 fcntl 不仅可以施加建议性锁,还可以施加强制锁。同时,fcntl 还能对文件的某一记录进行上锁,也就是记录锁。记录锁又可分为读取锁和写入锁,其中读取锁又称为共享锁,它能够使多个进程都能在文件的同一部分建立读取锁。而写入锁又称为排斥锁,在任何时刻只能有一个进程在文件的某个部分上建

27、立写入锁。当然,在文件的同一部分不能同时建立读取锁和写入锁。注意 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-

28、on-exec 标志,若标志未设置,则文件经过 exec函数之后仍保持打开状态 F_SETFD:设置 close-on-exec 标志,该标志以参数 arg 的 FD_CLOEXEC 位决定 F_GETFL:得到 open 设置的标志 函数传入值 cmd F_SETFL:改变 open 设置的标志 更多电子书教程下载请登陆h t t p:/d o w n.z z b a i k e.c o m/e b o o k 本站提供的电子书教程均为网上搜集,如果该教程涉及或侵害到您的版权请联系我们。华清远见嵌入式培训专家 http:/ 华清远见培训教材 F_GETFK:根据 lock 描述,决定是否上文

29、件锁 F_SETFK:设置 lock 描述的文件锁 F_SETLKW:这是 F_SETLK 的阻塞版本(命令名中的 W 表示等待(wait)。如果存在其他锁,则调用进程睡眠;如果捕捉到信号则睡眠中断 F_GETOWN:检索将收到 SIGIO 和 SIGURG 信号的进程号或进程组号 F_SETOWN:设置进程号或进程组号 Lock:结构为 flock,设置记录锁的具体状态,后面会详细说明 函数返回值 成功:0 1:出错 这里,lock 的结构如下所示:Struct flock short l_type;off_t l_start;short l_whence;off_t l_len;pid_t

30、 l_pid;lock 结构中每个变量的取值含义如表 6.7 所示。表 6.7 lock 结构变量取值 F_RDLCK:读取锁(共享锁)F_WRLCK:写入锁(排斥锁)l_type F_UNLCK:解锁 l_stat 相对位移量(字节)SEEK_SET:当前位置为文件的开头,新位置为偏移量的大小 SEEK_CUR:当前位置为文件指针的位置,新位置为当前位置加上偏移量 l_whence:相对位移量的起点(同 lseek的 whence)。SEEK_END:当前位置为文件的结尾,新位置为文件的大小加上偏移量的大小 l_len 加锁区域的长度 小技巧 为加锁整个文件,通常的方法是将 l_start

31、说明为 0,l_whence 说明为 SEEK_SET,l_len 说明为 0。(3)fcntl 使用实例 下面首先给出了使用 fcntl 函数的文件记录锁函数。在该函数中,首先给 flock 结构体的对应位赋予相应的值。接着使用两次 fcntl 函数分别用于给相关文件上锁和判断文件是否可以上锁,这里用到的 cmd 值分别为 F_SETLK 和 F_GETLK。这个函数的源代码如下所示:更多电子书教程下载请登陆h t t p:/d o w n.z z b a i k e.c o m/e b o o k 本站提供的电子书教程均为网上搜集,如果该教程涉及或侵害到您的版权请联系我们。嵌入式 Linu

32、x 应用程序开发详解第 6 章、文件 IO 编程 华清远见培训教材/*lock_set 函数*/void lock_set(int fd,int type)struct flock lock;lock.l_whence=SEEK_SET;/赋值 lock 结构体 lock.l_start=0;lock.l_len=0;while(1)lock.l_type=type;/*根据不同的 type 值给文件上锁或解锁*/if(fcntl(fd,F_SETLK,&lock)=0)if(lock.l_type=F_RDLCK)printf(read lock set by%dn,getpid();els

33、e if(lock.l_type=F_WRLCK)printf(write lock set by%dn,getpid();else if(lock.l_type=F_UNLCK)printf(release lock by%dn,getpid();return;/*判断文件是否可以上锁*/fcntl(fd,F_GETLK,&lock);/*判断文件不能上锁的原因*/if(lock.l_type!=F_UNLCK)/*/该文件已有写入锁*/if(lock.l_type=F_RDLCK)printf(read lock already set by%dn,lock.l_pid);/*该文件已有读

34、取锁*/else if(lock.l_type=F_WRLCK)printf(write lock already set by%dn,lock.l_pid);getchar();下面的实例是测试文件的写入锁,这里首先创建了一个 hello 文件,之后对其上写入锁,最后释放写入锁。代码如下所示:/*fcntl_write.c 测试文件写入锁主函数部分*/#include#include 更多电子书教程下载请登陆h t t p:/d o w n.z z b a i k e.c o m/e b o o k 本站提供的电子书教程均为网上搜集,如果该教程涉及或侵害到您的版权请联系我们。华清远见嵌入式培

35、训专家 http:/ 华清远见培训教材#include#include#include#include int main(void)int fd;/*首先打开文件*/fd=open(hello,O_RDWR|O_CREAT,0666);if(fd 0)perror(open);exit(1);/*给文件上写入锁*/lock_set(fd,F_WRLCK);getchar();/*给文件接锁*/lock_set(fd,F_UNLCK);getchar();close(fd);exit(0);为了能够使用多个终端,更好地显示写入锁的作用,本实例主要在 PC 机上测试,读者可将其交叉编译,下载到目标

36、板上运行。下面是在 PC 机上的运行结果。为了使程序有较大的灵活性,笔者采用文件上锁后由用户键入一任意键使程序继续运行。建议读者开启两个终端,并且在两个终端上同时运行该程序,以达到多个进程操作一个文件的效果。在这里,笔者首先运行终端一,请读者注意终端二中的第一句。终端一:rootlocalhost file#./fcntl_write write lock set by 4994 release lock by 4994 终端二:rootlocalhost file#./fcntl_write write lock already set by 4994 更多电子书教程下载请登陆h t t p

37、:/d o w n.z z b a i k e.c o m/e b o o k 本站提供的电子书教程均为网上搜集,如果该教程涉及或侵害到您的版权请联系我们。嵌入式 Linux 应用程序开发详解第 6 章、文件 IO 编程 华清远见培训教材 write lock set by 4997 release lock by 4997 由此可见,写入锁为互斥锁,一个时刻只能有一个写入锁存在。接下来的程序是测试文件的读取锁,原理同上面的程序一样。/*fcntl_read.c 测试文件读取锁主函数部分*/#include#include#include#include#include#include int

38、 main(void)int fd;fd=open(hello,O_RDWR|O_CREAT,0666);if(fd 0)perror(open);exit(1);/*给文件上读取锁*/lock_set(fd,F_RDLCK);getchar();/*给文件接锁*/lock_set(fd,F_UNLCK);getchar();close(fd);exit(0);同样开启两个终端,并首先启动终端一上的程序,其运行结果如下所示:终端一:rootlocalhost file#./fcntl2 read lock set by 5009 release lock by 5009 终端二:更多电子书教程

39、下载请登陆h t t p:/d o w n.z z b a i k e.c o m/e b o o k 本站提供的电子书教程均为网上搜集,如果该教程涉及或侵害到您的版权请联系我们。华清远见嵌入式培训专家 http:/ 华清远见培训教材 rootlocalhost file#./fcntl2 read lock set by 5010 release lock by 5010 读者可以将此结果与写入锁的运行结果相比较,可以看出,读取锁为共享锁,当进程 5009已设定读取锁后,进程 5010 还可以设置读取锁。思考 如果在一个终端上运行设置读取锁,则在另一个终端上运行设置写入锁,会有什么结果呢?6

40、.3.4 select(1)select 函数说明 前面的 fcntl 函数解决了文件的共享问题,接下来该处理 I/O 复用的情况了。总的来说,I/O 处理的模型有 5 种。阻塞 I/O 模型:在这种模型下,若所调用的 I/O 函数没有完成相关的功能就会使进程挂起,直到相关数据到才会出错返回。如常见对管道设备、终端设备和网络设备进行读写时经常会出现这种情况。非阻塞模型:在这种模型下,当请求的 I/O 操作不能完成时,则不让进程睡眠,而且返回一个错误。非阻塞 I/O 使用户可以调用不会永远阻塞的 I/O 操作,如 open、write和 read。如果该操作不能完成,则会立即出错返回,且表示该

41、I/O 如果该操作继续执行就会阻塞。I/O 多路转接模型:在这种模型下,如果请求的 I/O 操作阻塞,且它不是真正阻塞 I/O,而是让其中的一个函数等待,在这期间,I/O 还能进行其他操作。如本节要介绍的 select 函数和 poll 函数,就是属于这种模型。信号驱动 I/O 模型:在这种模型下,通过安装一个信号处理程序,系统可以自动捕获特定信号的到来,从而启动 I/O。这是由内核通知用户何时可以启动一个 I/O 操作决定的。异步 I/O 模型:在这种模型下,当一个描述符已准备好,可以启动 I/O 时,进程会通知内核。现在,并不是所有的系统都支持这种模型。可以看到,select 的 I/O

42、多路转接模型是处理 I/O 复用的一个高效的方法。它可以具体设置每一个所关心的文件描述符的条件、希望等待的时间等,从 select 函数返回时,内核会通知用户已准备好的文件描述符的数量、已准备好的条件等。通过使用 select 返回值,就可以调用相应的 I/O 处理函数了。(2)select 函数格式 Select 函数的语法格式如表 6.8 所示。表 6.8 fcntl 函数语法要点 所需头文件#include#include 更多电子书教程下载请登陆h t t p:/d o w n.z z b a i k e.c o m/e b o o k 本站提供的电子书教程均为网上搜集,如果该教程涉及

43、或侵害到您的版权请联系我们。嵌入式 Linux 应用程序开发详解第 6 章、文件 IO 编程 华清远见培训教材#include 函数原型 int select(int numfds,fd_set*readfds,fd_set*writefds,fd_set*exeptfds,struct timeval*timeout)numfds:需要检查的号码最高的文件描述符加 1 readfds:由 select()监视的读文件描述符集合 writefds:由 select()监视的写文件描述符集合 exeptfds:由 select()监视的异常处理文件描述符集合 NULL:永远等待,直到捕捉到信号或

44、文件描述符已准备好为止 具体值:struct timeval 类型的指针,若等待为 timeout 时间还没有文件描符准备好,就立即返回 函数传入值 timeout 0:从不等待,测试所有指定的描述符并立即返回 函数返回值 成功:准备好的文件描述符 1:出错 思考 请读者考虑一下如何确定最高的文件描述符?可以看到,select 函数根据希望进行的文件操作对文件描述符进行了分类处理,这里,对文件描述符的处理主要涉及到 4 个宏函数,如表 6.9 所示。表 6.9 select 文件描述符处理函数 FD_ZERO(fd_set*set)清除一个文件描述符集 FD_SET(int fd,fd_set

45、*set)将一个文件描述符加入文件描述符集中 FD_CLR(int fd,fd_set*set)将一个文件描述符从文件描述符集中清除 FD_ISSET(int fd,fd_set*set)测试该集中的一个给定位是否有变化 一般来说,在使用 select 函数之前,首先使用 FD_ZERO 和 FD_SET 来初始化文件描述符集,在使用了 select 函数时,可循环使用 FD_ISSET 测试描述符集,在执行完对相关后文件描述符后,使用 FD_CLR 来清楚描述符集。另外,select 函数中的 timeout 是一个 struct timeval 类型的指针,该结构体如下所示:struct

46、timeval long tv_sec;/*second*/long tv_unsec;/*and microseconds*/可以看到,这个时间结构体的精确度可以设置到微秒级,这对于大多数的应用而言已经足够了。(3)使用实例 由于 Select 函数多用于 I/O 操作可能会阻塞的情况下,而对于可能会有阻塞 I/O 的管道、更多电子书教程下载请登陆h t t p:/d o w n.z z b a i k e.c o m/e b o o k 本站提供的电子书教程均为网上搜集,如果该教程涉及或侵害到您的版权请联系我们。华清远见嵌入式培训专家 http:/ 华清远见培训教材 网络编程,本书到现在为

47、止还没有涉及。因此,本例主要表现了如何使用 select 函数,而其中的 I/O 操作是不会阻塞的。本实例中主要实现将文件 hello1 里的内容读出,并将此内容每隔 10s 写入 hello2 中去。在这里建立了两个描述符集,其中一个描述符集 inset1 是用于读取文件内容,另一个描述符集 inset2 是用于写入文件的。两个文件描述符 fds0和 fds1分别指向这一文件描述符。在首先初始化完各文件描述符集之后,就开始了循环测试这两个文件描述符是否可读写,由于在这里没有阻塞,所以文件描述符处于准备就绪的状态。这时,就分别对文件描述符 fds0和fsd1进行读写操作。该程序的流程图如图 6

48、.2 所示。/*select.c*/#include#include#include#include#include int main(void)int fds2;char buf7;int i,rc,maxfd;fd_set inset1,inset2;struct timeval tv;/*首先按一定的权限打开 hello1 文件*/if(fds0=open(hello1,O_RDWR|O_CREAT,0666)0)perror(open hello1);/*再按一定的权限打开 hello2 文件*/if(fds1=open(hello2,O_RDWR|O_CREAT,0666)fds1?

49、fds0:fds1;/*初始化读集合 inset1,并在读集合中加入相应的描述集*/FD_ZERO(&inset1);FD_SET(fds0,&inset1);/*初始化写集合 inset2,并在写集合中加入相应的描述集*/FD_ZERO(&inset2);更多电子书教程下载请登陆h t t p:/d o w n.z z b a i k e.c o m/e b o o k 本站提供的电子书教程均为网上搜集,如果该教程涉及或侵害到您的版权请联系我们。嵌入式 Linux 应用程序开发详解第 6 章、文件 IO 编程 华清远见培训教材 FD_SET(fds1,&inset2);tv.tv_sec=2

50、;tv.tv_usec=0;/*循环测试该文件描述符是否准备就绪,并调用 select 函数对相关文件描述符做对应操作*/while(FD_ISSET(fds0,&inset1)|FD_ISSET(fds1,&inset2)if(select(maxfd+1,&inset1,&inset2,NULL,&tv)0)bufrc=0;printf(read:%sn,buf);else perror(read);if(FD_ISSET(fds1,&inset2)rc=write(fds1,buf,7);if(rc0)bufrc=0;printf(rc=%d,write:%sn,rc,buf);else

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 技术资料 > 其他杂项

本站为文档C TO C交易模式,本站只提供存储空间、用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。本站仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知淘文阁网,我们立即给予删除!客服QQ:136780468 微信:18945177775 电话:18904686070

工信部备案号:黑ICP备15003705号© 2020-2023 www.taowenge.com 淘文阁