前言
在进行Linux嵌入式开发的过程中,我们会接触到一系列与IO操作相关的函数。一般而言,我们将其分为三类,分别是:
文件IO、标准IO和库函数。
其中,文件IO顾名思义便是对文件进行操作的函数,再加上在Linux系统中,一切皆文件,所以当我们在之后的时间内进行更加深入的学习之后,文件IO的操作函数将会使用的越来越多。所以,今天,我们首先从将从常见的文件IO函数开始进行学习。
open函数
概述
open函数的作用是打开一个文件,该函数的形式如下:
int open(const char *pathname, int flags, mode_t mode)
函数原型 | int open(const char *pathname, int flags, mode_t mode) |
---|---|
头文件 | <sys/types.h> <sys/stat.h> <fcntl.h> |
参数含义 | 如下 |
返回值 | 成功:返回一个大于2的文件描述符 失败:返回 -1 |
参数pathname是一个常量指针,即该指针指向的内容是一个只读的数据,即需要打开的文件的文件名,参数flags指的是打开该文件的方式,比如:以只读方式打开、以追加方式打开等,常见的打开方式如下:
O_RDONLY 以只读方式打开文件
O_WRONLY 以只写方式打开文件
O_RDWR 以读写的方式打开文件
O_CREAT 创建一个文件,如果文件不存在,则新建一个,否则将报错
O_APPEND 以追加的方式打开文件,即向文件写入新的内容时,不覆盖文件中之前的内容
O_TRUNC 向文件写入新的内容时,覆盖文件中之前的内容
更为详细的文件打开方式,可以利用man 2 open命令,在linux系统中进行了解。
参数mode指的是当利用O_CREAT新建一个文件时,需要指定该文件的访问权限r(读)w(写)x(执行),我们使用一个八进制的数指定文件的权限,但是在实际应用中,该权限的设定并非文件最终的权限,而是与文件的掩码umask(在Linux系统的命令行中输入umask,可以查看umask的值)有关,文件最终的权限是指定的权限与取反后的掩码进行相与之后的结果。
比如:如果掩码值是0022(第一个0是八进制数的标志,第二个0是针对文件所有者的掩码,第一个2是针对用户组的掩码,第二个2是针对其他用户的掩码),而我们在open函数中指定文件的权限为0777,那么,该文件的最终权限为:0777 & (~(0022)) = 755。
测例
//利用open函数新建一个权限为755的文件
int fd = 0;//存储open函数返回的文件描述符信息
fd = open("./a.c",O_CREAT|O_RDWR,0777);//在当面路径下,新建一个可读可写的新文件a.c
文件的新建结果:
write函数
概述
write函数的作用是向打开的文件中写入数据,函数的形式如下:
ssize_t write(int fd, const void *buf, size_t count);
函数原型 | ssize_t write(int fd, const void *buf, size_t count) |
---|---|
头文件 | <unistd.h> |
参数含义 | 如下 |
返回值 | 成功:返回向文件中写入的数据数量:返回 -1 |
参数fd即open函数的返回值文件描述符,参数buf即需要向文件中写入的内容,参数cout指的是希望向文件中写入的数据的数量。值得注意的是,该函数的返回值与count是相等的(区别于下面的read函数),但是如果指定希望写入的数据量大于写入的数据的实际长度时,将产生数组越界,这是程序员应该避免的操作。
测例
//向上述的a.c文件中写入一个字符串,内容为:I love china
char* write_buf = "I love China\n";//需要向文件中写入的内容
write(fd,write_buf,strlen(write_buf));
程序运行结果:
read函数
read函数的作用是从打开的文件中读取数据,函数的形式如下:
ssize_t read(int fd, void *buf, size_t count);
函数原型 | ssize_t read(int fd, void *buf, size_t count) |
---|---|
头文件 | <unistd.h> |
参数含义 | 如下 |
返回值 | 成功:返回从文件中读取到数据的实际数量:返回 -1 |
与write函数一样,参数fd指的是利用open函数打开的文件的文件描述符,而参数count则是用户希望从文件读取的数据数量,且不同于write函数,count参数与read函数的返回值可以不同。
测例
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
int main()
{
int fd = 0;
char read_buf[128] = {0};
fd = open("./a.c",O_CREAT|O_RDWR,0777);
read(fd,read_buf,128);
printf("read_buf = %s",read_buf);
close(fd);
return 0;
}
输出结果为:
lseek函数
lseek函数的作用是指定文件指针的位置,函数的形式如下:
off_t lseek(int fd, off_t offset, int whence);
函数原型 | off_t lseek(int fd, off_t offset, int whence) |
---|---|
头文件 | <sys/types.h> <unistd.h> |
参数含义 | 如下 |
返回值 | 成功:返回文件指针的当前指定的位置:返回 -1 |
参数fd与其他函数一样,也是open函数打开文件的文件描述符;参数offset则是文件指针的偏移位置,如果该参数为正数,那么便将文件指针向后移动,如果该参数为负数,则表示将文件指针向前移动,如果是0,则表示不进行移动;参数whence便是指定从哪个位置开始进行偏移,可选的参数为:
SEEK_SET---表示文件的开头
SEEK_CUR---表示当前位置
SEEK_END---表示文件结尾
值得注意的是,当我们打开一个文件时,文件指针默认指向文件的开头,但是当我们向一个新文件写入数据之后,文件指针便指向了文件的结尾,所以如果此时立即对文件中的内容进行读取,那么将读取不到任何内容。只有重新修改文件指针的位置之后,才能读到希望读取的数据。
测例
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
int main()
{
int fd = 0;
unsigned int numOfData = 0;
char read_buf[128] = {0};
char write_buf[] = "I love China very much!";
fd = open("./a.c",O_CREAT|O_RDWR,0777);
numOfData = write(fd,write_buf,sizeof(write_buf)/sizeof(write_buf[0]));
read(fd,read_buf,numOfData);//立即读取文件中的内容
printf("first read_buf = %s\n",read_buf);
lseek(fd,0,SEEK_SET);//将文件指针的位置设置为文件开头
read(fd,read_buf,128);
printf("second read_buf = %s\n",read_buf);
close(fd);
return 0;
}
输出结果如下:
close函数
close函数的作用是关闭一个打开的文件,需要注意的是,当使用open函数打开一个文件之后,必须在合适的地方利用close函数关闭该文件。函数的形式如下:
int close(int fd);
函数原型 | int close(int fd) |
---|---|
头文件 | <unistd.h> |
参数含义 | 如下 |
返回值 | 成功:返回0:失败:返回 -1 |
参数fd与前面的含义相同。
当我们在程序中使用open函数打开一个文件之后,一定记住在相应的地方利用close函数进行关闭操作!
综合训练
我们需要知道的是,Linux中的所有shell命令都是利用函数实现的。所以,我们也可以自己利用函数实现Linux中的相关命令。此处,我们就利用上述四个函数,实现Linux的文件拷贝命令:cp。
该命令的格式如下:
cp 源地址源文件 目标地址目标文件
代码如下:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
int main(int argc,char* argv[])
{
int src_fd = 0;//源文件文件描述符
int dst_fd = 0;//目标文件文件描述符
char read_buf[128] = {0};//从源文件读取到的数据
char* write_buf = "I love China ver much!\n";
ssize_t numOfData = 0;
src_fd = open(argv[1],O_RDWR);//以只读的方式打开源文件
if(src_fd < 0)//打开文件失败
{
printf("打开源文件失败!\n");
return -1;
}
write(src_fd,write_buf,sizeof(write_buf) / sizeof(write_buf[0]));
close(src_fd);
if(argc < 3)//需要输入三个参数
{
printf("参数输入有误!\n");
return -1;
}
src_fd = open(argv[1],O_RDONLY);//以只读的方式打开源文件
if(src_fd < 0)//打开文件失败
{
printf("打开源文件失败!\n");
return -1;
}
dst_fd = open(argv[2],O_CREAT | O_RDWR,0777 );//以读写的方式打开源文件
if(dst_fd < 0)//打开文件失败
{
printf("打开目标文件失败!\n");
return -1;
}
while(1)//从源文件中读取数据并写入目标文件中
{
numOfData = read(src_fd,read_buf,128);
write(dst_fd,read_buf,numOfData);
if(numOfData < 128)
{
break;
}
}
printf("文件拷贝成功");
close(src_fd);
close(dst_fd);
}
注意事项
上述的四个函数,当返回值为**-1**时,均表示该函数的使用出现了错误。
补充:文件描述符
Linux系统有三个默认的文件描述符,是其他文件不能够使用的,其中:
0:标准输入 宏定义:STDIN_FILENO
1:标准输出 宏定义:STDOUT_FILENO
2:标准错误 宏定义:STDERR_FILENO
所以,利用上述open函数打开文件的文件描述符都是从3开始的。
总结
由于本人能力有限,本文难免有疏漏和错误之处,还望各位读者能够不吝指正。