1.Linux 应用编程中最基础的知识,即文件 I/O(Input、 Outout) , 文件 I/O 指的是对文件的输入/输出操作,说白了就是对文件的读写操作;
Linux 下一切皆文件,文件作为 Linux 系统设计思想的核心理念,在 Linux 系统下显得尤为重要,所以对文件的 I/O 操作既是基础也是最重要的部分。
2.一个通用的 IO 模型通常包括打开文件、读写文件、关闭文件这些基本操作, 主要涉及到 4 个函数: open()、 read()、 write()以及 close()
一个简单地文件 IO 示例
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(void)
{
char buff[1024];
int fd1, fd2;
int ret;
/* 打开源文件 src_file(只读方式) */
fd1 = open("./src_file", O_RDONLY);
if (-1 == fd1)
return fd1;
/* 打开目标文件 dest_file(只写方式) */
fd2 = open("./dest_file", O_WRONLY);
if (-1 == fd2) {
ret = fd2;
goto out1;
}
/* 读取源文件 1KB 数据到 buff 中 */
ret = read(fd1, buff, sizeof(buff));
if (-1 == ret)
goto out2;
/* 将 buff 中的数据写入目标文件 */
ret = write(fd2, buff, sizeof(buff));
if (-1 == ret)
goto out2;
ret = 0;
out2:
/* 关闭目标文件 */
close(fd2);
out1:
/* 关闭源文件 */
close(fd1);
return ret;
}
3.文件描述符
调用 open 函数会有一个返回值, 譬如示例代码 2.1.1 中的 fd1 和 fd2, 这是一个 int 类型的数据,在 open函数执行成功的情况下, 会返回一个非负整数, 该返回值就是一个文件描述符(file descriptor) , 这说明文件描述符是一个非负整数; 对于 Linux 内核而言,所有打开的文件都会通过文件描述符进行索引。
当调用 open 函数打开一个现有文件或创建一个新文件时,内核会向进程返回一个文件描述符, 用于指代被打开的文件,所有执行 IO 操作的系统调用都是通过文件描述符来索引到对应的文件,譬如示例代码
2.1.1 中,当调用 read/write 函数进行文件读写时,会将文件描述符传送给 read/write 函数, 所以在代码中,fb1 就是源文件 src_file 被打开时所对应的文件描述符, 而 fd2 则是目标文件 dest_file 被打开时所对应的文件描述符。
文件描述符是一种有限资源, 文件描述符是从 0 开始分配的,以此类推。
调用 open 函数打开文件的时候,分配的文件描述符一般都是从 3 开始,但是 0、 1、 2 这三个文件描述符已经默认被系统占用了,分别分配给了系统标准输入(0)、 标准输出(1)以及标准错误(2)。
3.1标准输入一般对应的是键盘,可以理解为 0 便是打开键盘对应的设备文件时所得到的文件描述符;标准输出一般指的是 LCD 显示器,可以理解为 1 便是打开 LCD 设备对应的设备文件时所得到的文件描符;而标准错误一般指的也是 LCD 显示器。
4.open 打开文件
man 2 open #查看 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: 字符串类型,用于标识需要打开或创建的文件,可以包含路径(绝对路径或相对路径) 信息,譬如: "./src_file"(当前目录下的 src_file 文件)。
flags: 调用 open 函数时需要提供的标志, 包括文件访问模式标志以及其它文件相关标志,这些标志使用宏定义进行描述,都是常量, open 函数提供了非常多的标志,我们传入 flags 参数时既可以单独使用某一个标志,也可以通过位或运算(|) 将多个标志进行组合。
O_RDONLY 以只读方式打开文件
O_WRONLY 以只写方式打开文件
O_RDWR 以可读可写方式打开文件
mode: 此参数用于指定新建文件的访问权限,只有当 flags 参数中包含 O_CREAT 或 O_TMPFILE 标志时才有效(O_TMPFILE 标志用于创建一个临时文件)。
如果需要修改文件权限,可通过 chmod 命令对文件权限进行修改;
使用"ls -l"命令来查看到文件所对应的权限。
open 函数使用示例
(1)使用 open 函数打开一个已经存在的文件(例如当前目录下的 app.c 文件),使用只读方式打开:
int fd = open("./app.c", O_RDONLY)
if (-1 == fd)
return fd;
(2)使用 open 函数打开一个已经存在的文件(例如当前目录下的 app.c 文件),使用可读可写方式打开:
int fd = open("./app.c", O_RDWR)
if (-1 == fd)
return fd;
(3)使用 open 函数打开一个指定的文件(譬如/home/dengtao/hello) , 使用可读可写方式,如果该文件是一个符号链接文件,则不对其进行解引用,直接返回错误:
int fd = open("/home/dengtao/hello", O_RDWR | O_NOFOLLOW);
if (-1 == fd)
return fd;
(4)使用 open 函数打开一个指定的文件(譬如/home/dengtao/hello),如果该文件不存在则创建该文件,创建该文件时,将文件权限设置如下:
文件所属者拥有读、写、执行权限;
同组用户与其他用户只有读权限。
使用可读可写方式打开:
int fd = open("/home/dengtao/hello", O_RDWR | O_CREAT, S_IRWXU | S_IRGRP | S_IROTH);
if (-1 == fd)
return fd;
5.write 写文件 --- 调用 write 函数可向打开的文件写入数据。
man 2 write
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
fd: 文件描述符。需要将进行写操作的文件所对应的文件描述符传递给 write 函数。
buf: 指定写入数据对应的缓冲区。
count: 指定写入的字节数。
返回值: 如果成功将返回写入的字节数(0 表示未写入任何字节),如果此数字小于 count 参数,这不
是错误,譬如磁盘空间已满,可能会发生这种情况;如果写入出错,则返回-1。
6.read 读文件 --- 可从打开的文件中读取数据
man 2 read
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
fd: 文件描述符。与 write 函数的 fd 参数意义相同。
buf: 指定用于存储读取数据的缓冲区。
count: 指定需要读取的字节数。
返回值: 如果读取成功将返回读取到的字节数。
7.close 关闭文件 --- 关闭一个已经打开的文件
man 2 close
#include <unistd.h>
int close(int fd);
fd: 文件描述符,需要关闭的文件所对应的文件描述符。
返回值: 如果成功返回 0,如果失败则返回-1。
注:在我们的程序中打开了文件, 如果程序终止退出时没有关闭打开的文件,那么内核会自动将程序中打开的文件关闭。
7.