嵌入式Linux应用基础学习(3)— Linux系统下的文件IO
一、什么是文件
在 Linux 系统中,一切都是“ 文件”:普通文件、驱动程序、网络通信等等。
所有的操作,都是通过“文件 IO”来操作的。
Linux系统下的文件可以分分为如下几类:
- 普通文件:存在磁盘、Flash、SD卡、U盘等存储介质上的真实文件;
它们以某种格式(FAT32、EXT4…)保存在某个设备上(/dev/xxx),使用前要先mount。 - 虚拟文件:Linux内核提供的虚拟文件系统,也要先mount。
- 特殊文件:在/dev/xxx下还有一下其他的设备节点,如字符设备和块设备,这些就是所谓的驱动,Linux系统通过他们来操作硬件。
要深入了解可以参考如下书籍,这 2 本书的内容类似,第一本对知识点有更细致的描述,适合初学者;第二本比较直接,一上来就是各种函数的介绍,适合当作字典,不懂时就去翻看一下。
二、文件分类
1、普通文件
首先,在我们的STM32MP157开发板上插入SD卡,可以在串口终端下看到如下提示,注意:SHH下可能看不到
然后换一张SD卡插入,可以看到不同的打印,这是因为我们前一张SD卡只有一个分区,而这个SD卡有多个分区
接下来使用命令cat /proc/mounts
查看一下,可以看到这里并没有mmcblk1
的信息,这是因为系统并没有帮我们自动挂载
我们使用手动挂载命令来挂载一下,mount
是挂载命令,/dev/mmcblock1p1
是设备节点,需要根据上面的提示信息来输入,/root/sdcard/
是要挂载的目录
mount /dev/mmcblck1p1 /root/sdcard/
挂载完成后我们可以看一下,我们挂载的目录下确实是SD卡中存在的文件
2、虚拟文件
然后刚才用cat /proc/mounts
命令查看到的/sys
目录就是Linux内核提供的虚拟文件系统
我们ls /sys
查看一下,通过查看这下面的文件可以查看内核有哪些设备、固件等等等等
同样的,也可以将其挂载到其他目录,-t sysfs
表示虚拟文件系统,虚拟文件系统不需要真实的设备节点,用none
表示,然后挂载目录为/mnt
,查看一下,文件是一模一样的
3、特殊文件
使用ls -l /dev/*
查看一下,列如下面这些设备,其中第一个字符c
表示字符设备驱动,b
表示块设备驱动
三、访问文件
我们在C语言中可以通过如下方法来访问文件
通用的 IO 模型: open/read/write/lseek/close
不是通用的函数: ioctl/mmap
具体的用法可以通过man手册来查看,man手册含有 9 大分类,我们使用第2类和第3类就可以了知道用法了
1 Executable programs or shell commands // 命令
2 System calls (functions provided by the kernel) // 系统调用,比如 man 2 open
3 Library calls (functions within program libraries) // 函数库调用
4 Special files (usually found in /dev) // 特殊文件, 比如 man 4 tty
5 File formats and conventions eg /etc/passwd // 文件格式和约定, 比如 man 5 passwd
6 Games // 游戏
7 Miscellaneous (including macro packages and conventions), e.g. man(7), groff(7) //杂项
8 System administration commands (usually only for root) // 系统管理命令
9 Kernel routines [Non standard] // 内核例程
例如查看open的使用方法,man 2 open
,使用man手册只需要记住f
往前翻,b
往后翻,q
退出,这几个命令即可
从中我们可以知道,要使用open
函数,需要包含的头文件如下:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
open
函数的函数原型如下所示:
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
对于该函数的使用说明:
参数①:pathname
表示打开文件的路径;
参数②:Flags
表示打开文件的方式,常用的有以下 6 种,
O_RDWR
表示可读可写方式打开;O_RDONLY
表示只读方式打开;O_WRONLY
表示只写方式打开;O_APPEND
表示如果这个文件中本来是有内容的,则新写入的内容会接续到原来内容的后面;O_TRUNC
表示如果这个文件中本来是有内容的,则原来的内容会被丢弃, 截断;O_CREAT
表示当前打开文件不存在,我们创建它并打开它, 通常与 O_EXCL 结合使用,当没有文件时创建文件,有这个文件时会报错提醒我们;
参数③ :Mode
表示创建文件的权限,只有在 flags 中使用了 O_CREAT 时才有效, 否则忽略。
返回值: 打开成功返回文件描述符,失败将返回-1。
四、读写文件
编写代码实现如下功能:
- 1、新建一个文件
a1.txt
,并往该文件写入内容 - 2、读出该文件的内容
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
/*
* ./filerw strings
* argc = 2
* argv[0] = "./filerw"
* argv[1] = <strings>
*/
int main(int argc, char **argv)
{
int fd;
char buf[1024];
int len;
/* 1. 判断参数,当只有可执行文件名本身一个参数时,默认写入“Helloworld!” */
if (argc == 1)
printf("default: %s \"Helloworld!\"\n", argv[0]);
else if(argc == 2)
printf("write to a1.txt: %s \n", argv[1]);
else
{
printf("Usage: %s <strings>\n", argv[0]);
return -1;
}
/* 2. 创建文件,第二个参数:O_RDWR->文件可读写,O_CREAT->若此文件不存在则创建它,O_TRUNC-> 如果文件已存在并且可写,则将其长度截断为0字节;
第三个参数为新建的文件的权限:S_IRUSR->用户读权限,S_IWUSR->用户写权限,S_IRGRP->用户组读权限,S_IWGRP->用户组写权限,S_IROTH->其他组读权限,S_IWOTH->其他组写权限 */
fd = open("a1.txt", O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
if (fd == -1)
{
printf("can not creat file a1.txt\n");
return -1;
}
/* 3. 将内容写入文件中 */
if (argc == 1)
{
len = strlen("Helloworld!");
if (write(fd, "Helloworld!", len) != len)
{
printf("can not write a1.txt\n");
return -1;
}
}
else if(argc == 2)
{
len = strlen(argv[1]);
if (write(fd, argv[1], len) != len)
{
printf("can not write a1.txt\n");
return -1;
}
}
/* 4. 关闭文件 */
close(fd);
return 0;
}
首先,我们在ubuntu上编译运行,可以看到,写入读出成功
然后我们交叉编译,在开发板上运行,也可以看到,写入读出成功