网站域名注册需要什么手续WordPress主题 Slhao
网站域名注册需要什么手续,WordPress主题 Slhao,深圳搜索引擎优化推广便宜,wordpress 坐标目录
1 I/O 缓冲简介
1.1 什么是I/O 缓冲
1.2 I/O 缓冲的目的
2 文件 I/O 的内核缓冲
3 刷新文件 I/O 的内核缓冲区
3.1 什么是刷新文件 I/O 的内核缓冲区
3.2 控制文件 I/O 内核缓冲的系统调用函数
3.3 示例程序
4 控制文件 I/O 内核缓冲的标志
4.1 O_DSYNC 和 O_SY…目录
1 I/O 缓冲简介
1.1 什么是I/O 缓冲
1.2 I/O 缓冲的目的
2 文件 I/O 的内核缓冲
3 刷新文件 I/O 的内核缓冲区
3.1 什么是刷新文件 I/O 的内核缓冲区
3.2 控制文件 I/O 内核缓冲的系统调用函数
3.3 示例程序
4 控制文件 I/O 内核缓冲的标志
4.1 O_DSYNC 和 O_SYNC标志简介
4.2 示例程序
5 直接 I/O绕过内核缓冲
5.1 绕过内核缓冲含义
5.2 为什么不都使用直接 I/O
5.3 直接 I/O 的对齐限制
6 stdio 缓冲
6.1 stdio 缓冲简介
6.2 标准 I/O 的 stdio 缓冲函数
6.3 标准输出 printf()的行缓冲模式测试
6.4 将标准输出配置为无缓冲模式测试
6.5 fflush()刷新 stdio 缓冲区
6.6 关闭与退出时刷新 stdio 缓冲区 1 I/O 缓冲简介
1.1 什么是I/O 缓冲
出于速度和效率的考虑系统 I/O 调用即文件 I/O和标准 C 语言库 I/O 函数即标准 I/O 函数在操作磁盘文件时会对数据进行缓冲。I/O缓冲指的是在数据传输过程中用于临时存储数据的内存区域。它允许程序先将数据写入或从内存中的一个缓冲区读取然后再与外部设备如硬盘、网络或控制台进行数据交换。
1.2 I/O 缓冲的目的
提高效率减少实际的I/O操作次数因为可以积累一定量的数据后再执行一次较大的传输而不是每次只传输少量数据。
减少阻塞在缓冲的帮助下程序可以继续执行其他任务而不必等待每次I/O操作完成。
数据整合在输出时可以将多次小的数据写入合并为一次较大的写入操作。 2 文件 I/O 的内核缓冲
read()和 write()系统调用在进行文件读写操作的时候并不会直接访问磁盘设备而是仅仅在用户空间缓冲区和内核缓冲区kernel buffer cache之间复制数据。譬如调用 write()函数将 5 个字节数据从用户空间内存拷贝到内核空间的缓冲区中
write(fd, Hello, 5); //写入 5 个字节数据
调用 write()后仅仅只是将这 5 个字节数据拷贝到了内核空间的缓冲区中拷贝完成之后函数就返回了在后面的某个时刻内核会将其缓冲区中的数据写入刷新到磁盘设备中所以由此可知系调用 write()与磁盘操作并不是同步的 write()函数并不会等待数据真正写入到磁盘之后再返回。如果在此期间 其它进程调用 read()函数读取该文件的这几个字节数据那么内核将自动从缓冲区中读取这几个字节数据返回给应用程序。
与此同理对于读文件而言亦是如此内核会从磁盘设备中读取文件的数据并存储到内核的缓冲区中当调用 read()函数读取数据时 read()调用将从内核缓冲区中读取数据直至把缓冲区中的数据读完这时内核会将文件的下一段内容读入到内核缓冲区中进行缓存。
文件 I/O 的内核缓冲区自然是越大越好 Linux 内核本身对内核缓冲区的大小没有固定上限。内核会分配尽可能多的内核来作为文件 I/O 的内核缓冲区但受限于物理内存的总量如果系统可用的物理内存越多那自然对应的内核缓冲区也就越大操作越大的文件也要依赖于更大空间的内核缓冲。 3 刷新文件 I/O 的内核缓冲区
3.1 什么是刷新文件 I/O 的内核缓冲区
刷新文件 I/O 的内核缓冲区就是强制将文件 I/O 内核缓冲区中缓存的数据写入刷新到磁盘设备中。对于某些应用场景来说可能是很有必要的例如应用程序在进行某操作之前 必须要确保前面步骤调用 write()写入到文件的数据已经真正写入到了磁盘中 诸如一些数据库的日志进程。
以Ubuntu系统中的文件传输为例当用户将文件复制到U盘后拔除U盘前通常需要执行sync命令。sync命令的作用是确保所有文件I/O操作已经完成内核缓冲区中的数据已被强制写入到U盘。如果省略此步骤直接拔出U盘可能会导致数据丢失或文件损坏因为缓冲区中的数据可能尚未完全写入到U盘。
3.2 控制文件 I/O 内核缓冲的系统调用函数
Linux 中提供了一些系统调用可用于控制文件 I/O 内核缓冲包括系统调用 sync()、 syncfs()、 fsync()以及 fdatasync()。
#include unistd.hvoid sync(void);
int syncfs(int fd);
int fsync(int fd);
int fdatasync(int fd);
sync()将所有未写的或延迟写入的缓冲数据发送到磁盘。
用法通常在系统范围内刷新所有的文件系统缓冲区。
syncfs()同步指定文件描述符的文件系统缓冲区到磁盘。
用法syncfs() 调用只影响特定的文件系统而不是整个系统。
fsync()强制将指定文件描述符的所有未写入数据同步到磁盘。
用法fsync() 通常用于确保对某个特定文件的所有更改都已持久化。
fdatasync()类似于 fsync()但它只同步文件的数据部分不包括元数据如修改时间。
用法当只需要同步文件内容而不需要更新文件属性时使用 fdatasync()。
3.3 示例程序
下面是程序示例演示了如何使用 fsync() 和 fdatasync() 函数来确保文件数据被同步到磁盘
#include stdio.h
#include unistd.h
#include fcntl.h
#include string.hint main()
{// 打开文件用于写入int fd open(example.txt, O_WRONLY | O_CREAT | O_TRUNC, 0644);if (fd -1) {perror(Failed to open file);return 1;}// 写入数据到文件const char *data Important data to sync to disk;if (write(fd, data, strlen(data)) -1) {perror(Failed to write to file);close(fd);return 1;}// 使用 fsync() 同步文件的所有数据和元数据到磁盘if (fsync(fd) -1) {perror(fsync failed);close(fd);return 1;}printf(File and metadata synced to disk using fsync.\n);// 再次写入数据到文件if (write(fd, data, strlen(data)) -1) {perror(Failed to write to file);close(fd);return 1;}// 使用 fdatasync() 只同步文件的数据部分到磁盘if (fdatasync(fd) -1) {perror(fdatasync failed);close(fd);return 1;}printf(Data synced to disk using fdatasync.\n);// 关闭文件描述符close(fd);return 0;
}
程序首先打开或创建一个名为 example.txt 的文件用于写入然后向其中写入一段数据。接着使用 fsync() 函数确保这些数据和文件的元数据被同步到磁盘以保证数据的持久性。之后程序再次写入相同的数据并使用 fdatasync() 函数仅同步数据部分到磁盘而不包括元数据。最后关闭文件描述符。程序运行结果如下 4 控制文件 I/O 内核缓冲的标志
4.1 O_DSYNC 和 O_SYNC标志简介
O_DSYNC 和 O_SYNC 是两个用于控制文件I/O操作同步性的标志定义了文件操作的同步写入行为。可以与 open() 函数一起使用以改变文件的默认写入行为。
O_SYNC
当使用 O_SYNC 标志打开文件时所有对该文件的写操作都将被执行为同步写入。意味着每次写操作write()完成后系统都会确保数据被实际写入到磁盘而不仅仅是内核缓冲区但可能会降低性能因为每次写操作都需要等待磁盘I/O完成。
O_DSYNC
O_DSYNC 标志用于打开文件使得所有写操作都执行为同步的就像 O_SYNC 一样。但是与 O_SYNC 不同的是O_DSYNC 只保证数据的同步性而不保证文件元数据如修改时间的同步更新。意味着使用 O_DSYNC 标志时文件内容会及时写入磁盘但文件属性可能不会立即更新。
4.2 示例程序
下面的示例演示如使用 O_SYNC 和 O_DSYNC 标志打开文件并进行写操作。
#include stdio.h
#include fcntl.h
#include unistd.h
#include string.hint main() {const char *filename sync_example.txt;const char *data Data that needs to be synced immediately;// 使用 O_SYNC 标志打开文件每次写操作后都会同步数据和元数据int sync_fd open(filename, O_WRONLY | O_CREAT | O_SYNC, 0644);if (sync_fd -1) {perror(Failed to open file with O_SYNC);return 1;}write(sync_fd, data, strlen(data)); // 写操作会立即同步到磁盘close(sync_fd);// 使用 O_DSYNC 标志打开文件每次写操作后都会同步数据但元数据可能不会int dsync_fd open(filename, O_WRONLY | O_CREAT | O_DSYNC, 0644);if (dsync_fd -1) {perror(Failed to open file with O_DSYNC);return 1;}write(dsync_fd, data, strlen(data)); // 数据会立即同步到磁盘元数据可能不会close(dsync_fd);return 0;
}
使用 O_SYNC 标志打开文件 sync_example.txt 进行写操作这确保了每次写操作后数据和文件的元数据都会立即同步到磁盘。使用 O_DSYNC 标志再次打开同一个文件进行写操作这确保了数据会立即同步但文件的元数据如修改时间可能不会立即更新到磁盘。 5 直接 I/O绕过内核缓冲
5.1 绕过内核缓冲含义
前面的内容提到数据传输过程中会临时存储数据到内存缓冲区为了保持数据同步会利用相关函数和标志将数据立即更新到磁盘。那么有没有方法省略中间的缓冲步骤直接将数据传递到文件或磁盘设备
从Linux内核2.4版本起Linux系统就支持应用程序执行直接I/O操作即绕过内核缓冲区直接在用户空间和磁盘之间传输数据。
5.2 为什么不都使用直接 I/O
在有些情况下这种操作通常是很有必要的例如某应用程序的作用是测试磁盘设备的读写率 那么在这种应用需要下我们就需要保证 read/write 操作是直接访问磁盘设备而不经过内核缓冲如果不能得到这样的保证必然会导致测试结果出现比较大的误差。
在特定情况下如磁盘读写性能测试直接I/O是必要的以确保读写操作不经过内核缓冲从而获得准确的测试结果。对于大多数应用程序而言使用直接 I/O 可能会大大降低性能这是因为为了提高 I/O 性能内核针对文件 I/O 内核缓冲区做了不少的优化譬如包括按顺序预读取、在成簇磁盘块上执行 I/O、允许访问同一文件的多个进程共享高速缓存的缓冲区。如果应用程序使用直接 I/O 方式 将无法享受到这些优化措施所带来的性能上的提升直接 I/O 只在一些特定的需求场合譬如磁盘速率测试工具、数据库系统等。
要实现直接I/O可以在打开文件时通过open()函数并带上O_DIRECT标志来指定。例如
int fd open(file.dat, O_WRONLY | O_CREAT | O_DIRECT, 0644);
if (fd 0) {perror(open);// 处理错误
}
// 使用write()等函数进行I/O操作
// ...
close(fd);
示例中O_DIRECT标志被添加到open()函数的参数中操作系统进行直接I/O操作。
5.3 直接 I/O 的对齐限制
因为直接 I/O 涉及到对磁盘设备的直接访问所以在执行直接 I/O 时必须要遵守以下三个对齐限制要求
应用程序中用于存放数据的缓冲区其内存起始地址必须以块大小的整数倍进行对齐写文件时文件的位置偏移量必须是块大小的整数倍写入到文件的数据大小必须是块大小的整数倍。
如果不满足以上任何一个要求调用 write()均为以错误返回 Invalid argument。以上所说的块大小指的是磁盘设备的物理块大小block size 常见的块大小包括 512 字节、 1024 字节、 2048 以及 4096 字节。
可以如下命令进行查看磁盘分区的块大小 tune2fs -l /dev/sda | grep Block size -l 后面指定了需要查看的磁盘分区可以使用 df -h 命令查看 Ubuntu 系统的根文件系统所挂载的磁盘分区 运行得到块大小 6 stdio 缓冲
6.1 stdio 缓冲简介
标准 I/Ofopen、 fread、 fwrite、 fclose、 fseek 等是 C 语言标准库函数 而文件 I/Oopen、 read、 write、close、 lseek 等是系统调用虽然标准 I/O 是在文件 I/O 基础上进行封装而实现 但在效率、性能上标准 I/O 要优于文件 I/O其原因在于标准 I/O 实现维护了自己的缓冲区 我们把这个缓冲区称为 stdio 缓冲区。
前面提到了文件 I/O 内核缓冲这是由内核维护的缓冲区而标准 I/O 所维护的 stdio 缓冲是用户空间的缓冲区当应用程序中通过标准 I/O 操作磁盘文件时为了减少调用系统调用的次数标准 I/O 函数会将用户写入或读取文件的数据缓存在 stdio 缓冲区然后再一次性将 stdio 缓冲区中缓存的数据通过调用系统调用 I/O文件 I/O写入到文件 I/O 内核缓冲区或者拷贝到应用程序的 buf 中。通过这样的优化操作当操作磁盘文件时在用户空间缓存大块数据以减少调用系统调用的次数使得效率、性能得到优化。
6.2 标准 I/O 的 stdio 缓冲函数
C 语言提供了一些库函数可用于对标准 I/O 的 stdio 缓冲区进行相关的一些设置 包括 setbuf()、setbuffer()以及 setvbuf()。
setvbuf() 函数设置缓冲区。
int setvbuf(FILE *stream, char *buf, int mode, size_t size);
stream指向 FILE 结构的指针表示要设置缓冲区的流。buf用户提供的缓冲区或 NULL 以使用 stdio 的默认缓冲区。mode缓冲区模式可以是 _IOFBF全缓冲、_IONBF无缓冲或 _IOLBF行缓冲。size缓冲区的大小。返回值 成功返回 0失败将返回一个非 0 值并且会设置 errno 来指示错误原因。
无缓冲_IONBF在无缓冲模式下每次写操作都会直接发送到输出设备不经过任何内部缓冲区。行缓冲_IOLBF行缓冲模式下输出通常在遇到换行字符\n时被刷新到输出设备。全缓冲_IOFBF全缓冲模式下输出会被存储在缓冲区中直到缓冲区满或者通过显式调用刷新缓冲区的函数如fflush()时才会将数据发送到输出设备。
setbuf() 函数函数允许指定一个自定义的缓冲区如果提供了有效的 buf 指针将使用这个缓冲区进行数据缓冲。
void setbuf(FILE *stream, char *buf);
stream指向 FILE 结构的指针表示要设置缓冲区的输出流如 stdout 或文件流。buf指向字符数组的指针用作流的缓冲区。如果 buf 为 NULL则流变为无缓冲。
setbuffer() 函数setbuffer()函数类似于 setbuf()但允许调用者指定 buf 缓冲区的大小
void setbuffer(FILE *stream, char *buf, size_t size);
stream指向 FILE 结构的指针表示要设置缓冲区的流。buf用户提供的缓冲区。size缓冲区的大小。
6.3 标准输出 printf()的行缓冲模式测试
我们先看看下面这个简单地示例代码调用了 printf()函数区别在于第二个 printf()没有输出换行符。
#include stdio.h
#include stdlib.h
#include unistd.hint main(void)
{printf(Hello World 1\n);printf(Hello World 2);for ( ; ; )sleep(1);
}
运行之后可以发现只有第一个 printf()打印的信息显示出来了第二个并没有显示出来这是为什么呢 这就是 stdio 缓冲的问题前面提到了标准输出默认采用的是行缓冲模式 printf()输出的字符串写入到了标准输出的 stdio 缓冲区中只有输出换行符时不考虑缓冲区填满的情况 才会将这一行数据刷入到内核缓冲区。因为第一个 printf()包含了换行符所以已经刷入了内核缓冲区而第二个 printf 并没有包含换行符所以第二个 printf 输出的Hello World!还缓存在 stdio 缓冲区中。 scanf()函数的行缓冲模式格式化输入 scanf()函数通过键盘输入数据只有在按下回车键换行符键时程序才会接着往下执行因为标准输入默认也是采用了行缓冲模式。 6.4 将标准输出配置为无缓冲模式测试
修改上面的代码使标准输出变成无缓冲模式修改后代码如下所示
#include stdio.h
#include stdlib.h
#include unistd.h
int main(void)
{/* 将标准输出设置为无缓冲模式 */if (setvbuf(stdout, NULL, _IONBF, 0)) {perror(setvbuf error);exit(0);}printf(Hello World 1\n);printf(Hello World 2);for ( ; ; )sleep(1);
}
在使用 printf()之前调用 setvbuf()函数将标准输出的 stdio 缓冲设置为无缓冲模式可以发现该程序能够成功输出两个“Hello World!”。运行结果如下 6.5 fflush()刷新 stdio 缓冲区
无论我们采取何种缓冲模式在任何时候都可以使用库函数 fflush()来强制刷新stdio 缓冲区 该函数会刷新指定文件的 stdio 输出缓冲区此函数原型如下所示
int fflush(FILE *stream);
参数 stream 指定需要进行强制刷新的文件如果该参数设置为 NULL则表示刷新所有的 stdio 缓冲区。返回值函数调用成功返回 0否则将返回-1并设置 errno 以指示错误原因。
进一步修改上面的代码在第二个 printf 后面调用 fflush()函数修改后代码如下所示
#include stdio.h
#include stdlib.h
#include unistd.hint main(void)
{printf(Hello World 1\n);printf(Hello World 2);fflush(stdout); //刷新标准输出 stdio 缓冲区for ( ; ; )sleep(1);
}
可以看到打印了两次“Hello World” 这就是 fflush()的作用了强制刷新 stdio 缓冲区。运行结果如下 6.6 关闭与退出时刷新 stdio 缓冲区
当文件关闭时、程序退出时也会自动刷新 stdio 缓冲区。修改上面的代码在调用第二个 printf 函数后关闭标准输出如下所示
// 关闭文件时刷新 stdio 缓冲区
#include stdio.h
#include stdlib.h
#include unistd.hint main(void)
{printf(Hello World!\n);printf(Hello World!);fclose(stdout); //关闭标准输出for ( ; ; )sleep(1);
}
程序退出时也会自动刷新 stdio 缓冲区修改上面的代码去掉 for 死循环让程序结束修改完之后如下所示
// 程序结束时刷新 stdio 缓冲区
#include stdio.h
#include stdlib.h
#include unistd.hint main(void)
{printf(Hello World!\n);printf(Hello World!);
}
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/92557.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!