文件I/O操作的系统调用,主要用到5个函数:open()、read()、write()、lseek()和close()。这些函数的特点是不带缓存,直接对文件(包括设备)进行读写操作。这些函数虽然不是ANSI C的组成部分,但是是Posix的组成部分。
基本文件操作
1.函数说明
● open()函数用于打开或创建文件,在打开或创建文件时可以指定文件的属性及用户的权限等各种参数。
● close()函数用于关闭一个被打开的文件。当一个进程终止时,所有被它打开的文件都由内核自动关闭,很多程序都使用这一功能而不显式地关闭一个文件。
● read()函数用于将从指定的文件描述符中读出的数据放到缓存区中,并返回实际读入的字节数。若返回0,则表示没有数据可读,即已达到文件尾。读操作从文件的当前指针位置开始。当从终端设备文件中读出数据时,通常一次最多读一行。
● write()函数用于向打开的文件写数据,写操作从文件的当前指针位置开始,对磁盘文件进行写操作,若磁盘已满或超出该文件的长度,则write()函数返回失败。
● lseek()函数用于在指定的文件描述符中将文件指针定位到相应的位置。它只能用在可定位(可随机访问)文件操作中。管道、套接字和大部分字符设备文件是不可定位的,所以在这些文件的操作中无法使用lseek()调用。
2.函数格式
open()函数的语法要点如表2.1所示。
表2.1 open()函数语法要点
所需头文件 #include <sys/types.h> /* 提供类型pid_t的定义 */
#include <sys/stat.h>
#include <fcntl.h>
函数原型 int open(const char *pathname, int flags, int perms)
函数传入值 pathname 被打开的文件名(可包括路径名)
flag:文件打开的方式 O_RDONLY:以只读方式打开文件
O_WRONLY:以只写方式打开文件
O_RDWR:以读/写方式打开文件
O_CREAT:如果该文件不存在,就创建一个新的文件,并用第三个参数为其设置权限
O_EXCL:如果使用O_CREAT时文件存在,则可返回错误消息。这一参数可测试文件是否存在。此时open是原子操作,防止多个进程同时创建同一个文件
O_NOCTTY:使用本参数时,若文件为终端,那么该终端不会成为调用open()的那个进程的控制终端
O_TRUNC:若文件已经存在,那么会删除文件中的全部原有数据,并且设置文件大小为0
O_APPEND:以添加方式打开文件,在打开文件的同时,文件指针指向文件的末尾,即将写入的数据添加到文件的末尾
perms 被打开文件的存取权限
可以用一组宏定义:S_I(R/W/X)(USR/GRP/OTH)
其中R/W/X分别表示读/写/执行权限
USR/GRP/OTH分别表示文件所有者/文件所属组/其他用户
例如,S_IRUSR | S_IWUSR表示设置文件所有者的可读可写属性,八进制表示法中0600也表示同样的权限
函数返回值 成功:返回文件描述符
失败:-1
在open()函数中,flag参数可通过“|”组合构成,但前3个标志常量(O_RDONLY、O_WRONLY及O_RDWR)不能相互组合。perms是文件的存取权限,既可以用宏定义表示法,也可以用八进制表示法。
close()函数的语法要点如表2.2所示。
表2.2 close()函数语法要点
所需头文件 #include <unistd.h>
函数原型 int close(int fd)
函数输入值 fd:文件描述符
函数返回值 0:成功
1:出错
read()函数的语法要点如表2.3所示。
表2.3 read()函数语法要点
所需头文件 #include <unistd.h>
函数原型 ssize_t read(int fd, void *buf, size_t count)
函数传入值 fd:文件描述符
buf:指定存储器读出数据的缓冲区
count:指定读出的字节数
函数返回值 成功:读到的字节数
0:已到达文件尾
1:出错
在读普通文件时,若读到要求的字节数前已到达文件的尾部,则返回的字节数会小于希望读出的字节数。
write()函数的语法要点如表2.4所示。
表2.4 write()函数语法要点
所需头文件 #include <unistd.h>
函数原型 ssize_t write(int fd, void *buf, size_t count)
函数传入值 fd:文件描述符
buf:指定存储器写入数据的缓冲区
count:指定读出的字节数
函数返回值 成功:已写的字节数
1:出错
在写普通文件时,写操作从文件的当前指针位置开始。
lseek()函数的语法要点如表2.5所示。
表2.5 lseek()函数语法要点
所需头文件 #include <unistd.h>
#include <sys/types.h>
函数原型 off_t lseek(int fd, off_t offset, int whence)
函数传入值 fd:文件描述符
offset:偏移量,每一读写操作所需要移动的距离,单位是字节,可正可负(向前移,向后移)
whence:
当前位置的基点 SEEK_SET:当前位置为文件的开头,新位置为偏移量的大小
SEEK_CUR:当前位置为文件指针的位置,新位置为当前位置加上偏移量
SEEK_END:当前位置为文件的结尾,新位置为文件的大小加上偏移量的大小
函数返回值 成功:文件的当前位移
1:出错
3.函数使用实例
下面实例中的open()函数带有3个flag参数:O_CREAT、O_TRUNC和O_WRONLY,这样就可以对不同的情况指定相应的处理方法。另外,这里对该文件的权限设置为0600。其源码如下所示:
下面列出文件基本操作的实例,基本功能是从一个文件(源文件)中读取最后10KB数据并复制到另一个文件(目标文件)。在实例中源文件是以只读方式打开的,目标文件是以只写方式打开(可以是读/写方式)的。若目标文件不存在,可以创建并设置权限的初始值为644,即文件所有者可读可写,文件所属组和其他用户只能读。
读者需要留意的地方是改变每次读/写的缓存大小(实例中为1KB)会怎样影响运行效率。
/* copy_file.c */
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#define BUFFER_SIZE 1024 /* 每次读/写缓存大小,影响运行效率 */
#define SRC_FILE_NAME "src_file" /* 源文件名 */
#define DEST_FILE_NAME "dest_file" /* 目标文件名 */
#define OFFSET 10240 /* 复制的数据大小 */
int main()
{
int src_file, dest_file;
unsigned char buff[BUFFER_SIZE];
int real_read_len;
/* 以只读方式打开源文件 */
src_file = open(SRC_FILE_NAME, O_RDONLY);
/* 以只写方式打开目标文件,若此文件不存在则创建该文件, 访问权限值为644 */
dest_file = open(DEST_FILE_NAME, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR|
S_IRGRP|S_IROTH);
if (src_file < 0 || dest_file < 0)
{
printf("Open file error\n");
exit(1);
}
/* 将源文件的读/写指针移到最后10KB的起始位置 */
lseek(src_file, -OFFSET, SEEK_END);
/* 读取源文件的最后10KB数据并写到目标文件中,每次读写1KB */
while ((real_read_len = read(src_file, buff, sizeof(buff))) > 0)
{
write(dest_file, buff, real_read_len);
}
close(dest_file);
close(src_file);
return 0;
}
$ ./copy_file
$ ls -lh dest_file
-rw-r--r-- 1 david root 10K 14:06 dest_file
本文选自华清远见嵌入式培训教材《从实践中学嵌入式Linux应用程序开发》 |