您好,欢迎访问三七文档
当前位置:首页 > 行业资料 > 冶金工业 > 第13讲Linux文件IO编程
第13讲Linux文件I/O编程主要内容13.1Linux中文件及文件描述符概述13.2底层文件I/O操作13.3嵌入式Linux串口应用编程13.4标准I/O编程13.5实验13.1Linux中文件及文件描述符概述文件描述符内核如何区分和引用特定的文件呢?这里用到了一个重要的概念——文件描述符。对于Linux而言,所有对设备和文件的操作都是使用文件描述符来进行的。文件描述符是一个非负的整数,它是一个索引值,并指向在内核中每个进程打开文件的记录表。当打开一个现存文件或创建一个新文件时,内核就向进程返回一个文件描述符;当需要读写文件时,也需要把文件描述符作为参数传递给相应的函数。通常,一个进程启动时,都会打开3个文件:标准输入、标准输出和标准出错处理。这3个文件分别对应文件描述符为0、1和2(也就是宏替换STDIN_FILENO、STDOUT_FILENO和STDERR_FILENO)。13.2底层文件I/O操作函数说明open()函数是用于打开或创建文件,在打开或创建文件时可以指定文件的属性及用户的权限等各种参数。close()函数是用于关闭一个被打开的文件。当一个进程终止时,所有被它打开的文件都由内核自动关闭,很多程序都使用这一功能而不显示地关闭一个文件。read()函数是用于将从指定的文件描述符中读出的数据放到缓存区中,并返回实际读入的字节数。若返回0,则表示没有数据可读,即已达到文件尾。读操作从文件的当前指针位置开始。当从终端设备文件中读出数据时,通常一次最多读一行。write()函数是用于向打开的文件写数据,写操作从文件的当前指针位置开始。对磁盘文件进行写操作,若磁盘已满或超出该文件的长度,则write()函数返回失败。lseek()函数是用于在指定的文件描述符中将文件指针定位到相应的位置。它只能用在可定位(可随机访问)文件操作中。管道、套接字和大部分字符设备文件是不可定位的,所以在这些文件的操作中无法使用lseek()调用。函数格式(1)函数格式(2)函数格式(3)示例阅读代码并运行示例6-3-1文件锁文件锁包括建议性锁和强制性锁。建议性锁要求每个上锁文件的进程都要检查是否有锁存在,并且尊重已有的锁。在一般情况下,内核和系统都不使用建议性锁。强制性锁是由内核执行的锁,当一个文件被上锁进行写入操作的时候,内核将阻止其他任何文件对其进行读写操作。采用强制性锁对性能的影响很大,每次读写操作都必须检查是否有锁存在。在Linux中,实现文件上锁的函数有lockf()和fcntl(),其中lockf()用于对文件施加建议性锁,而fcntl()不仅可以施加建议性锁,还可以施加强制锁。同时,fcntl()还能对文件的某一记录上锁,也就是记录锁。记录锁又可分为读取锁和写入锁,其中读取锁又称为共享锁,它能够使多个进程都能在文件的同一部分建立读取锁。而写入锁又称为排斥锁,在任何时刻只能有一个进程在文件的某个部分上建立写入锁。当然,在文件的同一部分不能同时建立读取锁和写入锁。fcntl()函数格式(1)fcntl()函数格式(2)-flock结构structflock{shortl_type;off_tl_start;shortl_whence;off_tl_len;pid_tl_pid;}示例阅读代码并运行示例6-3-2I/O处理的模型阻塞I/O模型:在这种模型下,若所调用的I/O函数没有完成相关的功能,则会使进程挂起,直到相关数据到达才会返回。如常见对管道设备、终端设备和网络设备进行读写时经常会出现这种情况。非阻塞模型:在这种模型下,当请求的I/O操作不能完成时,则不让进程睡眠,而且立即返回。非阻塞I/O使用户可以调用不会阻塞的I/O操作,如open()、write()和read()。如果该操作不能完成,则会立即返回出错(例如:打不开文件)或者返回0(例如:在缓冲区中没有数据可以读取或者没空间可以写入数据)。I/O多路转接模型:在这种模型下,如果请求的I/O操作阻塞,且它不是真正阻塞I/O,而是让其中的一个函数等待,在这期间,I/O还能进行其他操作。如本节要介绍的select()和poll函数(),就是属于这种模型。信号驱动I/O模型:在这种模型下,通过安装一个信号处理程序,系统可以自动捕获特定信号的到来,从而启动I/O。这是由内核通知用户何时可以启动一个I/O操作决定的。异步I/O模型:在这种模型下,当一个描述符已准备好,可以启动I/O时,进程会通知内核。现在,并不是所有的系统都支持这种模型。select()和poll()select()和poll()的I/O多路转接模型是处理I/O复用的一个高效的方法。它可以具体设置程序中每一个所关心的文件描述符的条件、希望等待的时间等,从select()和poll()函数返回时,内核会通知用户已准备好的文件描述符的数量、已准备好的条件等。通过使用select()和poll()函数的返回结果,就可以调用相应的I/O处理函数了。select()函数格式(1)select()函数格式(2)一般来说,在使用select()函数之前,首先使用FD_ZERO()和FD_SET()来初始化文件描述符集,在使用了select()函数时,可循环使用FD_ISSET()来测试描述符集,在执行完对相关文件描述符的操作之后,使用FD_CLR()来清除描述符集。poll()函数格式示例阅读代码并运行示例6-3-3-1(select)和6-3-3-2(poll)获得三个文件描述符(两个管道和标准输入)准备工作:创建两个管道开始初始化读文件描述符集合Select:监视文件描述符集合的文件读文件描述符集合的重置Tmp_inset-Inset有效的文件描述符存在?Select的返回是否输入“q”?打印到屏幕上打印到屏幕上结束是标准输入管道2管道1是否超时/出错13.3嵌入式Linux串口应用编程串口概述常见的数据通信的基本方式可分为并行通信与串行通信两种。并行通信是指利用多条数据传输线将一个字数据的各比特位同时传送。它的特点是传输速度快,适用于传输距离短且传输速度较高的通信。串行通信是指利用一条传输线将数据以比特位为单位顺序传送。特点是通信线路简单,利用简单的线缆就可实现通信,降低成本,适用于传输距离长且传输速度较慢的通信。串口是计算机一种常用的接口,常用的串口有RS-232-C接口。它是于1970年由美国电子工业协会(EIA)联合贝尔系统、调制解调器厂家及计算机终端生产厂家共同制定的用于串行通讯的标准,它的全称是“数据终端设备(DTE)和数据通讯设备(DCE)之间串行二进制数据交换接口技术标准”。该标准规定采用一个DB25芯引脚的连接器或9芯引脚的连接器串口设置-termios结构structtermios{unsignedshortc_iflag;/*输入模式标志*/unsignedshortc_oflag;/*输出模式标志*/unsignedshortc_cflag;/*控制模式标志*/unsignedshortc_lflag;/*本地模式标志*/unsignedcharc_line;/*线路规程*/unsignedcharc_cc[NCC];/*控制特性*/speed_tc_ispeed;/*输入速度*/speed_tc_ospeed;/*输出速度*/};termios是在POSIX规范中定义的标准接口,表示终端设备(包括虚拟终端、串口等)。因为串口是一种终端设备,所以通过终端编程接口对其进行配置和控制。终端有三种工作模式,分别为规范模式(canonicalmode)、非规范模式(non-canonicalmode)和原始模式(rawmode)。通过在termios结构的c_lflag中设置ICANNON标志来定义终端是以规范模式(设置ICANNON标志)还是以非规范模式(清除ICANNON标志)工作,默认情况为规范模式。终端模式(1)-规范模式在规范模式下,所有的输入是基于行进行处理。在用户输入一个行结束符(回车符、EOF等)之前,系统调用read()函数读不到用户输入的任何字符。除了EOF之外的行结束符(回车符等)与普通字符一样会被read()函数读取到缓冲区之中。在规范模式中,行编辑是可行的,而且一次调用read()函数最多只能读取一行数据。如果在read()函数中被请求读取的数据字节数小于当前行可读取的字节数,则read()函数只会读取被请求的字节数,剩下的字节下次再被读取。终端模式(2)-非规范模式在非规范模式下,所有的输入是即时有效的,不需要用户另外输入行结束符,而且不可进行行编辑。在非规范模式下,对参数MIN(c_cc[VMIN])和TIME(c_cc[VTIME])的设置决定read()函数的调用方式。设置可以有4种不同的情况。MIN=0和TIME=0:read()函数立即返回。若有可读数据,则读取数据并返回被读取的字节数,否则读取失败并返回0。MIN0和TIME=0:read()函数会被阻塞直到MIN个字节数据可被读取。MIN=0和TIME0:只要有数据可读或者经过TIME个十分之一秒的时间,read()函数则立即返回,返回值为被读取的字节数。如果超时并且未读到数据,则read()函数返回0。MIN0和TIME0:当有MIN个字节可读或者两个输入字符之间的时间间隔超过TIME个十分之一秒时,read()函数才返回。因为在输入第一个字符之后系统才会启动定时器,所以在这种情况下,read()函数至少读取一个字节之后才返回。终端模式(3)-原始模式按照严格意义来讲原始模式是一种特殊的非规范模式。在原始模式下,所有的输入数据以字节为单位被处理。在这个模式下,终端是不可回显的,而且所有特定的终端输入/输出控制处理不可用。通过调用cfmakeraw()函数可以将终端设置为原始模式,而且该函数通过以下代码可以得到实现。termios_p-c_iflag&=~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);termios_p-c_oflag&=~OPOST;termios_p-c_lflag&=~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);termios_p-c_cflag&=~(CSIZE|PARENB);termios_p-c_cflag|=CS8;设置串口属性的基本流程(1)保存原先串口配置首先,为了安全起见和以后调试程序方便,可以先保存原先串口的配置,在这里可以使用函数tcgetattr(fd,&old_cfg)。该函数得到fd指向的终端的配置参数,并将它们保存于termios结构变量old_cfg中。该函数还可以测试配置是否正确、该串口是否可用等。若调用成功,函数返回值为0,若调用失败,函数返回值为1if(tcgetattr(fd,&old_cfg)!=0){perror(tcgetattr);return-1;}设置串口属性的基本流程(2)激活选项CLOCAL和CREAD分别用于本地连接和接受使能,因此,首先要通过位掩码的方式激活这两个选项。调用cfmakeraw()函数可以将终端设置为原始模式,在后面的实例中,采用原始模式进行串口数据通信。newtio.c_cflag|=CLOCAL|CREAD;cfmakeraw(&new_cfg);设置串口属性的基本流程(3)设置波特率设置波特率有专门的函数,用户不能直接通过位掩码来操作。设置波特率的主要函数有:cfsetispeed()和cfsetospeed()。设置字符大小与设置波特率不同,设置字符大小并没有现成可用的函数,需要用位掩码。一般首先去除数据位中的位掩码,再重新按要求设置。cfsetispeed(&new_cfg,B115200);cfsetospeed(&new_cfg,B115200);new_cfg.c_cflag&=~CSIZE;/*用数据位掩
本文标题:第13讲Linux文件IO编程
链接地址:https://www.777doc.com/doc-5855892 .html