当调用系统的write函数后为何数据还是会有丢失数据的风险

原文


在没接触操作系统原理之前一直不太明白这个问题,看一些io相关的资料上面写着的缓冲区概念也很模糊,现在才发现这些设计都是有它的原因的。写上层应用的我们是不关心读写函数操作系统的实现,比如要写一个数据库软件,用户通过你提供的操作界面或者api产生数据存储硬盘,有些环节是会导致数据丢失的,为什么会丢失,以及怎么合理使用缓冲区来提升应用整体的性能。

一般我们口中的软件是构建在操作系统之上的应用,这种应用所使用的接口归根结底都是对操作系统进行调用,存储数据时调用系统的write,读数据时调用read,系统实现的好坏直接影响到你的应用程序性能,比如在windows和linux下写10G的文件,前者要消耗10秒钟的时间,后者只需要9秒钟,这时我们可以判定linux下io实现相对的好一点,这只是打个比方这两个操作系统都是十分优秀的系统。你的读写命令发送给系统,系统收到命令要去调用磁盘驱动,操作系统本身就是一个很庞大的软件,就像一个管家一样。当你发送指令时它就会遵守命令去执行。假设一个例子,你是城堡的主人肚子饿了要安排管家从厨房拿来食物,厨房理解为磁盘存储着食物,吃饱需要10个面包,但是你命令管家一次拿来一个这个效率就很低下,吃完面包只能等待,映射到程序上就是读文件一次只读一点数据然后命令操作系统很多次。
解决这种低效的操作最简单的方式就是加入缓冲区,一次命令管家拿来两个面包,这样管家就会少跑5次。这种方式是应用层级的缓冲,其实操作系统很聪明,在你让它一次拿一个的时候它有可能会拿两个在你需要的时候直接递给你,这种是系统级的缓冲。系统之所以缓冲是因为来回的磁盘操作要耗费大量的时间,所以加了这么一个机制,当应用和系统都有缓冲应用的性能会得到比较大的提升。
写为什么会丢失? 读有缓冲区写也有,同样假设一个例子,你在农地收割农作物,然后把收割的作物递给管家让其存放到仓库,这个过程是产生数据,然后写入磁盘。但是你一次只递给管家一点点作物就让它跑一趟显然很浪费时间。管家的操作逻辑就像上面读操作那样。正常情况下这套逻辑没有问题,但是计算器是人造的始终达不到100%完美,总会有一些意外,比如停电,cpu停止工作,磁盘瞬间损坏,等等,当你的数据还在缓冲区中没有写入到磁盘,这份数据就丢失了,一般小网站停电什么的不会有什么影响,因为缓冲区刷新很快,慢是相对的。在一直处于高压的消息队列数据库场景会出现停电时数据丢失。

缓冲区设置多大合适,系统内核的缓冲区大小一般不需要动,除非你有充分的理由,应用层面的缓冲区也不是越大越好,jdk中BufferedInputStream 定义的是8k,应该是个通用的参数,以这个为基准再根据业务进行微调,应该能得到想要的参数。

总结
缓冲区的出现是对性能的妥协,因为cpu交互外部设备很耗时,而交互内存很快,从耗时多的地方一次性多取点,整体性能就会提升。整体有点乱想到哪写到哪q.q。不足以及错误的地方可在评论区指出,欢迎讨论交流。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用线程或者进程来实现同时调用一个函数并保证数据丢失。具体实现方法如下: 1. 线程实现 将函数封装成一个线程函数,在主线程中创建多个线程,每个线程调用函数并传入不同的参数,这样就可以同时调用一个函数并保证数据丢失。 例如: ```c #include <stdio.h> #include <pthread.h> void* write_data(void* arg) { int data = *(int*)arg; // 在这里写入数据到缓冲区 return NULL; } int main() { pthread_t threads[10]; int args[10]; for (int i = 0; i < 10; i++) { args[i] = i; pthread_create(&threads[i], NULL, write_data, &args[i]); } for (int i = 0; i < 10; i++) { pthread_join(threads[i], NULL); } // 从缓冲区逐个发送数据 // 调用read函数读取数据 return 0; } ``` 2. 进程实现 将函数封装成一个子进程,在主进程中创建多个子进程,每个子进程调用函数并传入不同的参数,这样就可以同时调用一个函数并保证数据丢失。 例如: ```c #include <stdio.h> #include <unistd.h> #include <sys/wait.h> void write_data(int data) { // 在这里写入数据到缓冲区 } int main() { pid_t pids[10]; for (int i = 0; i < 10; i++) { pids[i] = fork(); if (pids[i] == 0) { write_data(i); return 0; } } for (int i = 0; i < 10; i++) { waitpid(pids[i], NULL, 0); } // 从缓冲区逐个发送数据 // 调用read函数读取数据 return 0; } ``` 无论使用线程还是进程,都需要保证数据丢失。可以使用锁来保证多个线程或进程之间对共享资源的互斥访问,从而避免数据丢失的问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值