嵌入式linux开发-(四)文件I/O基础

系列文章目录



前言

Linux 下一切皆文件,文件作为 Linux 系统设计思想的核心理念,在 Linux 系统下显得尤为重要,所以对文件的 I/O 操作既是基础也是最重要的部分。本章将向大家介绍 Linux 系统下文件描述符的概念,随后会逐一讲解构成通用 I/O 模型的系统调用,譬如打开文件、关闭文件、从文件中读取数据和向文件中写入数据以及这些系统调用涉及的参数等内容。

一、文件描述符

这是一个int类型的数据,在open函数执行成功的情况下,会返回一个非负整数,该返回值就是一个文件描述符,这说明文件描述符是一个非负整数,所有打开的文件都会通过文件描述符就行索引。
我们可以通过ulimit命令来查看进程可以打开的最大文件数:

ulimit -n

默认值为1024。
调用open函数打开文件时,分配的文件描述符一般是从3开始,因为前三个已经分配:

  • 0:系统标准输入
  • 1:标准输出
  • 2:标准错误

标准输入一般对应的是键盘,可以理解为 0 便是打开键盘对应的设备文件时所得到的文件描述符;标准输出一般指的是 LCD 显示器,可以理解为 1 便是打开 LCD 设备对应的设备文件时所得到的文件描述符;而标准错误一般指的也是 LCD 显示器

二、open、close、read、write、lseek、perror、exit、_exit、_Exit、pread、pwrite、fcntl、ioctl、truncate、fruncate

open

函数原型:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);

函数参数返回值含义:
pathname:字符串类型,用于标识需要打开或创建的文件,可以包含路径(绝对路径或者相对路径)信息;如果pathname是一个符号链接,会对其进行解引用。
flags:调用open函数需要提供的标志包括文件访问模式标志以及其他文件相关标志。
open函数flags参数如下表:
请添加图片描述
请添加图片描述
还有: O_APPEND、O_ASYNC、O_DSYNC、O_NOATIME、O_NONBLOCK、O_SYNC 以 及 O_TRUNC等等
O_TRUNC 标志:调用 open 函数打开文件的时候会将文件原本的内容全部丢弃,文件大小变为 0。
O_APPEND:当每次使用 write()函数对文件进行写操作时,都会自动把文件当前位置偏移量移动到文件末尾,从文件末尾开始写入数据。

mode:此参数用于指定新建文件的访问权限,只有当flags参数包含O_CREAT或O_TEMPFILE(O_TEMOFILE标志用于创建一个临时文件)标志时才有效。参数类型:mode_t(32位无符号整型数据),权限表示方法如下所示:
在这里插入图片描述
我们从低位从上看,3个bit位分为一组,分别表示:
O—表示其他用户的权限;
G—表示同组用户(group)的权限,就是与文件所有者相同组ID的所有用户;
U—表示文件所属用户的权限,就是文件或者目录的所属者;
S—表示文件的特殊权限;
最高位(权值为 4)表示读权限,为 1 时表示具有读权限,为 0 时没有读权限;中间位(权值为 2)表示写权限,为 1 时表示具有写权限,为 0 时没有写权限;最低位(权值为 1)表示执行权限,为 1 时表示具有可执行权限,为 0 时没有执行权限。
open函数文件权限宏如下表:
请添加图片描述
返回值:成功将返回文件描述符,文件描述符是一个非负整数;失败将返回-1;

write写文件

函数原型:

#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);

函数参数
fd:文件描述符。
buf:指定写入数据对应的缓冲区。
**count:**指定写入的字节数。
**返回值:**如果成功将返回写入的字节数(0表示未写入任何字节),如果此数小于count参数,可能是磁盘空间已满;如果写入错误,则返回-1.

read读文件

函数原型

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);

函数参数和返回值:
fd:文件描述符。
buf:指定用于存储读取数据的缓冲区。
count:指定需要读取的字节数。
返回值:如果成功将但会读取的字节数,当读取到文件末尾时会返回0.

close关闭文件

函数原型

#include <unistd.h>
int close(int fd);

