标准C的IO缓存类型
- 全缓存
- 要求填满整个缓存区后才能进行IO系统调用。对磁盘文件通常使用全缓存访问。
- 行缓存
- 设计一个终端时(例如标准输入和标准输出),使用行缓存
- 行缓存满自动输出
- 碰到换行符自动输出
- 无缓存
- 标准错误流stderr通常是不带缓存区的,这使得错误信息能够尽快显示出来
/****************************************************
* Description: example1 行缓存案例:
* 1、行缓存满自动输出,"--hello-- "行缓存未满,不会显示;
* 2、碰到换行符自动输出,"--hello--\n"有换行服,可以显示;
* 3、如果没加\n,执行到return 也会强制输出;
*****************************************************/
void example1()
{
printf("--hello--\n");
while (1) {
sleep(1);
}
}
文件IO系统调用
常用函数
以下都是内核提供的不带缓存的函数,他们不是ANSI C的组成部分,但是POSIX的组成部分
-
open() 打开文件
open的flags参数:
-
create() 创建文件
-
close() 关闭文件
-
read() 读取文件
-
write() 写入文件
/*************************************************** * Description: example2 文件读写案例: * 1、从file1中读取数据; * 2、将读取的数据写入file2 ****************************************************/ #define BUFFER_LEN 1024 void example2() { char buffer[BUFFER_LEN]; int fd1, fd2; const char* filepath1 = "./../IoTest/file1.txt"; const char* filepath2 = "./../IoTest/file2.txt"; fd1 = open(filepath1, O_RDONLY); if (fd1 == -1) { perror("Open file1 fail! err"); _exit(-1); } fd2 = open(filepath2, O_WRONLY | O_CREAT | O_TRUNC | O_APPEND); if (fd2 == -1) { perror("Open file2 fail! err"); _exit(-1); } ssize_t nreads; while (nreads = read(fd1, buffer, BUFFER_LEN)) { if (nreads < 0) { perror("Read file1 fail! err"); } else { if (write(fd2, buffer, nreads) == -1) { perror("Write file2 error! err"); } } } }
-
Iseek() 文件定位
文件描述符
-
对于内核而言,所有打开的文件都由文件描述符引用。文件描述符是一个非负整数。
-
在POSIX应用程序中,整数0、1、2被替换为STDIN_FILENO、STDOUT_FILENO、STDERR_FILENO,这些常数被定义在<unistd.h>
-
文件描述符的范围是0~OPEN_MAX,LINUX最大为1024
-
文件描述符与文件指针转换
- FILE *fdopen(int fd,const char *mode);//文件描述符转指针
- int fileno(FILE *stream);//文件指针转文件描述符
文件操作
文件IO的内核数据结构
- 文件描述表
- 文件描述符标志
- 文件表项指针
- 文件表项
- 文件状态标志(读、写、追加、同步和非阻塞等状态标志)
- 当前文件偏移量
- i节点表项指针
- 引用计数器
- i节点
- 文件类型和对该文件的操作函数指针
- 当前文件长度
- 文件所有者
- 文件所在的设备、文件访问权限
- 文件所在的设备、文件访问权限
- 指向文件数据在磁盘上所在位置的指针等
dup和dup2函数
//文件内容的复制
void copy(int fdin, int fdout)
{
char buffer[BUFFER_LEN];
ssize_t size;
lseek(fdin, 0L, SEEK_SET);
while ((size = read(fdin, buffer, BUFFER_LEN)) > 0) {
if (write(fdout, buffer, size) != size) {
perror("Write error! err");
_exit(-1);
}
}
}
/********************************************************
* Description:实现cat 命令的功能
* 并将重定向符号<和> 利用dup和dup2替换为+和-
* 例:IO_test + temp.txt(+ 为输入重定项)
* IO_test - temp.txt(- 为输出重定项)
* ******************************************************/
void example4(int argc, char* argv[])
{
int fd_in, fd_out;
int flag = 0;
for (int i = 1; i < argc; i++) {
if (!strcmp("+", argv[i])) {
fd_in = open(argv[++i], O_RDONLY);
if (fd_in < 0) {
perror("Open file error!");
_exit(-1);
}
//将标准输入重定向到文件,让STDIN_FILENO指向fd_in所代表的文件
if (dup2(fd_in, STDIN_FILENO) != STDIN_FILENO) {
perror("dup2 error!");
_exit(-1);
}
close(fd_in);
} else if (!strcmp("-", argv[i])) {
fd_out = open(argv[++i], O_WRONLY | O_CREAT | O_TRUNC, 0777);
if (fd_out < 0) {
perror("Open file error!");
_exit(-1);
}
//将标准输出重定向到文件
if (dup2(fd_out, STDOUT_FILENO) != STDOUT_FILENO) {
perror("dup2 error!");
_exit(-1);
}
close(fd_out);
} else {
flag = 1;
fd_in = open(argv[i], O_RDONLY);
if (fd_in < 0) {
perror("open error!");
_exit(-1);
}
//让STDIN_FILENO指向fd_in所代表的文件
if (dup2(fd_in, STDIN_FILENO) != STDIN_FILENO) {
perror("dup2 error!");
_exit(-1);
}
copy(STDIN_FILENO, STDOUT_FILENO);
}
if (!flag) {
copy(STDIN_FILENO, STDOUT_FILENO);
}
}
}
fcntl函数
//设置文件标志位
void set_fl(int fd, int flag)
{
//获得原来的文件状态标志
int val = fcntl(fd, F_GETFL);
//增加新的文件状态标志
val |= flag;
//重新设置文件状态标志(val为新的文件状态标志)
if (fcntl(fd, F_SETFL, val) < 0) {
perror("fcntl error");
}
}
//清除文件标志位
void clr_fl(int fd, int flag)
{
int val = fcntl(fd, F_GETFL);
//清除文件标志位
val &= ~flag;
if (fcntl(fd, F_SETFL, val) < 0) {
perror("fcntl error");
}
}
/***************************************************************
* Description:实现文件状态标志未的追加和清除
* 1、对file1在open时不设置O_APPEND,使用set_fl设置O_APPEND,
* 写入文件时应该在原有内容后面写入
* 2、对file2在open时设置O_APPEND,使用clr_fl清除O_APPEND,
* 写入文件时应该在原有内容前写入
***************************************************************/
void example5()
{
int fd1, fd2;
off_t current_location;
const char* filepath1 = "./../IoTest/file1.txt";
const char* filepath2 = "./../IoTest/file2.txt";
fd1 = open(filepath1, O_WRONLY);
if (fd1 == -1) {
perror("Open file1 fail! err");
_exit(-1);
}
set_fl(fd1, O_APPEND);
char buffer[] = "\nset_fl\n";
if (write(fd1, buffer, strlen(buffer) * sizeof(char)) == -1) {
perror("Write file1 error! err");
}
fd2 = open(filepath2, O_WRONLY | O_APPEND);
if (fd2 == -1) {
perror("Open file1 fail! err");
_exit(-1);
}
clr_fl(fd2, O_APPEND);
char buffer2[] = "\nclr_fl\n";
if (write(fd2, buffer2, strlen(buffer2) * sizeof(char)) == -1) {
perror("Write file2 error! err");
}
}
IO处理方式
-
IO处理的五种模型
-
阻塞IO模型
若所调用的I/O函数没有完成相关的功能就会使进程挂起,直到相关数据到达才会返回。如:终端、网络设备访问。
-
非阻塞模型
当请求的IO操作不能完成时,则不让进程休眠,而且返回一个错误。
/******************************************************* * Description:演示非阻塞IO * 1、不设置O_NONBLOCK时,会阻塞IO, * 等待从键盘输入后,才忘下执行 * 2、设置 O_NONBLOCK后,不阻塞IO,sleep执行后, * 直接报错,读取失败,因为没有阻塞IO,没有从键盘上输入内容 *******************************************************/ void example6() { char buffer[4096] = { '\0' }; ssize_t size = 0; //设置非阻塞IO set_fl(STDIN_FILENO, O_NONBLOCK); sleep(5); size = read(STDIN_FILENO, buffer, sizeof(buffer)); if (size < 0) { perror("read error"); _exit(-1); } else if (size == 0) { //读到文件尾部 printf("read finished!\n"); } else { if (write(STDIN_FILENO, buffer, size) != size) { perror("write error"); _exit(-1); } } }
-
IO多路转接模型
如果请求的IO操作阻塞,且他不是真正阻塞IO,而且让其中的一个函数等待,在这期间,IO还能进行其他操作,如select函数
-
信号驱动IO模型
在这种模型下,通过安装一个信号处理程序,系统可以自动捕获特定信号的到来,从而启动IO
-
异步IO模型
在这种模型下,当一个描述符已准备好,可以启动IO时,进程会通知内核。由内核进行后续处理,这种用法现在较少。
-
文中所涉及的例程,完整工程皆上传至下面链接中,需要自取
https://download.csdn.net/download/qq_45601625/85112008