从此
文章
📄文章 #️⃣专题 🌐上网 📺 🛒 📱

Linux操作系统中的一切都是"文件" 通用IO模型和mmap技术

🕗2023-02-13

文件IO

在 Linux 系统中,一切都是“ 文件”:普通文件、驱动程序、网络通信等等。所有的操作,都是通过“文件 IO”来进行的。所以,很有必要掌握文件操作的常用接口。

 

访问文件的方式
类型 方法
通用的 IO 模型: open/read/write/lseek/close
非通用的函数 ioctl/mmap
Linux系统的文件有哪些

Linux 的文件既可以是真实保存到存储介质的文件也可以是自身内核提供的虚拟文件,还可以是设备节点 。

可通过一下表格命令行方式通过分类号来查询了解:

方法 功能
xxx --help 单个命令的用法
man 分类号 xxx 用法与函数详细介绍(最常用)
info 更加详细的内容(不常用)

man的9大分类(号):

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] // 内核例程

 

文件IO的常用函数(可通过man方法获取更多细节)
函数 功能
int open(const char *pathname, int flags, mode_t mode); 建立一条到文件或设备的访问路径,返回文件描述符。mode参数只有使用 O_CREAT 标志创建一个新文件时才有效。
ssize_t read(int fd, void *buf, size_t count); 通过文件描述符读字节到缓冲区(物理内存),并返回字节数,若文件为空,则返回-1
ssize_t write(int fd, const void *buf, size_t count); 通过文件描述符,从buf开始写count个字节到文件
int fstat(int fd, struct stat *statbuf); 返回文件的状态信息到statbuf结构体,通过结构体存储文件状态
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); 将磁盘文件映射到内存(虚拟内存),实际上会返回内存映射的起始地址

标准IO方式:

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

/**
 * argv[1]:新文件
 * argv[2]:旧文件
 **/
int main(int argc, char **argv)
{
	int fd_old, fd_new;
	char data[1024];   //1024个字节为一组
	int len;
	/*格式提醒*/
	if(argc != 3) {
		printf("Usage: %s <old-file> <new-file>\n", argv[0]);
		return -1;
	}
	/* 1.打开文件 */
	fd_old = open(argv[2], O_RDONLY);
	if(fd_old == -1) {
		printf("can not open file %s\n", argv[2]);  //打开文件失败
		return -1;
	}
	/* 2.创建新文件 */
	fd_new = open(argv[1], O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
	if(fd_new == -1) {
		printf("can not creat file %s\n", argv[1]);
		return -1;
	}
	/* 3.读取旧文件,写入新文件 */
	while((len = read(fd_old, data, 1024)) > 0) {
		if(write(fd_new, data, len) != len) {
			printf("can not wite file %s\n", argv[2]);
			return -1;
		}
	}
	/* 4.关闭文件 */
	close(fd_old);
	close(fd_new);

	return 0;
}

非通用IO(mmap):


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

int main(int argc, char **argv) {
	int fd_old, fd_new;
	struct stat stat;
	char *ptr;  //内存映射的起始地址
	/* 1.判断指令 */
	if(argc != 3) {
		printf("Usage: %s <new-file> <old-file>\n", argv[0]);
		return -1;
	}

	/* 2.打开旧文件 */
	fd_old = open(argv[2], O_RDONLY);
	if(fd_old == -1) {
		printf("can not open %s\n", argv[2]);
		return -1;
	}

	/* 3.获取文件长度 */
	if(fstat(fd_old, &stat) == -1) {   //获取文件信息
		printf("can not get stat of %s\n", argv[2]);
		return -1;
	}

	/* 4.映射旧文件 */
	ptr = mmap(NULL, stat.st_size, PROT_READ, MAP_SHARED, fd_old, 0);
	if(ptr == MAP_FAILED) {
		printf("can not mmap file %s\n", argv[2]);
		return -1;
	}

	/* 5.创建新文件 */
	fd_new = open(argv[1], O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
	if(fd_new == -1) {
		printf("can not creat file %s\n", argv[2]);
		return -1;
	}

	/* 6.写新文件 */
	if(write(fd_new, ptr, stat.st_size) != stat.st_size) {
		printf("can not write %s\n", argv[1]);
		return -1;
	}

	/* 7.关闭文件 */
	close(fd_new);
	close(fd_old);
	
	return 0;
}