函数参数和返回值
fd:文件描述符。
返回值:如果成功返回0,失败返回-1。

lseek文件偏移量

函数原型

#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);

函数参数和返回值
fd:文件描述符
offset:偏移量,以字节为单位
whence:用于定义offset偏移量的参考值,该参数为下面其中一种:

  • SEEK_SET:读写偏移量指向offset字节位置处。
  • SEEK_CUR:读写偏移量将指向当前位置偏移量 + offset 字节位置处,offset 可以为正、也可以为
    负,如果是正数表示往后偏移,如果是负数则表示往前偏移;
  • SEEK_END:读写偏移量将指向文件末尾 + offset 字节位置处,同样 offset 可以为正、也可以为负,如果是正数表示往后偏移、如果是负数则表示往前偏移。

返回值:成功将返回从文件头部开始算起的位置偏移量(字节为单位),也就是当前的读写位置;发生错误将返回-1。

perror 查看错误信息

函数原型

#include <stdio.h>
void perror(const char *s);

函数参数和返回值
s:在错误提示字符串信息之前,可加入自己的打印信息,也可不加,不加则传入空字符串即可。
返回值:void 无返回值

exit、_exit、_Exit

exit():库函数,终止进程
** _exit() **:调用_exit()函数会清除其使用的内存空间,并销毁其在内核中的各种数据结构,关闭进程的所有文件描述符,并结束进程、将控制权交给操作系统。_exit()函数原型如下所示:

#include <unistd.h>
void _exit(int status);
#include <stdlib.h>
void _Exit(int status);

注意:这两个函数都是系统调用

pread和pwrite

函数原型

#include <unistd.h>
ssize_t pread(int fd, void *buf, size_t count, off_t offset);
ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset);

函数参数和返回值
fd、buf、count 参数与 read 或 write 函数意义相同。
offset:表示当前需要进行读或写的位置偏移量。
返回值:返回值与 read、write 函数返回值意义一样。
⚫ 调用 pread 函数时,无法中断其定位和读操作(也就是原子操作);
⚫ 不更新文件表中的当前位置偏移量。

fcntl

功能:fcntl()函数可以对一个已经打开的文件描述符执行一系列控制操作,譬如复制一个文件描述符(与 dup、dup2 作用相同)、获取/设置文件描述符标志、获取/设置文件状态标志等,类似于一个多功能文件描述符管理工具箱。
函数原型

#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* arg */ )

函数参数和返回值
fd:文件描述符。
cmd:操作命令。例如:
⚫ 复制文件描述符(cmd=F_DUPFD 或 cmd=F_DUPFD_CLOEXEC);
⚫ 获取/设置文件描述符标志(cmd=F_GETFD 或 cmd=F_SETFD);
⚫ 获取/设置文件状态标志(cmd=F_GETFL 或 cmd=F_SETFL);
⚫ 获取/设置异步 IO 所有权(cmd=F_GETOWN 或 cmd=F_SETOWN);
⚫ 获取/设置记录锁(cmd=F_GETLK 或 cmd=F_SETLK);
返回值:执行失败情况下,返回-1,并且会设置 errno;执行成功的情况下,其返回值与 cmd(操作命令)有关,譬如 cmd=F_DUPFD(复制文件描述符)将返回一个新的文件描述符、cmd=F_GETFD(获取文件描述符标志)将返回文件描述符标志、cmd=F_GETFL(获取文件状态标志)将返回文件状态标志等。

ioctl

功能:一般用于操作特殊文件或硬件外设。
函数原型

#include <sys/ioctl.h>
int ioctl(int fd, unsigned long request, ...);

函数参数以及返回值
fd::文件描述符。
request:表示向文件描述符请求相应的操作。
:配合request使用。
返回值:成功返回0,失败返回-1.

truncate()和fruncate()

功能: 可将普通文件截断为指定字节长度。
函数原型

#include <unistd.h>
#include <sys/types.h>
int truncate(const char *path, off_t length);
int ftruncate(int fd, off_t length);

length<文件长度,多余的数据会丢失。
length>文件长度,对多的部分进行扩展,对扩展部分读取得到空字节"\0".
调用成功返回 0,失败将返回-1,并设置 errno 以指示错误原因。

三.简单地编程实战例子

(1)打开一个已经存在的文件(例如 src_file),使用只读方式;然后打开一个新建文件(例如 dest_file),使用只写方式,新建文件的权限设置如下:
文件所属者拥有读、写、执行权限;
同组用户与其他用户只有读权限。
从 src_file 文件偏移头部 500 个字节位置开始读取 1Kbyte 字节数据,然后将读取出来的数据写入到
dest_file 文件中,从文件开头处开始写入,1Kbyte 字节大小,操作完成之后使用 close 显式关闭所有文件,然后退出程序。

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main(){
    
    
  char data[1024];
  /*打开文件1*/
  int fd1  = open("./data.txt",O_RDWR,S_IRWXU|S_IRGRP|S_IROTH);
  if(-1 == fd1)
    printf("file no exist!");
  /*文件偏移量指到500*/
  lseek(fd1,500,SEEK_SET);
  /*读取文件1的1024个数据*/
  read(fd1,data,sizeof(data));
  //printf("%d",(int)sizeof(data));
  /*打开文件2*/
  int fd2 = open("./data2.txt",O_WRONLY);
  if(-1 == fd2) printf("open fd2 error!");
  /*写入数据*/
  int rec = write(fd2,data,sizeof(data));
  if(-1 == rec){
    
    
    printf("write error!\r\n");
  }
  else printf("write corrent!\r\n");
  /*关闭文件*/
  close(fd1);
  close(fd2);
  return 0;
}

(2)通过 open 函数判断文件是否存在(例如 test_file),并将判断结果显示出来。

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main(){
    
    
	/*打开文件,如果文件存在则返回-1,如果不存在则创建该文件*/
    int fd = open("./test_file",O_RDONLY|O_CREAT|O_EXCL);
    if(-1 == fd) printf("the file exist!\r\n");
    return 0;
}

(3)新建一个文件(例如 new_file),新建文件的权限设置为:
文件所属者拥有读、写、执行权限;
同组用户与其他用户只有读权限。
使用只写方式打开文件,将文件前 1Kbyte 字节数据填充为 0x00,将下 1Kbyte 字节数据填充为 0xFF,操作完成之后显式关闭文件,退出程序。

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main(){
    
    
    char start[1024];
    /*以只写方式打开文件,文件所属者拥有读、写、执行权限;*/
    int fd = open("./new_file.txt",O_WRONLY|O_CREAT,S_IRWXU|S_IRGRP|S_IROTH);
    if(-1 == fd) printf("OPEN ERROR!");
    /*写入0x00*/
    memset(start,0x00,sizeof(start));
    int fwr = write(fd,start,sizeof(start));
    if(-1 == fwr) printf("WRITE ERROR!");
    /*写入0xFF*/
    memset(start,0xFF,sizeof(start));
    fwr = write(fd,start,sizeof(start));
    if(-1 == fwr) printf("WRITE SECOND ERROR!");
    close(fd);
    printf("FILE CLSOE!");
    return 0;
}

(4)打开一个已经存在的文件(例如 test_file),通过 lseek 函数计算该文件的大小,并打印出来。

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main(){
    
    
    int i = 0;
    char data;
    /*以只读方式打开文件*/
    int fd = open("./data2.txt",O_RDONLY);
    if(-1 == fd) printf("OPEN ERROR!");
	/*将文件指针从开头读取到末尾,读取到末尾会返回0*/
    while(read(fd,&data,1)){
    
    
        lseek(fd,1,SEEK_CUR);
        i++;
        printf("%d\r\n",i);
    }
    close(fd);
    printf("THE FILE HAVE %d bite\r\n",i);
    return 0;
}

总结

以上对linux中的文件系统有一个初步的了解,主要是学习了几个函数的用法:open、close、read、write、lseek,并且练习了几个栗子。

猜你喜欢

转载自blog.csdn.net/qq_52608074/article/details/127535733